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

Re: [PATCH v2 2/8] pack-write.c: prepare to write 'pack-*.rev' files

From
Jeff King <peff@peff.net>
Date
Jan 22, 2021, 23:24 UTC
Message-ID
<YAtey9krU32mgEBV@coredump.intra.peff.net>
In-Reply-To
<8648c87fa71aec427dc11afcba6548fb66a1413b.1610576805.git.me@ttaylorr.com>
On Wed, Jan 13, 2021 at 05:28:11PM -0500, Taylor Blau wrote:
Show 11 quoted lines
> +static void write_rev_index_positions(struct hashfile *f,
> +				      struct pack_idx_entry **objects,
> +				      uint32_t nr_objects)
> +{
> +	uint32_t *pack_order;
> +	uint32_t i;
> +
> +	ALLOC_ARRAY(pack_order, nr_objects);
> +	for (i = 0; i < nr_objects; i++)
> +		pack_order[i] = i;
> +	QSORT_S(pack_order, nr_objects, pack_order_cmp, objects);
qsort? Don't we have a perfectly good radix sort for this exact purpose? :)

I guess it is awkward to use because it hard-codes the assumption that we are sorting revindex_entry structs, with their offsets directly available.

We _could_ actually just generate an in-memory revindex array, and then use that to write out the .rev file. That has the nice property that we'd continue exercising the fallback code in the tests.

But a revindex_entry is much larger than the uint32_t's we need here. So we'd be incurring extra memory costs during the generation, which is probably not worth it. If we want better test coverage, we probably should explicitly run a series of tests both with and without a .rev file.

It's possible we'd still benefit from using a more generalized radix sort, but it probably isn't worth the trouble. The timings from 8b8dfd5132 (pack-revindex: radix-sort the revindex, 2013-07-11) claim a 4x speedup on sorting 3M entries (the size matters because we are comparing an O(n) sort versus an O(n log n) one). We can imagine that at 10M entries for the current kernel, it might even be 8x. But the absolute numbers are pretty small. The radix sort takes ~150ms for linux.git on my machine. At 8x, that's 1.2s. For a repack of the kernel, that is mostly a drop in the bucket. It mattered a lot more when many processes were doing it on the fly.

Show 6 quoted lines
> +static int pack_order_cmp(const void *va, const void *vb, void *ctx)
> +{
> +	struct pack_idx_entry **objects = ctx;
> +
> +	off_t oa = objects[*(uint32_t*)va]->offset;
> +	off_t ob = objects[*(uint32_t*)vb]->offset;

Dereferencing a pointer to index another array always makes me nervous that we may have a bounds problem with bogus data.

In this case we know it is OK because we filled the array ourselves with in-bound numbers in write_rev_index_positions.

> +#define RIDX_SIGNATURE 0x52494458 /* "RIDX" */
> +#define RIDX_VERSION 1

I was surprised we didn't define these already on the reading side, but it looks like we didn't. In patch 1, we probably should be checking RIDX_SIGNATURE in load_revindex_from_disk(). But much more importantly, we should be checking that we find version 1, since that's what will make it safe to later invent a version 2.

Show 13 quoted lines
> +static void write_rev_header(struct hashfile *f)
> +{
> +	uint32_t oid_version;
> +	switch (hash_algo_by_ptr(the_hash_algo)) {
> +	case GIT_HASH_SHA1:
> +		oid_version = 1;
> +		break;
> +	case GIT_HASH_SHA256:
> +		oid_version = 2;
> +		break;
> +	default:
> +		die("write_rev_header: unknown hash version");
> +	}

I forgot to comment on this in patch 1, but: I think the format is really independent of the hash size. The contents are identical for a sha-1 versus sha-256 file.

That said, I don't overly mind having a hash identifier if it might help debug things (OTOH, how the heck do you end up with one that matches the trailer's packfile but _doesn't_ match the trailer's contents?).

If we do have it, should we also be checking it in the loading function?
Show 23 quoted lines
> +const char *write_rev_file(const char *rev_name,
> +			   struct pack_idx_entry **objects,
> +			   uint32_t nr_objects,
> +			   const unsigned char *hash,
> +			   unsigned flags)
> +{
> +	struct hashfile *f;
> +	int fd;
> +
> +	if ((flags & WRITE_REV) && (flags & WRITE_REV_VERIFY))
> +		die(_("cannot both write and verify reverse index"));
> +
> +	if (flags & WRITE_REV) {
> +		if (!rev_name) {
> +			struct strbuf tmp_file = STRBUF_INIT;
> +			fd = odb_mkstemp(&tmp_file, "pack/tmp_rev_XXXXXX");
> +			rev_name = strbuf_detach(&tmp_file, NULL);
> +		} else {
> +			unlink(rev_name);
> +			fd = open(rev_name, O_CREAT|O_EXCL|O_WRONLY, 0600);
> +			if (fd < 0)
> +				die_errno("unable to create '%s'", rev_name);
> +		}

So if the caller gave us a name, we force-overwrite it. That seemed weird to me at first, but it makes sense; the atomic rename-into-place writers will not be passing in the name. And this is exactly how write_idx_file() works.

I wonder if we could factor out some of this repeated logic, but I suspect it is mostly diminishing returns. Maybe this "open a pack file for writing" could become a helper function, though.

Show 10 quoted lines
> diff --git a/pack.h b/pack.h
> index 9fc0945ac9..30439e0784 100644
> --- a/pack.h
> +++ b/pack.h
> @@ -42,6 +42,8 @@ struct pack_idx_option {
>  	/* flag bits */
>  #define WRITE_IDX_VERIFY 01 /* verify only, do not write the idx file */
>  #define WRITE_IDX_STRICT 02
> +#define WRITE_REV 04
> +#define WRITE_REV_VERIFY 010

It is a little funny that the write-rev function has both WRITE_REV and WRITE_REV_VERIFY (which we must be sure are not both provided), but we do not need both for the IDX.

I thought maybe the reason is that we'd pass these flags to write_idx_file() or similar, and it would need to know whether to write just an idx, or both. That doesn't seem to happen in this patch, but maybe it does in a future one...

-Peff
Previous: Taylor BlauNext: Taylor Blau
Message 40 of 82 in “pack-revindex: introduce on-disk '.rev' format”
  1. 0/8 pack-revindex: introduce on-disk '.rev' formatTaylor Blau, Jan 8, 2021
  2. 2/8 pack-write.c: prepare to write 'pack-*.rev' filesTaylor Blau, Jan 8, 2021
  3. 8/8 pack-revindex: ensure that on-disk reverse indexes are given precedenceTaylor Blau, Jan 8, 2021
  4. 3/8 builtin/index-pack.c: write reverse indexesTaylor Blau, Jan 8, 2021
  5. 1/8 packfile: prepare for the existence of '*.rev' filesTaylor Blau, Jan 8, 2021
  6. 4/8 builtin/pack-objects.c: respect 'pack.writeReverseIndex'Taylor Blau, Jan 8, 2021
  7. 7/8 t: support GIT_TEST_WRITE_REV_INDEXTaylor Blau, Jan 8, 2021
  8. Derrick StoleeJan 12, 2021
  9. Taylor BlauJan 12, 2021
  10. Ævar Arnfjörð BjarmasonJan 12, 2021
  11. Derrick StoleeJan 12, 2021
  12. Taylor BlauJan 12, 2021
  13. 6/8 t: prepare for GIT_TEST_WRITE_REV_INDEXTaylor Blau, Jan 8, 2021
  14. Ævar Arnfjörð BjarmasonJan 12, 2021
  15. Taylor BlauJan 12, 2021
  16. 5/8 Documentation/config/pack.txt: advertise 'pack.writeReverseIndex'Taylor Blau, Jan 8, 2021
  17. 0/8 pack-revindex: introduce on-disk '.rev' formatTaylor Blau, Jan 13, 2021
  18. 8/8 pack-revindex: ensure that on-disk reverse indexes are given precedenceTaylor Blau, Jan 13, 2021
  19. 6/8 t: prepare for GIT_TEST_WRITE_REV_INDEXTaylor Blau, Jan 13, 2021
  20. 1/8 packfile: prepare for the existence of '*.rev' filesTaylor Blau, Jan 13, 2021
  21. Junio C HamanoJan 14, 2021
  22. Derrick StoleeJan 14, 2021
  23. Jeff KingJan 14, 2021
  24. Taylor BlauJan 14, 2021
  25. Junio C HamanoJan 14, 2021
  26. Taylor BlauJan 14, 2021
  27. Junio C HamanoJan 14, 2021
  28. Jeff KingJan 22, 2021
  29. Taylor BlauJan 25, 2021
  30. Jeff KingJan 25, 2021
  31. Junio C HamanoJan 25, 2021
  32. Taylor BlauJan 25, 2021
  33. 5/8 Documentation/config/pack.txt: advertise 'pack.writeReverseIndex'Taylor Blau, Jan 13, 2021
  34. 4/8 builtin/pack-objects.c: respect 'pack.writeReverseIndex'Taylor Blau, Jan 13, 2021
  35. Jeff KingJan 22, 2021
  36. Jeff KingJan 23, 2021
  37. Taylor BlauJan 25, 2021
  38. Jeff KingJan 25, 2021
  39. 2/8 pack-write.c: prepare to write 'pack-*.rev' filesTaylor Blau, Jan 13, 2021
  40. Jeff KingJan 22, 2021
  41. Taylor BlauJan 25, 2021
  42. Jeff KingJan 26, 2021
  43. 7/8 t: support GIT_TEST_WRITE_REV_INDEXTaylor Blau, Jan 13, 2021
  44. 3/8 builtin/index-pack.c: write reverse indexesTaylor Blau, Jan 13, 2021
  45. Jeff KingJan 22, 2021
  46. Taylor BlauJan 25, 2021
  47. 00/10 pack-revindex: introduce on-disk '.rev' formatTaylor Blau, Jan 25, 2021
  48. 01/10 packfile: prepare for the existence of '*.rev' filesTaylor Blau, Jan 25, 2021
  49. Jeff KingJan 29, 2021
  50. Taylor BlauJan 29, 2021
  51. Jeff KingJan 30, 2021
  52. 08/10 t: support GIT_TEST_WRITE_REV_INDEXTaylor Blau, Jan 25, 2021
  53. Jeff KingJan 29, 2021
  54. 07/10 t: prepare for GIT_TEST_WRITE_REV_INDEXTaylor Blau, Jan 25, 2021
  55. Jeff KingJan 29, 2021
  56. Eric SunshineJan 29, 2021
  57. Taylor BlauJan 29, 2021
  58. Jeff KingJan 30, 2021
  59. Junio C HamanoJan 29, 2021
  60. 05/10 builtin/pack-objects.c: respect 'pack.writeReverseIndex'Taylor Blau, Jan 25, 2021
  61. 09/10 pack-revindex: ensure that on-disk reverse indexes are given precedenceTaylor Blau, Jan 25, 2021
  62. Jeff KingJan 29, 2021
  63. Taylor BlauJan 29, 2021
  64. Jeff KingJan 30, 2021
  65. 10/10 t5325: check both on-disk and in-memory reverse indexTaylor Blau, Jan 25, 2021
  66. Jeff KingJan 29, 2021
  67. Jeff KingJan 29, 2021
  68. Taylor BlauJan 29, 2021
  69. Jeff KingJan 30, 2021
  70. 06/10 Documentation/config/pack.txt: advertise 'pack.writeReverseIndex'Taylor Blau, Jan 25, 2021
  71. Jeff KingJan 29, 2021
  72. Taylor BlauJan 29, 2021
  73. Jeff KingJan 30, 2021
  74. 02/10 pack-write.c: prepare to write 'pack-*.rev' filesTaylor Blau, Jan 25, 2021
  75. 04/10 builtin/index-pack.c: write reverse indexesTaylor Blau, Jan 25, 2021
  76. 03/10 builtin/index-pack.c: allow stripping arbitrary extensionsTaylor Blau, Jan 25, 2021
  77. Jeff KingJan 29, 2021
  78. Taylor BlauJan 29, 2021
  79. Junio C HamanoJan 26, 2021
  80. Taylor BlauJan 26, 2021
  81. Jeff KingJan 29, 2021
  82. Taylor BlauJan 29, 2021

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.