Browse Source

Improved probing hash map

Kajetan Johannes Hammerle 10 months ago
parent
commit
1b3fb934ef
2 changed files with 91 additions and 39 deletions
  1. 4 5
      data/ProbingHashMap.h
  2. 87 34
      performance/Main.cpp

+ 4 - 5
data/ProbingHashMap.h

@@ -286,7 +286,7 @@ namespace Core {
 
     private:
         int searchSlot(const K& key) const {
-            int baseHash = static_cast<int>(hashCode(key));
+            int baseHash = static_cast<int>(hashCode(key) * 514685581u);
             int end = keys.getLength() - 1;
             for(int i = 0; i <= end; i++) {
                 int hash = (baseHash + i) & end;
@@ -299,12 +299,11 @@ namespace Core {
 
         template<typename Value>
         Value* searchValue(const K& key) const {
-            int baseHash = static_cast<int>(hashCode(key));
+            int baseHash = static_cast<int>(hashCode(key) * 514685581u);
             int end = keys.getLength() - 1;
-            for(int i = 0; i <= end; i++) {
+            for(int i = 0; i <= end; i++) [[unlikely]] {
                 int hash = (baseHash + i) & end;
-                if(keys[hash] == key) //[[likely]]
-                {
+                if(keys[hash] == key) [[likely]] {
                     return values + hash;
                 } else if(keys[hash] == emptyValue<K>()) {
                     return nullptr;

+ 87 - 34
performance/Main.cpp

@@ -4,65 +4,118 @@
 #include "utils/Clock.h"
 #include "utils/Random.h"
 
-template<typename Map>
-static void test(const Map& m) {
-    Core::Clock::Nanos nanos = 0;
-    CORE_TEST_ERROR(Core::Clock::getNanos(nanos));
+using Millis = Core::Clock::Nanos;
+
+struct Timer {
+    Core::Clock::Nanos nanos;
 
-    int sum = 0;
+    Timer() {
+        CORE_TEST_ERROR(Core::Clock::getNanos(nanos));
+    }
+
+    Core::Clock::Nanos get() const {
+        Core::Clock::Nanos nanos2 = 0;
+        CORE_TEST_ERROR(Core::Clock::getNanos(nanos2));
+        return nanos2 - nanos;
+    }
+};
+
+template<typename Map>
+static Core::Clock::Nanos testSearch(const Map& m) {
+    Timer t;
+    volatile int sum = 0;
     for(int i = 0; i < 10000; i++) {
         for(int k = -5000; k < 5000; k++) {
             const int* s = m.search(i + k);
             if(s != nullptr) {
-                sum += *s;
+                sum = sum + *s;
             }
         }
     }
+    return t.get();
+}
 
-    Core::Clock::Nanos nanos2 = 0;
-    CORE_TEST_ERROR(Core::Clock::getNanos(nanos2));
-
-    CORE_LOG_INFO("# | # ns", sum, nanos2 - nanos);
+template<typename Map>
+static Core::Clock::Nanos testEmptySearch(const Map& m) {
+    Timer t;
+    volatile int sum = 0;
+    for(int i = 0; i < 100'000'000; i++) {
+        const int* s = m.search(-i);
+        if(s != nullptr) {
+            sum = sum + *s;
+        }
+    }
+    return t.get();
 }
 
-static void order() {
-    Core::HashMap<int, int> m;
-    Core::ProbingHashMap<int, int> m2;
+template<typename Map>
+static void fillOrder(Map& m) {
     for(int i = 0; i < 10000; i++) {
         CORE_TEST_ERROR(m.add(i, i * i));
-        CORE_TEST_ERROR(m2.add(i, i * i));
     }
-
-    test(m);
-    test(m);
-    test(m);
-
-    test(m2);
-    test(m2);
-    test(m2);
 }
 
-static void chaos() {
+template<typename Map>
+static void fillChaos(Map& m) {
     Core::Random random(0);
-    Core::HashMap<int, int> m;
-    Core::ProbingHashMap<int, int> m2;
     for(int i = 0; i < 10000; i++) {
         int r = random.next(0, 9999);
         CORE_TEST_ERROR(m.add(r, r * r));
-        CORE_TEST_ERROR(m2.add(r, r * r));
     }
+}
+
+template<typename Map>
+static Millis average(Map& m, Core::Clock::Nanos (*f)(const Map&), int n) {
+    Core::Clock::Nanos sum = 0;
+    for(int i = 0; i < n; i++) {
+        sum += f(m);
+    }
+    return sum / (n * 1'000'000);
+}
 
-    test(m);
-    test(m);
-    test(m);
+static void order(int n) {
+    Core::HashMap<int, int> m;
+    Core::ProbingHashMap<int, int> m2;
+    fillOrder(m);
+    fillOrder(m2);
+    CORE_LOG_INFO("Order Chaining | Order Probing");
+    CORE_LOG_INFO("Search | # ms | # ms", average(m, testSearch, n),
+                  average(m2, testSearch, n));
+    CORE_LOG_INFO("EmptySearch | # ms | # ms", average(m, testEmptySearch, n),
+                  average(m2, testEmptySearch, n));
+}
+
+static void chaos(int n) {
+    Core::HashMap<int, int> m;
+    Core::ProbingHashMap<int, int> m2;
+    fillChaos(m);
+    fillChaos(m2);
+    CORE_LOG_INFO("Order Chaining | Order Probing");
+    CORE_LOG_INFO("Search | # ms | # ms", average(m, testSearch, n),
+                  average(m2, testSearch, n));
+    CORE_LOG_INFO("EmptySearch | # ms | # ms", average(m, testEmptySearch, n),
+                  average(m2, testEmptySearch, n));
+}
 
-    test(m2);
-    test(m2);
-    test(m2);
+static void testProbing(int n) {
+    Core::ProbingHashMap<int, int> m;
+    Core::ProbingHashMap<int, int> m2;
+    fillOrder(m);
+    fillChaos(m2);
+    CORE_LOG_INFO("Order | Chaos");
+    CORE_LOG_INFO("Search | # ms | # ms", average(m, testSearch, n),
+                  average(m2, testSearch, n));
+    CORE_LOG_INFO("EmptySearch | # ms | # ms", average(m, testEmptySearch, n),
+                  average(m2, testEmptySearch, n));
 }
 
 int main() {
-    order();
-    chaos();
+    (void)order;
+    (void)chaos;
+    (void)testProbing;
+    order(3);
+    chaos(3);
+    // testProbing(3);
+    Core::Test::finalize();
     return 0;
 }