Skip to content

pack-bitmap: pseudo-merge reachability bitmaps

Taylor Blau git 4 versions – In review

Get series with b4
b4 am [email protected]

Versions newest first · linked by author and title

  1. v4 Latest24 patches 3 repliesIn review
  2. v325 of 30 patches 19 repliesSuperseded
  3. v223 patches 25 repliesSuperseded
Earlier versions · 1
VersionPostedPatchesRepliesStatus
v124 patches10 repliesSuperseded

Patches in v4 No applies recorded yet

#Subject and filesSizeRepliesApplied
1/24Documentation/gitpacking.txt: initial commitDocumentation/Makefile Documentation/gitpacking.txt+35 -00 replies
2/24Documentation/gitpacking.txt: describe pseudo-merge bitmapsDocumentation/gitpacking.txt+72 -00 replies
3/24Documentation/technical: describe pseudo-merge bitmaps formatDocumentation/technical/bitmap-format.txt+132 -00 replies
4/24ewah: implement `ewah_bitmap_is_subset()`ewah/bitmap.c ewah/ewok.h+49 -00 replies
5/24pack-bitmap: move some initialization to `bitmap_writer_init()`builtin/pack-objects.c midx-write.c pack-bitmap-write.c +1 more+22 -90 replies
6/24pseudo-merge.ch: initial commitMakefile pseudo-merge.c pseudo-merge.h+9 -00 replies
7/24pack-bitmap-write: support storing pseudo-merge commitsobject.h pack-bitmap-write.c pack-bitmap.h+67 -340 replies
8/24pack-bitmap: implement `bitmap_writer_has_bitmapped_object_id()`pack-bitmap-write.c pack-bitmap.h+8 -00 replies
9/24pack-bitmap: make `bitmap_writer_push_bitmapped_commit()` publicpack-bitmap-write.c pack-bitmap.h+6 -50 replies
10/24config: introduce `git_config_double()`config.c config.h parse.c +1 more+46 -00 replies
11/24pseudo-merge: implement support for selecting pseudo-merge commitsDocumentation/config.txt Documentation/config/bitmap-pseudo-merge.txt Documentation/gitpacking.txt +4 more+747 -01 reply
12/24pack-bitmap-write.c: write pseudo-merge tablepack-bitmap-write.c pack-bitmap.h+132 -00 replies
13/24pack-bitmap: extract `read_bitmap()` functionpack-bitmap.c pack-bitmap.h+17 -90 replies
14/24pseudo-merge: scaffolding for readspseudo-merge.c pseudo-merge.h+75 -00 replies
15/24pack-bitmap.c: read pseudo-merge extensionpack-bitmap.c+39 -00 replies
16/24pseudo-merge: implement support for reading pseudo-merge commitspseudo-merge.c pseudo-merge.h+279 -00 replies
17/24ewah: implement `ewah_bitmap_popcount()`ewah/bitmap.c ewah/ewok.h+15 -00 replies
18/24pack-bitmap: implement test helpers for pseudo-mergepack-bitmap.c pack-bitmap.h t/helper/test-bitmap.c+156 -70 replies
19/24t/test-lib-functions.sh: support `--notick` in `test_commit_bulk()`t/test-lib-functions.sh+8 -11 reply
20/24pack-bitmap.c: use pseudo-merges during traversalpack-bitmap.c t/t5333-pseudo-merge-bitmaps.sh+439 -10 replies
21/24pack-bitmap: extra trace2 informationpack-bitmap.c+25 -10 replies
22/24ewah: `bitmap_equals_ewah()`ewah/bitmap.c ewah/ewok.h+20 -00 replies
23/24pseudo-merge: implement support for finding existing mergespack-bitmap-write.c pack-bitmap.c pack-bitmap.h +3 more+174 -20 replies
24/24t/perf: implement performance tests for pseudo-merge bitmapst/perf/p5333-pseudo-merge-bitmaps.sh+32 -00 replies

Reviews trailers per patch

None of the 24 patches has a Reviewed-by or Acked-by.

PatchJeff King
1/24
2/24Helped-by
3/24
4/24
5/24
6/24
7/24
8/24
9/24
10/24
11/24
12/24
13/24
14/24
15/24
16/24
17/24
18/24
19/24
20/24
21/24
22/24
23/24
24/24

