Skip to main content

optimize-go-test-duration

Use when Go test wall time is too high, CI test duration regresses, a package needs trace-driven test performance analysis, or tests should be made safely parallel with t.Parallel, package splitting, subprocess isolation, semaphores, or Go built-in tracing.

Quellinformationen

Repository
kenn-io/forge
Letzte Quellaktivität
31. Juli 2026 um 12:44
Erkannte Sprache von SKILL.md
Englisch
Sterne
190
Forks
28

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

Datei-Explorer
2 Dateien

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
optimize-go-test-duration
description
Use when Go test wall time is too high, CI test duration regresses, a package needs trace-driven test performance analysis, or tests should be made safely parallel with t.Parallel, package splitting, subprocess isolation, semaphores, or Go built-in tracing.
# Optimize Go Test Duration ## Core Idea Optimize wall time from evidence, not guesses. Separate actual slow work from time spent waiting behind the Go test scheduler, package boundaries, subprocess startup, locks, I/O, sleeps, or scarce external resources. Keep coverage and realism unless the user explicitly asks to trade them away. ## Baseline Use comparable commands before and after each change. For package timing: ```bash go test ./path/to/pkg/... -shuffle=on ``` For per-test timing: ```bash go test ./path/to/pkg -shuffle=on -json > /tmp/pkg-tests.json jq -r 'select(.Action=="pass" and .Test != null and .Elapsed != null) | [.Elapsed, .Test] | @tsv' /tmp/pkg-tests.json | sort -nr | head -60 ``` Follow repository test rules first. In kenn-forge, always pass `-shuffle=on` for direct `go test`, do not add `-count=1`, and do not use `-v` unless needed for a specific failure. ## Trace Workflow Capture a Go trace for the slow package or focused group: ```bash go test ./path/to/pkg -shuffle=on -trace /tmp/pkg.trace ``` Extract profiles that explain where wall time went: ```bash go tool trace -pprof=sched /tmp/pkg.trace > /tmp/pkg-sched.pprof go tool trace -pprof=sync /tmp/pkg.trace > /tmp/pkg-sync.pprof go tool trace -pprof=syscall /tmp/pkg.trace > /tmp/pkg-syscall.pprof go tool trace -pprof=net /tmp/pkg.trace > /tmp/pkg-net.pprof go tool pprof -top /tmp/pkg-sched.pprof go tool pprof -top /tmp/pkg-sync.pprof go tool pprof -top /tmp/pkg-syscall.pprof go tool pprof -top /tmp/pkg-net.pprof ``` Read the results this way: - Large time in `testing.(*T).Parallel`, `testing.(*testState).waitParallel`, or scheduler delay means tests are queued behind serial work or the package parallelism limit. - Large `os/exec`, `os.StartProcess`, or `os.Process.Wait` time points at subprocess setup, helper processes, shell wrappers, or cleanup. - Large `sync.Mutex`, channel receive, or condition wait time points at shared fixtures, global locks, single-flight code, or test helpers that serialize work. - Large syscall or net time points at real I/O, sockets, PTY/tmux sessions, filesystem watchers, or cleanup. If the traced run fails but still writes a trace, inspect it anyway, then reproduce the failure with a focused command before editing. ## Safe Parallelization Prefer `t.Parallel()` for isolated tests whose fixtures are independent. Call it near the top, after skips and before expensive setup. Do not use parent-process global mutation in parallel tests: - Avoid `t.Setenv` in tests that should run parallel; it changes the current test process environment and prevents safe parallelism. - Pass per-child environment with `exec.Cmd.Env` when only the child needs it. - Prefer argv markers for Go helper subprocesses, for example `os.Args[0] -test.run=TestHelper -- helper-marker mode`. - Avoid `os.Chdir`, global temp paths, package-level mutable state, shared ports, and shared database files in parallel tests. Use `t.TempDir()`, isolated SQLite files, loopback listeners on random ports, unique workspace names, and per-test command arguments. ## Scarce Resources Real PTY, tmux, browser, container, or live-service tests can still run in parallel, but bound resource pressure. Use a package-level weighted semaphore: ```go var ptySem = semaphore.NewWeighted(4) func runParallelPTYE2E(t *testing.T) { t.Helper() t.Parallel() require.NoError(t, ptySem.Acquire(t.Context(), 1)) t.Cleanup(func() { ptySem.Release(1) }) } ``` Pick the limit from observed stability and machine constraints. Start conservative, then raise only when repeated runs stay reliable. ## Optimization Moves Use the trace to choose the smallest useful move: - Add `t.Parallel()` to isolated tests that are only waiting behind the test runner. - Remove `t.Setenv` or other parent-process global mutations by moving state into fixture structs, command args, or child `Env`. - Split large packages when independent test groups are blocked by unrelated serial tests and package boundaries are natural. - Replace fixed sleeps with readiness probes, channels, eventual assertions, or explicit synchronization. - Share expensive immutable setup only when it cannot leak mutable state between tests. - Keep full-stack/e2e coverage when the behavior needs real HTTP, SQLite, PTY, tmux, or subprocesses; make the realism concurrent instead of deleting it. ## Verification After each meaningful change: 1. Run the focused affected tests with `-shuffle=on`. 2. Run the package or subtree baseline command again. 3. Compare wall time, not just individual test elapsed time; parallel tests may report elapsed time that includes queueing. 4. Re-run or trace again if the result is noisy or the bottleneck moved. 5. Commit only after the relevant checks pass. Report the before/after numbers in seconds and percent reduction: ```text before: 170s after: 66s improvement: 104s faster, 61% lower wall time, 2.6x faster ```
Auf GitHub ansehen