From: Ævar Arnfjörð Bjarmason Date: Fri, 03 Dec 2021 13:07:58 GMT Subject: Re: [PATCH v4 5/5] unpack-objects: unpack_non_delta_entry() read data in a stream Message-ID: <211203.86zgphsu5a.gmgdl@evledraar.gmail.com> In-Reply-To: <20211203093530.93589-6-chiyutianyi@gmail.com> On Fri, Dec 03 2021, Han Xin wrote: > From: Han Xin > > We used to call "get_data()" in "unpack_non_delta_entry()" to read the > entire contents of a blob object, no matter how big it is. This > implementation may consume all the memory and cause OOM. > > By implementing a zstream version of input_stream interface, we can use > a small fixed buffer for "unpack_non_delta_entry()". > > However, unpack non-delta objects from a stream instead of from an entrie > buffer will have 10% performance penalty. Therefore, only unpack object > larger than the "big_file_threshold" in zstream. See the following > benchmarks: > > hyperfine \ > --setup \ > 'if ! test -d scalar.git; then git clone --bare https://github.com/microsoft/scalar.git; cp scalar.git/objects/pack/*.pack small.pack; fi' \ > --prepare 'rm -rf dest.git && git init --bare dest.git' \ > -n 'old' 'git -C dest.git unpack-objects -n 'new' 'new/git -C dest.git unpack-objects -n 'new (small threshold)' \ > 'new/git -c core.bigfilethreshold=16k -C dest.git unpack-objects Benchmark 1: old > Time (mean ± σ): 6.075 s ± 0.069 s [User: 5.047 s, System: 0.991 s] > Range (min … max): 6.018 s … 6.189 s 10 runs > > Benchmark 2: new > Time (mean ± σ): 6.090 s ± 0.033 s [User: 5.075 s, System: 0.976 s] > Range (min … max): 6.030 s … 6.142 s 10 runs > > Benchmark 3: new (small threshold) > Time (mean ± σ): 6.755 s ± 0.029 s [User: 5.150 s, System: 1.560 s] > Range (min … max): 6.711 s … 6.809 s 10 runs > > Summary > 'old' ran > 1.00 ± 0.01 times faster than 'new' > 1.11 ± 0.01 times faster than 'new (small threshold)' So before we wrote used core.bigfilethreshold for two things (or more?): Whether we show a diff for it (we mark it "binary") and whether it's split into a loose object. Now it's three things, we've added a "this is a threshold when we'll stream the object" to that. Might it make sense to squash something like this in, so we can have our cake & eat it too? With this I get, where HEAD~0 is this change: Summary './git -C dest.git -c core.bigfilethreshold=512m unpack-objects big_file_threshold) { + if (!dry_run && type == OBJ_BLOB && size > big_file_streaming_threshold) { write_stream_blob(nr, size); return; } diff --git a/cache.h b/cache.h index eba12487b99..4037c7fd849 100644 --- a/cache.h +++ b/cache.h @@ -964,6 +964,7 @@ extern size_t packed_git_window_size; extern size_t packed_git_limit; extern size_t delta_base_cache_limit; extern unsigned long big_file_threshold; +extern unsigned long big_file_streaming_threshold; extern unsigned long pack_size_limit_cfg; /* diff --git a/config.c b/config.c index c5873f3a706..7b122a142a8 100644 --- a/config.c +++ b/config.c @@ -1408,6 +1408,11 @@ static int git_default_core_config(const char *var, const char *value, void *cb) return 0; } + if (!strcmp(var, "core.bigfilestreamingthreshold")) { + big_file_streaming_threshold = git_config_ulong(var, value); + return 0; + } + if (!strcmp(var, "core.packedgitlimit")) { packed_git_limit = git_config_ulong(var, value); return 0; diff --git a/environment.c b/environment.c index 9da7f3c1a19..4fcc3de7417 100644 --- a/environment.c +++ b/environment.c @@ -46,6 +46,7 @@ size_t packed_git_window_size = DEFAULT_PACKED_GIT_WINDOW_SIZE; size_t packed_git_limit = DEFAULT_PACKED_GIT_LIMIT; size_t delta_base_cache_limit = 96 * 1024 * 1024; unsigned long big_file_threshold = 512 * 1024 * 1024; +unsigned long big_file_streaming_threshold = 128 * 1024 * 1024; int pager_use_color = 1; const char *editor_program; const char *askpass_program;