]> git.ipfire.org Git - thirdparty/git.git/commitdiff
index-pack, unpack-objects: increase input buffer from 4 KiB to 128 KiB
authorScott Bauersfeld <sbauersfeld@g.ucla.edu>
Mon, 27 Apr 2026 19:26:38 +0000 (19:26 +0000)
committerJunio C Hamano <gitster@pobox.com>
Tue, 28 Apr 2026 00:49:23 +0000 (09:49 +0900)
index-pack and unpack-objects both read pack data from stdin through
a 4 KiB static buffer. In index-pack, each fill() flushes consumed
bytes to the pack file via write_or_die(), capping every write(2)
at 4 KiB. unpack-objects uses the same buffer pattern for reads.

On FUSE-backed filesystems every write(2) is a synchronous round
trip through the FUSE protocol (userspace -> kernel -> userspace ->
back), so the 4 KiB buffer turns a clone into many unnecessary tiny
writes with noticeable latency overhead.

Increase the buffer from 4 KiB to 128 KiB. Introduce a shared
DEFAULT_IO_BUFFER_SIZE constant in git-compat-util.h (next to
MAX_IO_SIZE) and use it in index-pack, unpack-objects, and the
hashfile layer in csum-file (which already used 128 KiB but
hardcoded the value).

Syscall counts via strace on HTTPS clones of git/git (~296 MB pack,
5 runs per variant, isolated builds from the same v2.54.0 source):

  index-pack pack file writes: 72,465 -> 24,943 avg (65% fewer)
  total write() syscalls:     310,192 -> 259,530 avg (16% fewer)
  writes of exactly 4096 bytes: ~40,077 -> 0

Wall-clock time of git clone over HTTPS onto a FUSE passthrough
filesystem with writeback caching disabled, 3 runs per variant:

  vscode (~1.26 GB pack): 84.5s -> 75.7s avg (10% faster)
  git/git (~306 MB pack):  22.6s -> 20.0s avg (11% faster)

Signed-off-by: Scott Bauersfeld <sbauersfeld@g.ucla.edu>
Acked-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
builtin/index-pack.c
builtin/unpack-objects.c
csum-file.c
git-compat-util.h

index ca7784dc2c496978322f1d7a3469013e21368c5a..bb3639641cd5d7d7e8224614b183de9079bb36cf 100644 (file)
@@ -145,8 +145,7 @@ static int check_self_contained_and_connected;
 
 static struct progress *progress;
 
-/* We always read in 4kB chunks. */
-static unsigned char input_buffer[4096];
+static unsigned char input_buffer[DEFAULT_IO_BUFFER_SIZE];
 static unsigned int input_offset, input_len;
 static off_t consumed_bytes;
 static off_t max_input_size;
index e01cf6e360f6d1f523c04a0aee98a2bccf378ac0..af67d1a1d32a8648832b7ccebaf0d8e0d27e3f0b 100644 (file)
@@ -23,8 +23,7 @@
 static int dry_run, quiet, recover, has_errors, strict;
 static const char unpack_usage[] = "git unpack-objects [-n] [-q] [-r] [--strict]";
 
-/* We always read in 4kB chunks. */
-static unsigned char buffer[4096];
+static unsigned char buffer[DEFAULT_IO_BUFFER_SIZE];
 static unsigned int offset, len;
 static off_t consumed_bytes;
 static off_t max_input_size;
index 9558177a11b49a625f15180490728c5f45c6c3dd..d7a682c2b62102a26bfc809f98f5a9ec2392bc1a 100644 (file)
@@ -178,7 +178,7 @@ struct hashfile *hashfd_ext(const struct git_hash_algo *algop,
        f->algop = unsafe_hash_algo(algop);
        f->algop->init_fn(&f->ctx);
 
-       f->buffer_len = opts->buffer_len ? opts->buffer_len : 128 * 1024;
+       f->buffer_len = opts->buffer_len ? opts->buffer_len : DEFAULT_IO_BUFFER_SIZE;
        f->buffer = xmalloc(f->buffer_len);
        f->check_buffer = NULL;
 
index ae1bdc90a4cd6ab1e03b3afdf27ad21d2ed18fed..5024814bd4879929df385a355351e36b629fd305 100644 (file)
@@ -712,6 +712,12 @@ static inline uint64_t u64_add(uint64_t a, uint64_t b)
 # endif
 #endif
 
+/*
+ * Default buffer size for buffered I/O in index-pack, unpack-objects,
+ * and the hashfile layer in csum-file.
+ */
+#define DEFAULT_IO_BUFFER_SIZE (128 * 1024)
+
 #ifdef HAVE_ALLOCA_H
 # include <alloca.h>
 # define xalloca(size)      (alloca(size))