Created
January 23, 2026 22:01
-
-
Save jweinst1/c8c6589a01173318fc0f08175c510165 to your computer and use it in GitHub Desktop.
Basic probing map in C++
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
| enum class HashQueryRes { | |
| Empty, | |
| Occupied, | |
| Found | |
| }; | |
| class HashOnlyLinearMap { | |
| struct KVPair { | |
| uint32_t _hash = 0; | |
| uint32_t _value; | |
| inline bool add(uint32_t hash, uint32_t val) { | |
| if (_hash == 0) { | |
| _hash = hash; | |
| _value = val; | |
| return true; | |
| } else if (_hash == hash) { | |
| _value = val; | |
| return true; | |
| } | |
| return false; | |
| } | |
| inline HashQueryRes find(uint32_t hash, uint32_t* val) const { | |
| if (_hash == 0) { | |
| return HashQueryRes::Empty; | |
| } else if (_hash == hash) { | |
| *val = _value; | |
| return HashQueryRes::Found; | |
| } | |
| return HashQueryRes::Occupied; | |
| } | |
| }; | |
| struct Slot { | |
| KVPair pair; | |
| inline bool add(uint32_t hash, uint32_t val) { | |
| return pair.add(hash, val); | |
| } | |
| inline HashQueryRes find(uint32_t hash, uint32_t* val) const { | |
| return pair.find(hash, val); | |
| } | |
| }; | |
| std::vector<Slot> table; | |
| size_t mask; | |
| public: | |
| explicit HashOnlyLinearMap(size_t power_of_two_capacity) { | |
| size_t cap = 1ULL << power_of_two_capacity; | |
| table.resize(cap); | |
| mask = cap - 1; | |
| } | |
| void insert(uint32_t hash, uint32_t value) { | |
| size_t idx = hash & mask; | |
| while (true) { | |
| Slot& s = table[idx]; | |
| if (s.add(hash, value)) { | |
| return; | |
| } | |
| idx = (idx + 1) & mask; // linear probe | |
| } | |
| } | |
| uint32_t find(uint32_t hash) const { | |
| size_t idx = hash & mask; | |
| uint32_t myNum; | |
| while (true) { | |
| const Slot& s = table[idx]; | |
| const HashQueryRes res = s.find(hash, &myNum); | |
| if (res == HashQueryRes::Found) { | |
| return myNum; | |
| } else if (res == HashQueryRes::Empty) { | |
| return 0; | |
| } | |
| idx = (idx + 1) & mask; | |
| } | |
| } | |
| }; | |
| static void hashperftest() { | |
| static constexpr size_t keyMask3 = (size_t{1} << 20) - 1; | |
| IdGen gens; | |
| std::vector<uint32_t> nums; | |
| for (int i = 0; i < 4000000; ++i) | |
| { | |
| nums.push_back(gens.next() & keyMask3); | |
| } | |
| HashOnlyLinearMap foobar(22); | |
| auto startIns = std::chrono::high_resolution_clock::now(); | |
| for (const auto& num : nums) | |
| { | |
| foobar.insert(num, num); | |
| } | |
| auto endIns = std::chrono::high_resolution_clock::now(); | |
| std::cout << "hash ins " << std::chrono::duration_cast<std::chrono::microseconds>(endIns - startIns).count() << "us\n"; | |
| auto startFind = std::chrono::high_resolution_clock::now(); | |
| uint32_t total = 0; | |
| for (const auto& num : nums) | |
| { | |
| total += foobar.find(num); | |
| } | |
| auto endFind = std::chrono::high_resolution_clock::now(); | |
| std::cout << total << "hash find " << std::chrono::duration_cast<std::chrono::microseconds>(endFind - startFind).count() << "us\n"; | |
| } | |
| int main(int argc, char const *argv[]) | |
| { | |
| hashperftest(); | |
| return 0; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment