diff options
author | Tor Egge <Tor.Egge@broadpark.no> | 2021-03-26 10:59:16 +0100 |
---|---|---|
committer | Tor Egge <Tor.Egge@broadpark.no> | 2021-03-26 10:59:16 +0100 |
commit | 4705dcb8fd738546882adfb39ebce0bf2421ffe1 (patch) | |
tree | f02668da987112b071332cc1384d50e9b742464c | |
parent | 326d62a79c51ff7332bf8dca205f66862330f453 (diff) |
Rename SimpleHashMap to ShardedHashMap.
14 files changed, 224 insertions, 224 deletions
diff --git a/searchlib/src/vespa/searchlib/attribute/enum_store_dictionary.cpp b/searchlib/src/vespa/searchlib/attribute/enum_store_dictionary.cpp index 1d0430cfb63..5a65d4937e4 100644 --- a/searchlib/src/vespa/searchlib/attribute/enum_store_dictionary.cpp +++ b/searchlib/src/vespa/searchlib/attribute/enum_store_dictionary.cpp @@ -8,7 +8,7 @@ #include <vespa/vespalib/btree/btreenodeallocator.hpp> #include <vespa/vespalib/btree/btreeroot.hpp> #include <vespa/vespalib/datastore/datastore.hpp> -#include <vespa/vespalib/datastore/simple_hash_map.h> +#include <vespa/vespalib/datastore/sharded_hash_map.h> #include <vespa/vespalib/datastore/unique_store_dictionary.hpp> #include <vespa/searchlib/util/bufferwriter.h> @@ -355,7 +355,7 @@ template class EnumStoreDictionary<EnumTree>; template class EnumStoreDictionary<EnumPostingTree>; -template class EnumStoreDictionary<EnumPostingTree, vespalib::datastore::SimpleHashMap>; +template class EnumStoreDictionary<EnumPostingTree, vespalib::datastore::ShardedHashMap>; } diff --git a/searchlib/src/vespa/searchlib/attribute/enumstore.cpp b/searchlib/src/vespa/searchlib/attribute/enumstore.cpp index 5beafea0046..5065a4a6cdc 100644 --- a/searchlib/src/vespa/searchlib/attribute/enumstore.cpp +++ b/searchlib/src/vespa/searchlib/attribute/enumstore.cpp @@ -1,7 +1,7 @@ // Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. #include "enumstore.hpp" -#include <vespa/vespalib/datastore/simple_hash_map.h> +#include <vespa/vespalib/datastore/sharded_hash_map.h> #include <vespa/vespalib/util/rcuvector.hpp> #include <iomanip> @@ -51,7 +51,7 @@ make_enum_store_dictionary(IEnumStore &store, bool has_postings, search::Diction switch (type) { case search::DictionaryConfig::Type::HASH: case search::DictionaryConfig::Type::BTREE_AND_HASH: - return std::make_unique<EnumStoreDictionary<EnumPostingTree, vespalib::datastore::SimpleHashMap>>(store, std::move(compare)); + return std::make_unique<EnumStoreDictionary<EnumPostingTree, vespalib::datastore::ShardedHashMap>>(store, std::move(compare)); default: return std::make_unique<EnumStoreDictionary<EnumPostingTree>>(store, std::move(compare)); } diff --git a/vespalib/CMakeLists.txt b/vespalib/CMakeLists.txt index 9e06bb152fd..3bdd477fcb6 100644 --- a/vespalib/CMakeLists.txt +++ b/vespalib/CMakeLists.txt @@ -43,7 +43,7 @@ vespa_define_module( src/tests/datastore/buffer_type src/tests/datastore/datastore src/tests/datastore/fixed_size_hash_map - src/tests/datastore/simple_hash_map + src/tests/datastore/sharded_hash_map src/tests/datastore/unique_store src/tests/datastore/unique_store_dictionary src/tests/datastore/unique_store_string_allocator diff --git a/vespalib/src/tests/datastore/sharded_hash_map/CMakeLists.txt b/vespalib/src/tests/datastore/sharded_hash_map/CMakeLists.txt new file mode 100644 index 00000000000..f94b952f4a3 --- /dev/null +++ b/vespalib/src/tests/datastore/sharded_hash_map/CMakeLists.txt @@ -0,0 +1,9 @@ +# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(vespalib_sharded_hash_map_test_app + SOURCES + sharded_hash_map_test.cpp + DEPENDS + vespalib + GTest::GTest +) +vespa_add_test(NAME vespalib_sharded_hash_map_test_app COMMAND vespalib_sharded_hash_map_test_app) diff --git a/vespalib/src/tests/datastore/simple_hash_map/simple_hash_map_test.cpp b/vespalib/src/tests/datastore/sharded_hash_map/sharded_hash_map_test.cpp index 866c2a30818..9f11b96e672 100644 --- a/vespalib/src/tests/datastore/simple_hash_map/simple_hash_map_test.cpp +++ b/vespalib/src/tests/datastore/sharded_hash_map/sharded_hash_map_test.cpp @@ -1,6 +1,6 @@ // Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -#include <vespa/vespalib/datastore/simple_hash_map.h> +#include <vespa/vespalib/datastore/sharded_hash_map.h> #include <vespa/vespalib/datastore/unique_store_allocator.h> #include <vespa/vespalib/datastore/unique_store_comparator.h> @@ -13,18 +13,18 @@ #include <vespa/vespalib/datastore/unique_store_allocator.hpp> #include <vespa/log/log.h> -LOG_SETUP("vespalib_datastore_simple_hash_test"); +LOG_SETUP("vespalib_datastore_shared_hash_test"); using vespalib::datastore::EntryRef; using RefT = vespalib::datastore::EntryRefT<22>; using MyAllocator = vespalib::datastore::UniqueStoreAllocator<uint32_t, RefT>; using MyDataStore = vespalib::datastore::DataStoreT<RefT>; using MyCompare = vespalib::datastore::UniqueStoreComparator<uint32_t, RefT>; -using MyHashMap = vespalib::datastore::SimpleHashMap; +using MyHashMap = vespalib::datastore::ShardedHashMap; using GenerationHandler = vespalib::GenerationHandler; using vespalib::makeLambdaTask; -struct DataStoreSimpleHashTest : public ::testing::Test +struct DataStoreShardedHashTest : public ::testing::Test { GenerationHandler _generationHandler; MyAllocator _allocator; @@ -41,8 +41,8 @@ struct DataStoreSimpleHashTest : public ::testing::Test std::atomic<int> _stop_read; bool _report_work; - DataStoreSimpleHashTest(); - ~DataStoreSimpleHashTest(); + DataStoreShardedHashTest(); + ~DataStoreShardedHashTest(); void commit(); void insert(uint32_t key); void remove(uint32_t key); @@ -53,7 +53,7 @@ struct DataStoreSimpleHashTest : public ::testing::Test }; -DataStoreSimpleHashTest::DataStoreSimpleHashTest() +DataStoreShardedHashTest::DataStoreShardedHashTest() : _generationHandler(), _allocator(), _store(_allocator.get_data_store()), @@ -73,7 +73,7 @@ DataStoreSimpleHashTest::DataStoreSimpleHashTest() } -DataStoreSimpleHashTest::~DataStoreSimpleHashTest() +DataStoreShardedHashTest::~DataStoreShardedHashTest() { _readers.sync(); _readers.shutdown(); @@ -89,7 +89,7 @@ DataStoreSimpleHashTest::~DataStoreSimpleHashTest() void -DataStoreSimpleHashTest::commit() +DataStoreShardedHashTest::commit() { _store.transferHoldLists(_generationHandler.getCurrentGeneration()); _hash_map.transfer_hold_lists(_generationHandler.getCurrentGeneration()); @@ -99,7 +99,7 @@ DataStoreSimpleHashTest::commit() } void -DataStoreSimpleHashTest::insert(uint32_t key) +DataStoreShardedHashTest::insert(uint32_t key) { MyCompare comp(_store, key); std::function<EntryRef(void)> insert_entry([this, key]() -> EntryRef { return _allocator.allocate(key); }); @@ -110,7 +110,7 @@ DataStoreSimpleHashTest::insert(uint32_t key) } void -DataStoreSimpleHashTest::remove(uint32_t key) +DataStoreShardedHashTest::remove(uint32_t key) { MyCompare comp(_store, key); auto result = _hash_map.remove(comp, EntryRef()); @@ -124,7 +124,7 @@ DataStoreSimpleHashTest::remove(uint32_t key) void -DataStoreSimpleHashTest::read_work(uint32_t cnt) +DataStoreShardedHashTest::read_work(uint32_t cnt) { vespalib::Rand48 rnd; long found = 0; @@ -149,14 +149,14 @@ DataStoreSimpleHashTest::read_work(uint32_t cnt) void -DataStoreSimpleHashTest::read_work() +DataStoreShardedHashTest::read_work() { read_work(std::numeric_limits<uint32_t>::max()); } void -DataStoreSimpleHashTest::write_work(uint32_t cnt) +DataStoreShardedHashTest::write_work(uint32_t cnt) { vespalib::Rand48 &rnd(_rnd); for (uint32_t i = 0; i < cnt; ++i) { @@ -174,7 +174,7 @@ DataStoreSimpleHashTest::write_work(uint32_t cnt) } -TEST_F(DataStoreSimpleHashTest, single_threaded_reader_without_updates) +TEST_F(DataStoreShardedHashTest, single_threaded_reader_without_updates) { _report_work = true; write_work(10); @@ -182,7 +182,7 @@ TEST_F(DataStoreSimpleHashTest, single_threaded_reader_without_updates) read_work(10); } -TEST_F(DataStoreSimpleHashTest, single_threaded_reader_during_updates) +TEST_F(DataStoreShardedHashTest, single_threaded_reader_during_updates) { uint32_t cnt = 1000000; _report_work = true; @@ -190,7 +190,7 @@ TEST_F(DataStoreSimpleHashTest, single_threaded_reader_during_updates) _readers.execute(makeLambdaTask([this]() { read_work(); })); } -TEST_F(DataStoreSimpleHashTest, multi_threaded_reader_during_updates) +TEST_F(DataStoreShardedHashTest, multi_threaded_reader_during_updates) { uint32_t cnt = 1000000; _report_work = true; @@ -200,7 +200,7 @@ TEST_F(DataStoreSimpleHashTest, multi_threaded_reader_during_updates) } } -TEST_F(DataStoreSimpleHashTest, memory_usage_is_reported) +TEST_F(DataStoreShardedHashTest, memory_usage_is_reported) { auto initial_usage = _hash_map.get_memory_usage(); EXPECT_LT(0, initial_usage.allocatedBytes()); diff --git a/vespalib/src/tests/datastore/simple_hash_map/CMakeLists.txt b/vespalib/src/tests/datastore/simple_hash_map/CMakeLists.txt deleted file mode 100644 index c790481ebbc..00000000000 --- a/vespalib/src/tests/datastore/simple_hash_map/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -vespa_add_executable(vespalib_simple_hash_map_test_app - SOURCES - simple_hash_map_test.cpp - DEPENDS - vespalib - GTest::GTest -) -vespa_add_test(NAME vespalib_simple_hash_map_test_app COMMAND vespalib_simple_hash_map_test_app) diff --git a/vespalib/src/tests/datastore/unique_store/unique_store_test.cpp b/vespalib/src/tests/datastore/unique_store/unique_store_test.cpp index 25ed566f57a..3e8b144cf32 100644 --- a/vespalib/src/tests/datastore/unique_store/unique_store_test.cpp +++ b/vespalib/src/tests/datastore/unique_store/unique_store_test.cpp @@ -3,7 +3,7 @@ #include <vespa/vespalib/datastore/unique_store_remapper.h> #include <vespa/vespalib/datastore/unique_store_string_allocator.hpp> #include <vespa/vespalib/datastore/unique_store_string_comparator.h> -#include <vespa/vespalib/datastore/simple_hash_map.h> +#include <vespa/vespalib/datastore/sharded_hash_map.h> #include <vespa/vespalib/gtest/gtest.h> #include <vespa/vespalib/test/datastore/buffer_stats.h> #include <vespa/vespalib/test/insertion_operators.h> @@ -152,7 +152,7 @@ TestBase<UniqueStoreTypeAndDictionaryType>::TestBase() case DictionaryType::BTREE: break; default: - store.set_dictionary(std::make_unique<UniqueStoreDictionary<uniquestore::DefaultDictionary, IUniqueStoreDictionary, SimpleHashMap>>(std::make_unique<CompareType>(store.get_data_store()))); + store.set_dictionary(std::make_unique<UniqueStoreDictionary<uniquestore::DefaultDictionary, IUniqueStoreDictionary, ShardedHashMap>>(std::make_unique<CompareType>(store.get_data_store()))); } } diff --git a/vespalib/src/tests/datastore/unique_store_dictionary/unique_store_dictionary_test.cpp b/vespalib/src/tests/datastore/unique_store_dictionary/unique_store_dictionary_test.cpp index 2085292cdf4..2c1b4ad3cf1 100644 --- a/vespalib/src/tests/datastore/unique_store_dictionary/unique_store_dictionary_test.cpp +++ b/vespalib/src/tests/datastore/unique_store_dictionary/unique_store_dictionary_test.cpp @@ -2,7 +2,7 @@ #include <vespa/vespalib/datastore/unique_store.hpp> #include <vespa/vespalib/datastore/unique_store_dictionary.hpp> -#include <vespa/vespalib/datastore/simple_hash_map.h> +#include <vespa/vespalib/datastore/sharded_hash_map.h> #include <vespa/vespalib/util/memoryusage.h> #include <vespa/vespalib/gtest/gtest.h> @@ -59,7 +59,7 @@ struct DictionaryReadTest : public ::testing::Test { } }; -using DictionaryReadTestTypes = ::testing::Types<DefaultUniqueStoreDictionary, UniqueStoreDictionary<DefaultDictionary, IUniqueStoreDictionary, SimpleHashMap>>; +using DictionaryReadTestTypes = ::testing::Types<DefaultUniqueStoreDictionary, UniqueStoreDictionary<DefaultDictionary, IUniqueStoreDictionary, ShardedHashMap>>; VESPA_GTEST_TYPED_TEST_SUITE(DictionaryReadTest, DictionaryReadTestTypes); // Disable warnings emitted by gtest generated files when using typed tests diff --git a/vespalib/src/vespa/vespalib/datastore/CMakeLists.txt b/vespalib/src/vespa/vespalib/datastore/CMakeLists.txt index 1ee945f1b8f..b028a040f8d 100644 --- a/vespalib/src/vespa/vespalib/datastore/CMakeLists.txt +++ b/vespalib/src/vespa/vespalib/datastore/CMakeLists.txt @@ -9,7 +9,7 @@ vespa_add_library(vespalib_vespalib_datastore OBJECT datastorebase.cpp entryref.cpp fixed_size_hash_map.cpp - simple_hash_map.cpp + sharded_hash_map.cpp unique_store.cpp unique_store_string_allocator.cpp DEPENDS diff --git a/vespalib/src/vespa/vespalib/datastore/fixed_size_hash_map.cpp b/vespalib/src/vespa/vespalib/datastore/fixed_size_hash_map.cpp index 4822313e39e..cca61e3c852 100644 --- a/vespalib/src/vespa/vespalib/datastore/fixed_size_hash_map.cpp +++ b/vespalib/src/vespa/vespalib/datastore/fixed_size_hash_map.cpp @@ -20,7 +20,7 @@ FixedSizeHashMap::Node::on_free() _kv = std::make_pair(AtomicEntryRef(), AtomicEntryRef()); } -FixedSizeHashMap::FixedSizeHashMap(uint32_t modulo, uint32_t capacity, uint32_t num_stripes) +FixedSizeHashMap::FixedSizeHashMap(uint32_t modulo, uint32_t capacity, uint32_t num_shards) : _chain_heads(modulo), _nodes(), _modulo(modulo), @@ -30,13 +30,13 @@ FixedSizeHashMap::FixedSizeHashMap(uint32_t modulo, uint32_t capacity, uint32_t _hold_count(0u), _hold_1_list(), _hold_2_list(), - _num_stripes(num_stripes) + _num_shards(num_shards) { _nodes.reserve(capacity); } -FixedSizeHashMap::FixedSizeHashMap(uint32_t modulo, uint32_t capacity, uint32_t num_stripes, const FixedSizeHashMap &orig, const EntryComparator& comp) - : FixedSizeHashMap(modulo, capacity, num_stripes) +FixedSizeHashMap::FixedSizeHashMap(uint32_t modulo, uint32_t capacity, uint32_t num_shards, const FixedSizeHashMap &orig, const EntryComparator& comp) + : FixedSizeHashMap(modulo, capacity, num_shards) { for (const auto &chain_head : orig._chain_heads) { for (uint32_t node_idx = chain_head.load_relaxed(); node_idx != no_node_idx;) { @@ -52,7 +52,7 @@ FixedSizeHashMap::~FixedSizeHashMap() = default; void FixedSizeHashMap::force_add(const EntryComparator& comp, const KvType& kv) { - size_t hash_idx = comp.hash(kv.first.load_relaxed()) / _num_stripes; + size_t hash_idx = comp.hash(kv.first.load_relaxed()) / _num_shards; hash_idx %= _modulo; auto& chain_head = _chain_heads[hash_idx]; assert(_nodes.size() < _nodes.capacity()); @@ -65,7 +65,7 @@ FixedSizeHashMap::force_add(const EntryComparator& comp, const KvType& kv) FixedSizeHashMap::KvType& FixedSizeHashMap::add(const EntryComparator& comp, EntryRef key_ref, std::function<EntryRef(void)>& insert_entry) { - size_t hash_idx = comp.hash(key_ref) / _num_stripes; + size_t hash_idx = comp.hash(key_ref) / _num_shards; hash_idx %= _modulo; auto& chain_head = _chain_heads[hash_idx]; uint32_t node_idx = chain_head.load_relaxed(); @@ -129,7 +129,7 @@ FixedSizeHashMap::trim_hold_lists_slow(generation_t first_used) FixedSizeHashMap::KvType* FixedSizeHashMap::remove(const EntryComparator& comp, EntryRef key_ref) { - size_t hash_idx = comp.hash(key_ref) / _num_stripes; + size_t hash_idx = comp.hash(key_ref) / _num_shards; hash_idx %= _modulo; auto& chain_head = _chain_heads[hash_idx]; uint32_t node_idx = chain_head.load_relaxed(); @@ -157,7 +157,7 @@ FixedSizeHashMap::remove(const EntryComparator& comp, EntryRef key_ref) FixedSizeHashMap::KvType* FixedSizeHashMap::find(const EntryComparator& comp, EntryRef key_ref) { - size_t hash_idx = comp.hash(key_ref) / _num_stripes; + size_t hash_idx = comp.hash(key_ref) / _num_shards; hash_idx %= _modulo; auto& chain_head = _chain_heads[hash_idx]; uint32_t node_idx = chain_head.load_acquire(); diff --git a/vespalib/src/vespa/vespalib/datastore/fixed_size_hash_map.h b/vespalib/src/vespa/vespalib/datastore/fixed_size_hash_map.h index 8153bc82e06..a0ca1bf0568 100644 --- a/vespalib/src/vespa/vespalib/datastore/fixed_size_hash_map.h +++ b/vespalib/src/vespa/vespalib/datastore/fixed_size_hash_map.h @@ -89,14 +89,14 @@ private: uint32_t _hold_count; Array<uint32_t> _hold_1_list; std::deque<std::pair<generation_t, uint32_t>> _hold_2_list; - uint32_t _num_stripes; + uint32_t _num_shards; void transfer_hold_lists_slow(generation_t generation); void trim_hold_lists_slow(generation_t first_used); void force_add(const EntryComparator& comp, const KvType& kv); public: - FixedSizeHashMap(uint32_t module, uint32_t capacity, uint32_t num_stripes); - FixedSizeHashMap(uint32_t module, uint32_t capacity, uint32_t num_stripes, const FixedSizeHashMap &orig, const EntryComparator& comp); + FixedSizeHashMap(uint32_t module, uint32_t capacity, uint32_t num_shards); + FixedSizeHashMap(uint32_t module, uint32_t capacity, uint32_t num_shards, const FixedSizeHashMap &orig, const EntryComparator& comp); ~FixedSizeHashMap(); KvType& add(const EntryComparator& comp, EntryRef key_ref, std::function<EntryRef(void)>& insert_entry); diff --git a/vespalib/src/vespa/vespalib/datastore/sharded_hash_map.cpp b/vespalib/src/vespa/vespalib/datastore/sharded_hash_map.cpp new file mode 100644 index 00000000000..7c609f89972 --- /dev/null +++ b/vespalib/src/vespa/vespalib/datastore/sharded_hash_map.cpp @@ -0,0 +1,168 @@ +// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include "sharded_hash_map.h" +#include "fixed_size_hash_map.h" +#include "entry_comparator.h" +#include <vespa/vespalib/util/memoryusage.h> + +namespace vespalib::datastore { + +class ShardedHashMapShardHeld : public GenerationHeldBase +{ + std::unique_ptr<const FixedSizeHashMap> _data; +public: + ShardedHashMapShardHeld(size_t size, std::unique_ptr<const FixedSizeHashMap> data); + ~ShardedHashMapShardHeld(); +}; + +ShardedHashMapShardHeld::ShardedHashMapShardHeld(size_t size, std::unique_ptr<const FixedSizeHashMap> data) + : GenerationHeldBase(size), + _data(std::move(data)) +{ +} + +ShardedHashMapShardHeld::~ShardedHashMapShardHeld() = default; + +ShardedHashMap::ShardedHashMap(std::unique_ptr<const EntryComparator> comp) + : _gen_holder(), + _maps(), + _comp(std::move(comp)) +{ +} + +ShardedHashMap::~ShardedHashMap() +{ + _gen_holder.clearHoldLists(); + for (size_t i = 0; i < num_shards; ++i) { + auto map = _maps[i].load(std::memory_order_relaxed); + delete map; + } +} + +size_t +ShardedHashMap::get_shard_idx(const EntryComparator& comp, EntryRef key_ref) const +{ + return comp.hash(key_ref) % num_shards; +} + +void +ShardedHashMap::alloc_shard(size_t shard_idx) +{ + auto map = _maps[shard_idx].load(std::memory_order_relaxed); + if (map == nullptr) { + auto umap = std::make_unique<FixedSizeHashMap>(2u, 3u, num_shards); + _maps[shard_idx].store(umap.release(), std::memory_order_release); + } else { + auto umap = std::make_unique<FixedSizeHashMap>(map->size() * 2 + 2, map->size() * 3 + 3, num_shards, *map, *_comp); + _maps[shard_idx].store(umap.release(), std::memory_order_release); + hold_shard(std::unique_ptr<const FixedSizeHashMap>(map)); + } +} + +void +ShardedHashMap::hold_shard(std::unique_ptr<const FixedSizeHashMap> map) +{ + auto usage = map->get_memory_usage(); + auto hold = std::make_unique<ShardedHashMapShardHeld>(usage.allocatedBytes(), std::move(map)); + _gen_holder.hold(std::move(hold)); +} + +ShardedHashMap::KvType& +ShardedHashMap::add(const EntryComparator& comp, EntryRef key_ref, std::function<EntryRef(void)>& insert_entry) +{ + size_t shard_idx = get_shard_idx(comp, key_ref); + auto map = _maps[shard_idx].load(std::memory_order_relaxed); + if (map == nullptr || map->full()) { + alloc_shard(shard_idx); + map = _maps[shard_idx].load(std::memory_order_relaxed); + } + return map->add(comp, key_ref, insert_entry); +} + +ShardedHashMap::KvType* +ShardedHashMap::remove(const EntryComparator& comp, EntryRef key_ref) +{ + size_t shard_idx = get_shard_idx(comp, key_ref); + auto map = _maps[shard_idx].load(std::memory_order_relaxed); + if (map == nullptr) { + return nullptr; + } + return map->remove(comp, key_ref); +} + +ShardedHashMap::KvType* +ShardedHashMap::find(const EntryComparator& comp, EntryRef key_ref) +{ + size_t shard_idx = get_shard_idx(comp, key_ref); + auto map = _maps[shard_idx].load(std::memory_order_relaxed); + if (map == nullptr) { + return nullptr; + } + return map->find(comp, key_ref); +} + +const ShardedHashMap::KvType* +ShardedHashMap::find(const EntryComparator& comp, EntryRef key_ref) const +{ + size_t shard_idx = get_shard_idx(comp, key_ref); + auto map = _maps[shard_idx].load(std::memory_order_relaxed); + if (map == nullptr) { + return nullptr; + } + return map->find(comp, key_ref); +} + +void +ShardedHashMap::transfer_hold_lists(generation_t generation) +{ + for (size_t i = 0; i < num_shards; ++i) { + auto map = _maps[i].load(std::memory_order_relaxed); + if (map != nullptr) { + map->transfer_hold_lists(generation); + } + } + _gen_holder.transferHoldLists(generation); +} + +void +ShardedHashMap::trim_hold_lists(generation_t first_used) +{ + for (size_t i = 0; i < num_shards; ++i) { + auto map = _maps[i].load(std::memory_order_relaxed); + if (map != nullptr) { + map->trim_hold_lists(first_used); + } + } + _gen_holder.trimHoldLists(first_used); +} + +size_t +ShardedHashMap::size() const noexcept +{ + size_t result = 0; + for (size_t i = 0; i < num_shards; ++i) { + auto map = _maps[i].load(std::memory_order_relaxed); + if (map != nullptr) { + result += map->size(); + } + } + return result; +} + +MemoryUsage +ShardedHashMap::get_memory_usage() const +{ + MemoryUsage memory_usage(sizeof(ShardedHashMap), sizeof(ShardedHashMap), 0, 0); + for (size_t i = 0; i < num_shards; ++i) { + auto map = _maps[i].load(std::memory_order_relaxed); + if (map != nullptr) { + memory_usage.merge(map->get_memory_usage()); + } + } + size_t gen_holder_held_bytes = _gen_holder.getHeldBytes(); + memory_usage.incAllocatedBytes(gen_holder_held_bytes); + memory_usage.incAllocatedBytesOnHold(gen_holder_held_bytes); + return memory_usage; +} + +} diff --git a/vespalib/src/vespa/vespalib/datastore/simple_hash_map.h b/vespalib/src/vespa/vespalib/datastore/sharded_hash_map.h index bc052295511..7ea60646b06 100644 --- a/vespalib/src/vespa/vespalib/datastore/simple_hash_map.h +++ b/vespalib/src/vespa/vespalib/datastore/sharded_hash_map.h @@ -30,23 +30,23 @@ class EntryComparator; * trim_hold_lists as needed to free up memory no longer needed by any * readers. */ -class SimpleHashMap { +class ShardedHashMap { public: using KvType = std::pair<AtomicEntryRef, AtomicEntryRef>; using generation_t = GenerationHandler::generation_t; using sgeneration_t = GenerationHandler::sgeneration_t; private: GenerationHolder _gen_holder; - static constexpr size_t num_stripes = 3; - std::atomic<FixedSizeHashMap *> _maps[num_stripes]; + static constexpr size_t num_shards = 3; + std::atomic<FixedSizeHashMap *> _maps[num_shards]; std::unique_ptr<const EntryComparator> _comp; - size_t get_stripe(const EntryComparator& comp, EntryRef key_ref) const; - void alloc_stripe(size_t stripe); - void hold_stripe(std::unique_ptr<const FixedSizeHashMap> map); + size_t get_shard_idx(const EntryComparator& comp, EntryRef key_ref) const; + void alloc_shard(size_t shard_idx); + void hold_shard(std::unique_ptr<const FixedSizeHashMap> map); public: - SimpleHashMap(std::unique_ptr<const EntryComparator> comp); - ~SimpleHashMap(); + ShardedHashMap(std::unique_ptr<const EntryComparator> comp); + ~ShardedHashMap(); KvType& add(const EntryComparator& comp, EntryRef key_ref, std::function<EntryRef(void)> &insert_entry); KvType* remove(const EntryComparator& comp, EntryRef key_ref); KvType* find(const EntryComparator& comp, EntryRef key_ref); diff --git a/vespalib/src/vespa/vespalib/datastore/simple_hash_map.cpp b/vespalib/src/vespa/vespalib/datastore/simple_hash_map.cpp deleted file mode 100644 index 2295cecd613..00000000000 --- a/vespalib/src/vespa/vespalib/datastore/simple_hash_map.cpp +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. - -#include "simple_hash_map.h" -#include "fixed_size_hash_map.h" -#include "entry_comparator.h" -#include <vespa/vespalib/util/memoryusage.h> - -namespace vespalib::datastore { - -class SimpleHashMapStripeHeld : public GenerationHeldBase -{ - std::unique_ptr<const FixedSizeHashMap> _data; -public: - SimpleHashMapStripeHeld(size_t size, std::unique_ptr<const FixedSizeHashMap> data); - ~SimpleHashMapStripeHeld(); -}; - -SimpleHashMapStripeHeld::SimpleHashMapStripeHeld(size_t size, std::unique_ptr<const FixedSizeHashMap> data) - : GenerationHeldBase(size), - _data(std::move(data)) -{ -} - -SimpleHashMapStripeHeld::~SimpleHashMapStripeHeld() = default; - -SimpleHashMap::SimpleHashMap(std::unique_ptr<const EntryComparator> comp) - : _gen_holder(), - _maps(), - _comp(std::move(comp)) -{ -} - -SimpleHashMap::~SimpleHashMap() -{ - _gen_holder.clearHoldLists(); - for (size_t i = 0; i < num_stripes; ++i) { - auto map = _maps[i].load(std::memory_order_relaxed); - delete map; - } -} - -size_t -SimpleHashMap::get_stripe(const EntryComparator& comp, EntryRef key_ref) const -{ - return comp.hash(key_ref) % num_stripes; -} - -void -SimpleHashMap::alloc_stripe(size_t stripe) -{ - auto map = _maps[stripe].load(std::memory_order_relaxed); - if (map == nullptr) { - auto umap = std::make_unique<FixedSizeHashMap>(2u, 3u, num_stripes); - _maps[stripe].store(umap.release(), std::memory_order_release); - } else { - auto umap = std::make_unique<FixedSizeHashMap>(map->size() * 2 + 2, map->size() * 3 + 3, num_stripes, *map, *_comp); - _maps[stripe].store(umap.release(), std::memory_order_release); - hold_stripe(std::unique_ptr<const FixedSizeHashMap>(map)); - } -} - -void -SimpleHashMap::hold_stripe(std::unique_ptr<const FixedSizeHashMap> map) -{ - auto usage = map->get_memory_usage(); - auto hold = std::make_unique<SimpleHashMapStripeHeld>(usage.allocatedBytes(), std::move(map)); - _gen_holder.hold(std::move(hold)); -} - -SimpleHashMap::KvType& -SimpleHashMap::add(const EntryComparator& comp, EntryRef key_ref, std::function<EntryRef(void)>& insert_entry) -{ - size_t stripe = get_stripe(comp, key_ref); - auto map = _maps[stripe].load(std::memory_order_relaxed); - if (map == nullptr || map->full()) { - alloc_stripe(stripe); - map = _maps[stripe].load(std::memory_order_relaxed); - } - return map->add(comp, key_ref, insert_entry); -} - -SimpleHashMap::KvType* -SimpleHashMap::remove(const EntryComparator& comp, EntryRef key_ref) -{ - size_t stripe = get_stripe(comp, key_ref); - auto map = _maps[stripe].load(std::memory_order_relaxed); - if (map == nullptr) { - return nullptr; - } - return map->remove(comp, key_ref); -} - -SimpleHashMap::KvType* -SimpleHashMap::find(const EntryComparator& comp, EntryRef key_ref) -{ - size_t stripe = get_stripe(comp, key_ref); - auto map = _maps[stripe].load(std::memory_order_relaxed); - if (map == nullptr) { - return nullptr; - } - return map->find(comp, key_ref); -} - -const SimpleHashMap::KvType* -SimpleHashMap::find(const EntryComparator& comp, EntryRef key_ref) const -{ - size_t stripe = get_stripe(comp, key_ref); - auto map = _maps[stripe].load(std::memory_order_relaxed); - if (map == nullptr) { - return nullptr; - } - return map->find(comp, key_ref); -} - -void -SimpleHashMap::transfer_hold_lists(generation_t generation) -{ - for (size_t i = 0; i < num_stripes; ++i) { - auto map = _maps[i].load(std::memory_order_relaxed); - if (map != nullptr) { - map->transfer_hold_lists(generation); - } - } - _gen_holder.transferHoldLists(generation); -} - -void -SimpleHashMap::trim_hold_lists(generation_t first_used) -{ - for (size_t i = 0; i < num_stripes; ++i) { - auto map = _maps[i].load(std::memory_order_relaxed); - if (map != nullptr) { - map->trim_hold_lists(first_used); - } - } - _gen_holder.trimHoldLists(first_used); -} - -size_t -SimpleHashMap::size() const noexcept -{ - size_t result = 0; - for (size_t i = 0; i < num_stripes; ++i) { - auto map = _maps[i].load(std::memory_order_relaxed); - if (map != nullptr) { - result += map->size(); - } - } - return result; -} - -MemoryUsage -SimpleHashMap::get_memory_usage() const -{ - MemoryUsage memory_usage(sizeof(SimpleHashMap), sizeof(SimpleHashMap), 0, 0); - for (size_t i = 0; i < num_stripes; ++i) { - auto map = _maps[i].load(std::memory_order_relaxed); - if (map != nullptr) { - memory_usage.merge(map->get_memory_usage()); - } - } - size_t gen_holder_held_bytes = _gen_holder.getHeldBytes(); - memory_usage.incAllocatedBytes(gen_holder_held_bytes); - memory_usage.incAllocatedBytesOnHold(gen_holder_held_bytes); - return memory_usage; -} - -} |