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

[PATCH 04/12] bloom: de-duplicate directory entries

From
Derrick Stolee via GitGitGadget <gitgitgadget@gmail.com>
Date
May 1, 2020, 15:30 UTC
Message-ID
<07d0a25f1c438f02d71918e4ca05f2901eb802be.1588347029.git.gitgitgadget@gmail.com>
In-Reply-To
<pull.622.git.1588347029.gitgitgadget@gmail.com>
From: Derrick Stolee <dstolee@microsoft.com>

When computing a changed-path Bloom filter, we need to take the files that changed from the diff computation and extract the parent directories. That way, a directory pathspec such as "Documentation" could match commits that change "Documentation/git.txt".

However, the current code does a poor job of this process. The paths are added to a hashmap, but we do not check if an entry already exists with that path. This can create many duplicate entries and cause the filter to have a much larger length than it should. This means that the filter is more sparse than intended, which helps the false positive rate, but wastes a lot of space.

Properly use hashmap_get() before hashmap_add(). Also be sure to include a comparison function so these can be matched correctly.

This has an effect on a test in t0095-bloom.sh. This makes sense, there are ten changes inside "smallDir" so the total number of paths in the filter should be 11. This would result in 11 * 10 bits required, and with 8 bits per byte, this results in 14 bytes.

Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
---
 bloom.c          | 35 ++++++++++++++++++++++++++---------
 t/t0095-bloom.sh |  4 ++--
 2 files changed, 28 insertions(+), 11 deletions(-)
diff --git a/bloom.c b/bloom.c
index 2e3e0f5037b..eb08571c628 100644
--- a/bloom.c
+++ b/bloom.c
@@ -158,6 +158,19 @@ void init_bloom_filters(void)
 	init_bloom_filter_slab(&bloom_filters);
 }
 
+static int pathmap_cmp(const void *hashmap_cmp_fn_data,
+		       const struct hashmap_entry *eptr,
+		       const struct hashmap_entry *entry_or_key,
+		       const void *keydata)
+{
+	const struct pathmap_hash_entry *e1, *e2;
+
+	e1 = container_of(eptr, const struct pathmap_hash_entry, entry);
+	e2 = container_of(entry_or_key, const struct pathmap_hash_entry, entry);
+
+	return strcmp(e1->path, e2->path);
+}
+
 struct bloom_filter *get_bloom_filter(struct repository *r,
 				      struct commit *c,
 				      int compute_if_not_present)
