Conversation
…rden fallback behavior
|
I tried this version and it works fine in local environment, decreased wiring time from 20s to roughly 1s.
|
Replace mtime-based cache invalidation with content hashing: - artifact_cache: use SHA-256 of file content instead of ModTime - discovery_cache: use content hash for file matching, add WIRE_DISCOVERY_CACHE_DIR env var - Bump cache versions (artifact v4, discovery v4) This enables wire cache to work correctly in CI environments where file mtimes are not preserved across runs (e.g., S3 cache restore, git checkout). Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Thank you! Merged your changes in and unified the cache directory settings. You can now point all Wire-managed caches at one base directory with WIRE_CACHE_DIR. That makes Wire use: The existing per-cache env vars still work and take precedence if set:
To test go install github.com/goforj/wire/cmd/wire@8a3ed6dGoing back to your quote
Is this 20s to 1s on a pubg service ? |
This PR overhauls the loader process for Wire which has significant performance issues under various circumstances. Most of which were tied to go/packages semantics and go/types type checking work. With larger codebases, you incur a growing cost of both as a baseline. For example, in a project that stock Google Wire would take 1.3s to run, 400-600ms of that was go list deps and the rest was type checking all external and internal packages recursively in order to do what Wire needs to do. This PR replaces the loader process, caches the heavy work and invalidates it when necessary.
Safety
Safety, accuracy is important above all else. Existing Wire tests pass along with a plethora of newly added scenario based testing.
Test it Yourself
You can test the new loader yourself by installing this branch:
Revert back to goforj
Or to stock google wire
Custom Loader
This custom loader implementation is far more consistent than previous cache attempts (on goforj/wire) where those cache attempts were tackling the issue further down the line when they needed to be addressed more upstream in the loader. While this was helpful, you'd lose your compile speed under certain edit types. This implementation provides consistent lightning fast compile times across the board in many scenarios. See table below for measurements.
The table below illustrates up to 70x+ speed improvements, but these improvements scale with codebase size and complexity so you can maintain a great development experience even under massive repositories.
Benchmarks
Implementation Details
This PR replaces the primary
go/packagespath with a custom loader that is much more intentional about what work it does and when it does it.There are two different kinds of work:
By separating those layers we can skip far more repeated work than before.
Loader
Most of the new implementation lives in
internal/loader.custom.gois the main custom loading backenddiscovery.goanddiscovery_cache.gobuild and reuse the discovered graphartifact_cache.gostores typed package artifacts for reuse between runsfallback.gopreserves a safe fallback path when neededtiming.goexposes timings so we can see where time is actually goingCaching
Caching is now a first class part of generation instead of a thin optimization layered on afterward.
This is what enables the very fast
unchanged rerunandbody-only local editpaths in the benchmark table.Parser / Wire Integration
The parser and provider set handling needed to be updated to operate cleanly with cached semantic state.
internal/wire/parse.gonow reconstructs provider information from semantic artifacts when safeinternal/wire/output_cache.gowas updated around the new loader flowinternal/wire/wire.gonow drives generation through the new loading backendinternal/wire/load_debug.goandinternal/wire/loader_timing_bridge.gomake the new path observable via -timings flagOne important detail here is that the system still prefers falling back cleanly over trusting cached reconstruction when it is not safe to do so.
CLI
The command surface was updated to route through the same generation model instead of splitting behavior between commands.
wire gen,wire check,wire diff, andwire shownow run through the new backendwire watchstays on the same core generation path rather than inventing a separate implementationwire cachewas expanded so cache inspection and clearing are easiercmd/wire/main.gonow wires cache and loader behavior more explicitlyColorization There are now new colorization for errors (red) and success (green) to make things a little easier to read in between watcher tooling spam. Multiline errors are now presented more user friendly.
Compatibility / Safety
This is meant to be a conservative loader change, not a semantic rewrite of Wire. Everything works the same outside of some of the tweaks goforj/wire has made by introducing
wire cache clearwire serveThat shows up in:
internal/loader/loader_test.gointernal/wire/wire_test.gointernal/wire/parse_coverage_test.gocmd/wire/main_test.goBenchmarks
The benchmark harness was also expanded so it measures concrete developer workflows instead of only raw repo scale. (Seen in the table above)
scripts/import-benchmarks.shnow prints both scale and scenario tablesinternal/wire/import_bench_test.gonow measures edit types like unchanged reruns, body edits, shape edits, and import togglesThere are now a few benchmark profiles:
localfor a modest local graphlocal-highfor a very large local graphexternalfor a graph with a much heavier external dependency surface