csum-file.c: use unsafe SHA-1 implementation when available

Update hashwrite() and friends to use the unsafe_-variants of hashing functions, calling for e.g., "the_hash_algo->unsafe_update_fn()" instead of "the_hash_algo->update_fn()". These callers only use the_hash_algo to produce a checksum, which we depend on for data integrity, but not for cryptographic purposes, so these callers are safe to use the unsafe (non-collision detecting) SHA-1 implementation. To time this, I took a freshly packed copy of linux.git, and ran the following with and without the OPENSSL_SHA1_UNSAFE=1 build-knob. Both versions were compiled with -O3: $ git for-each-ref --format='%(objectname)' refs/heads refs/tags >in $ valgrind --tool=callgrind ~/src/git/git-pack-objects \ --revs --stdout --all-progress --use-bitmap-index <in >/dev/null Without OPENSSL_SHA1_UNSAFE=1 (that is, using the collision-detecting SHA-1 implementation for both cryptographic and non-cryptographic purposes), we spend a significant amount of our instruction count in hashwrite(): $ callgrind_annotate --inclusive=yes | grep hashwrite | head -n1 159,998,868,413 (79.42%) /home/ttaylorr/src/git/csum-file.c:hashwrite [/home/ttaylorr/src/git/git-pack-objects] , and the resulting "clone" takes 19.219 seconds of wall clock time, 18.94 seconds of user time and 0.28 seconds of system time. Compiling with OPENSSL_SHA1_UNSAFE=1, we spend ~60% fewer instructions in hashwrite(): $ callgrind_annotate --inclusive=yes | grep hashwrite | head -n1 59,164,001,176 (58.79%) /home/ttaylorr/src/git/csum-file.c:hashwrite [/home/ttaylorr/src/git/git-pack-objects] , and generate the resulting "clone" much faster, in only 11.597 seconds of wall time, 11.37 seconds of user time, and 0.23 seconds of system time, for a ~40% speed-up. Signed-off-by: Taylor Blau <me@ttaylorr.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Taylor Blau committed Sep 26, 2024 at 11:22 UTC 1b9e9be8b4694ea52d8aae93f311b1607c3576b7
1 file changed +9 -9
csum-file.c
+9 -9
@@ -50,7 +50,7 @@ void hashflush(struct hashfile *f)
50
51 if (offset) {
52 if (!f->skip_hash)
53 - the_hash_algo->update_fn(&f->ctx, f->buffer, offset);
53 + the_hash_algo->unsafe_update_fn(&f->ctx, f->buffer, offset);
54 flush(f, f->buffer, offset);
55 f->offset = 0;
56 }
@@ -73,7 +73,7 @@ int finalize_hashfile(struct hashfile *f, unsigned char *result,
73 if (f->skip_hash)
74 hashclr(f->buffer, the_repository->hash_algo);
75 else
76 - the_hash_algo->final_fn(f->buffer, &f->ctx);
76 + the_hash_algo->unsafe_final_fn(f->buffer, &f->ctx);
77
78 if (result)
79 hashcpy(result, f->buffer, the_repository->hash_algo);
@@ -128,7 +128,7 @@ void hashwrite(struct hashfile *f, const void *buf, unsigned int count)
128 * f->offset is necessarily zero.
129 */
130 if (!f->skip_hash)
131 - the_hash_algo->update_fn(&f->ctx, buf, nr);
131 + the_hash_algo->unsafe_update_fn(&f->ctx, buf, nr);
132 flush(f, buf, nr);
133 } else {
134 /*
@@ -174,7 +174,7 @@ static struct hashfile *hashfd_internal(int fd, const char *name,
174 f->name = name;
175 f->do_crc = 0;
176 f->skip_hash = 0;
177 - the_hash_algo->init_fn(&f->ctx);
177 + the_hash_algo->unsafe_init_fn(&f->ctx);
178
179 f->buffer_len = buffer_len;
180 f->buffer = xmalloc(buffer_len);
@@ -208,7 +208,7 @@ void hashfile_checkpoint(struct hashfile *f, struct hashfile_checkpoint *checkpo
208 {
209 hashflush(f);
210 checkpoint->offset = f->total;
211 - the_hash_algo->clone_fn(&checkpoint->ctx, &f->ctx);
211 + the_hash_algo->unsafe_clone_fn(&checkpoint->ctx, &f->ctx);
212 }
213
214 int hashfile_truncate(struct hashfile *f, struct hashfile_checkpoint *checkpoint)
@@ -219,7 +219,7 @@ int hashfile_truncate(struct hashfile *f, struct hashfile_checkpoint *checkpoint
219 lseek(f->fd, offset, SEEK_SET) != offset)
220 return -1;
221 f->total = offset;
222 - the_hash_algo->clone_fn(&f->ctx, &checkpoint->ctx);
222 + the_hash_algo->unsafe_clone_fn(&f->ctx, &checkpoint->ctx);
223 f->offset = 0; /* hashflush() was called in checkpoint */
224 return 0;
225 }
@@ -245,9 +245,9 @@ int hashfile_checksum_valid(const unsigned char *data, size_t total_len)
245 if (total_len < the_hash_algo->rawsz)
246 return 0; /* say "too short"? */
247
248 - the_hash_algo->init_fn(&ctx);
249 - the_hash_algo->update_fn(&ctx, data, data_len);
250 - the_hash_algo->final_fn(got, &ctx);
248 + the_hash_algo->unsafe_init_fn(&ctx);
249 + the_hash_algo->unsafe_update_fn(&ctx, data, data_len);
250 + the_hash_algo->unsafe_final_fn(got, &ctx);
251
252 return hasheq(got, data + data_len, the_repository->hash_algo);
253 }