Skip to content

Remove or avoid redundant work to speed up zkg#243

Open
bbannier wants to merge 5 commits into
masterfrom
topic/bbannier/speedups
Open

Remove or avoid redundant work to speed up zkg#243
bbannier wants to merge 5 commits into
masterfrom
topic/bbannier/speedups

Conversation

@bbannier

@bbannier bbannier commented Jul 22, 2026

Copy link
Copy Markdown
Member

Warning

All code changes in this branch were generated with Claude Sonnet 4.6. I reviewed all changes and am accountable.

This PR contains a number of unrelated cleanups which make zkg more snappy. Since I used timing of the BTest suite as a benchmark there might be more lingering improvements which are not covered by tests, or do not show up clearly. The changes fall into two categories:

  1. completely avoid unneeded, expensive work, this falls more in a cleanup category
  2. use light caching to avoid repeated recomputation, either during the same process invocation or by persisting slowly changing information to disk to speed up repeated zkg invocations

On my machine I see the following runtime changes of the BTest test suite:

commit time [s] vs prev
master (baseline) 165.0 --
Skip source git_checkout 141.7 -23s (-14%)
Cache Manager.info() 129.3 -12s (-9%)
Disk-persist zeek --build-info 132.4 +3s (+2%)
Skip submodule fetch in scratch clone 97.8 -35s (-26%)
Always shallow scratch clone 98.0 +0s (0%)

Like mentioned, I'd expect the commits without improvement to simply be not captured by the test suite (the increase at Disk-persist zeek --build-info seems consistent with noise).

Even though all code here was generated by an LLM, the code changes seem pretty straightforward. Some new unit tests tend to go pretty heavy on mocking, but if we agree that they are still readable I think this can be justified since they are meant to capture regressions where we accidentally introduce calls of expensive operations again.

bbannier added 5 commits July 22, 2026 11:10
On every `Manager` construction, `Source.__init__` was unconditionally
running `git_checkout` on each source clone, spawning three Git subprocesses
(`checkout`, `submodule sync --recursive`, `submodule update --recursive --init`)
even when the working tree was already at the target ref.

Only call `git_checkout` when the current branch/SHA differs from the target.
`test()` calls `self.info(pkg_path)` and then `validate_dependencies()`
calls `self.info(name)` again for the same package with identical arguments.
For git-backed packages this means two scratch clones of the same repo.

Add `_info_cache` on `Manager` and extract `_info_lookup()` so `info()`
returns a cached `PackageInfo` on repeated calls with the same arguments.
`zeek --build-info` ran on every `Manager` construction with no
cross-run persistence. Cache the result to
`state_dir/zeek_build_info_cache.json`, keyed on the `zeek` binary path,
mtime, and size, so subsequent runs with an unchanged binary skip the
subprocess entirely.

This does not show up in btest benchmarks because each test runs with a
fresh `state_dir`, so the cache is always cold. The benefit is real for
users: the subprocess is paid on every `zkg` command and is free on all
subsequent ones once the binary is unchanged.
`_info()` clones a package into `scratch_dir` solely to read its metadata
(`zkg.meta`), version tags, and default branch. Neither submodule content
nor a fully initialised submodule tree is needed for this.

The code called `_clone_package()` with `recursive=True` (fetching all
submodules at clone time) and `git_checkout()` with `update_submodules=True`
(running `git submodule sync --recursive` and `git submodule update
--recursive --init`), causing up to four redundant network roundtrips per
submodule for packages that would later be cloned again into the test or
install staging directory.

Hardcode `recursive=False` and `update_submodules=False` in `_info()` since
the scratch clone is throwaway and metadata-only. Remove the now-unused
`update_submodules` parameter from `_info()`, `_info_lookup()`, and
`info()`.
The scratch clone exists only to read `zkg.meta`, version tags, and the
default branch; none of which require full history. `_clone_package()`
used a deep clone whenever a specific version was requested (to safely
`git checkout` arbitrary historical commits), but that conservatism is
only needed for the operational clone used during install/test staging.

Inline `delete_path` + `git_clone(..., shallow=True, recursive=False)`
directly in `_info()` so the scratch clone is always a `--depth 1
--no-single-branch` clone regardless of the requested version.

This does not show up in btest benchmarks because test packages are local
repos created with 1-2 commits -- there is no history to truncate. The
benefit is real for users running `zkg info` or `zkg install` against
remote packages with non-trivial history, where a shallow clone avoids
transferring the full object graph.
@bbannier bbannier self-assigned this Jul 22, 2026
@bbannier
bbannier marked this pull request as ready for review July 22, 2026 10:32

@timwoj timwoj left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great to me. I had one question that was the resolved in a subsequent commit. Much better 👍🏼

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants