-
Notifications
You must be signed in to change notification settings - Fork 18.4k
Description
Proposal Details
Context
The implementation of Swiss maps brought in Go 1.24 is more complex than previously seen implementations, but it retains one of the original details that everyone seems to implement in the same way: the control word used to denote an empty slot is 0b10000000 (and the one for deleted items is 0b11111110.
Problem
Every time a new table is allocated or it grows, it has to be filled with this specific "empty control word" pattern. For large maps, this is quite a lot of work.
Proposal
I propose to:
- Use zero-byte
0b00000000to denote an empty slot. - Use byte 1 (
0b00000001) for deleted slots. - Add
2toh2to ensure that it always has some non-zero bit set in bits 1-7.
While writing this issue I saw a similar proposal in a TODO comment in the implementation.
// TODO(prattmic): Consider inverting the top bit so that the zero value is empty.
type ctrl uint8What does this mean:
- The most important thing: an zeroed group is filled with empty control words out of the box, so we don't have to do that manually on each allocation/growth of the table.
- There's an extra operation to be done when splitting a key into the
h1/h2pair (plus two, my guess is no modern CPU will have a noticeable impact of that, but it's worth noting). - Depending on how the SIMD code is written, this may result in less operations needed.
- When I proposed this to
github.com/dolthub/swiss, I was able to use 2 less operations, although their version appears to be longer than the one implemented here in stdlib, but it's also designed for 16-element groups, which isn't done in Go yet. - I checked the code in stdlib and I don't even understand why it works yet1.
- When I proposed this to
I previously sent a PR to dolthub's swiss map implementation implementing this change, and the benchmarks were quite promising (although I didn't test the SIMD path, since I was benchmarking on arm64).
Given that Go's version is quite more complex than that one, I decided to drop an issue before attempting to hack a proof-of-concept.
cc @prattmic
Footnotes
-
The comment says that Empty slots are negated, becoming 1000 0000 (unchanged!)., but negating something should change it, right? I didn't find any docs regarding this behaviour, only ChatGPT explained me that since it's already the most negative value, it remains unchanged. ↩
Metadata
Metadata
Assignees
Labels
Type
Projects
Status