Trailers given in replies are filled; those carried in the patches from earlier versions are outlined with dashes.

Read the v4 thread

Cover letter [PATCH v4 00/24] pack-bitmap: pseudo-merge reachability bitmaps

Here is another reroll my topic to introduce pseudo-merge bitmaps.

The implementation is still relatively unchanged compared to last time, save for the review that Peff provided on the remaining parts of this series.

As usual, there is a range-diff below, but the significant changes since last time are as follows:

- replace git_config_float() with git_config_double() (and matching tweaks in the callers)

- add a NOTE to gitpacking(7) reflecting that the pseudo-merge bitmaps feature is considered experimental

- add a length check when reading extended pseudo merges via the pseudo_merge_ext_at() function

- replace the new --date option for test_commit_bulk with a --notick option (and set the GIT_COMMITTER_DATE values appropriately at the callers)

- fix broken performance tests due to a typo on "pseudo", and include results from a large repository.

The series is still based on 'tb/pack-bitmap-write-cleanups'.

Taylor Blau (24):
Documentation/gitpacking.txt: initial commit Documentation/gitpacking.txt: describe pseudo-merge bitmaps Documentation/technical: describe pseudo-merge bitmaps format
ewah: implement ewah_bitmap_is_subset()
pack-bitmap: move some initialization to bitmap_writer_init() pseudo-merge.ch: initial commit
pack-bitmap-write: support storing pseudo-merge commits
pack-bitmap: implement bitmap_writer_has_bitmapped_object_id()
pack-bitmap: make bitmap_writer_push_bitmapped_commit() public
config: introduce git_config_double()
pseudo-merge: implement support for selecting pseudo-merge commits pack-bitmap-write.c: write pseudo-merge table
pack-bitmap: extract read_bitmap() function
pseudo-merge: scaffolding for reads
pack-bitmap.c: read pseudo-merge extension
pseudo-merge: implement support for reading pseudo-merge commits
ewah: implement ewah_bitmap_popcount()
pack-bitmap: implement test helpers for pseudo-merge t/test-lib-functions.sh: support --notick in test_commit_bulk() pack-bitmap.c: use pseudo-merges during traversal
pack-bitmap: extra trace2 information
ewah: bitmap_equals_ewah()
pseudo-merge: implement support for finding existing merges t/perf: implement performance tests for pseudo-merge bitmaps

 Documentation/Makefile                       |   1 +
 Documentation/config.txt                     |   2 +
 Documentation/config/bitmap-pseudo-merge.txt |  91 +++
 Documentation/gitpacking.txt                 | 189 +++++
 Documentation/technical/bitmap-format.txt    | 132 ++++
 Makefile                                     |   1 +
 builtin/pack-objects.c                       |   3 +-
 config.c                                     |   9 +
 config.h                                     |   7 +
 ewah/bitmap.c                                |  76 ++
 ewah/ewok.h                                  |   8 +
 midx-write.c                                 |   2 +-
 object.h                                     |   2 +-
 pack-bitmap-write.c                          | 274 ++++++-
 pack-bitmap.c                                | 359 ++++++++-
 pack-bitmap.h                                |  19 +-
 parse.c                                      |  29 +
 parse.h                                      |   1 +
 pseudo-merge.c                               | 756 +++++++++++++++++++
 pseudo-merge.h                               | 216 ++++++
 t/helper/test-bitmap.c                       |  34 +-
 t/perf/p5333-pseudo-merge-bitmaps.sh         |  32 +
 t/t5333-pseudo-merge-bitmaps.sh              | 393 ++++++++++
 t/test-lib-functions.sh                      |   9 +-
 24 files changed, 2590 insertions(+), 55 deletions(-)
 create mode 100644 Documentation/config/bitmap-pseudo-merge.txt
 create mode 100644 Documentation/gitpacking.txt
 create mode 100644 pseudo-merge.c
 create mode 100644 pseudo-merge.h
 create mode 100755 t/perf/p5333-pseudo-merge-bitmaps.sh
 create mode 100755 t/t5333-pseudo-merge-bitmaps.sh

