Skip to content

Fast-path the ASCII key digest with a big-endian load - #40

Merged
hadashiA merged 1 commit into
mainfrom
claude/ascii-digest-fastpath
Sep 9, 2026
Merged

hadashiA merged 1 commit into
mainfrom
claude/ascii-digest-fastpath

Conversation

@hadashiA

@hadashiA hadashiA commented Sep 9, 2026

Copy link
Copy Markdown
Owner

Summary

AsciiOrdinalEncoding.GetKeyDigest packed 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 + rev on ARM64, mov + bswap/movbe on 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; KeyDigestContractTest and the shared-prefix tests cover it.

Measured (Apple M5, .NET 10, standalone microbenchmark, results asserted equal)

key length loop BE load speedup
8B (key00123) 3.61 ns 1.58 ns 2.3×
17B (commonprefix00123) 2.82 ns 0.83 ns 3.4×
5B (short path) 1.91 ns 2.03 ns ~1× (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 (~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.ReverseEndianness exists on netstandard2.1).

🤖 Generated with Claude Code

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>
@hadashiA
hadashiA merged commit 6b17592 into main Sep 9, 2026
1 check passed
@hadashiA
hadashiA deleted the claude/ascii-digest-fastpath branch September 9, 2026 00:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant