Repository navigation
Conversation
GetMany(keys, values, found) does what Get does for each key. For up to 32 keys at a time, while at least four are left, it hashes them, loads the index slot that each lookup starts at and the node it points to, and only then looks them up, so that the CPU fetches the memory of all of them in parallel. The last few keys of a call go through Get. With a million entries and keys picked at random, a key took 47 to 62% less time than with a Get loop with 32 keys per call, on one CPU and on eight, and 27 to 49% less with 8 keys per call. With 16K entries, which fit in the CPU's caches, it is within 10% of Get. A single key is faster with Get: through GetMany it took 32 to 86% longer, for a reason not found. Medians of three runs of BenchmarkGetMany on an Apple M3 Max. The gain needs the keys in hand at once: touching one key ahead of each Get does nothing, and the CAS that pins a node is not what slows Get. stats.Wrap passes GetMany on to the cache it wraps if it has one, and calls Get for each key otherwise; it counts a hit or a miss per key. Each package checks that GetMany counts reads and hits as Get does, also when it has to take the lock, that it helps reclaim, that short slices make it panic before it does anything, and that it allocates nothing. The inline check covers it, directly and through a generic wrapper. Co-Authored-By: Claude Opus 5.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.
What
GetMany(keys, values, found)on the three lock-free caches and onstats.Cache. It does whatGetdoes for each key, in order.cslab.Slab.Touch) and the node it points to (TouchNode), then runs the package's own lookup. The CPU fetches the memory of the whole batch in parallel.Get.valuesorfoundis shorter thankeys, before it looks anything up.stats.Wrapuses the wrapped cache'sGetManyif it has one, else loopsGet, and counts a hit or a miss per key.evict.Cache:lruandtwoqhave noGetMany.Results (Apple M3 Max)
Change in time per key against a
Getloop: ranges over the three caches of medians of three runs ofBenchmarkGetMany, keys picked at random.intentries, 32 keys per callintentries, 8 keys per callintentries, 32 keys per callintentries, 1 key per callGetManyeven though it only callsGet, and I did not find why: not the batch arrays, not the frame size, not inlining in the benchmark. The docs say to useGetfor one key.Why a batch method
A prototype was reviewed twice by separate agents, who reproduced the gain with interleaved one-process-per-variant runs and looked for ways to get it without a new method:
Getmade int lookups barely faster.Getgained nothing.Getgot 85–90% of the gain but hashed every key twice, 36% slower on a 16K cache.What pays is many independent loads issued back to back, which needs the keys in hand.
Checked
GetManymatchesPeekkey by key, for lengths around 32 and every remainder from 0 to 4, and withvaluesbeingkeysitself.Getand afterGetManyare compared per key, also with every reader slot taken so that lookups go through the lock.s3fifoalso compares its queues after evictions,sievethe keys left, andtinylfuthe hits its window hears of.Peekor skipping keys;tinylfunot tallying hits on either path.GetManycalls neitherTouchnorTouchNode, used directly or through a generic type of another package; marking either//go:noinlinemakes it fail.stats.Cache.GetManyis covered the same way.golangci-lint0 issues;go test -race ./...passes;GOOS=linux GOARCH=386 go vet ./...clean.🤖 Generated with Claude Code