git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH v3] repack: enable bitmaps by default on bare repos

From
Derrick Stolee <stolee@gmail.com>
Date
May 23, 2019, 12:53 UTC
Message-ID
<1e0d216b-5e68-faff-883c-778a04f55bde@gmail.com>
In-Reply-To
<20190523113031.GA17448@sigill.intra.peff.net>
On 5/23/2019 7:30 AM, Jeff King wrote:
Show 26 quoted lines
> On Wed, Apr 10, 2019 at 06:57:21PM -0400, Jeff King wrote:
> 
>>   2. The answer we get from a bitmap versus a regular traversal are not
>>      apples-to-apples equivalent. The regular traversal walks down to
>>      the UNINTERESTING commits, marks the boundaries trees and blobs as
>>      UNINTERESTING, and then adds in all the interesting trees and blobs
>>      minus the UNINTERESTING parts. So it can sometimes give the wrong
>>      answer, claiming something is interesting when it is not.
>>
>>      Whereas with bitmaps we fill in the trees and blobs as we walk, and
>>      you get the true answer. But it means we may open up a lot more
>>      trees than the equivalent traversal would.
>>
>>      So one thing I've never really experimented with (and indeed, never
>>      really thought about until writing this email) is that the bitmaps
>>      could try to do that looser style of traversal, knowing that we
>>      might err on the side of calling things interesting in a few cases.
>>      But hopefully spending a lot less time opening trees.
>>
>>      I'm not even 100% sure what that would look like in code, but just
>>      thinking about it from a high level, I don't there's a particular
>>      mathematical reason it couldn't work.
> 
> I spent a while thinking and experimenting with this tonight. The result
> is the patch below. Ævar, do you still have a copy of the repo that
> misbehaved? I'd be curious to see how it fares.

This patch caught my attention, and I think I understand some of the issues at hand. I'm not well-versed specifically in Git's bitmap implementation. The fundamental ideas are there, but my perspective is biased by my own independent bitmap implementation for Azure Repos. What worked there may not work at all here.

> Finding the right trees to explore is a little tricky with bitmaps.  In
> a normal traversal, we consider the "edges" to be worth exploring.
> I.e., the places where an UNINTERESTING commit is the parent of an
> interesting one.
This is the "commit frontier" which I bring up again below.
Show 10 quoted lines
> But with bitmaps, we don't have that information in the same way,
> because we're trying to avoid walking the commits in the first place. So
> e.g., in a history like this:
> 
>   A--B--C
>       \
>        D
> 
> Let's imagine we're computing "C..D", and that D has a bitmap on disk
> but C does not.

(As I read your discussion below, I'm confused. For "C..D", C is a have and D is a want. We should explore all the haves _first_, then walk the wants, right?)

> When we visit D, we'll see that it has a bitmap, fill in
> the results in our cumulative "want" bitmap, and then stop walking its
> parents (because we know everything they could tell us already).

I may be misreading something, but we would construct _a_ bitmap for C by walking its reachable objects until hitting a bitmap we know about. Perhaps A or B have a bitmap to start the construction. If we never construct a bitmap for C, then we don't know what to remove from the "D" bitmap to show the difference.

If "C" is not even in the bitmap pack, then we don't use bitmaps for this calculation because of this condition:

        /*
         * if we have a HAVES list, but none of those haves is contained
         * in the packfile that has a bitmap, we don't have anything to
         * optimize here
         */
        if (haves && !in_bitmapped_pack(bitmap_git, haves))
                goto cleanup;
Show 31 quoted lines
> Then we visit C. It's not bitmapped, so we keep walking. Unlike a normal
> traversal, we can't stop walking even though there are only
> UNINTERESTING commits left, because we have to fill the complete bitmap,
> including A and B (and you can imagine there might be thousands of
> ancestors of A, too). The reason is that we skipped adding ancestors of
> D when we saw its bitmap, so no still_interesting() cutoff will work
> there.
> 
> But how do we know that it's worth looking into the tree of "B"? If we
> assume we visit commits before their ancestors (because of the commit
> timestamp ordering), then we'd see that "B" is in the "want" bitmap
> already, which makes it worth visiting its tree.
> 
> Unfortunately we'd then continue on to "A", and it would _also_ look
> interesting, because it's also in the "want" bitmap. We don't have an
> easy way of knowing that "A" is basically already covered by "B", and is
> therefore not worth pursuing. In this example, we could pass down a bit
> from B to A as we traverse. But you can imagine another interesting
> commit branched from A, which _would_ make A's tree worth considering.
> 
> Fundamentally, as soon as we load a bitmap and stop traversing, we lose
> all information about the graph structure.
> 
> So the patch below just looks at every tree that might be worth
> exploring (so both "A" and "B" here, but not "C"). That shouldn't be any
> _worse_ than what the current code does (because it looks at all the
> trees). It appears to behave correctly, at least so far as passing the
> test suite. Running p5310 and p5311 against git.git and linux.git, it
> seems to perform worse. I'm not quite sure why that is (i.e., if I
> screwed up something in the implementation, or if the algorithm is
> fundamentally worse).