Range-diff against v3:

 -:  ----------- >  1:  0f20c9becf4 Documentation/gitpacking.txt: initial commit
 1:  528b591bd84 !  2:  48afaa74928 Documentation/gitpacking.txt: describe pseudo-merge bitmaps
    @@ Documentation/gitpacking.txt: There are many aspects of packing in Git that are
      
     +== Pseudo-merge bitmaps
     +
    ++NOTE: Pseudo-merge bitmaps are considered an experimental feature, so
    ++the configuration and many of the ideas are subject to change.
    ++
     +=== Background
     +
     +Reachability bitmaps are most efficient when we have on-disk stored
 2:  12f318b3d7e =  3:  44046f83c1a Documentation/technical: describe pseudo-merge bitmaps format
 3:  40eb6137618 =  4:  211d6f14128 ewah: implement `ewah_bitmap_is_subset()`
 4:  487fb7c6e9c =  5:  650cac2dcf9 pack-bitmap: move some initialization to `bitmap_writer_init()`
 5:  827732acf99 =  6:  6647d8832ce pseudo-merge.ch: initial commit
 6:  8608dd1860f =  7:  e8ef1ef5ee4 pack-bitmap-write: support storing pseudo-merge commits
 7:  99d2b6872ba =  8:  fe458728c8a pack-bitmap: implement `bitmap_writer_has_bitmapped_object_id()`
 8:  e7209c60fa5 =  9:  6bf372f4020 pack-bitmap: make `bitmap_writer_push_bitmapped_commit()` public
 9:  3070135eb4b ! 10:  6c77671ae9c config: introduce git_config_float()

@@ Metadata
Author: Taylor Blau <[email protected]>

## Commit message ##
- config: introduce git_config_float()
+ config: introduce git_config_double()

- Future commits will want to parse a floating point value from
- configuration, but we have no way to parse such a value prior to this
- patch.
+ Future commits will want to parse a double-precision floating point
+ value from configuration, but we have no way to parse such a value prior
+ to this patch.

- The core of the routine is implemented in git_parse_float(). Unlike
+ The core of the routine is implemented in git_parse_double(). Unlike
git_parse_unsigned() and git_parse_signed(), however, the function
- implemented here only works on type "float", and not related types like
- "double", or "long double".
+ implemented here only works on type "double", and not related types like
+ "float", or "long double".

