From daaa317327433caa3ae54954bdab41eab65420b1 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Sat, 17 Oct 2026 19:08:55 -0500 Subject: [PATCH 1/4] docs: explain the GoForj Wire fork and migration --- README.md | 109 ++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 86 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index b759319..7d66a35 100644 --- a/README.md +++ b/README.md @@ -20,16 +20,11 @@ No runtime container, no reflection, no hidden magic - just fast, explicit initialization.

-> [!NOTE] -> This is a maintained fork of `google/wire`. -> The original project is no longer actively maintained. -> This fork preserves Wire’s API and behavior while focusing on: -> -> - 8-10x+ Faster compile times -> - Improved generator determinism -> - Better documentation and developer ergonomics (wire watch etc.) -> -> Existing Wire codebases should work without modification. +**GoForj Wire is a maintained fork of [Google Wire](https://github.com/google/wire), +which is archived and no longer maintained.** Keep explicit constructor injection +and ordinary generated Go code, with incremental generation, a native watcher, +and project-owned tooling for modern Go workspaces. No GoForj framework dependency +is required. Wire is a code generation tool that automates connecting components using [dependency injection][]. Dependencies between components are represented in @@ -43,8 +38,63 @@ For an overview, see the [introductory blog post](https://blog.golang.org/wire). [dependency injection]: https://en.wikipedia.org/wiki/Dependency_injection [godoc]: https://godoc.org/github.com/goforj/wire +## Why GoForj Wire? + +The improvements are in the generator and development workflow. Providers, +`wire.Build`, provider sets, interface bindings, and cleanup functions retain +Wire's familiar model. + +| Improvement over Google Wire | What it gives you | +| --- | --- | +| **Incremental generation** | Reuse package-loading and generated-output caches on repeated runs, reducing the wait as your dependency graph grows. See the [measured comparison](#generation-performance). | +| **Native watch mode** | Run `wire watch ./...` once to regenerate as Go files change, with filesystem notifications and a polling fallback. | +| **Project-owned generation commands** | Use `-generate_command="go tool wire"` to keep generated directives on the tool version declared by your project. This also avoids the default command's `-mod=mod` restriction in Go workspaces. | +| **Workspace cache correctness** | Provider signature changes in sibling `go.work` modules and local replacements invalidate cached output, including when generation runs from a separate tooling module. Fixed in [v1.3.0](https://github.com/goforj/wire/releases/tag/v1.3.0). | +| **Cache and profiling controls** | Inspect or clear Wire caches with `wire cache` and `wire cache clear`; use `-timings`, `-cpuprofile`, `-memprofile`, and `-trace` to investigate generation costs. | +| **Ongoing maintenance** | Updated Go tooling dependencies, regression tests, and releases, while keeping the provider API small. | + +Generated injectors still call constructors directly. These improvements reduce +generation work; they do not add a runtime container or change how your +application resolves dependencies. + +### Generation performance + +The repository includes a reproducible comparison against Google Wire at +[`9c25c90`](https://github.com/google/wire/commit/9c25c9016f6825302537c4efdd5e897976f9c826). +One run of the synthetic import-scale benchmark on macOS ARM64 with Go 1.25.0, +comparing GoForj Wire v1.3.0, produced: + +| Imported fixture packages | Google Wire | GoForj initial series | GoForj unchanged | Unchanged speedup vs. Google Wire | +| ---: | ---: | ---: | ---: | ---: | +| 10 | 21.2 ms | 16.9 ms | 3.7 ms | 5.69x | +| 100 | 34.1 ms | 34.4 ms | 9.1 ms | 3.75x | +| 1,000 | 180.8 ms | 193.8 ms | 71.9 ms | 2.52x | + +Each value is the median of three generator invocations. The benchmark runs +`go list -deps` before each sample; the unchanged series also runs the generator +once before each timed invocation. The initial series is labeled `cold` by the +script, but reuses caches across its three trials, so it is not a fully cold-cache +comparison. Timings exclude building the generator binaries. + +The main benefit in this run is repeated generation. Initial runs can be slower, +and results depend on graph shape, toolchain, hardware, and cache state. These +numbers measure code generation, not application compilation or runtime speed. + +To reproduce from a repository checkout with Git history: + +```sh +bash scripts/import-benchmarks.sh table +``` + +The script prints the comparison table above with timings from your machine. +For edits that change bodies, signatures, or imports, run +`bash scripts/import-benchmarks.sh scenarios` to compare those paths separately. + ## Installing +**Go 1.25 or later is required for v1.3.0.** Update local and CI toolchains +before upgrading from an older release. + ```sh go install github.com/goforj/wire/cmd/wire@latest ``` @@ -57,7 +107,7 @@ To manage Wire as a project tool dependency, add it to the module and opt into the tool-based generation command: ```sh -go get -tool github.com/goforj/wire/cmd/wire +go get -tool github.com/goforj/wire/cmd/wire@v1.3.0 go tool wire gen -generate_command="go tool wire" ./... ``` @@ -69,8 +119,7 @@ Wire then emits a directive that keeps this choice on subsequent runs: This works with `go generate` in a Go workspace without the default command's `-mod=mod` flag. The project must declare the Wire tool dependency. The tool -directive requires Go 1.24 or newer; the selected Wire version's own Go minimum -also applies. +directive was introduced in Go 1.24; Wire v1.3.0 requires Go 1.25 or later. `-generate_command` is available on `gen`, `diff`, and `watch`. Its value is the command prefix that invokes Wire; Wire appends `gen`, the override itself, and @@ -81,18 +130,31 @@ environment-variable expansion. They are not interpreted by a shell. Omitting the flag preserves the existing `go run -mod=mod` directive and generated output. To restore that default, run `wire gen` without the override. -## Compatibility with google/wire +## Moving from Google Wire -Wire remains compatible with codebases that import `github.com/google/wire`. -The generator emits a `//go:generate` directive pointing at this fork so -`go generate` consistently uses the maintained toolchain. +You can adopt this generator without rewriting provider sets or injector stubs. +It recognizes both `github.com/google/wire` and `github.com/goforj/wire` imports. +Keep your existing `github.com/google/wire` module requirement if your source +still imports it; no module replacement is needed to use the fork's generator. -To keep existing imports unchanged: +Install the fork, then run it in your application's module: ```sh -go mod edit -replace=github.com/google/wire=github.com/goforj/wire@latest +go install github.com/goforj/wire/cmd/wire@v1.3.0 +wire gen ./... ``` +Review and commit the regenerated files. The default `//go:generate` directive +now invokes `github.com/goforj/wire/cmd/wire`; generated files will therefore +change even when the provider graph stays the same. For a pinned, project-owned +tool or a `go.work` workspace, use the [tool dependency setup](#project-owned-tools-and-workspaces) +instead. + +New projects can import `github.com/goforj/wire` directly. The provider API stays +familiar, and generated application code has no runtime dependency on Wire. +The fork's Go 1.25 minimum applies to its module and generator; it does not +require adding a runtime dependency to generated applications. + ## How Wire Works (in 60 seconds) Wire is a **compile-time dependency injection** tool. Instead of building a runtime @@ -109,7 +171,7 @@ A provider is a normal Go function that produces a value (and optionally an erro func NewDB(cfg Config) (*sql.DB, error) { // ... } -```` +``` Dependencies are expressed as function parameters - no globals, no hidden state. @@ -230,9 +292,10 @@ initialization is often simpler and clearer. ## Project Status -This fork tracks the original Wire feature set while prioritizing performance, -determinism, and documentation. Compatibility with existing Wire codebases is a -core goal. +This fork maintains Google Wire's provider model while improving generation +performance, workspace support, and developer tooling. Compatibility with existing +provider definitions is a core goal. See the [release notes](https://github.com/goforj/wire/releases) +for shipped changes and toolchain requirements. ## Community From fed09d32def1708c6a89075fb5e7f3fe1e557789 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Sat, 17 Oct 2026 19:54:17 -0500 Subject: [PATCH 2/4] docs: make the Wire README easier to read --- README.md | 145 ++++++++++++++++++++++++------------------------------ 1 file changed, 65 insertions(+), 80 deletions(-) diff --git a/README.md b/README.md index 7d66a35..9d78436 100644 --- a/README.md +++ b/README.md @@ -20,80 +20,70 @@ No runtime container, no reflection, no hidden magic - just fast, explicit initialization.

-**GoForj Wire is a maintained fork of [Google Wire](https://github.com/google/wire), -which is archived and no longer maintained.** Keep explicit constructor injection -and ordinary generated Go code, with incremental generation, a native watcher, -and project-owned tooling for modern Go workspaces. No GoForj framework dependency -is required. - -Wire is a code generation tool that automates connecting components using -[dependency injection][]. Dependencies between components are represented in -Wire as function parameters, encouraging explicit initialization instead of -global variables. Because Wire operates without runtime state or reflection, -code written to be used with Wire is useful even for hand-written -initialization. +**GoForj Wire is a maintained fork of [Google Wire](https://github.com/google/wire).** +Google Wire is archived. This fork adds caching for faster repeat runs, watch +mode, and support for `go tool wire`. You can use it in any Go project. -For an overview, see the [introductory blog post](https://blog.golang.org/wire). +Wire connects your components by generating the Go code that calls their +constructors. You write normal functions and declare which ones Wire can use. +Wire works out the call order and passes each dependency where it's needed. +The result is code you can read, debug, or write by hand. -[dependency injection]: https://en.wikipedia.org/wiki/Dependency_injection -[godoc]: https://godoc.org/github.com/goforj/wire +For an overview, see the [introductory blog post](https://blog.golang.org/wire). ## Why GoForj Wire? -The improvements are in the generator and development workflow. Providers, -`wire.Build`, provider sets, interface bindings, and cleanup functions retain -Wire's familiar model. +Your providers, `wire.Build` calls, provider sets, interface bindings, and +cleanup functions work the same way as in Google Wire. Here's what this fork adds: -| Improvement over Google Wire | What it gives you | +| Feature | How it helps | | --- | --- | -| **Incremental generation** | Reuse package-loading and generated-output caches on repeated runs, reducing the wait as your dependency graph grows. See the [measured comparison](#generation-performance). | -| **Native watch mode** | Run `wire watch ./...` once to regenerate as Go files change, with filesystem notifications and a polling fallback. | -| **Project-owned generation commands** | Use `-generate_command="go tool wire"` to keep generated directives on the tool version declared by your project. This also avoids the default command's `-mod=mod` restriction in Go workspaces. | -| **Workspace cache correctness** | Provider signature changes in sibling `go.work` modules and local replacements invalidate cached output, including when generation runs from a separate tooling module. Fixed in [v1.3.0](https://github.com/goforj/wire/releases/tag/v1.3.0). | -| **Cache and profiling controls** | Inspect or clear Wire caches with `wire cache` and `wire cache clear`; use `-timings`, `-cpuprofile`, `-memprofile`, and `-trace` to investigate generation costs. | -| **Ongoing maintenance** | Updated Go tooling dependencies, regression tests, and releases, while keeping the provider API small. | +| **Faster repeat runs** | Wire caches package information and generated code so it can skip work on later runs. See the [benchmark results](#generation-performance). | +| **Watch mode** | Run `wire watch ./...` to regenerate when Go files change. It uses filesystem notifications, with polling as a fallback. | +| **Pin Wire in your project** | Use `-generate_command="go tool wire"` so `go generate` uses the Wire version in your `go.mod`. This also works in Go workspaces, where the default command's `-mod=mod` flag causes problems. | +| **Workspace fixes** | Change a constructor in another module, rerun Wire, and its generated calls update too. This works with `go.work` and local `replace` directives, even if you keep Wire in a separate tools module. Fixed in [v1.3.0](https://github.com/goforj/wire/releases/tag/v1.3.0). | +| **Cache and profiling tools** | Use `wire cache` to find the cache directory or `wire cache clear` to clear it. For slow runs, `-timings`, `-cpuprofile`, `-memprofile`, and `-trace` help you find where the time goes. | -Generated injectors still call constructors directly. These improvements reduce -generation work; they do not add a runtime container or change how your -application resolves dependencies. +The generated code still calls your constructors directly, with no runtime +container or reflection. ### Generation performance -The repository includes a reproducible comparison against Google Wire at -[`9c25c90`](https://github.com/google/wire/commit/9c25c9016f6825302537c4efdd5e897976f9c826). -One run of the synthetic import-scale benchmark on macOS ARM64 with Go 1.25.0, -comparing GoForj Wire v1.3.0, produced: +We compared GoForj Wire v1.3.0 with Google Wire at +[`9c25c90`](https://github.com/google/wire/commit/9c25c9016f6825302537c4efdd5e897976f9c826) +using test projects with 10, 100, and 1,000 imported packages. These results are +from macOS ARM64 with Go 1.25.0: -| Imported fixture packages | Google Wire | GoForj initial series | GoForj unchanged | Unchanged speedup vs. Google Wire | +| Imported packages | Google Wire | GoForj initial runs | GoForj warmed cache | Warmed cache speedup | | ---: | ---: | ---: | ---: | ---: | | 10 | 21.2 ms | 16.9 ms | 3.7 ms | 5.69x | | 100 | 34.1 ms | 34.4 ms | 9.1 ms | 3.75x | | 1,000 | 180.8 ms | 193.8 ms | 71.9 ms | 2.52x | -Each value is the median of three generator invocations. The benchmark runs -`go list -deps` before each sample; the unchanged series also runs the generator -once before each timed invocation. The initial series is labeled `cold` by the -script, but reuses caches across its three trials, so it is not a fully cold-cache -comparison. Timings exclude building the generator binaries. +Each number is the median of three runs. The script runs `go list -deps` before +each measurement. For the warmed-cache results, it also runs Wire once before +timing it. The initial runs reuse caches across their three measurements, even +though the script labels them `cold`. They aren't a fully cold-cache test. +Building the generator binaries isn't included in the timings. -The main benefit in this run is repeated generation. Initial runs can be slower, -and results depend on graph shape, toolchain, hardware, and cache state. These -numbers measure code generation, not application compilation or runtime speed. +Repeat runs were faster in this test. The initial runs were slower at 1,000 +packages. Your results will depend on your project, toolchain, hardware, and +cache state. These numbers time Wire, not `go build` or your running application. -To reproduce from a repository checkout with Git history: +Run the same comparison from a checkout with Git history: ```sh bash scripts/import-benchmarks.sh table ``` -The script prints the comparison table above with timings from your machine. -For edits that change bodies, signatures, or imports, run -`bash scripts/import-benchmarks.sh scenarios` to compare those paths separately. +The script prints a table with your timings. To compare what happens after +changing a function body, a constructor signature, or an import, run +`bash scripts/import-benchmarks.sh scenarios`. ## Installing -**Go 1.25 or later is required for v1.3.0.** Update local and CI toolchains -before upgrading from an older release. +**Wire v1.3.0 requires Go 1.25 or later.** Check your local and CI Go versions +before upgrading. ```sh go install github.com/goforj/wire/cmd/wire@latest @@ -101,41 +91,39 @@ go install github.com/goforj/wire/cmd/wire@latest Ensure `$GOPATH/bin` is in your `$PATH`. -### Project-owned tools and workspaces +### Pinning Wire in your project -To manage Wire as a project tool dependency, add it to the module and opt into -the tool-based generation command: +To pin Wire's version in your `go.mod`, add it as a Go tool: ```sh go get -tool github.com/goforj/wire/cmd/wire@v1.3.0 go tool wire gen -generate_command="go tool wire" ./... ``` -Wire then emits a directive that keeps this choice on subsequent runs: +Wire adds this command to the generated file: ```go //go:generate go tool wire gen -generate_command "go tool wire" ``` -This works with `go generate` in a Go workspace without the default command's -`-mod=mod` flag. The project must declare the Wire tool dependency. The tool -directive was introduced in Go 1.24; Wire v1.3.0 requires Go 1.25 or later. +Now `go generate` uses `go tool wire` too. This works inside a `go.work` workspace, +where the default `go run -mod=mod` command would fail. Keep the tool dependency +in your `go.mod`. Wire v1.3.0 requires Go 1.25 or later. -`-generate_command` is available on `gen`, `diff`, and `watch`. Its value is the -command prefix that invokes Wire; Wire appends `gen`, the override itself, and -any build tags. Custom wrappers must forward those arguments. Commands follow -Go generate's argument rules, including Go-style double-quoted arguments and -environment-variable expansion. They are not interpreted by a shell. +You can use `-generate_command` with `gen`, `diff`, and `watch`. Wire adds `gen`, +the `-generate_command` flag, and any build tags to the command you supply. If +you use a wrapper script, make sure it forwards those arguments. Quoting and +environment variables follow `go generate` rules; shell operators aren't supported. -Omitting the flag preserves the existing `go run -mod=mod` directive and generated -output. To restore that default, run `wire gen` without the override. +Without the flag, Wire generates its usual `go run -mod=mod` directive. Run +`wire gen` without the flag to switch back to that default. ## Moving from Google Wire -You can adopt this generator without rewriting provider sets or injector stubs. -It recognizes both `github.com/google/wire` and `github.com/goforj/wire` imports. -Keep your existing `github.com/google/wire` module requirement if your source -still imports it; no module replacement is needed to use the fork's generator. +Keep your existing provider sets and injector stubs. The generator accepts both +`github.com/google/wire` and `github.com/goforj/wire` imports. If your code still +imports Google Wire, keep that dependency in your `go.mod`. You don't need a +`replace` directive to use this generator. Install the fork, then run it in your application's module: @@ -144,16 +132,14 @@ go install github.com/goforj/wire/cmd/wire@v1.3.0 wire gen ./... ``` -Review and commit the regenerated files. The default `//go:generate` directive -now invokes `github.com/goforj/wire/cmd/wire`; generated files will therefore -change even when the provider graph stays the same. For a pinned, project-owned -tool or a `go.work` workspace, use the [tool dependency setup](#project-owned-tools-and-workspaces) -instead. +Review and commit the regenerated files. Their `//go:generate` command now points +to `github.com/goforj/wire/cmd/wire`, so you'll see a diff even if your providers +haven't changed. To pin the generator version or use it in a `go.work` workspace, +follow [Pinning Wire in your project](#pinning-wire-in-your-project). -New projects can import `github.com/goforj/wire` directly. The provider API stays -familiar, and generated application code has no runtime dependency on Wire. -The fork's Go 1.25 minimum applies to its module and generator; it does not -require adding a runtime dependency to generated applications. +New projects can import `github.com/goforj/wire` directly. Its module and generator +require Go 1.25. The generated initialization code has no runtime dependency on +Wire. ## How Wire Works (in 60 seconds) @@ -235,7 +221,7 @@ runtime dependency on Wire. ## Running generation manually -Use `make generate` to run `wire gen` across the repository. The CLI remains useful for targeted generation: +Use `make generate` to run `wire gen` across this repository. In your own project, run: ```sh wire @@ -255,7 +241,7 @@ wire watch wire watch ./... ``` -Detects the package root automatically and uses native filesystem notifications when available (with a polling fallback). +Wire finds the package root automatically. It uses filesystem notifications when available and falls back to polling. ## Design Guidance @@ -292,10 +278,9 @@ initialization is often simpler and clearer. ## Project Status -This fork maintains Google Wire's provider model while improving generation -performance, workspace support, and developer tooling. Compatibility with existing -provider definitions is a core goal. See the [release notes](https://github.com/goforj/wire/releases) -for shipped changes and toolchain requirements. +We maintain this fork so existing Wire projects can keep using it with current +Go tooling. See the [release notes](https://github.com/goforj/wire/releases) for +changes and required Go versions. ## Community From 9f516302466558dbd8cc0a241c99bf45571ec60b Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Sat, 17 Oct 2026 20:33:26 -0500 Subject: [PATCH 3/4] docs: lead with verified generation benchmarks --- README.md | 57 +- docs/benchmarks/cold-go-cache.csv | 13 + docs/benchmarks/newer-go-comparison.csv | 241 +++++++ docs/benchmarks/release-comparison-go1.25.csv | 601 ++++++++++++++++++ docs/benchmarks/workspace-comparison.csv | 81 +++ docs/performance.md | 301 +++++++++ 6 files changed, 1273 insertions(+), 21 deletions(-) create mode 100644 docs/benchmarks/cold-go-cache.csv create mode 100644 docs/benchmarks/newer-go-comparison.csv create mode 100644 docs/benchmarks/release-comparison-go1.25.csv create mode 100644 docs/benchmarks/workspace-comparison.csv create mode 100644 docs/performance.md diff --git a/README.md b/README.md index 9d78436..5850d3a 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,11 @@ For an overview, see the [introductory blog post](https://blog.golang.org/wire). ## Why GoForj Wire? +**Fast reruns, even with hundreds of dependencies.** In our test apps, unchanged +generation was **84-109x faster than Google Wire** with warm caches. After a +function body edit, it was **18-19x faster**. See the [benchmarks](#generation-performance) +for project sizes, timings, and first-run costs. + Your providers, `wire.Build` calls, provider sets, interface bindings, and cleanup functions work the same way as in Google Wire. Here's what this fork adds: @@ -50,35 +55,45 @@ container or reflection. ### Generation performance We compared GoForj Wire v1.3.0 with Google Wire at -[`9c25c90`](https://github.com/google/wire/commit/9c25c9016f6825302537c4efdd5e897976f9c826) -using test projects with 10, 100, and 1,000 imported packages. These results are -from macOS ARM64 with Go 1.25.0: +[`9c25c90`](https://github.com/google/wire/commit/9c25c9016f6825302537c4efdd5e897976f9c826). +For a test app with **117 local packages and 342 external packages**, these were +the median times across five runs: + +| Change | Google Wire | GoForj Wire | Speedup | +| --- | ---: | ---: | ---: | +| Unchanged | 1,582.0 ms | 18.9 ms | 83.7x | +| Function body edit | 1,580.0 ms | 87.3 ms | 18.1x | +| Constructor argument added | 1,604.8 ms | 262.6 ms | 6.1x | +| Import added | 1,567.2 ms | 267.8 ms | 5.9x | + +The app with 42 local and 342 external packages reached **109x** on unchanged +runs. The project with 1,016 local packages and few external dependencies saw +**9x**. The kind of dependencies matters as well as the package count. -| Imported packages | Google Wire | GoForj initial runs | GoForj warmed cache | Warmed cache speedup | -| ---: | ---: | ---: | ---: | ---: | -| 10 | 21.2 ms | 16.9 ms | 3.7 ms | 5.69x | -| 100 | 34.1 ms | 34.4 ms | 9.1 ms | 3.75x | -| 1,000 | 180.8 ms | 193.8 ms | 71.9 ms | 2.52x | +These measurements use Go 1.25.0 on an Apple M4 Max, with Go's dependency build +data already cached. Each edit starts from a warmed Wire cache. They time +**code generation**, not `go build` or your running application. -Each number is the median of three runs. The script runs `go list -deps` before -each measurement. For the warmed-cache results, it also runs Wire once before -timing it. The initial runs reuse caches across their three measurements, even -though the script labels them `cold`. They aren't a fully cold-cache test. -Building the generator binaries isn't included in the timings. +First runs can be slower while Wire builds its caches. The workspace fix also +adds some checking: in the large local workspace, a cached rerun went from +59.9 to 62.9 ms. With opt-in content hashing, it went from 62.1 to 72.6 ms because +Wire now reads sibling files that older releases skipped. -Repeat runs were faster in this test. The initial runs were slower at 1,000 -packages. Your results will depend on your project, toolchain, hardware, and -cache state. These numbers time Wire, not `go build` or your running application. +We also compared v1.2.0 and v1.3.0 across Go 1.25, 1.26, and 1.27, checked cold +caches, and compiled the generated code after edits. See the +[full results and raw measurements](docs/performance.md) for the release +comparison, cache settings, and tradeoffs. -Run the same comparison from a checkout with Git history: +To run the repository's application benchmark: ```sh -bash scripts/import-benchmarks.sh table +bash scripts/import-benchmarks.sh scenarios ``` -The script prints a table with your timings. To compare what happens after -changing a function body, a constructor signature, or an import, run -`bash scripts/import-benchmarks.sh scenarios`. +That runner prints three-run medians and includes a cold Go cache case. The +five-run comparison above warms Go's dependency build data first; the full +report explains both setups. For the smaller import-only test, run +`bash scripts/import-benchmarks.sh table`. ## Installing diff --git a/docs/benchmarks/cold-go-cache.csv b/docs/benchmarks/cold-go-cache.csv new file mode 100644 index 0000000..1ef82fe --- /dev/null +++ b/docs/benchmarks/cold-go-cache.csv @@ -0,0 +1,13 @@ +profile,version,trial,ms +local-high,v1.2.0,0,7487.182 +local-high,v1.3.0,0,7627.225 +local-high,v1.3.0,1,6945.317 +local-high,v1.2.0,1,7283.108 +local-high,v1.2.0,2,7256.294 +local-high,v1.3.0,2,7435.483 +external-high,v1.2.0,0,10548.371 +external-high,v1.3.0,0,10133.144 +external-high,v1.3.0,1,10281.93 +external-high,v1.2.0,1,10156.272 +external-high,v1.2.0,2,10136.897 +external-high,v1.3.0,2,10218.191 diff --git a/docs/benchmarks/newer-go-comparison.csv b/docs/benchmarks/newer-go-comparison.csv new file mode 100644 index 0000000..069ba49 --- /dev/null +++ b/docs/benchmarks/newer-go-comparison.csv @@ -0,0 +1,241 @@ +toolchain,profile,cache_mode,trial,version,scenario,ms +1.26.3,local-high,mtime,0,v1.2.0,unchanged,60.922 +1.26.3,local-high,mtime,0,v1.2.0,body,138.757 +1.26.3,local-high,mtime,0,v1.2.0,shape,502.716 +1.26.3,local-high,mtime,0,v1.3.0,unchanged,66.359 +1.26.3,local-high,mtime,0,v1.3.0,body,140.299 +1.26.3,local-high,mtime,0,v1.3.0,shape,434.193 +1.26.3,local-high,mtime,1,v1.3.0,unchanged,65.636 +1.26.3,local-high,mtime,1,v1.3.0,body,141.51 +1.26.3,local-high,mtime,1,v1.3.0,shape,431.35 +1.26.3,local-high,mtime,1,v1.2.0,unchanged,63.621 +1.26.3,local-high,mtime,1,v1.2.0,body,140.052 +1.26.3,local-high,mtime,1,v1.2.0,shape,420.623 +1.26.3,local-high,mtime,2,v1.2.0,unchanged,64.859 +1.26.3,local-high,mtime,2,v1.2.0,body,148.76 +1.26.3,local-high,mtime,2,v1.2.0,shape,438.903 +1.26.3,local-high,mtime,2,v1.3.0,unchanged,64.95 +1.26.3,local-high,mtime,2,v1.3.0,body,140.083 +1.26.3,local-high,mtime,2,v1.3.0,shape,410.389 +1.26.3,local-high,mtime,3,v1.3.0,unchanged,64.076 +1.26.3,local-high,mtime,3,v1.3.0,body,134.838 +1.26.3,local-high,mtime,3,v1.3.0,shape,417.922 +1.26.3,local-high,mtime,3,v1.2.0,unchanged,62.555 +1.26.3,local-high,mtime,3,v1.2.0,body,139.969 +1.26.3,local-high,mtime,3,v1.2.0,shape,423.112 +1.26.3,local-high,mtime,4,v1.2.0,unchanged,61.408 +1.26.3,local-high,mtime,4,v1.2.0,body,138.633 +1.26.3,local-high,mtime,4,v1.2.0,shape,411.109 +1.26.3,local-high,mtime,4,v1.3.0,unchanged,62.385 +1.26.3,local-high,mtime,4,v1.3.0,body,136.757 +1.26.3,local-high,mtime,4,v1.3.0,shape,419.258 +1.26.3,local-high,content,0,v1.2.0,unchanged,81.917 +1.26.3,local-high,content,0,v1.2.0,body,170.805 +1.26.3,local-high,content,0,v1.2.0,shape,464.183 +1.26.3,local-high,content,0,v1.3.0,unchanged,74.36 +1.26.3,local-high,content,0,v1.3.0,body,173.664 +1.26.3,local-high,content,0,v1.3.0,shape,451.953 +1.26.3,local-high,content,1,v1.3.0,unchanged,72.997 +1.26.3,local-high,content,1,v1.3.0,body,171.421 +1.26.3,local-high,content,1,v1.3.0,shape,441.859 +1.26.3,local-high,content,1,v1.2.0,unchanged,71.794 +1.26.3,local-high,content,1,v1.2.0,body,169.956 +1.26.3,local-high,content,1,v1.2.0,shape,440.114 +1.26.3,local-high,content,2,v1.2.0,unchanged,73.018 +1.26.3,local-high,content,2,v1.2.0,body,175.972 +1.26.3,local-high,content,2,v1.2.0,shape,438.055 +1.26.3,local-high,content,2,v1.3.0,unchanged,74.406 +1.26.3,local-high,content,2,v1.3.0,body,174.34 +1.26.3,local-high,content,2,v1.3.0,shape,468.181 +1.26.3,local-high,content,3,v1.3.0,unchanged,75.566 +1.26.3,local-high,content,3,v1.3.0,body,175.521 +1.26.3,local-high,content,3,v1.3.0,shape,448.674 +1.26.3,local-high,content,3,v1.2.0,unchanged,73.274 +1.26.3,local-high,content,3,v1.2.0,body,173.669 +1.26.3,local-high,content,3,v1.2.0,shape,443.099 +1.26.3,local-high,content,4,v1.2.0,unchanged,73.838 +1.26.3,local-high,content,4,v1.2.0,body,173.385 +1.26.3,local-high,content,4,v1.2.0,shape,452.678 +1.26.3,local-high,content,4,v1.3.0,unchanged,74.447 +1.26.3,local-high,content,4,v1.3.0,body,170.553 +1.26.3,local-high,content,4,v1.3.0,shape,436.453 +1.26.3,external-high,mtime,0,v1.2.0,unchanged,19.298 +1.26.3,external-high,mtime,0,v1.2.0,body,93.016 +1.26.3,external-high,mtime,0,v1.2.0,shape,331.957 +1.26.3,external-high,mtime,0,v1.3.0,unchanged,19.521 +1.26.3,external-high,mtime,0,v1.3.0,body,87.103 +1.26.3,external-high,mtime,0,v1.3.0,shape,279.499 +1.26.3,external-high,mtime,1,v1.3.0,unchanged,20.012 +1.26.3,external-high,mtime,1,v1.3.0,body,99.305 +1.26.3,external-high,mtime,1,v1.3.0,shape,274.584 +1.26.3,external-high,mtime,1,v1.2.0,unchanged,18.166 +1.26.3,external-high,mtime,1,v1.2.0,body,88.919 +1.26.3,external-high,mtime,1,v1.2.0,shape,262.575 +1.26.3,external-high,mtime,2,v1.2.0,unchanged,19.996 +1.26.3,external-high,mtime,2,v1.2.0,body,88.913 +1.26.3,external-high,mtime,2,v1.2.0,shape,261.798 +1.26.3,external-high,mtime,2,v1.3.0,unchanged,19.147 +1.26.3,external-high,mtime,2,v1.3.0,body,90.002 +1.26.3,external-high,mtime,2,v1.3.0,shape,280.133 +1.26.3,external-high,mtime,3,v1.3.0,unchanged,19.222 +1.26.3,external-high,mtime,3,v1.3.0,body,87.402 +1.26.3,external-high,mtime,3,v1.3.0,shape,270.104 +1.26.3,external-high,mtime,3,v1.2.0,unchanged,19.606 +1.26.3,external-high,mtime,3,v1.2.0,body,91.084 +1.26.3,external-high,mtime,3,v1.2.0,shape,268.714 +1.26.3,external-high,mtime,4,v1.2.0,unchanged,21.846 +1.26.3,external-high,mtime,4,v1.2.0,body,89.721 +1.26.3,external-high,mtime,4,v1.2.0,shape,276.864 +1.26.3,external-high,mtime,4,v1.3.0,unchanged,18.436 +1.26.3,external-high,mtime,4,v1.3.0,body,85.619 +1.26.3,external-high,mtime,4,v1.3.0,shape,258.508 +1.26.3,external-high,content,0,v1.2.0,unchanged,20.071 +1.26.3,external-high,content,0,v1.2.0,body,151.383 +1.26.3,external-high,content,0,v1.2.0,shape,325.355 +1.26.3,external-high,content,0,v1.3.0,unchanged,19.567 +1.26.3,external-high,content,0,v1.3.0,body,148.001 +1.26.3,external-high,content,0,v1.3.0,shape,327.68 +1.26.3,external-high,content,1,v1.3.0,unchanged,20.495 +1.26.3,external-high,content,1,v1.3.0,body,149.986 +1.26.3,external-high,content,1,v1.3.0,shape,311.048 +1.26.3,external-high,content,1,v1.2.0,unchanged,20.634 +1.26.3,external-high,content,1,v1.2.0,body,151.034 +1.26.3,external-high,content,1,v1.2.0,shape,327.038 +1.26.3,external-high,content,2,v1.2.0,unchanged,21.4 +1.26.3,external-high,content,2,v1.2.0,body,150.358 +1.26.3,external-high,content,2,v1.2.0,shape,334.869 +1.26.3,external-high,content,2,v1.3.0,unchanged,20.788 +1.26.3,external-high,content,2,v1.3.0,body,153.642 +1.26.3,external-high,content,2,v1.3.0,shape,338.686 +1.26.3,external-high,content,3,v1.3.0,unchanged,19.847 +1.26.3,external-high,content,3,v1.3.0,body,162.934 +1.26.3,external-high,content,3,v1.3.0,shape,330.397 +1.26.3,external-high,content,3,v1.2.0,unchanged,20.374 +1.26.3,external-high,content,3,v1.2.0,body,152.024 +1.26.3,external-high,content,3,v1.2.0,shape,357.434 +1.26.3,external-high,content,4,v1.2.0,unchanged,20.572 +1.26.3,external-high,content,4,v1.2.0,body,155.703 +1.26.3,external-high,content,4,v1.2.0,shape,322.39 +1.26.3,external-high,content,4,v1.3.0,unchanged,20.473 +1.26.3,external-high,content,4,v1.3.0,body,146.879 +1.26.3,external-high,content,4,v1.3.0,shape,310.578 +1.27.2,local-high,mtime,0,v1.2.0,unchanged,61.819 +1.27.2,local-high,mtime,0,v1.2.0,body,150.314 +1.27.2,local-high,mtime,0,v1.2.0,shape,500.785 +1.27.2,local-high,mtime,0,v1.3.0,unchanged,65.607 +1.27.2,local-high,mtime,0,v1.3.0,body,145.606 +1.27.2,local-high,mtime,0,v1.3.0,shape,423.897 +1.27.2,local-high,mtime,1,v1.3.0,unchanged,64.446 +1.27.2,local-high,mtime,1,v1.3.0,body,142.411 +1.27.2,local-high,mtime,1,v1.3.0,shape,423.563 +1.27.2,local-high,mtime,1,v1.2.0,unchanged,65.542 +1.27.2,local-high,mtime,1,v1.2.0,body,150.072 +1.27.2,local-high,mtime,1,v1.2.0,shape,413.172 +1.27.2,local-high,mtime,2,v1.2.0,unchanged,61.364 +1.27.2,local-high,mtime,2,v1.2.0,body,141.067 +1.27.2,local-high,mtime,2,v1.2.0,shape,665.999 +1.27.2,local-high,mtime,2,v1.3.0,unchanged,63.022 +1.27.2,local-high,mtime,2,v1.3.0,body,138.836 +1.27.2,local-high,mtime,2,v1.3.0,shape,421.227 +1.27.2,local-high,mtime,3,v1.3.0,unchanged,61.517 +1.27.2,local-high,mtime,3,v1.3.0,body,136.614 +1.27.2,local-high,mtime,3,v1.3.0,shape,399.679 +1.27.2,local-high,mtime,3,v1.2.0,unchanged,60.883 +1.27.2,local-high,mtime,3,v1.2.0,body,138.404 +1.27.2,local-high,mtime,3,v1.2.0,shape,389.488 +1.27.2,local-high,mtime,4,v1.2.0,unchanged,60.928 +1.27.2,local-high,mtime,4,v1.2.0,body,134.339 +1.27.2,local-high,mtime,4,v1.2.0,shape,394.651 +1.27.2,local-high,mtime,4,v1.3.0,unchanged,60.038 +1.27.2,local-high,mtime,4,v1.3.0,body,139.344 +1.27.2,local-high,mtime,4,v1.3.0,shape,387.564 +1.27.2,local-high,content,0,v1.2.0,unchanged,69.421 +1.27.2,local-high,content,0,v1.2.0,body,181.924 +1.27.2,local-high,content,0,v1.2.0,shape,423.663 +1.27.2,local-high,content,0,v1.3.0,unchanged,70.152 +1.27.2,local-high,content,0,v1.3.0,body,173.224 +1.27.2,local-high,content,0,v1.3.0,shape,422.784 +1.27.2,local-high,content,1,v1.3.0,unchanged,69.649 +1.27.2,local-high,content,1,v1.3.0,body,169.763 +1.27.2,local-high,content,1,v1.3.0,shape,438.999 +1.27.2,local-high,content,1,v1.2.0,unchanged,71.442 +1.27.2,local-high,content,1,v1.2.0,body,186.526 +1.27.2,local-high,content,1,v1.2.0,shape,415.296 +1.27.2,local-high,content,2,v1.2.0,unchanged,69.564 +1.27.2,local-high,content,2,v1.2.0,body,165.983 +1.27.2,local-high,content,2,v1.2.0,shape,453.892 +1.27.2,local-high,content,2,v1.3.0,unchanged,80.58 +1.27.2,local-high,content,2,v1.3.0,body,182.564 +1.27.2,local-high,content,2,v1.3.0,shape,443.961 +1.27.2,local-high,content,3,v1.3.0,unchanged,75.641 +1.27.2,local-high,content,3,v1.3.0,body,178.881 +1.27.2,local-high,content,3,v1.3.0,shape,439.73 +1.27.2,local-high,content,3,v1.2.0,unchanged,94.36 +1.27.2,local-high,content,3,v1.2.0,body,187.584 +1.27.2,local-high,content,3,v1.2.0,shape,470.815 +1.27.2,local-high,content,4,v1.2.0,unchanged,77.623 +1.27.2,local-high,content,4,v1.2.0,body,187.504 +1.27.2,local-high,content,4,v1.2.0,shape,449.649 +1.27.2,local-high,content,4,v1.3.0,unchanged,71.481 +1.27.2,local-high,content,4,v1.3.0,body,325.203 +1.27.2,local-high,content,4,v1.3.0,shape,449.397 +1.27.2,external-high,mtime,0,v1.2.0,unchanged,19.406 +1.27.2,external-high,mtime,0,v1.2.0,body,95.207 +1.27.2,external-high,mtime,0,v1.2.0,shape,324.342 +1.27.2,external-high,mtime,0,v1.3.0,unchanged,19.609 +1.27.2,external-high,mtime,0,v1.3.0,body,90.416 +1.27.2,external-high,mtime,0,v1.3.0,shape,305.12 +1.27.2,external-high,mtime,1,v1.3.0,unchanged,21.403 +1.27.2,external-high,mtime,1,v1.3.0,body,92.52 +1.27.2,external-high,mtime,1,v1.3.0,shape,289.854 +1.27.2,external-high,mtime,1,v1.2.0,unchanged,33.835 +1.27.2,external-high,mtime,1,v1.2.0,body,97.768 +1.27.2,external-high,mtime,1,v1.2.0,shape,409.431 +1.27.2,external-high,mtime,2,v1.2.0,unchanged,21.638 +1.27.2,external-high,mtime,2,v1.2.0,body,92.461 +1.27.2,external-high,mtime,2,v1.2.0,shape,291.045 +1.27.2,external-high,mtime,2,v1.3.0,unchanged,19.892 +1.27.2,external-high,mtime,2,v1.3.0,body,109.513 +1.27.2,external-high,mtime,2,v1.3.0,shape,467.358 +1.27.2,external-high,mtime,3,v1.3.0,unchanged,23.095 +1.27.2,external-high,mtime,3,v1.3.0,body,93.044 +1.27.2,external-high,mtime,3,v1.3.0,shape,294.048 +1.27.2,external-high,mtime,3,v1.2.0,unchanged,19.615 +1.27.2,external-high,mtime,3,v1.2.0,body,95.279 +1.27.2,external-high,mtime,3,v1.2.0,shape,294.424 +1.27.2,external-high,mtime,4,v1.2.0,unchanged,20.392 +1.27.2,external-high,mtime,4,v1.2.0,body,94.558 +1.27.2,external-high,mtime,4,v1.2.0,shape,308.421 +1.27.2,external-high,mtime,4,v1.3.0,unchanged,23.552 +1.27.2,external-high,mtime,4,v1.3.0,body,96.173 +1.27.2,external-high,mtime,4,v1.3.0,shape,470.395 +1.27.2,external-high,content,0,v1.2.0,unchanged,22.805 +1.27.2,external-high,content,0,v1.2.0,body,150.887 +1.27.2,external-high,content,0,v1.2.0,shape,352.197 +1.27.2,external-high,content,0,v1.3.0,unchanged,20.798 +1.27.2,external-high,content,0,v1.3.0,body,152.549 +1.27.2,external-high,content,0,v1.3.0,shape,378.251 +1.27.2,external-high,content,1,v1.3.0,unchanged,20.795 +1.27.2,external-high,content,1,v1.3.0,body,153.777 +1.27.2,external-high,content,1,v1.3.0,shape,325.195 +1.27.2,external-high,content,1,v1.2.0,unchanged,22.666 +1.27.2,external-high,content,1,v1.2.0,body,180.567 +1.27.2,external-high,content,1,v1.2.0,shape,565.808 +1.27.2,external-high,content,2,v1.2.0,unchanged,23.148 +1.27.2,external-high,content,2,v1.2.0,body,163.153 +1.27.2,external-high,content,2,v1.2.0,shape,418.052 +1.27.2,external-high,content,2,v1.3.0,unchanged,20.965 +1.27.2,external-high,content,2,v1.3.0,body,166.478 +1.27.2,external-high,content,2,v1.3.0,shape,411.494 +1.27.2,external-high,content,3,v1.3.0,unchanged,21.096 +1.27.2,external-high,content,3,v1.3.0,body,151.89 +1.27.2,external-high,content,3,v1.3.0,shape,358.877 +1.27.2,external-high,content,3,v1.2.0,unchanged,20.53 +1.27.2,external-high,content,3,v1.2.0,body,150.272 +1.27.2,external-high,content,3,v1.2.0,shape,310.338 +1.27.2,external-high,content,4,v1.2.0,unchanged,21.208 +1.27.2,external-high,content,4,v1.2.0,body,146.976 +1.27.2,external-high,content,4,v1.2.0,shape,315.426 +1.27.2,external-high,content,4,v1.3.0,unchanged,21.329 +1.27.2,external-high,content,4,v1.3.0,body,154.658 +1.27.2,external-high,content,4,v1.3.0,shape,323.282 diff --git a/docs/benchmarks/release-comparison-go1.25.csv b/docs/benchmarks/release-comparison-go1.25.csv new file mode 100644 index 0000000..df6d21a --- /dev/null +++ b/docs/benchmarks/release-comparison-go1.25.csv @@ -0,0 +1,601 @@ +profile,trial,version,cache_mode,scenario,ms +external-high,0,stock,n/a,wire-cold,1756.253 +external-high,0,stock,n/a,unchanged,1667.037 +external-high,0,stock,n/a,body,1544.813 +external-high,0,stock,n/a,shape,1570.83 +external-high,0,stock,n/a,import,1546.163 +external-high,0,stock,n/a,known-import-toggle,1573.319 +external-high,0,v1.2.0,mtime,wire-cold,1648.651 +external-high,0,v1.2.0,mtime,unchanged,19.591 +external-high,0,v1.2.0,mtime,body,94.541 +external-high,0,v1.2.0,mtime,shape,254.685 +external-high,0,v1.2.0,mtime,import,276.717 +external-high,0,v1.2.0,mtime,known-import-toggle,279.463 +external-high,0,v1.3.0,mtime,wire-cold,1697.418 +external-high,0,v1.3.0,mtime,unchanged,18.924 +external-high,0,v1.3.0,mtime,body,86.761 +external-high,0,v1.3.0,mtime,shape,269.778 +external-high,0,v1.3.0,mtime,import,263.526 +external-high,0,v1.3.0,mtime,known-import-toggle,274.988 +external-high,0,v1.2.0,content,wire-cold,1877.132 +external-high,0,v1.2.0,content,unchanged,19.439 +external-high,0,v1.2.0,content,body,150.086 +external-high,0,v1.2.0,content,shape,313.558 +external-high,0,v1.2.0,content,import,315.638 +external-high,0,v1.2.0,content,known-import-toggle,193.201 +external-high,0,v1.3.0,content,wire-cold,1864.373 +external-high,0,v1.3.0,content,unchanged,20.703 +external-high,0,v1.3.0,content,body,146.609 +external-high,0,v1.3.0,content,shape,315.254 +external-high,0,v1.3.0,content,import,320.135 +external-high,0,v1.3.0,content,known-import-toggle,196.964 +external-high,1,v1.2.0,mtime,wire-cold,1544.048 +external-high,1,v1.2.0,mtime,unchanged,18.34 +external-high,1,v1.2.0,mtime,body,86.605 +external-high,1,v1.2.0,mtime,shape,258.317 +external-high,1,v1.2.0,mtime,import,275.493 +external-high,1,v1.2.0,mtime,known-import-toggle,264.224 +external-high,1,v1.3.0,mtime,wire-cold,1547.103 +external-high,1,v1.3.0,mtime,unchanged,18.404 +external-high,1,v1.3.0,mtime,body,87.253 +external-high,1,v1.3.0,mtime,shape,262.586 +external-high,1,v1.3.0,mtime,import,291.899 +external-high,1,v1.3.0,mtime,known-import-toggle,284.47 +external-high,1,v1.2.0,content,wire-cold,1884.351 +external-high,1,v1.2.0,content,unchanged,21.856 +external-high,1,v1.2.0,content,body,145.81 +external-high,1,v1.2.0,content,shape,329.374 +external-high,1,v1.2.0,content,import,317.374 +external-high,1,v1.2.0,content,known-import-toggle,191.355 +external-high,1,v1.3.0,content,wire-cold,1808.51 +external-high,1,v1.3.0,content,unchanged,19.222 +external-high,1,v1.3.0,content,body,138.539 +external-high,1,v1.3.0,content,shape,302.168 +external-high,1,v1.3.0,content,import,318.224 +external-high,1,v1.3.0,content,known-import-toggle,205.051 +external-high,1,stock,n/a,wire-cold,1592.842 +external-high,1,stock,n/a,unchanged,1549.526 +external-high,1,stock,n/a,body,1590.747 +external-high,1,stock,n/a,shape,1604.779 +external-high,1,stock,n/a,import,1592.054 +external-high,1,stock,n/a,known-import-toggle,1615.101 +external-high,2,v1.3.0,mtime,wire-cold,1584.637 +external-high,2,v1.3.0,mtime,unchanged,19.06 +external-high,2,v1.3.0,mtime,body,88.083 +external-high,2,v1.3.0,mtime,shape,262.523 +external-high,2,v1.3.0,mtime,import,274.947 +external-high,2,v1.3.0,mtime,known-import-toggle,272.286 +external-high,2,v1.2.0,content,wire-cold,1903.168 +external-high,2,v1.2.0,content,unchanged,21.166 +external-high,2,v1.2.0,content,body,144.271 +external-high,2,v1.2.0,content,shape,314.617 +external-high,2,v1.2.0,content,import,333.374 +external-high,2,v1.2.0,content,known-import-toggle,200.291 +external-high,2,v1.3.0,content,wire-cold,1844.696 +external-high,2,v1.3.0,content,unchanged,20.084 +external-high,2,v1.3.0,content,body,139.352 +external-high,2,v1.3.0,content,shape,315.977 +external-high,2,v1.3.0,content,import,313.672 +external-high,2,v1.3.0,content,known-import-toggle,201.086 +external-high,2,stock,n/a,wire-cold,1648.426 +external-high,2,stock,n/a,unchanged,1587.368 +external-high,2,stock,n/a,body,1507.565 +external-high,2,stock,n/a,shape,1633.731 +external-high,2,stock,n/a,import,1669.271 +external-high,2,stock,n/a,known-import-toggle,1544.156 +external-high,2,v1.2.0,mtime,wire-cold,1542.504 +external-high,2,v1.2.0,mtime,unchanged,18.474 +external-high,2,v1.2.0,mtime,body,90.426 +external-high,2,v1.2.0,mtime,shape,265.019 +external-high,2,v1.2.0,mtime,import,265.976 +external-high,2,v1.2.0,mtime,known-import-toggle,270.35 +external-high,3,v1.2.0,content,wire-cold,1828.029 +external-high,3,v1.2.0,content,unchanged,19.972 +external-high,3,v1.2.0,content,body,148.321 +external-high,3,v1.2.0,content,shape,312.56 +external-high,3,v1.2.0,content,import,326.19 +external-high,3,v1.2.0,content,known-import-toggle,197.776 +external-high,3,v1.3.0,content,wire-cold,1853.158 +external-high,3,v1.3.0,content,unchanged,19.894 +external-high,3,v1.3.0,content,body,142.148 +external-high,3,v1.3.0,content,shape,312.494 +external-high,3,v1.3.0,content,import,325.165 +external-high,3,v1.3.0,content,known-import-toggle,196.087 +external-high,3,stock,n/a,wire-cold,1592.082 +external-high,3,stock,n/a,unchanged,1573.995 +external-high,3,stock,n/a,body,1596.583 +external-high,3,stock,n/a,shape,1560.602 +external-high,3,stock,n/a,import,1552.445 +external-high,3,stock,n/a,known-import-toggle,1574.893 +external-high,3,v1.2.0,mtime,wire-cold,1552.241 +external-high,3,v1.2.0,mtime,unchanged,17.845 +external-high,3,v1.2.0,mtime,body,84.284 +external-high,3,v1.2.0,mtime,shape,253.884 +external-high,3,v1.2.0,mtime,import,269.849 +external-high,3,v1.2.0,mtime,known-import-toggle,268.627 +external-high,3,v1.3.0,mtime,wire-cold,1561.707 +external-high,3,v1.3.0,mtime,unchanged,18.898 +external-high,3,v1.3.0,mtime,body,87.527 +external-high,3,v1.3.0,mtime,shape,260.127 +external-high,3,v1.3.0,mtime,import,266.279 +external-high,3,v1.3.0,mtime,known-import-toggle,261.452 +external-high,4,v1.3.0,content,wire-cold,1872.055 +external-high,4,v1.3.0,content,unchanged,19.966 +external-high,4,v1.3.0,content,body,146.142 +external-high,4,v1.3.0,content,shape,313.305 +external-high,4,v1.3.0,content,import,328.986 +external-high,4,v1.3.0,content,known-import-toggle,204.747 +external-high,4,stock,n/a,wire-cold,1600.328 +external-high,4,stock,n/a,unchanged,1582.004 +external-high,4,stock,n/a,body,1580.019 +external-high,4,stock,n/a,shape,1614.754 +external-high,4,stock,n/a,import,1567.15 +external-high,4,stock,n/a,known-import-toggle,1569.128 +external-high,4,v1.2.0,mtime,wire-cold,1550.634 +external-high,4,v1.2.0,mtime,unchanged,18.39 +external-high,4,v1.2.0,mtime,body,87.457 +external-high,4,v1.2.0,mtime,shape,259.822 +external-high,4,v1.2.0,mtime,import,266.523 +external-high,4,v1.2.0,mtime,known-import-toggle,278.232 +external-high,4,v1.3.0,mtime,wire-cold,1571.686 +external-high,4,v1.3.0,mtime,unchanged,18.817 +external-high,4,v1.3.0,mtime,body,86.323 +external-high,4,v1.3.0,mtime,shape,263.039 +external-high,4,v1.3.0,mtime,import,267.779 +external-high,4,v1.3.0,mtime,known-import-toggle,292.008 +external-high,4,v1.2.0,content,wire-cold,1891.472 +external-high,4,v1.2.0,content,unchanged,19.953 +external-high,4,v1.2.0,content,body,153.386 +external-high,4,v1.2.0,content,shape,337.696 +external-high,4,v1.2.0,content,import,331.747 +external-high,4,v1.2.0,content,known-import-toggle,204.73 +external-low,0,stock,n/a,wire-cold,1654.503 +external-low,0,stock,n/a,unchanged,1548.981 +external-low,0,stock,n/a,body,1579.931 +external-low,0,stock,n/a,shape,1580.235 +external-low,0,stock,n/a,import,1591.894 +external-low,0,stock,n/a,known-import-toggle,1628.038 +external-low,0,v1.2.0,mtime,wire-cold,1523.653 +external-low,0,v1.2.0,mtime,unchanged,14.415 +external-low,0,v1.2.0,mtime,body,78.533 +external-low,0,v1.2.0,mtime,shape,278.777 +external-low,0,v1.2.0,mtime,import,280.37 +external-low,0,v1.2.0,mtime,known-import-toggle,243.019 +external-low,0,v1.3.0,mtime,wire-cold,1555.405 +external-low,0,v1.3.0,mtime,unchanged,14.406 +external-low,0,v1.3.0,mtime,body,81.401 +external-low,0,v1.3.0,mtime,shape,241.896 +external-low,0,v1.3.0,mtime,import,240.047 +external-low,0,v1.3.0,mtime,known-import-toggle,245.125 +external-low,0,v1.2.0,content,wire-cold,1812.687 +external-low,0,v1.2.0,content,unchanged,14.935 +external-low,0,v1.2.0,content,body,148.35 +external-low,0,v1.2.0,content,shape,302.185 +external-low,0,v1.2.0,content,import,300.963 +external-low,0,v1.2.0,content,known-import-toggle,181.139 +external-low,0,v1.3.0,content,wire-cold,1838.422 +external-low,0,v1.3.0,content,unchanged,15.179 +external-low,0,v1.3.0,content,body,135.283 +external-low,0,v1.3.0,content,shape,294.447 +external-low,0,v1.3.0,content,import,299.797 +external-low,0,v1.3.0,content,known-import-toggle,175.53 +external-low,1,v1.2.0,mtime,wire-cold,1503.387 +external-low,1,v1.2.0,mtime,unchanged,15.357 +external-low,1,v1.2.0,mtime,body,80.965 +external-low,1,v1.2.0,mtime,shape,240.206 +external-low,1,v1.2.0,mtime,import,245.576 +external-low,1,v1.2.0,mtime,known-import-toggle,244.635 +external-low,1,v1.3.0,mtime,wire-cold,1526.387 +external-low,1,v1.3.0,mtime,unchanged,14.434 +external-low,1,v1.3.0,mtime,body,77.099 +external-low,1,v1.3.0,mtime,shape,242.336 +external-low,1,v1.3.0,mtime,import,239.819 +external-low,1,v1.3.0,mtime,known-import-toggle,242.033 +external-low,1,v1.2.0,content,wire-cold,1800.425 +external-low,1,v1.2.0,content,unchanged,14.665 +external-low,1,v1.2.0,content,body,136.322 +external-low,1,v1.2.0,content,shape,299.155 +external-low,1,v1.2.0,content,import,300.453 +external-low,1,v1.2.0,content,known-import-toggle,181.635 +external-low,1,v1.3.0,content,wire-cold,1842.868 +external-low,1,v1.3.0,content,unchanged,15.512 +external-low,1,v1.3.0,content,body,131.059 +external-low,1,v1.3.0,content,shape,307.757 +external-low,1,v1.3.0,content,import,295.267 +external-low,1,v1.3.0,content,known-import-toggle,183.607 +external-low,1,stock,n/a,wire-cold,1626.603 +external-low,1,stock,n/a,unchanged,1552.724 +external-low,1,stock,n/a,body,1563.657 +external-low,1,stock,n/a,shape,1543.878 +external-low,1,stock,n/a,import,1560.306 +external-low,1,stock,n/a,known-import-toggle,1564.635 +external-low,2,v1.3.0,mtime,wire-cold,1540.903 +external-low,2,v1.3.0,mtime,unchanged,14.461 +external-low,2,v1.3.0,mtime,body,76.296 +external-low,2,v1.3.0,mtime,shape,232.861 +external-low,2,v1.3.0,mtime,import,252.799 +external-low,2,v1.3.0,mtime,known-import-toggle,244.212 +external-low,2,v1.2.0,content,wire-cold,1809.172 +external-low,2,v1.2.0,content,unchanged,15.05 +external-low,2,v1.2.0,content,body,135.081 +external-low,2,v1.2.0,content,shape,295.823 +external-low,2,v1.2.0,content,import,301.355 +external-low,2,v1.2.0,content,known-import-toggle,175.176 +external-low,2,v1.3.0,content,wire-cold,1812.931 +external-low,2,v1.3.0,content,unchanged,14.929 +external-low,2,v1.3.0,content,body,129.855 +external-low,2,v1.3.0,content,shape,295.07 +external-low,2,v1.3.0,content,import,309.512 +external-low,2,v1.3.0,content,known-import-toggle,174.705 +external-low,2,stock,n/a,wire-cold,1551.731 +external-low,2,stock,n/a,unchanged,1613.246 +external-low,2,stock,n/a,body,1611.52 +external-low,2,stock,n/a,shape,1562.962 +external-low,2,stock,n/a,import,1565.307 +external-low,2,stock,n/a,known-import-toggle,1550.269 +external-low,2,v1.2.0,mtime,wire-cold,1519.845 +external-low,2,v1.2.0,mtime,unchanged,14.656 +external-low,2,v1.2.0,mtime,body,80.153 +external-low,2,v1.2.0,mtime,shape,240.747 +external-low,2,v1.2.0,mtime,import,247.176 +external-low,2,v1.2.0,mtime,known-import-toggle,244.519 +external-low,3,v1.2.0,content,wire-cold,1836.065 +external-low,3,v1.2.0,content,unchanged,15.186 +external-low,3,v1.2.0,content,body,135.962 +external-low,3,v1.2.0,content,shape,308.462 +external-low,3,v1.2.0,content,import,309.19 +external-low,3,v1.2.0,content,known-import-toggle,185.413 +external-low,3,v1.3.0,content,wire-cold,1816.693 +external-low,3,v1.3.0,content,unchanged,16.332 +external-low,3,v1.3.0,content,body,137.179 +external-low,3,v1.3.0,content,shape,307.266 +external-low,3,v1.3.0,content,import,300.458 +external-low,3,v1.3.0,content,known-import-toggle,178.038 +external-low,3,stock,n/a,wire-cold,1517.418 +external-low,3,stock,n/a,unchanged,1581.03 +external-low,3,stock,n/a,body,1504.796 +external-low,3,stock,n/a,shape,1596.159 +external-low,3,stock,n/a,import,1543.581 +external-low,3,stock,n/a,known-import-toggle,1541.811 +external-low,3,v1.2.0,mtime,wire-cold,1563.708 +external-low,3,v1.2.0,mtime,unchanged,14.186 +external-low,3,v1.2.0,mtime,body,80.288 +external-low,3,v1.2.0,mtime,shape,247.263 +external-low,3,v1.2.0,mtime,import,262.44 +external-low,3,v1.2.0,mtime,known-import-toggle,262.351 +external-low,3,v1.3.0,mtime,wire-cold,1614.139 +external-low,3,v1.3.0,mtime,unchanged,15.516 +external-low,3,v1.3.0,mtime,body,81.644 +external-low,3,v1.3.0,mtime,shape,251.528 +external-low,3,v1.3.0,mtime,import,265.893 +external-low,3,v1.3.0,mtime,known-import-toggle,238.735 +external-low,4,v1.3.0,content,wire-cold,1878.914 +external-low,4,v1.3.0,content,unchanged,15.033 +external-low,4,v1.3.0,content,body,138.39 +external-low,4,v1.3.0,content,shape,299.317 +external-low,4,v1.3.0,content,import,304.404 +external-low,4,v1.3.0,content,known-import-toggle,186.007 +external-low,4,stock,n/a,wire-cold,1667.713 +external-low,4,stock,n/a,unchanged,1614.997 +external-low,4,stock,n/a,body,1644.968 +external-low,4,stock,n/a,shape,1635.55 +external-low,4,stock,n/a,import,1651.563 +external-low,4,stock,n/a,known-import-toggle,1594.743 +external-low,4,v1.2.0,mtime,wire-cold,1558.709 +external-low,4,v1.2.0,mtime,unchanged,14.939 +external-low,4,v1.2.0,mtime,body,79.963 +external-low,4,v1.2.0,mtime,shape,241.33 +external-low,4,v1.2.0,mtime,import,245.279 +external-low,4,v1.2.0,mtime,known-import-toggle,239.777 +external-low,4,v1.3.0,mtime,wire-cold,1540.907 +external-low,4,v1.3.0,mtime,unchanged,15.34 +external-low,4,v1.3.0,mtime,body,82.42 +external-low,4,v1.3.0,mtime,shape,255.136 +external-low,4,v1.3.0,mtime,import,243.092 +external-low,4,v1.3.0,mtime,known-import-toggle,256.243 +external-low,4,v1.2.0,content,wire-cold,1877.172 +external-low,4,v1.2.0,content,unchanged,14.922 +external-low,4,v1.2.0,content,body,142.273 +external-low,4,v1.2.0,content,shape,296.703 +external-low,4,v1.2.0,content,import,312.267 +external-low,4,v1.2.0,content,known-import-toggle,180.755 +local,0,stock,n/a,wire-cold,417.625 +local,0,stock,n/a,unchanged,392.784 +local,0,stock,n/a,body,396.892 +local,0,stock,n/a,shape,409.933 +local,0,stock,n/a,import,398.59 +local,0,stock,n/a,known-import-toggle,401.482 +local,0,v1.2.0,mtime,wire-cold,390.175 +local,0,v1.2.0,mtime,unchanged,8.931 +local,0,v1.2.0,mtime,body,26.882 +local,0,v1.2.0,mtime,shape,139.735 +local,0,v1.2.0,mtime,import,135.543 +local,0,v1.2.0,mtime,known-import-toggle,102.255 +local,0,v1.3.0,mtime,wire-cold,385.252 +local,0,v1.3.0,mtime,unchanged,9.295 +local,0,v1.3.0,mtime,body,26.99 +local,0,v1.3.0,mtime,shape,107.275 +local,0,v1.3.0,mtime,import,99.909 +local,0,v1.3.0,mtime,known-import-toggle,107.514 +local,0,v1.2.0,content,wire-cold,448.746 +local,0,v1.2.0,content,unchanged,9.971 +local,0,v1.2.0,content,body,44.509 +local,0,v1.2.0,content,shape,115.095 +local,0,v1.2.0,content,import,112.993 +local,0,v1.2.0,content,known-import-toggle,83.219 +local,0,v1.3.0,content,wire-cold,472.499 +local,0,v1.3.0,content,unchanged,10.198 +local,0,v1.3.0,content,body,41.8 +local,0,v1.3.0,content,shape,116.054 +local,0,v1.3.0,content,import,115.087 +local,0,v1.3.0,content,known-import-toggle,87.018 +local,1,v1.2.0,mtime,wire-cold,379.707 +local,1,v1.2.0,mtime,unchanged,9.092 +local,1,v1.2.0,mtime,body,27.383 +local,1,v1.2.0,mtime,shape,98.782 +local,1,v1.2.0,mtime,import,97.526 +local,1,v1.2.0,mtime,known-import-toggle,99.49 +local,1,v1.3.0,mtime,wire-cold,382.552 +local,1,v1.3.0,mtime,unchanged,9.502 +local,1,v1.3.0,mtime,body,27.416 +local,1,v1.3.0,mtime,shape,100.977 +local,1,v1.3.0,mtime,import,102.455 +local,1,v1.3.0,mtime,known-import-toggle,101.191 +local,1,v1.2.0,content,wire-cold,454.546 +local,1,v1.2.0,content,unchanged,9.383 +local,1,v1.2.0,content,body,41.177 +local,1,v1.2.0,content,shape,118.019 +local,1,v1.2.0,content,import,118.116 +local,1,v1.2.0,content,known-import-toggle,81.37 +local,1,v1.3.0,content,wire-cold,445.413 +local,1,v1.3.0,content,unchanged,9.518 +local,1,v1.3.0,content,body,41.056 +local,1,v1.3.0,content,shape,112.812 +local,1,v1.3.0,content,import,118.356 +local,1,v1.3.0,content,known-import-toggle,85.346 +local,1,stock,n/a,wire-cold,384.612 +local,1,stock,n/a,unchanged,394.73 +local,1,stock,n/a,body,394.168 +local,1,stock,n/a,shape,382.942 +local,1,stock,n/a,import,398.087 +local,1,stock,n/a,known-import-toggle,403.674 +local,2,v1.3.0,mtime,wire-cold,371.883 +local,2,v1.3.0,mtime,unchanged,8.728 +local,2,v1.3.0,mtime,body,25.184 +local,2,v1.3.0,mtime,shape,95.415 +local,2,v1.3.0,mtime,import,99.839 +local,2,v1.3.0,mtime,known-import-toggle,100.424 +local,2,v1.2.0,content,wire-cold,444.635 +local,2,v1.2.0,content,unchanged,10.094 +local,2,v1.2.0,content,body,43.115 +local,2,v1.2.0,content,shape,109.198 +local,2,v1.2.0,content,import,113.174 +local,2,v1.2.0,content,known-import-toggle,80.58 +local,2,v1.3.0,content,wire-cold,438.461 +local,2,v1.3.0,content,unchanged,9.596 +local,2,v1.3.0,content,body,45.727 +local,2,v1.3.0,content,shape,116.007 +local,2,v1.3.0,content,import,110.524 +local,2,v1.3.0,content,known-import-toggle,79.425 +local,2,stock,n/a,wire-cold,379.869 +local,2,stock,n/a,unchanged,388.178 +local,2,stock,n/a,body,389.568 +local,2,stock,n/a,shape,402.562 +local,2,stock,n/a,import,386.916 +local,2,stock,n/a,known-import-toggle,391.16 +local,2,v1.2.0,mtime,wire-cold,377.146 +local,2,v1.2.0,mtime,unchanged,8.987 +local,2,v1.2.0,mtime,body,28.857 +local,2,v1.2.0,mtime,shape,101.244 +local,2,v1.2.0,mtime,import,103.048 +local,2,v1.2.0,mtime,known-import-toggle,99.319 +local,3,v1.2.0,content,wire-cold,456.969 +local,3,v1.2.0,content,unchanged,9.635 +local,3,v1.2.0,content,body,42.296 +local,3,v1.2.0,content,shape,116.468 +local,3,v1.2.0,content,import,116.061 +local,3,v1.2.0,content,known-import-toggle,84.261 +local,3,v1.3.0,content,wire-cold,452.138 +local,3,v1.3.0,content,unchanged,10.229 +local,3,v1.3.0,content,body,41.875 +local,3,v1.3.0,content,shape,120.0 +local,3,v1.3.0,content,import,123.327 +local,3,v1.3.0,content,known-import-toggle,85.567 +local,3,stock,n/a,wire-cold,401.739 +local,3,stock,n/a,unchanged,404.705 +local,3,stock,n/a,body,391.715 +local,3,stock,n/a,shape,427.425 +local,3,stock,n/a,import,388.725 +local,3,stock,n/a,known-import-toggle,392.563 +local,3,v1.2.0,mtime,wire-cold,373.316 +local,3,v1.2.0,mtime,unchanged,9.133 +local,3,v1.2.0,mtime,body,26.005 +local,3,v1.2.0,mtime,shape,95.372 +local,3,v1.2.0,mtime,import,101.473 +local,3,v1.2.0,mtime,known-import-toggle,101.335 +local,3,v1.3.0,mtime,wire-cold,372.45 +local,3,v1.3.0,mtime,unchanged,9.592 +local,3,v1.3.0,mtime,body,26.779 +local,3,v1.3.0,mtime,shape,99.898 +local,3,v1.3.0,mtime,import,102.732 +local,3,v1.3.0,mtime,known-import-toggle,102.956 +local,4,v1.3.0,content,wire-cold,444.156 +local,4,v1.3.0,content,unchanged,10.115 +local,4,v1.3.0,content,body,42.78 +local,4,v1.3.0,content,shape,117.716 +local,4,v1.3.0,content,import,123.831 +local,4,v1.3.0,content,known-import-toggle,86.556 +local,4,stock,n/a,wire-cold,405.628 +local,4,stock,n/a,unchanged,407.211 +local,4,stock,n/a,body,390.867 +local,4,stock,n/a,shape,400.864 +local,4,stock,n/a,import,404.613 +local,4,stock,n/a,known-import-toggle,395.975 +local,4,v1.2.0,mtime,wire-cold,381.588 +local,4,v1.2.0,mtime,unchanged,9.633 +local,4,v1.2.0,mtime,body,28.134 +local,4,v1.2.0,mtime,shape,99.882 +local,4,v1.2.0,mtime,import,103.453 +local,4,v1.2.0,mtime,known-import-toggle,108.772 +local,4,v1.3.0,mtime,wire-cold,378.073 +local,4,v1.3.0,mtime,unchanged,9.069 +local,4,v1.3.0,mtime,body,26.849 +local,4,v1.3.0,mtime,shape,100.865 +local,4,v1.3.0,mtime,import,99.564 +local,4,v1.3.0,mtime,known-import-toggle,102.045 +local,4,v1.2.0,content,wire-cold,447.237 +local,4,v1.2.0,content,unchanged,9.671 +local,4,v1.2.0,content,body,42.807 +local,4,v1.2.0,content,shape,114.212 +local,4,v1.2.0,content,import,121.573 +local,4,v1.2.0,content,known-import-toggle,85.532 +local-high,0,stock,n/a,wire-cold,580.939 +local-high,0,stock,n/a,unchanged,548.847 +local-high,0,stock,n/a,body,543.294 +local-high,0,stock,n/a,shape,540.599 +local-high,0,stock,n/a,import,521.901 +local-high,0,stock,n/a,known-import-toggle,564.979 +local-high,0,v1.2.0,mtime,wire-cold,742.819 +local-high,0,v1.2.0,mtime,unchanged,64.071 +local-high,0,v1.2.0,mtime,body,135.814 +local-high,0,v1.2.0,mtime,shape,505.924 +local-high,0,v1.2.0,mtime,import,496.992 +local-high,0,v1.2.0,mtime,known-import-toggle,394.981 +local-high,0,v1.3.0,mtime,wire-cold,713.827 +local-high,0,v1.3.0,mtime,unchanged,62.061 +local-high,0,v1.3.0,mtime,body,130.956 +local-high,0,v1.3.0,mtime,shape,407.264 +local-high,0,v1.3.0,mtime,import,423.649 +local-high,0,v1.3.0,mtime,known-import-toggle,422.972 +local-high,0,v1.2.0,content,wire-cold,784.568 +local-high,0,v1.2.0,content,unchanged,69.771 +local-high,0,v1.2.0,content,body,164.319 +local-high,0,v1.2.0,content,shape,430.795 +local-high,0,v1.2.0,content,import,420.035 +local-high,0,v1.2.0,content,known-import-toggle,340.626 +local-high,0,v1.3.0,content,wire-cold,819.271 +local-high,0,v1.3.0,content,unchanged,72.583 +local-high,0,v1.3.0,content,body,180.631 +local-high,0,v1.3.0,content,shape,446.037 +local-high,0,v1.3.0,content,import,464.426 +local-high,0,v1.3.0,content,known-import-toggle,364.215 +local-high,1,v1.2.0,mtime,wire-cold,694.396 +local-high,1,v1.2.0,mtime,unchanged,64.834 +local-high,1,v1.2.0,mtime,body,135.142 +local-high,1,v1.2.0,mtime,shape,397.165 +local-high,1,v1.2.0,mtime,import,408.574 +local-high,1,v1.2.0,mtime,known-import-toggle,406.795 +local-high,1,v1.3.0,mtime,wire-cold,708.714 +local-high,1,v1.3.0,mtime,unchanged,61.696 +local-high,1,v1.3.0,mtime,body,136.542 +local-high,1,v1.3.0,mtime,shape,398.923 +local-high,1,v1.3.0,mtime,import,381.269 +local-high,1,v1.3.0,mtime,known-import-toggle,397.389 +local-high,1,v1.2.0,content,wire-cold,783.387 +local-high,1,v1.2.0,content,unchanged,69.05 +local-high,1,v1.2.0,content,body,163.803 +local-high,1,v1.2.0,content,shape,416.999 +local-high,1,v1.2.0,content,import,413.59 +local-high,1,v1.2.0,content,known-import-toggle,315.775 +local-high,1,v1.3.0,content,wire-cold,791.984 +local-high,1,v1.3.0,content,unchanged,68.431 +local-high,1,v1.3.0,content,body,163.197 +local-high,1,v1.3.0,content,shape,413.953 +local-high,1,v1.3.0,content,import,412.455 +local-high,1,v1.3.0,content,known-import-toggle,329.672 +local-high,1,stock,n/a,wire-cold,557.988 +local-high,1,stock,n/a,unchanged,508.957 +local-high,1,stock,n/a,body,514.331 +local-high,1,stock,n/a,shape,498.235 +local-high,1,stock,n/a,import,494.703 +local-high,1,stock,n/a,known-import-toggle,505.066 +local-high,2,v1.3.0,mtime,wire-cold,689.334 +local-high,2,v1.3.0,mtime,unchanged,59.772 +local-high,2,v1.3.0,mtime,body,130.409 +local-high,2,v1.3.0,mtime,shape,406.216 +local-high,2,v1.3.0,mtime,import,386.494 +local-high,2,v1.3.0,mtime,known-import-toggle,389.639 +local-high,2,v1.2.0,content,wire-cold,793.041 +local-high,2,v1.2.0,content,unchanged,70.778 +local-high,2,v1.2.0,content,body,167.667 +local-high,2,v1.2.0,content,shape,426.183 +local-high,2,v1.2.0,content,import,435.692 +local-high,2,v1.2.0,content,known-import-toggle,329.651 +local-high,2,v1.3.0,content,wire-cold,806.998 +local-high,2,v1.3.0,content,unchanged,70.265 +local-high,2,v1.3.0,content,body,173.619 +local-high,2,v1.3.0,content,shape,435.717 +local-high,2,v1.3.0,content,import,431.382 +local-high,2,v1.3.0,content,known-import-toggle,338.766 +local-high,2,stock,n/a,wire-cold,524.824 +local-high,2,stock,n/a,unchanged,544.097 +local-high,2,stock,n/a,body,519.94 +local-high,2,stock,n/a,shape,519.482 +local-high,2,stock,n/a,import,509.298 +local-high,2,stock,n/a,known-import-toggle,514.435 +local-high,2,v1.2.0,mtime,wire-cold,739.231 +local-high,2,v1.2.0,mtime,unchanged,59.171 +local-high,2,v1.2.0,mtime,body,129.264 +local-high,2,v1.2.0,mtime,shape,395.001 +local-high,2,v1.2.0,mtime,import,387.962 +local-high,2,v1.2.0,mtime,known-import-toggle,437.889 +local-high,3,v1.2.0,content,wire-cold,822.29 +local-high,3,v1.2.0,content,unchanged,72.845 +local-high,3,v1.2.0,content,body,168.971 +local-high,3,v1.2.0,content,shape,449.254 +local-high,3,v1.2.0,content,import,432.132 +local-high,3,v1.2.0,content,known-import-toggle,326.747 +local-high,3,v1.3.0,content,wire-cold,783.995 +local-high,3,v1.3.0,content,unchanged,71.312 +local-high,3,v1.3.0,content,body,166.767 +local-high,3,v1.3.0,content,shape,439.923 +local-high,3,v1.3.0,content,import,439.374 +local-high,3,v1.3.0,content,known-import-toggle,329.145 +local-high,3,stock,n/a,wire-cold,529.562 +local-high,3,stock,n/a,unchanged,550.389 +local-high,3,stock,n/a,body,561.547 +local-high,3,stock,n/a,shape,545.626 +local-high,3,stock,n/a,import,545.143 +local-high,3,stock,n/a,known-import-toggle,537.877 +local-high,3,v1.2.0,mtime,wire-cold,737.118 +local-high,3,v1.2.0,mtime,unchanged,62.55 +local-high,3,v1.2.0,mtime,body,139.293 +local-high,3,v1.2.0,mtime,shape,423.151 +local-high,3,v1.2.0,mtime,import,408.349 +local-high,3,v1.2.0,mtime,known-import-toggle,414.617 +local-high,3,v1.3.0,mtime,wire-cold,733.429 +local-high,3,v1.3.0,mtime,unchanged,60.25 +local-high,3,v1.3.0,mtime,body,132.728 +local-high,3,v1.3.0,mtime,shape,427.165 +local-high,3,v1.3.0,mtime,import,408.263 +local-high,3,v1.3.0,mtime,known-import-toggle,413.457 +local-high,4,v1.3.0,content,wire-cold,802.44 +local-high,4,v1.3.0,content,unchanged,74.206 +local-high,4,v1.3.0,content,body,171.889 +local-high,4,v1.3.0,content,shape,445.276 +local-high,4,v1.3.0,content,import,441.673 +local-high,4,v1.3.0,content,known-import-toggle,345.792 +local-high,4,stock,n/a,wire-cold,564.453 +local-high,4,stock,n/a,unchanged,531.832 +local-high,4,stock,n/a,body,565.831 +local-high,4,stock,n/a,shape,535.008 +local-high,4,stock,n/a,import,556.113 +local-high,4,stock,n/a,known-import-toggle,550.443 +local-high,4,v1.2.0,mtime,wire-cold,729.622 +local-high,4,v1.2.0,mtime,unchanged,63.059 +local-high,4,v1.2.0,mtime,body,138.891 +local-high,4,v1.2.0,mtime,shape,420.149 +local-high,4,v1.2.0,mtime,import,428.866 +local-high,4,v1.2.0,mtime,known-import-toggle,426.158 +local-high,4,v1.3.0,mtime,wire-cold,744.245 +local-high,4,v1.3.0,mtime,unchanged,60.109 +local-high,4,v1.3.0,mtime,body,136.139 +local-high,4,v1.3.0,mtime,shape,417.518 +local-high,4,v1.3.0,mtime,import,417.864 +local-high,4,v1.3.0,mtime,known-import-toggle,420.005 +local-high,4,v1.2.0,content,wire-cold,816.072 +local-high,4,v1.2.0,content,unchanged,73.851 +local-high,4,v1.2.0,content,body,172.11 +local-high,4,v1.2.0,content,shape,449.843 +local-high,4,v1.2.0,content,import,458.085 +local-high,4,v1.2.0,content,known-import-toggle,350.654 diff --git a/docs/benchmarks/workspace-comparison.csv b/docs/benchmarks/workspace-comparison.csv new file mode 100644 index 0000000..09824fc --- /dev/null +++ b/docs/benchmarks/workspace-comparison.csv @@ -0,0 +1,81 @@ +profile,cache_mode,trial,version,layout,ms +local-high,mtime,0,v1.2.0,module,64.035 +local-high,mtime,0,v1.2.0,workspace,62.246 +local-high,mtime,0,v1.3.0,module,62.046 +local-high,mtime,0,v1.3.0,workspace,63.526 +local-high,mtime,1,v1.3.0,module,59.856 +local-high,mtime,1,v1.3.0,workspace,59.359 +local-high,mtime,1,v1.2.0,module,60.888 +local-high,mtime,1,v1.2.0,workspace,57.78 +local-high,mtime,2,v1.2.0,module,59.513 +local-high,mtime,2,v1.2.0,workspace,58.666 +local-high,mtime,2,v1.3.0,module,60.907 +local-high,mtime,2,v1.3.0,workspace,60.677 +local-high,mtime,3,v1.3.0,module,61.057 +local-high,mtime,3,v1.3.0,workspace,66.078 +local-high,mtime,3,v1.2.0,module,59.688 +local-high,mtime,3,v1.2.0,workspace,59.945 +local-high,mtime,4,v1.2.0,module,62.602 +local-high,mtime,4,v1.2.0,workspace,60.534 +local-high,mtime,4,v1.3.0,module,60.041 +local-high,mtime,4,v1.3.0,workspace,62.872 +local-high,content,0,v1.2.0,module,74.78 +local-high,content,0,v1.2.0,workspace,58.808 +local-high,content,0,v1.3.0,module,69.964 +local-high,content,0,v1.3.0,workspace,72.634 +local-high,content,1,v1.3.0,module,70.361 +local-high,content,1,v1.3.0,workspace,73.169 +local-high,content,1,v1.2.0,module,72.674 +local-high,content,1,v1.2.0,workspace,62.133 +local-high,content,2,v1.2.0,module,71.345 +local-high,content,2,v1.2.0,workspace,63.675 +local-high,content,2,v1.3.0,module,72.413 +local-high,content,2,v1.3.0,workspace,74.242 +local-high,content,3,v1.3.0,module,70.827 +local-high,content,3,v1.3.0,workspace,72.281 +local-high,content,3,v1.2.0,module,70.191 +local-high,content,3,v1.2.0,workspace,58.963 +local-high,content,4,v1.2.0,module,75.848 +local-high,content,4,v1.2.0,workspace,62.668 +local-high,content,4,v1.3.0,module,73.313 +local-high,content,4,v1.3.0,workspace,71.006 +external-high,mtime,0,v1.2.0,module,19.355 +external-high,mtime,0,v1.2.0,workspace,19.081 +external-high,mtime,0,v1.3.0,module,18.22 +external-high,mtime,0,v1.3.0,workspace,19.227 +external-high,mtime,1,v1.3.0,module,17.882 +external-high,mtime,1,v1.3.0,workspace,19.077 +external-high,mtime,1,v1.2.0,module,17.628 +external-high,mtime,1,v1.2.0,workspace,18.073 +external-high,mtime,2,v1.2.0,module,19.328 +external-high,mtime,2,v1.2.0,workspace,18.682 +external-high,mtime,2,v1.3.0,module,18.695 +external-high,mtime,2,v1.3.0,workspace,19.012 +external-high,mtime,3,v1.3.0,module,18.834 +external-high,mtime,3,v1.3.0,workspace,17.87 +external-high,mtime,3,v1.2.0,module,19.759 +external-high,mtime,3,v1.2.0,workspace,18.436 +external-high,mtime,4,v1.2.0,module,18.746 +external-high,mtime,4,v1.2.0,workspace,18.066 +external-high,mtime,4,v1.3.0,module,19.617 +external-high,mtime,4,v1.3.0,workspace,19.569 +external-high,content,0,v1.2.0,module,19.631 +external-high,content,0,v1.2.0,workspace,19.153 +external-high,content,0,v1.3.0,module,19.407 +external-high,content,0,v1.3.0,workspace,19.739 +external-high,content,1,v1.3.0,module,18.292 +external-high,content,1,v1.3.0,workspace,21.086 +external-high,content,1,v1.2.0,module,20.028 +external-high,content,1,v1.2.0,workspace,18.68 +external-high,content,2,v1.2.0,module,19.429 +external-high,content,2,v1.2.0,workspace,18.034 +external-high,content,2,v1.3.0,module,19.94 +external-high,content,2,v1.3.0,workspace,21.173 +external-high,content,3,v1.3.0,module,20.614 +external-high,content,3,v1.3.0,workspace,21.557 +external-high,content,3,v1.2.0,module,19.51 +external-high,content,3,v1.2.0,workspace,19.053 +external-high,content,4,v1.2.0,module,20.529 +external-high,content,4,v1.2.0,workspace,18.619 +external-high,content,4,v1.3.0,module,20.633 +external-high,content,4,v1.3.0,workspace,20.922 diff --git a/docs/performance.md b/docs/performance.md new file mode 100644 index 0000000..a828f5b --- /dev/null +++ b/docs/performance.md @@ -0,0 +1,301 @@ +# Wire v1.3.0 performance + +For the two projects with 342 external packages, v1.3.0 was 84-109x faster than +Google Wire on unchanged runs and 18-19x faster after a function body edit. With +1,016 local packages and few external dependencies, an unchanged run was 9x +faster. These are generation times for test projects, not application runtime +benchmarks or a promise for every codebase. + +Compared directly with v1.2.0, no single-module case took more than 5% longer +in v1.3.0 across Go 1.25.0, 1.26.3, and 1.27.2. Some cases were faster. +Workspaces have one extra cost: +Wire now checks sibling source files that the old cache could miss. The largest +workspace measured here added 2.9 ms in default mode and 10.5 ms with opt-in +content hashing. That extra work fixes stale constructor calls. + +## Test setup + +Measured on an Apple M4 Max with 48 GiB RAM and macOS 26.5.2. Generator processes +used `GOMAXPROCS=4`, and Go builds used `-p=4`. Runs were serial, and version +order was rotated or alternated to reduce ordering effects. This was an active workstation; +small differences can come from other activity. Treat small percentage changes +as approximate with five samples per case. + +The compared sources were: + +- Google Wire: [`9c25c90`](https://github.com/google/wire/commit/9c25c9016f6825302537c4efdd5e897976f9c826). +- GoForj Wire v1.2.0: [`a64b614`](https://github.com/goforj/wire/commit/a64b6147462d563640d769c527176ed3edf14d05). +- GoForj Wire v1.3.0: [`2367f18`](https://github.com/goforj/wire/commit/2367f18afe9990c379d49864e1efc8be85ed5c65). + +The project sources come from the factories in +[`internal/wire/import_bench_test.go`](../internal/wire/import_bench_test.go). +Each has ten application features and ordinary constructors. The external +profiles import a mix of database, HTTP, Redis, and other library packages. + +| Project | Local packages | Standard library packages | External packages | +| --- | ---: | ---: | ---: | +| local | 41 | 185 | 1 | +| local-high | 1,016 | 185 | 1 | +| external-low | 42 | 240 | 342 | +| external-high | 117 | 240 | 342 | + +Counts above use Go 1.25.0 and cover packages reachable from the injector +with the `wireinject` tag. The external count includes Wire's marker package. +Both GoForj versions used the same project files and marker-package checkout. +Google Wire used the same factory inputs with its own import path. Local +replacements selected the marker packages; the generator binaries were built +from the exact source commits listed above. This isolates the generator release +comparison from changes to the application. + +The main comparison used five samples for each case, with both `mtime` (default) +and `content` cache modes. The newer-Go checks repeated unchanged, body-edit, and +constructor-edit cases on the two larger projects. In total, the comparisons +below contain **932 timed generator invocations**. Untimed setup and warmup runs +are additional. + +Building the generator and downloading modules happened before timing. For the +main comparison, `go list -export -deps -tags=wireinject` also populated Go's build +cache before timing. A fresh Wire cache therefore measures Wire startup and +cache creation without charging it for compiling all dependency export data. +Cold Go caches are measured separately below. + +Before measuring an unchanged fork run, the audit verified a generated-output +cache hit. Edit cases warmed the base state, changed one file, and then timed +regeneration. The first full pass compiled the generated projects for every +version, mode, project, and edit case. Later timing samples repeated the edits +without repeating those compilation checks. + +## Google Wire versus GoForj Wire + +Go 1.25.0, default cache mode, five-run medians. All times are milliseconds. +A fresh Wire cache starts with Go's build cache warm; subsequent rows start from +a warmed Wire cache. + +| Project | Change | Google Wire | GoForj v1.3.0 | Speedup | +| --- | --- | ---: | ---: | ---: | +| external-high | Fresh Wire cache | 1,600.3 | 1,571.7 | 1.02x | +| external-high | Unchanged | 1,582.0 | 18.9 | 83.71x | +| external-high | Function body edit | 1,580.0 | 87.3 | 18.11x | +| external-high | Constructor argument added | 1,604.8 | 262.6 | 6.11x | +| external-high | Import added | 1,567.2 | 267.8 | 5.85x | +| external-high | Return to previous imports | 1,573.3 | 275.0 | 5.72x | +| external-low | Fresh Wire cache | 1,626.6 | 1,540.9 | 1.06x | +| external-low | Unchanged | 1,581.0 | 14.5 | 109.33x | +| external-low | Function body edit | 1,579.9 | 81.4 | 19.41x | +| external-low | Constructor argument added | 1,580.2 | 242.3 | 6.52x | +| external-low | Import added | 1,565.3 | 243.1 | 6.44x | +| external-low | Return to previous imports | 1,564.6 | 244.2 | 6.41x | +| local-high | Fresh Wire cache | 558.0 | 713.8 | 0.78x | +| local-high | Unchanged | 544.1 | 60.2 | 9.03x | +| local-high | Function body edit | 543.3 | 132.7 | 4.09x | +| local-high | Constructor argument added | 535.0 | 407.3 | 1.31x | +| local-high | Import added | 521.9 | 408.3 | 1.28x | +| local-high | Return to previous imports | 537.9 | 413.5 | 1.30x | +| local | Fresh Wire cache | 401.7 | 378.1 | 1.06x | +| local | Unchanged | 394.7 | 9.3 | 42.47x | +| local | Function body edit | 391.7 | 26.8 | 14.59x | +| local | Constructor argument added | 402.6 | 100.9 | 3.99x | +| local | Import added | 398.1 | 99.9 | 3.98x | +| local | Return to previous imports | 396.0 | 102.0 | 3.88x | + +The 1,016-local-package project is a useful limit on the claim. Its first run +with a fresh Wire cache was slower than Google Wire. Reruns were faster, but +constructor and import edits had much smaller gains than unchanged runs. + +## v1.3.0 versus v1.2.0 + +Go 1.25.0, five-run medians. Positive changes mean v1.3.0 took longer. Both +versions used identical project inputs and started each case with the same kind +of cache state. Small differences should be read alongside their absolute cost. + +### Default cache mode + +| Project | Change | v1.2.0 (ms) | v1.3.0 (ms) | Change | +| --- | --- | ---: | ---: | ---: | +| external-high | Fresh Wire cache | 1,550.6 | 1,571.7 | +1.4% | +| external-high | Unchanged | 18.4 | 18.9 | +2.8% | +| external-high | Function body edit | 87.5 | 87.3 | -0.2% | +| external-high | Constructor argument added | 258.3 | 262.6 | +1.6% | +| external-high | Import added | 269.8 | 267.8 | -0.8% | +| external-high | Return to previous imports | 270.4 | 275.0 | +1.7% | +| external-low | Fresh Wire cache | 1,523.7 | 1,540.9 | +1.1% | +| external-low | Unchanged | 14.7 | 14.5 | -1.3% | +| external-low | Function body edit | 80.2 | 81.4 | +1.6% | +| external-low | Constructor argument added | 241.3 | 242.3 | +0.4% | +| external-low | Import added | 247.2 | 243.1 | -1.6% | +| external-low | Return to previous imports | 244.5 | 244.2 | -0.1% | +| local-high | Fresh Wire cache | 737.1 | 713.8 | -3.2% | +| local-high | Unchanged | 63.1 | 60.2 | -4.5% | +| local-high | Function body edit | 135.8 | 132.7 | -2.3% | +| local-high | Constructor argument added | 420.1 | 407.3 | -3.1% | +| local-high | Import added | 408.6 | 408.3 | -0.1% | +| local-high | Return to previous imports | 414.6 | 413.5 | -0.3% | +| local | Fresh Wire cache | 379.7 | 378.1 | -0.4% | +| local | Unchanged | 9.1 | 9.3 | +2.2% | +| local | Function body edit | 27.4 | 26.8 | -1.9% | +| local | Constructor argument added | 99.9 | 100.9 | +1.0% | +| local | Import added | 103.0 | 99.9 | -3.0% | +| local | Return to previous imports | 101.3 | 102.0 | +0.7% | + +### Content-hash cache mode + +| Project | Change | v1.2.0 (ms) | v1.3.0 (ms) | Change | +| --- | --- | ---: | ---: | ---: | +| external-high | Fresh Wire cache | 1,884.4 | 1,853.2 | -1.7% | +| external-high | Unchanged | 20.0 | 20.0 | -0.0% | +| external-high | Function body edit | 148.3 | 142.1 | -4.2% | +| external-high | Constructor argument added | 314.6 | 313.3 | -0.4% | +| external-high | Import added | 326.2 | 320.1 | -1.9% | +| external-high | Return to previous imports | 197.8 | 201.1 | +1.7% | +| external-low | Fresh Wire cache | 1,812.7 | 1,838.4 | +1.4% | +| external-low | Unchanged | 14.9 | 15.2 | +1.6% | +| external-low | Function body edit | 136.3 | 135.3 | -0.8% | +| external-low | Constructor argument added | 299.2 | 299.3 | +0.1% | +| external-low | Import added | 301.4 | 300.5 | -0.3% | +| external-low | Return to previous imports | 181.1 | 178.0 | -1.7% | +| local-high | Fresh Wire cache | 793.0 | 802.4 | +1.2% | +| local-high | Unchanged | 70.8 | 71.3 | +0.8% | +| local-high | Function body edit | 167.7 | 171.9 | +2.5% | +| local-high | Constructor argument added | 430.8 | 439.9 | +2.1% | +| local-high | Import added | 432.1 | 439.4 | +1.7% | +| local-high | Return to previous imports | 329.7 | 338.8 | +2.8% | +| local | Fresh Wire cache | 448.7 | 445.4 | -0.7% | +| local | Unchanged | 9.7 | 10.1 | +4.6% | +| local | Function body edit | 42.8 | 41.9 | -2.2% | +| local | Constructor argument added | 115.1 | 116.1 | +0.8% | +| local | Import added | 116.1 | 118.4 | +2.0% | +| local | Return to previous imports | 83.2 | 85.6 | +2.8% | + +The main matrix has 600 timings and 100 successful generation/compilation checks. +Its raw samples are in [release-comparison-go1.25.csv](benchmarks/release-comparison-go1.25.csv). + +## Go 1.26 and Go 1.27 + +The two larger projects were also checked with both releases built using Go +1.26.3 and Go 1.27.2. Each row is the median of five samples. These runs covered +both cache modes and added 240 timings and 48 compilation checks. + +| Go | Project | Cache mode | Change | v1.2.0 (ms) | v1.3.0 (ms) | Change | +| --- | --- | --- | --- | ---: | ---: | ---: | +| 1.26.3 | local-high | mtime | Unchanged | 62.6 | 65.0 | +3.8% | +| 1.26.3 | local-high | mtime | Function body edit | 140.0 | 140.1 | +0.1% | +| 1.26.3 | local-high | mtime | Constructor argument added | 423.1 | 419.3 | -0.9% | +| 1.26.3 | local-high | content | Unchanged | 73.3 | 74.4 | +1.6% | +| 1.26.3 | local-high | content | Function body edit | 173.4 | 173.7 | +0.2% | +| 1.26.3 | local-high | content | Constructor argument added | 443.1 | 448.7 | +1.3% | +| 1.26.3 | external-high | mtime | Unchanged | 19.6 | 19.2 | -2.0% | +| 1.26.3 | external-high | mtime | Function body edit | 89.7 | 87.4 | -2.6% | +| 1.26.3 | external-high | mtime | Constructor argument added | 268.7 | 274.6 | +2.2% | +| 1.26.3 | external-high | content | Unchanged | 20.6 | 20.5 | -0.5% | +| 1.26.3 | external-high | content | Function body edit | 151.4 | 150.0 | -0.9% | +| 1.26.3 | external-high | content | Constructor argument added | 327.0 | 327.7 | +0.2% | +| 1.27.2 | local-high | mtime | Unchanged | 61.4 | 63.0 | +2.7% | +| 1.27.2 | local-high | mtime | Function body edit | 141.1 | 139.3 | -1.2% | +| 1.27.2 | local-high | mtime | Constructor argument added | 413.2 | 421.2 | +1.9% | +| 1.27.2 | local-high | content | Unchanged | 71.4 | 71.5 | +0.1% | +| 1.27.2 | local-high | content | Function body edit | 186.5 | 178.9 | -4.1% | +| 1.27.2 | local-high | content | Constructor argument added | 449.6 | 439.7 | -2.2% | +| 1.27.2 | external-high | mtime | Unchanged | 20.4 | 21.4 | +5.0% | +| 1.27.2 | external-high | mtime | Function body edit | 95.2 | 93.0 | -2.3% | +| 1.27.2 | external-high | mtime | Constructor argument added | 308.4 | 305.1 | -1.1% | +| 1.27.2 | external-high | content | Unchanged | 22.7 | 21.0 | -7.5% | +| 1.27.2 | external-high | content | Function body edit | 150.9 | 153.8 | +1.9% | +| 1.27.2 | external-high | content | Constructor argument added | 352.2 | 358.9 | +1.9% | + +Raw samples: [newer-go-comparison.csv](benchmarks/newer-go-comparison.csv). + +## Fully cold Go caches + +These are first generation runs with a fresh Go build cache and a fresh Wire +cache. Module downloads and the generator build are still excluded. Each value +is the median of three samples, with version order alternated. Go 1.25.0, +default cache mode. + +| Project | v1.2.0 | v1.3.0 | Change | +| --- | ---: | ---: | ---: | +| local-high | 7.28 s | 7.44 s | +2.1% | +| external-high | 10.16 s | 10.22 s | +0.6% | + +Cold starts are a real cost. The repository's separate three-sample scenario +runner measured Google Wire at 0.75 s versus GoForj at 7.27 s for `local-high`, +and 2.36 s versus 10.28 s for `external-high`, with fresh Go/Wire caches. The +release comparison above shows that the cold-start cost was already present +in v1.2.0. The large speedups apply after caches are populated. + +Raw samples: [cold-go-cache.csv](benchmarks/cold-go-cache.csv). + +## Generation from a separate tools module + +The same two projects were measured from their own module and from a separate +tools module in a `go.work` workspace. Before timing, the audit warmed the cache +and checked for an output-cache hit. Each row has five samples, with compilation +checked on the first pass. Go 1.25.0. These checks added 80 timings and 16 +compilation checks. + +| Project | Cache mode | Run from | v1.2.0 (ms) | v1.3.0 (ms) | Change | +| --- | --- | --- | ---: | ---: | ---: | +| local-high | mtime | module | 60.9 | 60.9 | +0.0% | +| local-high | mtime | workspace | 59.9 | 62.9 | +4.9% | +| local-high | content | module | 72.7 | 70.8 | -2.5% | +| local-high | content | workspace | 62.1 | 72.6 | +16.9% | +| external-high | mtime | module | 19.3 | 18.7 | -3.3% | +| external-high | mtime | workspace | 18.4 | 19.1 | +3.5% | +| external-high | content | module | 19.6 | 19.9 | +1.6% | +| external-high | content | workspace | 18.7 | 21.1 | +12.9% | + +For `local-high`, default workspace reruns increased by 2.9 ms. With content +hashing they increased by 10.5 ms, about 17%. The smaller set of local files in +`external-high` added 0.6 ms and 2.4 ms respectively. + +The extra work matters for correctness. v1.2.0 could reuse old output after a +constructor changed in another workspace module, leaving code that no longer +compiled. v1.3.0 checks those files before reusing its output. Content hashing +reads their contents on each run, so it pays more than the default file-metadata +check. These are measured costs, not a claim of zero overhead. + +Raw samples: [workspace-comparison.csv](benchmarks/workspace-comparison.csv). + +## Correctness checks + +- All 164 generation/compilation checks in the comparisons passed. +- The full repository tests passed on Go 1.25.0 with the race detector, and on Go 1.26.3 and 1.27.2. +- `go vet ./...` passed on Go 1.25.0. +- The published v1.3.0 tool was installed through the Go module proxy without a local Wire replacement. Both cache modes regenerated and compiled two workspace targets after removing a constructor argument and its options type. +- The workspace regression test also checks that a newly missing dependency produces an error after a successful cache warmup. + +See [`workspace_cache_test.go`](../internal/wire/workspace_cache_test.go) for the +workspace/local-replacement correctness checks. The ordinary module benchmarks +used `GOWORK=off`; only the workspace-specific checks enabled a workspace. + +## Running the repository benchmarks + +For the application-shaped inputs: + +```sh +bash scripts/import-benchmarks.sh scenarios +``` + +For the small import-only inputs: + +```sh +bash scripts/import-benchmarks.sh table +``` + +The import-only test uses small constructors with few standard library or +external imports. Its 2-6x speedups measure a different kind of input from the +application tests above. Package count alone does not tell you how much work a +cache hit saves. + +The checked-in scenario runner reports three-sample medians and includes fresh +Go build caches for its cold-run row. The five-sample release comparison in this +report adds an explicit export-data warmup, rotates release order, checks both +cache modes, and compiles edited outputs. Its `Return to previous imports` row +removes the import added by the preceding step; the repository runner's `known +import toggle` row instead returns from a constructor/import shape change. Those +rows should not be treated as identical tests. + +To repeat a comparison between releases, use the same generated project files, +Go version, thread limit, and cache settings for both binaries. Measure fresh +Wire caches separately from fresh Go build caches. Warm past the creation of +`wire_gen.go` before timing unchanged runs, and compile the output after changing +constructors. A fast result that leaves stale code is not a successful run. From 6c2cadde48f67634f3584fcb68f43ed7398818c2 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Sat, 17 Oct 2026 20:53:56 -0500 Subject: [PATCH 4/4] docs: show why Wire helps as applications grow --- README.md | 191 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 112 insertions(+), 79 deletions(-) diff --git a/README.md b/README.md index 5850d3a..596e3e2 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,118 @@ constructors. You write normal functions and declare which ones Wire can use. Wire works out the call order and passes each dependency where it's needed. The result is code you can read, debug, or write by hand. -For an overview, see the [introductory blog post](https://blog.golang.org/wire). +Already using Google Wire? This fork generated code **84-109x faster on unchanged +runs** in our larger test apps with warm caches. See +[what the fork adds](#why-goforj-wire) and the [measured tradeoffs](#generation-performance). + +## Why would I use Wire? + +**Keep writing constructors. Stop maintaining the code that connects them.** + +An orders API and a background worker might share a service, which needs a +repository, an inventory client, and a mailer. Wiring that by hand is straightforward: + +```go +// InitializeApp connects the API and worker to the same order service. +func InitializeApp(cfg Config, db *sql.DB, logger *slog.Logger) *App { + repo := NewOrderRepository(db) + inventory := NewInventoryClient(cfg) + mailer := NewMailer(cfg) + orders := NewOrderService(repo, inventory, mailer) + handler := NewOrderHandler(orders, logger) + worker := NewOrderWorker(orders, logger) + return NewApp(handler, worker) +} +``` + +There is nothing wrong with this code. The work comes later: a constructor +changes, a dependency needs its own dependencies, or another command needs a +different combination of the same services. You update each setup function and +keep the call order, arguments, and shared instances straight. + +With Wire, you list the constructors once and declare what you want to build: + +```go +//go:build wireinject + +package orders + +import ( + "database/sql" + "log/slog" + + "github.com/goforj/wire" +) + +// AppSet lists the constructors available to the application. +var AppSet = wire.NewSet( + NewOrderRepository, + NewInventoryClient, + NewMailer, + NewOrderService, + NewOrderHandler, + NewOrderWorker, + NewApp, +) + +// InitializeApp connects the API and worker to the same order service. +func InitializeApp(cfg Config, db *sql.DB, logger *slog.Logger) *App { + wire.Build(AppSet) + return nil +} +``` + +Run `wire gen`. Wire writes `wire_gen.go` with the same constructor calls as +the manual version above. The build tag keeps the stub out of normal builds. +`Config`, `App`, and the constructors are your application's ordinary Go code; +Wire doesn't require a container or special constructor signatures. + +### When a dependency changes + +Suppose orders now need an audit log. You add a constructor and change the +service's signature: + +```go +// NewAuditLog creates an audit log backed by the application's database. +func NewAuditLog(db *sql.DB) *AuditLog { + return &AuditLog{db: db} +} + +// NewOrderService supplies the dependencies needed to process orders. +func NewOrderService( + repo *OrderRepository, + inventory *InventoryClient, + mailer *Mailer, + audit *AuditLog, +) *OrderService { + return &OrderService{repo: repo, inventory: inventory, mailer: mailer, audit: audit} +} +``` + +Add `NewAuditLog` to `AppSet` and rerun Wire. You don't edit the injector's +signature or hand-update its constructor calls. The generated code now includes: + +```go +orderRepository := NewOrderRepository(db) +inventoryClient := NewInventoryClient(cfg) +mailer := NewMailer(cfg) +auditLog := NewAuditLog(db) +orderService := NewOrderService(orderRepository, inventoryClient, mailer, auditLog) +``` + +The handler and worker still receive the same `orderService`. If you forget to +register `NewAuditLog`, generation fails with a missing-provider error and shows +the dependency chain that needs it. You find out before starting the app. + +**You still own the design.** You choose constructors, interfaces, and which +values enter the injector. Wire follows those choices; it doesn't discover +services at runtime. Tests can call `NewOrderService` directly with test +dependencies, without running Wire. + +If your app has a few constructors and one setup function, hand wiring is +probably enough. Wire earns its place when keeping those calls up to date across +a growing application becomes repetitive. Start with the +[working tutorial](_tutorial/README.md) to try it. ## Why GoForj Wire? @@ -156,84 +267,6 @@ New projects can import `github.com/goforj/wire` directly. Its module and genera require Go 1.25. The generated initialization code has no runtime dependency on Wire. -## How Wire Works (in 60 seconds) - -Wire is a **compile-time dependency injection** tool. Instead of building a runtime -container, Wire generates ordinary Go code that explicitly initializes your -application. - -There are only two concepts to learn: - -### Providers - -A provider is a normal Go function that produces a value (and optionally an error). - -```go -func NewDB(cfg Config) (*sql.DB, error) { - // ... -} -``` - -Dependencies are expressed as function parameters - no globals, no hidden state. - -### Injectors - -An injector is a stub function whose body calls `wire.Build(...)`. -Wire replaces this stub with generated code that calls providers in dependency -order. - -```go -func InitializeApp(cfg Config) (*App, error) { - wire.Build(AppSet) - return nil, nil -} -``` - -The generated output is plain Go - readable, debuggable, and fast. - -## Minimal Example - -```go -// providers.go -type Message string - -func ProvideMessage() Message { - return "hello wire" -} - -type Greeter struct { - Msg Message -} - -func NewGreeter(msg Message) *Greeter { - return &Greeter{Msg: msg} -} - -var GreeterSet = wire.NewSet( - ProvideMessage, - NewGreeter, -) -``` - -```go -// wire.go -//go:build wireinject - -func InitializeGreeter() *Greeter { - wire.Build(GreeterSet) - return nil -} -``` - -Generate the injector: - -```sh -wire -``` - -Wire produces `wire_gen.go` containing explicit initialization code with no -runtime dependency on Wire. - ## Running generation manually Use `make generate` to run `wire gen` across this repository. In your own project, run: