Repository navigation
Fast-path the ASCII key digest with a big-endian load - #40
Merged
Merged
Conversation
The byte-at-a-time packing loop becomes a single unaligned 8-byte load plus ReverseEndianness (ldr + rev on ARM64, mov + bswap/movbe on x64) whenever the key has at least 8 bytes; shorter keys keep the loop, since a whole-ulong load would read past the key. The results are bit-identical (the loop already packed byte i at bits 56 - 8i, i.e. big-endian order). Measured 2.3x for 8-byte keys (3.61 -> 1.58 ns) and 3.4x for longer keys (2.82 -> 0.83 ns) on Apple M5; sub-8-byte keys are unchanged within noise. The digest is computed once per query and once per entry at build time, so this trims a couple of ns from every ASCII-keyed lookup and lines all built-in encodings up at a-few-instructions digest cost (Int64: flip, Ulid: BE load, ASCII >= 8B: BE load). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.
Summary
AsciiOrdinalEncoding.GetKeyDigestpacked the first 8 key bytes with a byte-at-a-time loop. For keys of 8+ bytes (the common case) the same value is exactly "read the first 8 bytes as a big-endian number", which compiles to one unaligned load plus one byte-swap instruction (ldr + revon ARM64,mov + bswap/movbeon x64). Shorter keys keep the loop — a whole-ulong load would read past the key.The two forms are bit-identical for length >= 8 (the loop already placed byte i at bits
56 - 8i, i.e. big-endian order), so this changes no on-disk bytes and no search results;KeyDigestContractTestand the shared-prefix tests cover it.Measured (Apple M5, .NET 10, standalone microbenchmark, results asserted equal)
key00123)commonprefix00123)The digest is computed once per query and once per entry at build time, so this trims a couple of ns from every ASCII-keyed lookup (~8% of a cached point lookup) and benefits secondary indexes too (composite keys delegate to the source digest). With this, every built-in encoding's digest is a few instructions: Int64 = sign flip, Ulid = BE load, ASCII (>= 8B) = BE load, UUIDv7 = three-field pack.
Tests
95/95 passing on all target frameworks (
BinaryPrimitives.ReverseEndiannessexists on netstandard2.1).🤖 Generated with Claude Code