- This is because "double" and "long double" use different functions to
- convert from ASCII strings to floating point values (strtod() and
+ This is because "float" and "long double" use different functions to
+ convert from ASCII strings to floating point values (strtof() and
strtold(), respectively). Likewise, there is no pointer type that can assign to any of these values (except for "void *"), so the only way to define this trio of functions would be with a macro expansion that is parameterized over the floating point type and conversion function.

That is all doable, but likely to be overkill given our current needs,
- which is only to parse floats.
+ which is only to parse double-precision floats.

@@ config.c: ssize_t git_config_ssize_t(const char *name, const char *value,
return ret;
}

-+float git_config_float(const char *name, const char *value,
-+ const struct key_value_info *kvi)
++double git_config_double(const char *name, const char *value,
++ const struct key_value_info *kvi)
+{
-+ float ret;
-+ if (!git_parse_float(value, &ret))
++ double ret;
++ if (!git_parse_double(value, &ret))
+ die_bad_number(name, value, kvi);
+ return ret;
+}
@@ config.h: unsigned long git_config_ulong(const char *, const char *,
const struct key_value_info *);

+/**
-+ * Identical to git_config_int, but for floating point values. ++ * Identically to git_config_double, but for double-precision floating point ++ * values.
+ */
-+float git_config_float(const char *, const char *,
-+ const struct key_value_info *);
++double git_config_double(const char *, const char *,
++ const struct key_value_info *);
+
/**
* Same as git_config_bool, except that integers are returned as-is, and
@@ parse.c: int git_parse_ssize_t(const char *value, ssize_t *ret)
return 1;
}

-+int git_parse_float(const char *value, float *ret) ++int git_parse_double(const char *value, double *ret)
+{
+ char *end;
-+ float val;
++ double val;
+ uintmax_t factor;
+
+ if (!value || !*value) {
@@ parse.c: int git_parse_ssize_t(const char *value, ssize_t *ret)
+ }
+
+ errno = 0;
-+ val = strtof(value, &end);
++ val = strtod(value, &end);
+ if (errno == ERANGE)
+ return 0;
+ if (end == value) {
@@ parse.h: int git_parse_ssize_t(const char *, ssize_t *);
int git_parse_ulong(const char *, unsigned long *); int git_parse_int(const char *value, int *ret);
int git_parse_int64(const char *value, int64_t *ret);
-+int git_parse_float(const char *value, float *ret); ++int git_parse_double(const char *value, double *ret);

/**
* Same as git_config_bool, except that it returns -1 on error rather
10: 3029473c094 ! 11: 180072ce848 pseudo-merge: implement support for selecting pseudo-merge commits

    @@ Documentation/config/bitmap-pseudo-merge.txt (new)
     @@
     +NOTE: The configuration options in `bitmapPseudoMerge.*` are considered
     +EXPERIMENTAL and may be subject to change or be removed entirely in the
    -+future.
    ++future. For more information about the pseudo-merge bitmap feature, see
    ++the "Pseudo-merge bitmaps" section of linkgit:gitpacking[7].
     +
     +bitmapPseudoMerge.<name>.pattern::
     +	Regular expression used to match reference names. Commits
    @@ pseudo-merge.c
     +#include "alloc.h"
     +#include "progress.h"
     +
    -+#define DEFAULT_PSEUDO_MERGE_DECAY 1.0f
    ++#define DEFAULT_PSEUDO_MERGE_DECAY 1.0
     +#define DEFAULT_PSEUDO_MERGE_MAX_MERGES 64
     +#define DEFAULT_PSEUDO_MERGE_SAMPLE_RATE 1
     +#define DEFAULT_PSEUDO_MERGE_THRESHOLD approxidate("1.week.ago")
     +#define DEFAULT_PSEUDO_MERGE_STABLE_THRESHOLD approxidate("1.month.ago")
     +#define DEFAULT_PSEUDO_MERGE_STABLE_SIZE 512
     +
    -+static float gitexp(float base, int exp)
    ++static double gitexp(double base, int exp)
     +{
    -+	float result = 1;
    ++	double result = 1;
     +	while (1) {
     +		if (exp % 2)
     +			result *= base;
    @@ pseudo-merge.c
     +					const struct pseudo_merge_matches *matches,
     +					uint32_t i)
     +{
    -+	float C = 0.0f;
    ++	double C = 0.0f;
     +	uint32_t n;
     +
     +	/*
    @@ pseudo-merge.c
     +	 *   { 5012, 1772, 964, 626, 448, 341, 271, 221, 186, 158 }
     +	 */
     +	for (n = 0; n < group->max_merges; n++)
    -+		C += 1.0f / gitexp(n + 1, group->decay);
    ++		C += 1.0 / gitexp(n + 1, group->decay);
     +	C = matches->unstable_nr / C;
     +
     +	return (uint32_t)((C / gitexp(i + 1, group->decay)) + 0.5);
    @@ pseudo-merge.c
     +
     +		strbuf_release(&re);
     +	} else if (!strcmp(key, "decay")) {
    -+		group->decay = git_config_float(var, value, ctx->kvi);
    ++		group->decay = git_config_double(var, value, ctx->kvi);
     +		if (group->decay < 0) {
     +			warning(_("%s must be non-negative, using default"), var);
     +			group->decay = DEFAULT_PSEUDO_MERGE_DECAY;
     +		}
     +	} else if (!strcmp(key, "samplerate")) {
    -+		group->sample_rate = git_config_float(var, value, ctx->kvi);
    ++		group->sample_rate = git_config_double(var, value, ctx->kvi);
     +		if (!(0 <= group->sample_rate && group->sample_rate <= 1)) {
     +			warning(_("%s must be between 0 and 1, using default"), var);
     +			group->sample_rate = DEFAULT_PSEUDO_MERGE_SAMPLE_RATE;
    @@ pseudo-merge.c
     +			struct commit *c = matches->unstable[j];
     +			struct pseudo_merge_commit_idx *pmc;
     +
    -+			if (j % (uint32_t)(1.0f / group->sample_rate))
    ++			if (j % (uint32_t)(1.0 / group->sample_rate))
     +				continue;
     +
     +			pmc = pseudo_merge_idx(writer->pseudo_merge_commits,
    @@ pseudo-merge.h
     +	 * Pseudo-merge grouping parameters. See git-config(1) for
     +	 * more information.
     +	 */
    -+	float decay;
    ++	double decay;
     +	int max_merges;
    -+	float sample_rate;
    ++	double sample_rate;
     +	int stable_size;
     +	timestamp_t threshold;
     +	timestamp_t stable_threshold;
11:  311226f65c2 = 12:  90df19e43f5 pack-bitmap-write.c: write pseudo-merge table
12:  55dd7a8023e = 13:  c653a10f8e4 pack-bitmap: extract `read_bitmap()` function
13:  3cc5434e44e = 14:  435ac048003 pseudo-merge: scaffolding for reads
14:  7664f5f9648 = 15:  fa7a948964c pack-bitmap.c: read pseudo-merge extension
15:  8ba0a9c5402 ! 16:  3a72e66cb69 pseudo-merge: implement support for reading pseudo-merge commits
    @@ pseudo-merge.c
      #include "progress.h"
     +#include "hex.h"
      
    - #define DEFAULT_PSEUDO_MERGE_DECAY 1.0f
    + #define DEFAULT_PSEUDO_MERGE_DECAY 1.0
      #define DEFAULT_PSEUDO_MERGE_MAX_MERGES 64
     @@ pseudo-merge.c: void free_pseudo_merge_map(struct pseudo_merge_map *pm)
      	}
    @@ pseudo-merge.c: void free_pseudo_merge_map(struct pseudo_merge_map *pm)
     +		return error(_("extended pseudo-merge read out-of-bounds "
     +			       "(%"PRIuMAX" >= %"PRIuMAX")"),
     +			     (uintmax_t)at, (uintmax_t)pm->map_size);
    ++	if (at + 4 >= pm->map_size)
    ++		return error(_("extended pseudo-merge entry is too short "
    ++			       "(%"PRIuMAX" >= %"PRIuMAX")"),
    ++			     (uintmax_t)(at + 4), (uintmax_t)pm->map_size);
     +
     +	ext->nr = get_be32(pm->map + at);
     +	ext->ptr = pm->map + at + sizeof(uint32_t);
16:  2c02f303b6f = 17:  42a836fda8a ewah: implement `ewah_bitmap_popcount()`
17:  82cce72bf55 = 18:  06ba1a5bbfd pack-bitmap: implement test helpers for pseudo-merge
18:  890f6c4b9de ! 19:  936f6d1b7e3 t/test-lib-functions.sh: support `--date` in `test_commit_bulk()`

@@ Metadata
Author: Taylor Blau <[email protected]>

## Commit message ##
- t/test-lib-functions.sh: support --date in test_commit_bulk()
+ t/test-lib-functions.sh: support --notick in test_commit_bulk()

One of the tests we'll want to add for pseudo-merge bitmaps needs to be able to generate a large number of commits at a specific date.

- Support the --date option (with identical semantics to the --date
- option for test_commit()) within test_commit_bulk as a prerequisite
- for that.
+ Support the --notick option (with identical semantics to the
+ --notick option for test_commit()) within test_commit_bulk as a
+ prerequisite for that. Callers can then set the various _DATE variables
+ themselves.

@@ t/test-lib-functions.sh: test_commit_bulk () {
filename="${1#--*=}-%s.t"
contents="${1#--*=} %s"
;;
-+ --date)
++ --notick)
+ notick=yes
-+ GIT_COMMITTER_DATE="$2"
-+ GIT_AUTHOR_DATE="$2"
-+ shift
+ ;;
-*)
BUG "invalid test_commit_bulk option: $1"
19: 41691824f78 ! 20: cad38608aae pack-bitmap.c: use pseudo-merges during traversal

    @@ t/t5333-pseudo-merge-bitmaps.sh (new)
     +		new="1672549200" && # 2023-01-01
     +		old="1641013200" && # 2022-01-01
     +
    -+		test_commit_bulk --message="new" --date "$new +0000" 128 &&
    -+		test_commit_bulk --message="old" --date "$old +0000" 128 &&
    -+		test_tick &&
    ++		GIT_COMMITTER_DATE="$new +0000" &&
    ++		export GIT_COMMITTER_DATE &&
    ++		test_commit_bulk --message="new" --notick 128 &&
    ++
    ++		GIT_COMMITTER_DATE="$old +0000" &&
    ++		export GIT_COMMITTER_DATE &&
    ++		test_commit_bulk --message="old" --notick 128 &&
     +
     +		tag_everything &&
     +
    @@ t/t5333-pseudo-merge-bitmaps.sh (new)
     +		mid="1654059600" && # 2022-06-01
     +		old="1641013200" && # 2022-01-01
     +
    -+		test_commit_bulk --message="mid" --date "$mid +0000" 128 &&
    -+		test_tick &&
    ++		GIT_COMMITTER_DATE="$mid +0000" &&
    ++		export GIT_COMMITTER_DATE &&
    ++		test_commit_bulk --message="mid" --notick 128 &&
     +
     +		git for-each-ref --format="delete %(refname)" refs/tags >in &&
     +		git update-ref --stdin <in &&
