Repository navigation
perf: avoid allocating the assigned worker path prefix - #2755
Merged
Merged
Conversation
dunglas
requested changes
Oct 23, 2026
dunglas
approved these changes
Oct 23, 2026
henderkes
approved these changes
Oct 24, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
When a worker is explicitly assigned, deriving SCRIPT_NAME builds
documentRoot + separatorand then checks the document-root prefix a second time inTrimPrefix. For roots longer than the temporary string buffer, this also allocates on every request.Check the length, separator byte and root prefix directly, then slice the verified root prefix. Keep
filepath.ToSlash, the original handling of workers outside the root, and all SCRIPT_NAME/PATH_INFO values unchanged. No cache or request fields are added.Added boundary cases and differential fuzzing against the previous full
splitCgiPath, plus a reusableBenchmarkAssignedWorkerScriptPath. The benchmark includes the whole function for explicitly assigned workers, rather than only the prefix expression.Validation
Covers inside/outside/neighboring roots, equal and empty paths, filesystem roots, trailing/repeated separators, long roots, Unicode, NUL and invalid UTF-8. Differential fuzzing varies root, worker filename and request path, comparing the complete resulting context with the previous implementation.
-raceagainst both versions.gofmtandgit diff --checkpass../go.sh test -tags nowatcher -run '^TestAssignedWorkerScriptPath$' -count=1 .; compilation stops at missingsapi/embed/php_embed.h. Local PHP 8.5.12 is NTS. Native callback/C integration, full FrankenPHP/Caddy suites and end-to-end load tests have not passed locally; they require a supported embed/ZTS environment.Time and allocation measurements
Baseline:
d138a78717413a589cb9af097483024c19f6ae92(main). PR head:02313c063270e3497b5b35debb31a526a54fde2b. Apple M2, darwin/arm64, macOS 27.0, Go 1.27.3. This PR is independently based on main.The standalone harness extracts
splitCgiPathand its helpers from the baseline and this PR without changing their bodies. It uses the production context declaration, committed tests/benchmark, and minimal worker/server stubs. It does not link PHP. Neither version includes changes from the separate splitPos PR.Ten before/after pairs, alternating which version runs first,
-test.cpu=1 -test.benchmem, 150ms per sub-benchmark. Separate optimized binaries, without race instrumentation or PGO. Setup is outsideb.Loop, and results are consumed. Table entries are median and observed min-max in ns/op, not confidence intervals. Positive time reduction means faster. These are operation microbenchmarks, not whole HTTP-request throughput. These final measurements supersede the earlier narrower helper timings.short_rootlong_rootoutside_rootempty_roottrailing_separatorexact_rootHeap allocation numbers count Go allocations during each operation. The optimization adds no cache or persistent state. Whole-process RSS, retained-process memory and PHP/C allocations were not measured, so zero/reduced B/op is not claimed to prove unchanged RSS.
Stack measurements
Generated optimized compiler assembly (
go test -c -gcflags=-S) for the same standalone function bodies:These are direct frames in the isolated Go compilation, not total nested stack peaks or native cgo-export wrapper frames. Linux and Windows were cross-compiled only; no runtime speed or test execution on those targets is claimed.
Reproduction
With supported PHP embed/ZTS development libraries installed, run the committed tests in the repository:
The following script reproduces the actual isolated timing setup without PHP. Save it outside the checkout and run it with Python 3 from this PR checkout. It extracts the baseline/current source, copies the committed tests, runs race checks, emits compiler assembly, and alternates ten benchmark pairs. For the output PR, the script also includes its Go-only benchmark fixture.
Standalone reproduction script
All baseline benchmark samples
All optimized benchmark samples
Fuzzing output