I'm of the opinion that the old method was better. It followed a very clear three-step process:

 1. Compute the "haves" bitmap.
 2. Compute the "wants" bitmap, but don't explore into the "haves" bitmap.
 3. Subtract the "haves" bitmap from the "wants" (in case we added bits to
    the wants before they were in the haves).

But there is hope in your idea to improve some cases. As noted, we give up if all of the haves are not in the bitmapped pack. By starting with a commit walk, we can find the "commit frontier," which is the set of commits that are explored by paint_down_to_common(). In this case, the set {B, C, D}.

After walking commits and finding a set of UNINTERESTING commits, we could look for any UNINTERESTING commits that have bitmaps (or simply are in the bitmapped pack) and use those to see our "haves" bitmap. So, if B has a bitmap, but C is too new for the bitmap, then we can still create the haves based on B and then take a bitmap diff "D minus B".

In fact, using the "merge base set" for the diff reflects what the non-bitmap algorithm does in this case. It ignores C and only explores B.

I learned a lot looking at both versions of this method side-by-side. Thanks!
-Stolee
Previous: Jeff KingNext: Jeff King
Message 36 of 57 in “some prune optimizations”
  1. 0/3 some prune optimizationsJeff King, Feb 14, 2019
  2. 1/3 prune: lazily perform reachability traversalJeff King, Feb 14, 2019
  3. Eric SunshineFeb 14, 2019
  4. Jeff KingFeb 14, 2019
  5. 2/3 prune: use bitmaps for reachability traversalJeff King, Feb 14, 2019
  6. bitmaps by default? [was: prune: use bitmaps for reachability traversal]Eric Wong, Mar 9, 2019
  7. Jeff KingMar 10, 2019
  8. repack: enable bitmaps by default on bare reposEric Wong, Mar 12, 2019
  9. Ævar Arnfjörð BjarmasonMar 12, 2019
  10. Jeff KingMar 12, 2019
  11. Jeff KingMar 12, 2019
  12. Eric WongMar 13, 2019
  13. Jeff KingMar 13, 2019
  14. repack: enable bitmaps by default on bare reposEric Wong, Mar 14, 2019
  15. Jeff KingMar 14, 2019
  16. 0/2 enable bitmap hash-cache by defaultJeff King, Mar 15, 2019
  17. 1/2 t5310: correctly remove bitmaps for jgit testJeff King, Mar 15, 2019
  18. SZEDER GáborMar 15, 2019
  19. Jeff KingMar 15, 2019
  20. 2/2 pack-objects: default to writing bitmap hash-cacheJeff King, Mar 15, 2019
  21. Ævar Arnfjörð BjarmasonApr 9, 2019
  22. Jeff KingApr 10, 2019
  23. Junio C HamanoApr 25, 2019
  24. Jeff KingMay 4, 2019
  25. Ævar Arnfjörð BjarmasonMay 4, 2019
  26. SZEDER GáborMay 4, 2019
  27. Ævar Arnfjörð BjarmasonMay 8, 2019
  28. Junio C HamanoMay 9, 2019
  29. Jeff KingMay 7, 2019
  30. Ævar Arnfjörð BjarmasonMay 7, 2019
  31. Jeff KingMay 8, 2019
  32. Derrick StoleeMay 8, 2019
  33. Ævar Arnfjörð BjarmasonMay 8, 2019
  34. Jeff KingMay 8, 2019
  35. Jeff KingMay 23, 2019
  36. Derrick StoleeMay 23, 2019
  37. Jeff KingMay 24, 2019
  38. Derrick StoleeMay 24, 2019
  39. Ævar Arnfjörð BjarmasonMay 23, 2019
  40. Jeff KingMay 24, 2019
  41. Ævar Arnfjörð BjarmasonMay 24, 2019
  42. Jeff KingMay 24, 2019
  43. Ævar Arnfjörð BjarmasonMay 24, 2019
  44. SZEDER GáborMay 24, 2019
  45. Ævar Arnfjörð BjarmasonMay 24, 2019
  46. SZEDER GáborMay 24, 2019
  47. Ævar Arnfjörð BjarmasonMay 24, 2019
  48. SZEDER GáborMay 24, 2019
  49. Ævar Arnfjörð BjarmasonMay 24, 2019
  50. pack-bitmap: look for an uninteresting bitmapDerrick Stolee, May 24, 2019
  51. Derrick StoleeApr 15, 2019
  52. Jeff KingApr 18, 2019
  53. t5304: add a test for pruning with bitmapsJeff King, Apr 18, 2019
  54. Derrick StoleeApr 20, 2019
  55. Jeff KingApr 20, 2019
  56. Derrick StoleeApr 20, 2019
  57. 3/3 prune: check SEEN flag for reachabilityJeff King, Feb 14, 2019

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.