20:  a34a60c3ef8 = 21:  9240b06a7d8 pack-bitmap: extra trace2 information
21:  da2fb5b4b48 = 22:  625596a1432 ewah: `bitmap_equals_ewah()`
22:  ff21247281f ! 23:  fdd506d4544 pseudo-merge: implement support for finding existing merges
    @@ t/t5333-pseudo-merge-bitmaps.sh: test_expect_success 'pseudo-merge overlap stale
     +		stable="1641013200" && # 2022-01-01
     +		unstable="1672549200" && # 2023-01-01
     +
    -+		for date in $stable $unstable
    -+		do
    -+			test_commit_bulk --date "$date +0000" 128 &&
    -+			test_tick || return 1
    -+		done &&
    ++		GIT_COMMITTER_DATE="$stable +0000" &&
    ++		export GIT_COMMITTER_DATE &&
    ++		test_commit_bulk --notick 128 &&
    ++		GIT_COMMITTER_DATE="$unstable +0000" &&
    ++		export GIT_COMMITTER_DATE &&
    ++		test_commit_bulk --notick 128 &&
     +
     +		tag_everything &&
     +

23: 6a6d88fa512 ! 24: cf0316ad0e9 t/perf: implement performace tests for pseudo-merge bitmaps
@@ Metadata
Author: Taylor Blau <[email protected]>

