Repository navigation
Allocate nothing of our own, build errors lazily, publish the benchmarks - #12
Merged
Merged
Conversation
With, Marshalers and Unmarshalers build their options once for each set of arguments and share them afterwards, so a call takes about 5 ns and allocates nothing. Before, every call built them anew: 262 ns and 13 allocations for With(ISO8601). The codecs are one struct with a switch instead of an interface, so the buffers of Format and Parse stay on the stack: Format allocates only the string it returns, and Parse nothing for input of up to 32 bytes. An error about a rejected duration builds its message when Error is called. Checking one costs 292 ns and 5 allocations where it cost 530 ns and 10, and 3 of the 5 are the standard library's. What callers can see: - The order of the formats in AlsoAccept makes no difference any more. Error messages name the written format first and the others in the order of Formats. - An invalid format in AlsoAccept panics in AlsoAccept itself, with the same message. - errors.Unwrap of an error about a rejected duration returns its cause, where it returned nil. BENCHMARKS.md reports every part of the API next to four ways to do without the package, and the README links to it.
The test compared json.Unmarshal with what the standard library allocates for an error of its own. That count is 3 on darwin/arm64 and 4 on linux/amd64, so the test failed on Linux. It now measures the decoder's own part, which is the same everywhere.
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.
The package now allocates nothing of its own on the JSON path, and
BENCHMARKS.mdshows what a duration costsnext to the standard library.
What gets cheaper
Medians of ten runs on an Apple M2 with Go 1.27.3.
With(ISO8601)With(ISO8601, AlsoAccept(Nano))AcceptDaystoo, 0 allocationsFormat.FormatFormat.ParseWriting and reading a valid duration allocated nothing of ours before either; that is now pinned by tests and
measured against an
int64in the same place.How
With,MarshalersandUnmarshalersbuild their options once for each set of argumentsand return the same ones afterwards. They sit in a fixed array of atomic pointers, indexed by the written
format, the set of other accepted formats, and
AcceptDays: no hashing and no lock.allocating.
FormatandParsestay on the stack.input they quote and build the message when
Erroris called. The format names in the message are built oncefor each decoder.
What callers can see
AlsoAcceptmakes no difference any more. Error messages name the written formatfirst and the others in the order of
Formats(), as iniso8601 or units or nano.AlsoAcceptpanics inAlsoAcceptitself, with the same message as before.errors.Unwrapof an error about a rejected duration returns its cause, where it returned nil.errors.Ismatches
ErrInvalidDurationand the cause as before, and the messages are unchanged.Benchmarks
bench_test.gocovers every part of the API: building the options, writing and reading one duration with keptoptions, with
Withon every call and with a wrapper type,AlsoAccept, a thousand durations, a struct with otherfields, invalid input, and
FormatandParse. Each is compared with anint64field, the standard library'sFormatDurationAsNano, the smallest possible function option, and a hand-written type with text methods.BENCHMARKS.mdreports the results, and the README has a short section that links to it. One finding is worthknowing: in a struct with many fields, wrapper types are faster than options, because the standard library asks
for every field whether a function applies to it.
BENCHMARKS.mdsays that versions up to v0.3.1 built the options on every call, so it reads correctly once thisis released.
Checks
just checkpasses, with 100% statement coverage.just mutate: 215 changes, a test fails for every one.go vetpasses forlinux/386. The 32-bit tests run here in CI.