diff options
author | Tor Egge <Tor.Egge@online.no> | 2021-04-13 14:25:03 +0200 |
---|---|---|
committer | Tor Egge <Tor.Egge@online.no> | 2021-04-13 14:25:03 +0200 |
commit | 16892237c5b241bfada8870435d92aa67ce4d897 (patch) | |
tree | d0633fd725ad39d916600545d5c69ec8137fc021 /vespalib | |
parent | f9cc338cc28a79045da882db7ddcc8e02cb301df (diff) |
Compact enum store dictionary when needed.
Diffstat (limited to 'vespalib')
6 files changed, 129 insertions, 11 deletions
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 ce1ebe395ce..7b46196780f 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 @@ -34,25 +34,37 @@ public: return resolve(lhs).ref() == resolve(rhs).ref(); } size_t hash(const EntryRef rhs) const override { - return rhs.ref(); + return rhs.valid() ? rhs.ref() : _to_find.ref(); } }; template <typename UniqueStoreDictionaryType> -struct DictionaryReadTest : public ::testing::Test { +struct UniqueStoreDictionaryTest : public ::testing::Test { UniqueStoreDictionaryType dict; std::unique_ptr<IUniqueStoreDictionaryReadSnapshot> snapshot; + vespalib::GenerationHandler gen_handler; - DictionaryReadTest() + UniqueStoreDictionaryTest() : dict(std::make_unique<Comparator>(0)), - snapshot() + snapshot(), + gen_handler() { } - DictionaryReadTest& add(uint32_t value) { + UniqueStoreDictionaryTest& add(uint32_t value) { auto result = dict.add(Comparator(value), [=]() noexcept { return EntryRef(value); }); assert(result.inserted()); return *this; } + UniqueStoreDictionaryTest& remove(uint32_t value) { + dict.remove(Comparator(value), EntryRef(value)); + return *this; + } + void inc_generation() { + dict.freeze(); + dict.transfer_hold_lists(gen_handler.getCurrentGeneration()); + gen_handler.incGeneration(); + dict.trim_hold_lists(gen_handler.getFirstUsedGeneration()); + } void take_snapshot() { dict.freeze(); snapshot = dict.get_read_snapshot(); @@ -61,8 +73,8 @@ struct DictionaryReadTest : public ::testing::Test { } }; -using DictionaryReadTestTypes = ::testing::Types<DefaultUniqueStoreDictionary, UniqueStoreDictionary<DefaultDictionary, IUniqueStoreDictionary, ShardedHashMap>, UniqueStoreDictionary<NoBTreeDictionary, IUniqueStoreDictionary, ShardedHashMap>>; -VESPA_GTEST_TYPED_TEST_SUITE(DictionaryReadTest, DictionaryReadTestTypes); +using UniqueStoreDictionaryTestTypes = ::testing::Types<DefaultUniqueStoreDictionary, UniqueStoreDictionary<DefaultDictionary, IUniqueStoreDictionary, ShardedHashMap>, UniqueStoreDictionary<NoBTreeDictionary, IUniqueStoreDictionary, ShardedHashMap>>; +VESPA_GTEST_TYPED_TEST_SUITE(UniqueStoreDictionaryTest, UniqueStoreDictionaryTestTypes); // Disable warnings emitted by gtest generated files when using typed tests #pragma GCC diagnostic push @@ -70,7 +82,7 @@ VESPA_GTEST_TYPED_TEST_SUITE(DictionaryReadTest, DictionaryReadTestTypes); #pragma GCC diagnostic ignored "-Wsuggest-override" #endif -TYPED_TEST(DictionaryReadTest, can_count_occurrences_of_a_key) +TYPED_TEST(UniqueStoreDictionaryTest, can_count_occurrences_of_a_key) { this->add(3).add(5).take_snapshot(); EXPECT_EQ(0, this->snapshot->count(Comparator(2))); @@ -79,7 +91,7 @@ TYPED_TEST(DictionaryReadTest, can_count_occurrences_of_a_key) EXPECT_EQ(1, this->snapshot->count(Comparator(5))); } -TYPED_TEST(DictionaryReadTest, can_count_occurrences_of_keys_in_a_range) +TYPED_TEST(UniqueStoreDictionaryTest, can_count_occurrences_of_keys_in_a_range) { if (!this->dict.get_has_btree_dictionary()) { return; @@ -95,7 +107,7 @@ TYPED_TEST(DictionaryReadTest, can_count_occurrences_of_keys_in_a_range) EXPECT_EQ(0, this->snapshot->count_in_range(Comparator(5), Comparator(3))); } -TYPED_TEST(DictionaryReadTest, can_iterate_all_keys) +TYPED_TEST(UniqueStoreDictionaryTest, can_iterate_all_keys) { using EntryRefVector = std::vector<EntryRef>; this->add(3).add(5).add(7).take_snapshot(); @@ -104,7 +116,7 @@ TYPED_TEST(DictionaryReadTest, can_iterate_all_keys) EXPECT_EQ(EntryRefVector({EntryRef(3), EntryRef(5), EntryRef(7)}), refs); } -TYPED_TEST(DictionaryReadTest, memory_usage_is_reported) +TYPED_TEST(UniqueStoreDictionaryTest, memory_usage_is_reported) { auto initial_usage = this->dict.get_memory_usage(); this->add(10); @@ -114,6 +126,43 @@ TYPED_TEST(DictionaryReadTest, memory_usage_is_reported) EXPECT_EQ(0, usage.allocatedBytesOnHold()); } +TYPED_TEST(UniqueStoreDictionaryTest, compaction_works) +{ + for (uint32_t i = 1; i < 100; ++i) { + this->add(i); + } + for (uint32_t i = 10; i < 100; ++i) { + this->remove(i); + } + this->inc_generation(); + auto btree_memory_usage_before = this->dict.get_btree_memory_usage(); + auto hash_memory_usage_before = this->dict.get_hash_memory_usage(); + for (uint32_t i = 0; i < 15; ++i) { + this->dict.compact_worst(true, true); + } + this->inc_generation(); + auto btree_memory_usage_after = this->dict.get_btree_memory_usage(); + auto hash_memory_usage_after = this->dict.get_hash_memory_usage(); + if (this->dict.get_has_btree_dictionary()) { + EXPECT_LT(btree_memory_usage_after.deadBytes(), btree_memory_usage_before.deadBytes()); + } else { + EXPECT_EQ(btree_memory_usage_after.deadBytes(), btree_memory_usage_before.deadBytes()); + } + if (this->dict.get_has_hash_dictionary()) { + EXPECT_LT(hash_memory_usage_after.deadBytes(), hash_memory_usage_before.deadBytes()); + } else { + EXPECT_EQ(hash_memory_usage_after.deadBytes(), hash_memory_usage_before.deadBytes()); + } + std::vector<EntryRef> exp_refs; + for (uint32_t i = 1; i < 10; ++i) { + exp_refs.emplace_back(EntryRef(i)); + } + this->take_snapshot(); + std::vector<EntryRef> refs; + this->snapshot->foreach_key([&](EntryRef ref){ refs.emplace_back(ref); }); + EXPECT_EQ(exp_refs, refs); +} + #pragma GCC diagnostic pop GTEST_MAIN_RUN_ALL_TESTS() diff --git a/vespalib/src/vespa/vespalib/datastore/i_unique_store_dictionary.h b/vespalib/src/vespa/vespalib/datastore/i_unique_store_dictionary.h index 4f53a872822..13c389c9a31 100644 --- a/vespalib/src/vespa/vespalib/datastore/i_unique_store_dictionary.h +++ b/vespalib/src/vespa/vespalib/datastore/i_unique_store_dictionary.h @@ -39,6 +39,8 @@ public: virtual bool get_has_hash_dictionary() const = 0; virtual vespalib::MemoryUsage get_btree_memory_usage() const = 0; virtual vespalib::MemoryUsage get_hash_memory_usage() const = 0; + virtual bool has_held_buffers() const = 0; + virtual void compact_worst(bool compact_btree_dictionary, bool compact_hash_dictionary) = 0; }; } diff --git a/vespalib/src/vespa/vespalib/datastore/sharded_hash_map.cpp b/vespalib/src/vespa/vespalib/datastore/sharded_hash_map.cpp index 36d68873176..a04163f120c 100644 --- a/vespalib/src/vespa/vespalib/datastore/sharded_hash_map.cpp +++ b/vespalib/src/vespa/vespalib/datastore/sharded_hash_map.cpp @@ -194,4 +194,30 @@ ShardedHashMap::normalize_values(std::function<EntryRef(EntryRef)> normalize) return changed; } +bool +ShardedHashMap::has_held_buffers() const +{ + return _gen_holder.getHeldBytes() != 0; +} + +void +ShardedHashMap::compact_worst() +{ + size_t worst_index = 0u; + size_t worst_dead_bytes = 0u; + for (size_t i = 0; i < num_shards; ++i) { + auto map = _maps[i].load(std::memory_order_relaxed); + if (map != nullptr) { + auto memory_usage = map->get_memory_usage(); + if (memory_usage.deadBytes() > worst_dead_bytes) { + worst_index = i; + worst_dead_bytes = memory_usage.deadBytes(); + } + } + } + if (worst_dead_bytes > 0u) { + alloc_shard(worst_index); + } +} + } diff --git a/vespalib/src/vespa/vespalib/datastore/sharded_hash_map.h b/vespalib/src/vespa/vespalib/datastore/sharded_hash_map.h index aa787421634..54d6481f34c 100644 --- a/vespalib/src/vespa/vespalib/datastore/sharded_hash_map.h +++ b/vespalib/src/vespa/vespalib/datastore/sharded_hash_map.h @@ -58,6 +58,8 @@ public: void foreach_key(std::function<void(EntryRef)> callback) const; void move_keys(std::function<EntryRef(EntryRef)> callback); bool normalize_values(std::function<EntryRef(EntryRef)> normalize); + bool has_held_buffers() const; + void compact_worst(); }; } diff --git a/vespalib/src/vespa/vespalib/datastore/unique_store_dictionary.h b/vespalib/src/vespa/vespalib/datastore/unique_store_dictionary.h index faaa2294ff3..1775fda7ae3 100644 --- a/vespalib/src/vespa/vespalib/datastore/unique_store_dictionary.h +++ b/vespalib/src/vespa/vespalib/datastore/unique_store_dictionary.h @@ -90,6 +90,8 @@ public: bool get_has_hash_dictionary() const override; vespalib::MemoryUsage get_btree_memory_usage() const override; vespalib::MemoryUsage get_hash_memory_usage() const override; + bool has_held_buffers() const override; + void compact_worst(bool compact_btree_dictionary, bool compact_hash_dictionary) override; }; } diff --git a/vespalib/src/vespa/vespalib/datastore/unique_store_dictionary.hpp b/vespalib/src/vespa/vespalib/datastore/unique_store_dictionary.hpp index 4a9cf3d96b2..99037b7aaf8 100644 --- a/vespalib/src/vespa/vespalib/datastore/unique_store_dictionary.hpp +++ b/vespalib/src/vespa/vespalib/datastore/unique_store_dictionary.hpp @@ -318,4 +318,41 @@ UniqueStoreDictionary<BTreeDictionaryT, ParentT, HashDictionaryT>::get_hash_memo return {}; } +template <typename BTreeDictionaryT, typename ParentT, typename HashDictionaryT> +bool +UniqueStoreDictionary<BTreeDictionaryT, ParentT, HashDictionaryT>::has_held_buffers() const +{ + if constexpr (has_btree_dictionary) { + if (this->_btree_dict.getAllocator().getNodeStore().has_held_buffers()) { + return true; + } + } + if constexpr (has_hash_dictionary) { + if (this->_hash_dict.has_held_buffers()) { + return true; + } + } + return false; +} + +template <typename BTreeDictionaryT, typename ParentT, typename HashDictionaryT> +void +UniqueStoreDictionary<BTreeDictionaryT, ParentT, HashDictionaryT>::compact_worst(bool compact_btree_dictionary, bool compact_hash_dictionary) +{ + if constexpr (has_btree_dictionary) { + if (compact_btree_dictionary) { + this->_btree_dict.compact_worst(); + } + } else { + (void) compact_btree_dictionary; + } + if constexpr (has_hash_dictionary) { + if (compact_hash_dictionary) { + this->_hash_dict.compact_worst(); + } + } else { + (void) compact_hash_dictionary; + } +} + } |