## Commit message ##
- t/perf: implement performace tests for pseudo-merge bitmaps
+ t/perf: implement performance tests for pseudo-merge bitmaps

Implement a straightforward performance test demonstrating the benefit of pseudo-merge bitmaps by measuring how long it takes to count
@@ Commit message

Test this tree
-----------------------------------------------------------------------------------
- 5333.2: git rev-list --count --all --objects (no bitmaps) 3.46(3.37+0.09)
- 5333.3: git rev-list --count --all --objects (no pseudo-merges) 0.13(0.11+0.01)
+ 5333.2: git rev-list --count --all --objects (no bitmaps) 3.54(3.45+0.08)
+ 5333.3: git rev-list --count --all --objects (no pseudo-merges) 0.43(0.40+0.03)
5333.4: git rev-list --count --all --objects (with pseudo-merges) 0.12(0.11+0.01)

+ On a private repository which is much larger, and has many spikey parts
+ of history that aren't merged into the 'master' branch, the results are
+ as follows:
+
+ Test this tree
+ ---------------------------------------------------------------------------------------
+ 5333.1: git rev-list --count --all --objects (no bitmaps) 122.29(121.31+0.97)
+ 5333.2: git rev-list --count --all --objects (no pseudo-merges) 21.88(21.30+0.58)
+ 5333.3: git rev-list --count --all --objects (with pseudo-merges) 5.05(4.77+0.28)
+

## t/perf/p5333-pseudo-merge-bitmaps.sh (new) ##
@@ t/perf/p5333-pseudo-merge-bitmaps.sh (new)
+'
+
+test_perf 'git rev-list --count --all --objects (no pseudo-merges)' '
-+ GIT_TEST_USE_PSEDUO_MERGES=0 \
++ GIT_TEST_USE_PSEUDO_MERGES=0 \
+ git rev-list --objects --all --use-bitmap-index
+'
+
+test_perf 'git rev-list --count --all --objects (with pseudo-merges)' '
-+ GIT_TEST_USE_PSEDUO_MERGES=1 \
++ GIT_TEST_USE_PSEUDO_MERGES=1 \
+ git rev-list --objects --all --use-bitmap-index
+'
+

base-commit: bf65967764f34adc2ca00d4c8195840ad3e4e127

Signature · 1 line
2.45.1.175.gcf0316ad0e9