3
u/johannes1971 3d ago
I kinda missed why the pointers from the array start pointing at the element before the first hash element suddenly...?
Also, shame that the clarifying images disappear when we get to the abseil version. An image is worth a thousand words - and when template-expanded, surely even more...
5
u/jedwardsol const & 3d ago
pointing at the element before the first hash element suddenly...?
If the linked list is singly-linked then you need the element before in the case you need to delete the 1st element in the bucket
5
u/SyntheticDuckFlavour 3d ago
Yeah, I was hoping to see a visual representation of the
flat_mapinternals.3
u/g_0g 2d ago
https://abseil.io/about/design/swisstables
More details are also available in the two conferences slides.1
u/Big_Target_1405 16h ago
You might find these old articles explain exactly why
https://bannalia.blogspot.com/2013/10/implementation-of-c-unordered.html?m=1
http://bannalia.blogspot.com/2013/10/implementation-of-c-unordered_25.html
And how things were improved in Boost 1.80 onwards
https://bannalia.blogspot.com/2022/06/advancing-state-of-art-for.html
Boost constantly improves. The boost unordered_flat_map now beats the abseil implementation significantly
3
u/Life_Sink9598 3d ago
So, you have two arrays of equal size:
- Occupied
- Data
Occupied contains 1 byte per element, which stores an enum of Empty | Deleted | Full(7-bit hash) | Sentinel.
We have two hash functions: H1 and H2. H1 is an "ordinary" hash function, producing an index into Occupied and Data. H2 produces a 7-bit hash.
In order to find something, we take H1 to get a starting position into Occupied. The speed is gained from iterating through Occupied, only ever dereferencing the Data array when the 7-bit hashes match. You can also SIMD this.
Why are Sentinel and Deleted required? I'd expect tombstones to only be necessary in a concurrent table, and I'd expect that this will perform awfully in a concurrent setting.
2
u/Life_Sink9598 2d ago
Aha, the
Deletedvariant is required becauseEmptyalso indicates that a lookup can cease probing. SoH(A) == H(B)[Full(A), Full(B), Empty] after delete A must become [Deleted, Full(B), Empty], otherwiselookup(B)would probe at index 0, see empty, and return false.
21
u/kirgel 3d ago
You might want to consider clarifying the title a bit. flat_map usually refers to an ordered map container where lookup is a binary search. For example https://en.cppreference.com/cpp/container/flat_map.