Google dense hash table (google-dense) implements an open addressing hash table with quardric probing. It requires users to set an empty element and a deleted element which are distinct from all the other valid keys in the hash table. Google-dense tests whether a bucket is empty or deleted by performing key comparisons. It is fast for integer keys where comparisons are cheap but will have overhead for string keys. In contrast, khash uses a bit vector to record which bucket is empty or deleted. On string keys, it avoids overhead for additional string comparisons, but will incur a potential cache miss when it is checking the bit vector. It seems that this cache miss costs a little less than one or two string comparisons. As a consequence, google dense hash is more efficient for simple keys and khash is more efficient for complex keys. This is what we see from my hash table benchmark.
Another difference between google-map (plus almost all the other hash map implementations I am aware of) and khash-map is that google-map keeps key-value pair in one array while khash-map keeps keys and values in two separate arrays. Keeping one array helps cache efficiency because we will not incur a cache miss when we have located the bucket via a key. However, putting a key and a value in one struct/class will waste memory when the key type and the value type are not aligned. For example, on 64-bit systems, if the key type is “const char*” and the value type is “int”, 25% of memory is wasted on memory alignment. Keeping two separate arrays like khash will not have this problem, but this strategy will cost one additional cache miss when we retrieve a value. Khash was initially designed for a huge hash table with 64-bit integers as keys and 8-bit integers as values. 44% of memory would be wasted on memory alignment if I put key-value pair in a struct. This was unacceptable for that application. Whether to separate keys and values is a tradeoff between memory and speed.
Love it! You are right, blogging teaches you a lot about yourself.