]> git-server-git.apps.pok.os.sepia.ceph.com Git - rocksdb.git/commitdiff
Use more efficient hash map for deadlock detection
authorManuel Ung <mung@fb.com>
Sat, 19 Nov 2016 19:34:26 +0000 (11:34 -0800)
committerFacebook Github Bot <facebook-github-bot-bot@fb.com>
Sat, 19 Nov 2016 19:39:15 +0000 (11:39 -0800)
Summary:
Currently, deadlock cycles are held in std::unordered_map. The problem with it is that it allocates/deallocates memory on every insertion/deletion. This limits throughput since we're doing this expensive operation while holding a global mutex. Fix this by using a vector which caches memory instead.

Running the deadlock stress test, this change increased throughput from 39k txns/s -> 49k txns/s. The effect is more noticeable in MyRocks.
Closes https://github.com/facebook/rocksdb/pull/1545

Differential Revision: D4205662

Pulled By: lth

fbshipit-source-id: ff990e4

util/hash_map.h [new file with mode: 0644]
utilities/transactions/transaction_lock_mgr.cc
utilities/transactions/transaction_lock_mgr.h

diff --git a/util/hash_map.h b/util/hash_map.h
new file mode 100644 (file)
index 0000000..d39f592
--- /dev/null
@@ -0,0 +1,67 @@
+//  Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.  This
+//  source code is licensed under the BSD-style license found in the LICENSE
+//  file in the root directory of this source tree. An additional grant of
+//  patent rights can be found in the PATENTS file in the same directory.
+//
+
+#pragma once
+
+#include <algorithm>
+#include <array>
+#include <utility>
+
+#include "util/autovector.h"
+
+namespace rocksdb {
+
+// This is similar to std::unordered_map, except that it tries to avoid
+// allocating or deallocating memory as much as possible. With
+// std::unordered_map, an allocation/deallocation is made for every insertion
+// or deletion because of the requirement that iterators remain valid even
+// with insertions or deletions. This means that the hash chains will be
+// implemented as linked lists.
+//
+// This implementation uses autovector as hash chains insteads.
+//
+template <typename K, typename V, size_t size = 128>
+class HashMap {
+  std::array<autovector<std::pair<K, V>, 1>, size> table_;
+
+ public:
+  bool Contains(K key) {
+    auto& bucket = table_[key % size];
+    auto it = std::find_if(
+        bucket.begin(), bucket.end(),
+        [key](const std::pair<K, V>& p) { return p.first == key; });
+    return it != bucket.end();
+  }
+
+  void Insert(K key, V value) {
+    auto& bucket = table_[key % size];
+    bucket.push_back({key, value});
+  }
+
+  void Delete(K key) {
+    auto& bucket = table_[key % size];
+    auto it = std::find_if(
+        bucket.begin(), bucket.end(),
+        [key](const std::pair<K, V>& p) { return p.first == key; });
+    if (it != bucket.end()) {
+      auto last = bucket.end() - 1;
+      if (it != last) {
+        *it = *last;
+      }
+      bucket.pop_back();
+    }
+  }
+
+  V& Get(K key) {
+    auto& bucket = table_[key % size];
+    auto it = std::find_if(
+        bucket.begin(), bucket.end(),
+        [key](const std::pair<K, V>& p) { return p.first == key; });
+    return it->second;
+  }
+};
+
+}  // namespace rocksdb
index 792e014b18f6f858ab8e0cd1ae6722fa1d7142c8..8a49b221c8dd21cd2132a8f6738bf35808062b1d 100644 (file)
@@ -351,12 +351,12 @@ void TransactionLockMgr::DecrementWaiters(const TransactionImpl* txn,
 void TransactionLockMgr::DecrementWaitersImpl(const TransactionImpl* txn,
                                               TransactionID wait_id) {
   auto id = txn->GetID();
-  assert(wait_txn_map_.count(id) > 0);
-  wait_txn_map_.erase(id);
+  assert(wait_txn_map_.Contains(id));
+  wait_txn_map_.Delete(id);
 
-  rev_wait_txn_map_[wait_id]--;
-  if (rev_wait_txn_map_[wait_id] == 0) {
-    rev_wait_txn_map_.erase(wait_id);
+  rev_wait_txn_map_.Get(wait_id)--;
+  if (rev_wait_txn_map_.Get(wait_id) == 0) {
+    rev_wait_txn_map_.Delete(wait_id);
   }
 }
 
@@ -364,12 +364,17 @@ bool TransactionLockMgr::IncrementWaiters(const TransactionImpl* txn,
                                           TransactionID wait_id) {
   auto id = txn->GetID();
   std::lock_guard<std::mutex> lock(wait_txn_map_mutex_);
-  assert(wait_txn_map_.count(id) == 0);
-  wait_txn_map_[id] = wait_id;
-  rev_wait_txn_map_[wait_id]++;
+  assert(!wait_txn_map_.Contains(id));
+  wait_txn_map_.Insert(id, wait_id);
+
+  if (rev_wait_txn_map_.Contains(wait_id)) {
+    rev_wait_txn_map_.Get(wait_id)++;
+  } else {
+    rev_wait_txn_map_.Insert(wait_id, 1);
+  }
 
   // No deadlock if nobody is waiting on self.
-  if (rev_wait_txn_map_.count(id) == 0) {
+  if (!rev_wait_txn_map_.Contains(id)) {
     return false;
   }
 
@@ -378,10 +383,10 @@ bool TransactionLockMgr::IncrementWaiters(const TransactionImpl* txn,
     if (next == id) {
       DecrementWaitersImpl(txn, wait_id);
       return true;
-    } else if (wait_txn_map_.count(next) == 0) {
+    } else if (!wait_txn_map_.Contains(next)) {
       return false;
     } else {
-      next = wait_txn_map_[next];
+      next = wait_txn_map_.Get(next);
     }
   }
 
index da9913e16635f68d798f6bfc850ffcc8a69d893a..ea0352d5364af0aaef160e312ea8a0e1cfc8ca0c 100644 (file)
@@ -13,6 +13,7 @@
 #include <vector>
 
 #include "rocksdb/utilities/transaction.h"
+#include "util/hash_map.h"
 #include "util/instrumented_mutex.h"
 #include "util/thread_local.h"
 #include "utilities/transactions/transaction_impl.h"
@@ -88,9 +89,9 @@ class TransactionLockMgr {
   std::mutex wait_txn_map_mutex_;
 
   // Maps from waitee -> number of waiters.
-  std::unordered_map<TransactionID, int> rev_wait_txn_map_;
+  HashMap<TransactionID, int> rev_wait_txn_map_;
   // Maps from waiter -> waitee.
-  std::unordered_map<TransactionID, TransactionID> wait_txn_map_;
+  HashMap<TransactionID, TransactionID> wait_txn_map_;
 
   // Used to allocate mutexes/condvars to use when locking keys
   std::shared_ptr<TransactionDBMutexFactory> mutex_factory_;