Repository navigation
Write straight into the writer buffer, with a noalias writer - #4
Merged
Merged
Conversation
lalinsky
added this pull request to stack #5
October 16, 2026 20:04
Writer.writeAll loops over write, so every key and every string run went through an out-of-line memcpy, even for a few bytes. writeByte and writeAll now copy into the buffer directly when it has room, and leave the drain to a cold out-of-line function. Both take the writer as noalias. Without it, a byte stored into the buffer may have changed the writer's own fields, so end and the buffer are reloaded after every write. Median ns per encode, ReleaseFast, i7-6700: twitter 197309 -> 145861 (-26%) canada 4998434 -> 4796230 (-4%, std's float formatting dominates) small 218 -> 187 (-14%)
lalinsky
force-pushed
the
perf-writer-helpers
branch
from
October 16, 2026 20:11
ab5552d to
524c652
Compare
Both from zt (#41, #44): writeInt formatted into a temporary buffer and copied that out, a memcpy call of variable length for every number. When the buffer has room for the longest value of the type, count the digits and write them in place. Strings shorter than 16 bytes never reach the vector loop, but still paid for a call to writeString, which set up the vector constants and sent the writer's state through memory. Escape them inline with a lookup table and keep the vector scan out of line for longer strings. The string, integer, float and escape writers take the writer as noalias too, like writeAll. Median ns per encode, ReleaseFast, i7-6700, against the previous commit: twitter 145224 -> 138658 (-4.5%) canada 4777284 -> 4744561 (-0.7%) small 186 -> 171 (-8.3%) Instructions per encode drop by about 5% on twitter and 4% on small, so this isn't code layout moving around.
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 writer changes msgpack.zig got in #50 and #51, plus two more from zt (#41, #44).
Write straight into the buffer, with a noalias writer
Writer.writeAllloops overwrite, so every key and every run of string bytes went through an out-of-linememcpy, even for a few bytes. On twitter that was a third of the encode profile.writeByteandwriteAllnow copy into the buffer directly when it has room, and leave the drain to a cold out-of-line function.They take the writer as
noalias. Without it, a byte stored into the buffer may, as far as the compiler knows, have changed the writer's own fields, soendand the buffer are reloaded after every write.Integers in place, short strings inline
writeIntformatted into a temporary buffer and copied it out, a variable-lengthmemcpycall per number. When the buffer has room for the longest value of the type, the digits are now written in place.writeString. They're escaped inline with a lookup table now, and the vector scan stays out of line for longer strings.noaliastoo.Numbers
Median ns per encode, ReleaseFast, i7-6700, the same harness built against each version, 5 interleaved runs:
Instructions per encode drop at each step as well, so it isn't code layout moving around. That's worth checking on this machine, because it is a Skylake with the JCC erratum, where an unrelated change can move a loop by ±30%.
On canada, 82% of the time is
std.fmt.float.render, which none of this touches.zig build testpasses on 0.16 and 0.17. Encoding twitter, canada and small round-trips to the same document.The string loop here keeps the old
@bitCastmask, so it will conflict with #6 on one line. Whichever lands second takescompat.vectorMask.