From: Aaron Plattner Date: Fri, 05 Dec 2025 21:56:23 GMT Subject: Re: [PATCH] packfile: skip decompressing and hashing blobs in add_promisor_object() Message-ID: <4bd18399-26b3-44cd-93a7-8d2d32bef709@nvidia.com> In-Reply-To: <20251205212839.GA35153@coredump.intra.peff.net> On 12/5/25 1:28 PM, Jeff King wrote: > On Fri, Dec 05, 2025 at 10:50:02AM -0800, Aaron Plattner wrote: > >> Unfortunately, setting that flag doesn't seem to improve performance for me >> because in parse_object_with_flags(), lookup_object() returns an obj pointer >> with obj->parsed == 0 and obj->type == OBJ_NONE. So it skips this block and >> ends up inflating the object anyway: >> >> if ((!obj || obj->type == OBJ_BLOB) && >> odb_read_object_info(r->objects, oid, NULL) == OBJ_BLOB) { >> if (!skip_hash && stream_object_signature(r, repl) < 0) { >> error(_("hash mismatch %s"), oid_to_hex(oid)); >> return NULL; >> } >> parse_blob_buffer(lookup_blob(r, oid)); >> return lookup_object(r, oid); >> } >> >> I was confused about why the check was structured that way, but reading the >> description of commit 8db2dad7a045e376b9c4f51ddd33da43c962e3a4 cleared that >> up. Thank you for thoroughly documenting that! >> >> Are OBJ_NONE objects expected here? Should the check be >> >> if ((!obj || obj->type == OBJ_NONE || obj->type == OBJ_BLOB) && >> odb_read_object_info(r->objects, oid, NULL) == OBJ_BLOB) { >> [...] >> } >> >> ? > > Yeah, that feels like a bug to me. The idea of that conditional is > "could it be a blob?" and obviously OBJ_NONE does not rule that out. > > I do wonder how you end up with OBJ_NONE, though. That implies somebody > created the "struct object" but without knowing which type it was > supposed to be, and then did not follow up by actually parsing it. If I'm understanding correctly, this loop creates a dummy struct object for every object in the promisor packs: if (revs->exclude_promisor_objects) { for_each_packed_object(revs->repo, mark_uninteresting, revs, FOR_EACH_OBJECT_PROMISOR_ONLY); } Backtrace for one such object: #0 create_object #1 lookup_unknown_object #2 mark_uninteresting #3 for_each_object_in_pack #4 for_each_packed_object #5 prepare_revision_walk #6 cmd_rev_list #7 run_builtin #8 handle_builtin #9 cmd_main #10 main Then the is_promisor_object() loop finds these dummy objects when it loops over all the objects again. > That's probably immaterial to what parse_object() should be doing, but > it is certainly a curiosity. And I'm also not sure why I got good > results from my rev-list invocation, but you did not. Weird. Yeah, that's still a mystery. > I think we could probably proceed without satisfying our curiosity here, > but if you felt like it, it would be interesting to find such an object > that is fed with OBJ_NONE to parse_object(), then run the command in a > debugger trying to break on the original create_object() call that > matches that oid. (Or if you want to be fancy use a reverse debugger > like rr). I might play around with it and see if I can stimulate it. > >> If I make that change combined with your PARSE_OBJECT_SKIP_HASH_CHECK change >> then the time drops to 1:58, so that's great! > > Cool, though I think that's about the same that you got with your patch? It's a bit better than my patch and yours seems cleaner. I'll put this together as a v2 of this patch. > I was hoping for a little bit more from skipping the hash checks and > commits, but maybe: > > 1. Your commit/tree structure is dominated much more by the blobs than > the linux.git I used for testing. So there's not much extra gain to > be had. That's certainly possible. Let's just say this codebase has a lot of cruft in it. :) > 2. You didn't have a commit-graph built. This repository came from "scalar clone" and then I created a worktree and disabled sparse checkout. I didn't do anything special to enable or disable commit-graph. What I do notice is that usually, a `git pull` from the server this repository is hosted on is fast, but occasionally it hits this pathological case. I was using git-rev-list as a proxy for what git-pull was getting stuck on. Is it possible that having a working commit-graph is what avoids the problem in the first place? I'll admit to not having a great understanding of how the commit graph is used during a normal pull. > -Peff