@@ -203,25 +216,29 @@ struct bloom_filter *get_bloom_filter(struct repository *r,
 		struct hashmap pathmap;
 		struct pathmap_hash_entry *e;
 		struct hashmap_iter iter;
-		hashmap_init(&pathmap, NULL, NULL, 0);
+		hashmap_init(&pathmap, pathmap_cmp, NULL, 0);
 
 		for (i = 0; i < diff_queued_diff.nr; i++) {
 			const char *path = diff_queued_diff.queue[i]->two->path;
 
 			/*
-			* Add each leading directory of the changed file, i.e. for
-			* 'dir/subdir/file' add 'dir' and 'dir/subdir' as well, so
-			* the Bloom filter could be used to speed up commands like
-			* 'git log dir/subdir', too.
-			*
-			* Note that directories are added without the trailing '/'.
-			*/
+			 * Add each leading directory of the changed file, i.e. for
+			 * 'dir/subdir/file' add 'dir' and 'dir/subdir' as well, so
+			 * the Bloom filter could be used to speed up commands like
+			 * 'git log dir/subdir', too.
+			 *
+			 * Note that directories are added without the trailing '/'.
+			 */
 			do {
 				char *last_slash = strrchr(path, '/');
 
 				FLEX_ALLOC_STR(e, path, path);
 				hashmap_entry_init(&e->entry, strhash(path));
-				hashmap_add(&pathmap, &e->entry);
+
+				if (!hashmap_get(&pathmap, &e->entry, NULL))
+					hashmap_add(&pathmap, &e->entry);
+				else
+					free(e);
 
 				if (!last_slash)
 					last_slash = (char*)path;
diff --git a/t/t0095-bloom.sh b/t/t0095-bloom.sh
index 8f9eef116dc..6defeb544f1 100755
--- a/t/t0095-bloom.sh
+++ b/t/t0095-bloom.sh
@@ -89,8 +89,8 @@ test_expect_success 'get bloom filter for commit with 10 changes' '
 	git add smallDir &&
 	git commit -m "commit with 10 changes" &&
 	cat >expect <<-\EOF &&
-	Filter_Length:25
-	Filter_Data:82|a0|65|47|0c|92|90|c0|a1|40|02|a0|e2|40|e0|04|0a|9a|66|cf|80|19|85|42|23|
+	Filter_Length:14
+	Filter_Data:02|b3|c4|a0|34|e7|fe|eb|cb|47|fe|a0|e8|72|
 	EOF
 	test-tool bloom get_filter_for_commit "$(git rev-parse HEAD)" >actual &&
 	test_cmp expect actual
-- 
gitgitgadget
Previous: Taylor BlauNext: Taylor Blau
Message 8 of 42 in “Integrating line-log and changed-path Bloom filters”
  1. 00/12 Integrating line-log and changed-path Bloom filtersDerrick Stolee via GitGitGadget, May 1, 2020
  2. 01/12 bloom: fix whitespace around tab lengthDerrick Stolee via GitGitGadget, May 1, 2020
  3. Taylor BlauMay 1, 2020
  4. 03/12 Documentation: changed-path Bloom filters use byte wordsDerrick Stolee via GitGitGadget, May 1, 2020
  5. Taylor BlauMay 1, 2020
  6. 02/12 test-bloom: fix usage typoDerrick Stolee via GitGitGadget, May 1, 2020
  7. Taylor BlauMay 1, 2020
  8. 04/12 bloom: de-duplicate directory entriesDerrick Stolee via GitGitGadget, May 1, 2020
  9. Taylor BlauMay 1, 2020
  10. 05/12 bloom: parse commit before computing filtersDerrick Stolee via GitGitGadget, May 1, 2020
  11. Taylor BlauMay 1, 2020
  12. 07/12 completion: offer '--(no-)patch' among 'git log' optionsSZEDER Gábor via GitGitGadget, May 1, 2020
  13. Taylor BlauMay 1, 2020
  14. 08/12 line-log: remove unused fields from 'struct line_log_data'SZEDER Gábor via GitGitGadget, May 1, 2020
  15. Taylor BlauMay 1, 2020
  16. 06/12 bloom: use num_changes not nr for limit detectionDerrick Stolee via GitGitGadget, May 1, 2020
  17. Taylor BlauMay 1, 2020
  18. 09/12 t4211-line-log: add tests for parent oidsSZEDER Gábor via GitGitGadget, May 1, 2020
  19. Taylor BlauMay 4, 2020
  20. 11/12 line-log: try to use generation number-based topo-orderingSZEDER Gábor via GitGitGadget, May 1, 2020
  21. Taylor BlauMay 4, 2020
  22. 10/12 line-log: more responsive, incremental 'git log -L'SZEDER Gábor via GitGitGadget, May 1, 2020
  23. Taylor BlauMay 4, 2020
  24. Derrick StoleeMay 4, 2020
  25. 12/12 line-log: integrate with changed-path Bloom filtersDerrick Stolee via GitGitGadget, May 1, 2020
  26. Taylor BlauMay 4, 2020
  27. Junio C HamanoMay 1, 2020
  28. 00/12 Integrating line-log and changed-path Bloom filtersDerrick Stolee via GitGitGadget, May 11, 2020
  29. 02/12 test-bloom: fix usage typoDerrick Stolee via GitGitGadget, May 11, 2020
  30. 03/12 bloom: parse commit before computing filtersDerrick Stolee via GitGitGadget, May 11, 2020
  31. 05/12 bloom: de-duplicate directory entriesDerrick Stolee via GitGitGadget, May 11, 2020
  32. SZEDER GáborJun 7, 2020
  33. 07/12 completion: offer '--(no-)patch' among 'git log' optionsSZEDER Gábor via GitGitGadget, May 11, 2020
  34. 08/12 line-log: remove unused fields from 'struct line_log_data'SZEDER Gábor via GitGitGadget, May 11, 2020
  35. 09/12 t4211-line-log: add tests for parent oidsSZEDER Gábor via GitGitGadget, May 11, 2020
  36. 10/12 line-log: more responsive, incremental 'git log -L'SZEDER Gábor via GitGitGadget, May 11, 2020
  37. 12/12 line-log: integrate with changed-path Bloom filtersDerrick Stolee via GitGitGadget, May 11, 2020
  38. 11/12 line-log: try to use generation number-based topo-orderingSZEDER Gábor via GitGitGadget, May 11, 2020
  39. 06/12 bloom: use num_changes not nr for limit detectionDerrick Stolee via GitGitGadget, May 11, 2020
  40. SZEDER GáborAug 4, 2020
  41. 01/12 bloom: fix whitespace around tab lengthDerrick Stolee via GitGitGadget, May 11, 2020
  42. 04/12 Documentation: changed-path Bloom filters use byte wordsDerrick Stolee via GitGitGadget, May 11, 2020

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.