From: Taylor Blau Date: Wed, 22 Jan 2025 22:17:36 GMT Subject: Re: [PATCH v3 2/8] pack-objects: add --name-hash-version option Message-ID: In-Reply-To: On Fri, Dec 20, 2024 at 05:19:48PM +0000, Derrick Stolee via GitGitGadget wrote: > From: Derrick Stolee > > The previous change introduced a new pack_name_hash_v2() function that > intends to satisfy much of the hash locality features of the existing > pack_name_hash() function while also distinguishing paths with similar > final components of their paths. > > This change adds a new --name-hash-version option for 'git pack-objects' > to allow users to select their preferred function version. This use of > an integer version allows for future expansion and a direct way to later > store a name hash version in the .bitmap format. > > For now, let's consider how effective this mechanism is when repacking a > repository with different name hash versions. Specifically, we will > execute 'git pack-objects' the same way a 'git repack -adf' process > would, except we include --name-hash-version= for testing. > > On the Git repository, we do not expect much difference. All path names > are short. This is backed by our results: > > | Stage | Pack Size | Repack Time | > |-----------------------|-----------|-------------| > | After clone | 260 MB | N/A | > | --name-hash-version=1 | 127 MB | 129s | > | --name-hash-version=2 | 127 MB | 112s | > > This example demonstrates how there is some natural overhead coming from > the cloned copy because the server is hosting many forks and has not > optimized for exactly this set of reachable objects. But the full repack > has similar characteristics for both versions. > > Let's consider some repositories that are hitting too many collisions > with version 1. First, let's explore the kinds of paths that are > commonly causing these collisions: > > * "/CHANGELOG.json" is 15 characters, and is created by the beachball > [1] tool. Only the final character of the parent directory can > differentiate different versions of this file, but also only the two > most-significant digits. If that character is a letter, then this is > always a collision. Similar issues occur with the similar > "/CHANGELOG.md" path, though there is more opportunity for > differences In the parent directory. > > * Localization files frequently have common filenames but > differentiates via parent directories. In C#, the name > "/strings.resx.lcl" is used for these localization files and they > will all collide in name-hash. > > [1] https://github.com/microsoft/beachball > > I've come across many other examples where some internal tool uses a > common name across multiple directories and is causing Git to repack > poorly due to name-hash collisions. > > One open-source example is the fluentui [2] repo, which uses beachball > to generate CHANGELOG.json and CHANGELOG.md files, and these files have > very poor delta characteristics when comparing against versions across > parent directories. > > | Stage | Pack Size | Repack Time | > |-----------------------|-----------|-------------| > | After clone | 694 MB | N/A | > | --name-hash-version=1 | 438 MB | 728s | > | --name-hash-version=2 | 168 MB | 142s | > > [2] https://github.com/microsoft/fluentui > > In this example, we see significant gains in the compressed packfile > size as well as the time taken to compute the packfile. > > Using a collection of repositories that use the beachball tool, I was > able to make similar comparisions with dramatic results. While the > fluentui repo is public, the others are private so cannot be shared for > reproduction. The results are so significant that I find it important to > share here: > > | Repo | --name-hash-version=1 | --name-hash-version=2 | > |----------|-----------------------|-----------------------| > | fluentui | 440 MB | 161 MB | > | Repo B | 6,248 MB | 856 MB | > | Repo C | 37,278 MB | 6,755 MB | > | Repo D | 131,204 MB | 7,463 MB | > > Future changes could include making --name-hash-version implied by a config > value or even implied by default during a full repack. > > It is important to point out that the name hash value is stored in the > .bitmap file format, so we must force --name-hash-version=1 when bitmaps > are being read or written. Later, the bitmap format could be updated to > be aware of the name hash version so deltas can be quickly computed > across the bitmapped/not-bitmapped boundary. > > Signed-off-by: Derrick Stolee > --- > Documentation/git-pack-objects.txt | 32 ++++++++++++++++++- > builtin/pack-objects.c | 49 +++++++++++++++++++++++++++--- > t/t5300-pack-object.sh | 31 +++++++++++++++++++ > 3 files changed, 106 insertions(+), 6 deletions(-) > > diff --git a/Documentation/git-pack-objects.txt b/Documentation/git-pack-objects.txt > index e32404c6aae..7f69ae4855f 100644 > --- a/Documentation/git-pack-objects.txt > +++ b/Documentation/git-pack-objects.txt > @@ -15,7 +15,8 @@ SYNOPSIS > [--revs [--unpacked | --all]] [--keep-pack=] > [--cruft] [--cruft-expiration=