export: pack loose objects
We do this in separate threads, by default up to the core count available or four, as a sensible limit.
There are two possible ways that we could pack objects:
- Gather up the objects in-memory, and write them as a single pack.
- Write the objects as loose, and then pack them later.
We want to pack as many objects as possible into a single pack, as that yields the best compression. However, the first option has two main downsides: First of all, we have to have the objects in memory, and for long-running conversions, this could be a lot of objects. Second, hg-git currently assumes that it can just look up the tree in the Git repository. Sure, we could cache that, but that's extra complexity.
Instead, we go for the second option. Packing is a rather slow operation, as it involves actually compressing the objects, and Dulwich doesn't seem particularly fast at that. However, as a mostly I/O and compression bound operation, it mostly sidesteps Python's infamous Global Interpreter Lock, making it amenable to normal threading. While this is also somewhat complicated, it's complexity that we can isolate into a single module, avoiding further cluttering in the already complicated code we have elsewhere.
The current implementation uses worker threads that regularly check whether the export is complete. Some systems have a lot of cores, and the conversion will always be bound on the single-thread actual conversion anyway. Hence, I limited the default to using just three threads for packing; packing shouldn't take more than about three times as long as exporting, but we'll see…
Closes: #429 (closed)