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: ff990e4main
parent
a13bde39ee
commit
e63350e726
@ -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
|
Loading…
Reference in new issue