Skip to content

Commit 0078639

Browse files
hzhdlrpapolukhin
authored andcommitted
feat multi-index-lru: implement without Boost.Intrusive and const_cast
Tests: протестировано CI --- Pull Request resolved: #1067 Co-authored-by: antoshkka <[email protected]> commit_hash:02835f364790081afdc274bc1f8269756c534646
1 parent fb5cfbb commit 0078639

File tree

3 files changed

+46
-118
lines changed

3 files changed

+46
-118
lines changed

libraries/multi-index-lru/include/userver/multi-index-lru/container.hpp

Lines changed: 39 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -3,94 +3,40 @@
33
/// @file userver/multi-index-lru/container.hpp
44
/// @brief @copybrief multi_index_lru::Container
55

6-
#include <boost/intrusive/link_mode.hpp>
7-
#include <boost/intrusive/list.hpp>
8-
#include <boost/intrusive/list_hook.hpp>
9-
#include <boost/multi_index/hashed_index.hpp>
10-
#include <boost/multi_index/mem_fun.hpp>
11-
#include <boost/multi_index/member.hpp>
6+
#include <boost/multi_index/identity.hpp>
127
#include <boost/multi_index/ordered_index.hpp>
13-
#include <boost/multi_index/tag.hpp>
8+
#include <boost/multi_index/sequenced_index.hpp>
149
#include <boost/multi_index_container.hpp>
1510

11+
#include <boost/mpl/joint_view.hpp>
12+
#include <boost/mpl/list.hpp>
13+
1614
#include <cstddef>
1715
#include <utility>
1816

1917
USERVER_NAMESPACE_BEGIN
2018

2119
namespace multi_index_lru {
2220

23-
namespace impl {
24-
25-
template <typename Value>
26-
struct ValueWithHook {
27-
Value value;
28-
mutable boost::intrusive::list_member_hook<> list_hook;
29-
30-
const ValueWithHook* GetPointerToSelf() const { return this; };
31-
32-
explicit ValueWithHook(const Value& val)
33-
: value(val)
34-
{}
35-
36-
explicit ValueWithHook(Value&& val)
37-
: value(std::move(val))
38-
{}
39-
40-
ValueWithHook() = delete;
41-
ValueWithHook(const ValueWithHook&) = delete;
42-
ValueWithHook(ValueWithHook&&) = delete;
43-
44-
ValueWithHook& operator=(const ValueWithHook&) = delete;
45-
ValueWithHook& operator=(ValueWithHook&&) = delete;
46-
47-
operator Value&() { return value; }
48-
operator const Value&() const { return value; }
49-
50-
Value* operator->() { return &value; }
51-
const Value* operator->() const { return &value; }
52-
53-
Value& get() { return value; }
54-
const Value& get() const { return value; }
55-
};
56-
57-
template <class List, class Node>
58-
void PushBackToList(List& lst, const Node& node) {
59-
lst.push_back(const_cast<Node&>(node)); // TODO:
60-
}
61-
62-
template <class List, class Node>
63-
void SpliceInList(List& lst, Node& node) {
64-
lst.splice(lst.end(), lst, lst.iterator_to(node));
65-
}
66-
67-
struct InternalPtrTag {};
68-
69-
} // namespace impl
70-
7121
/// @ingroup userver_containers
7222
///
7323
/// @brief MultiIndex LRU container
74-
template <typename Value, typename IndexSpecifierList, typename Allocator = std::allocator<impl::ValueWithHook<Value>>>
24+
template <typename Value, typename IndexSpecifierList, typename Allocator = std::allocator<Value> >
7525
class Container {
7626
public:
7727
explicit Container(size_t max_size)
78-
: max_size(max_size)
28+
: max_size_(max_size)
7929
{}
8030

8131
template <typename... Args>
8232
bool emplace(Args&&... args) {
83-
if (container.size() >= max_size) {
84-
EvictLru();
85-
}
86-
87-
auto result = container.emplace(std::forward<Args>(args)...);
33+
auto& seq_index = container_.template get<0>();
34+
auto result = seq_index.emplace_front(std::forward<Args>(args)...);
8835

89-
auto& value = *result.first;
90-
if (result.second) {
91-
impl::PushBackToList(usage_list, value);
92-
} else {
93-
impl::SpliceInList(usage_list, value);
36+
if (!result.second) {
37+
seq_index.relocate(seq_index.begin(), result.first);
38+
} else if (seq_index.size() > max_size_) {
39+
seq_index.pop_back();
9440
}
9541
return result.second;
9642
}
@@ -101,74 +47,56 @@ class Container {
10147

10248
template <typename Tag, typename Key>
10349
auto find(const Key& key) {
104-
auto& primary_index = container.template get<Tag>();
50+
auto& primary_index = container_.template get<Tag>();
10551
auto it = primary_index.find(key);
10652

10753
if (it != primary_index.end()) {
108-
impl::SpliceInList(usage_list, *it);
54+
auto& seq_index = container_.template get<0>();
55+
auto seq_it = container_.template project<0>(it);
56+
seq_index.relocate(seq_index.begin(), seq_it);
10957
}
11058

11159
return it;
11260
}
11361

114-
template <typename Tag>
115-
auto end() {
116-
return container.template get<Tag>().end();
117-
}
118-
11962
template <typename Tag, typename Key>
12063
bool contains(const Key& key) {
121-
return this->template find<Tag, Key>(key) != container.template get<Tag>().end();
64+
return this->template find<Tag, Key>(key) != container_.template get<Tag>().end();
12265
}
12366

12467
template <typename Tag, typename Key>
12568
bool erase(const Key& key) {
126-
auto& primary_index = container.template get<Tag>();
127-
auto it = primary_index.find(key);
128-
if (it != primary_index.end()) {
129-
usage_list.erase(usage_list.iterator_to(*it));
130-
}
131-
return container.template get<Tag>().erase(key) > 0;
69+
return container_.template get<Tag>().erase(key) > 0;
13270
}
13371

134-
std::size_t size() const { return container.size(); }
135-
bool empty() const { return container.empty(); }
136-
std::size_t capacity() const { return max_size; }
72+
size_t size() const { return container_.size(); }
73+
bool empty() const { return container_.empty(); }
74+
size_t capacity() const { return max_size_; }
13775

13876
void set_capacity(size_t new_capacity) {
139-
max_size = new_capacity;
140-
while (container.size() > max_size) {
141-
EvictLru();
77+
max_size_ = new_capacity;
78+
auto& seq_index = container_.template get<0>();
79+
while (container_.size() > max_size_) {
80+
seq_index.pop_back();
14281
}
14382
}
14483

145-
void clear() { container.clear(); }
84+
void clear() { container_.clear(); }
14685

147-
private:
148-
using CacheItem = impl::ValueWithHook<Value>;
149-
using List = boost::intrusive::list<
150-
CacheItem,
151-
boost::intrusive::member_hook<CacheItem, boost::intrusive::list_member_hook<>, &CacheItem::list_hook>>;
152-
153-
using ExtendedIndexSpecifierList = typename boost::mpl::push_back<
154-
IndexSpecifierList,
155-
boost::multi_index::hashed_unique<
156-
boost::multi_index::tag<impl::InternalPtrTag>,
157-
boost::multi_index::const_mem_fun<CacheItem, const CacheItem*, &CacheItem::GetPointerToSelf>>>::type;
158-
159-
using BoostContainer = boost::multi_index::multi_index_container<CacheItem, ExtendedIndexSpecifierList, Allocator>;
160-
161-
void EvictLru() {
162-
if (!usage_list.empty()) {
163-
CacheItem* ptr_to_erase = &*usage_list.begin();
164-
usage_list.erase(usage_list.begin());
165-
container.template get<impl::InternalPtrTag>().erase(ptr_to_erase);
166-
}
86+
template <typename Tag>
87+
auto end() {
88+
return container_.template get<Tag>().end();
16789
}
16890

169-
BoostContainer container;
170-
std::size_t max_size;
171-
List usage_list;
91+
private:
92+
using AdditionalIndices = boost::mpl::list<boost::multi_index::sequenced<> >;
93+
94+
using ExtendedIndexSpecifierList = boost::mpl::joint_view<AdditionalIndices, IndexSpecifierList>;
95+
96+
using BoostContainer = boost::multi_index::multi_index_container<Value, ExtendedIndexSpecifierList, Allocator>;
97+
98+
BoostContainer container_;
99+
size_t max_size_;
172100
};
173101
} // namespace multi_index_lru
174102

libraries/multi-index-lru/src/main_benchmark.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ void LruFindEmplaceMix(benchmark::State& state) {
8686
cache.find<IdTag, int>(ids[i]);
8787
}
8888

89-
for (size_t i = 0; i < writing_kOperationsNumber; ++i) {
89+
for (std::size_t i = 0; i < writing_kOperationsNumber; ++i) {
9090
cache.emplace(users[i]);
9191
}
9292
}
@@ -141,12 +141,12 @@ static void EmplaceOperations(::benchmark::State& state) {
141141
for (auto _ : state) {
142142
state.PauseTiming();
143143
std::vector<User> users;
144-
for (size_t i = 0; i < operations_count; ++i) {
144+
for (std::size_t i = 0; i < operations_count; ++i) {
145145
users.push_back(GenerateUser());
146146
}
147147
state.ResumeTiming();
148148

149-
for (size_t i = 0; i < operations_count; ++i) {
149+
for (std::size_t i = 0; i < operations_count; ++i) {
150150
cache.emplace(users[i]);
151151
}
152152
}

libraries/multi-index-lru/src/main_test.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,17 +54,17 @@ TEST_F(LRUUsersTest, BasicOperations) {
5454
// Test find by id
5555
auto by_id = cache.find<IdTag, int>(1);
5656
ASSERT_NE(by_id, cache.end<IdTag>());
57-
EXPECT_EQ(by_id->get().name, "Alice");
57+
EXPECT_EQ(by_id->name, "Alice");
5858

5959
// Test find by email
6060
auto by_email = cache.find<EmailTag, std::string>("[email protected]");
6161
ASSERT_NE(by_email, cache.end<EmailTag>());
62-
EXPECT_EQ(by_email->get().id, 2);
62+
EXPECT_EQ(by_email->id, 2);
6363

6464
// Test find by name
6565
auto by_name = cache.find<NameTag, std::string>("Charlie");
6666
ASSERT_NE(by_name, cache.end<NameTag>());
67-
EXPECT_EQ(by_name->get().email, "[email protected]");
67+
EXPECT_EQ(by_name->email, "[email protected]");
6868

6969
// Test template find method
7070
auto it = cache.find<EmailTag, std::string>("[email protected]");
@@ -125,7 +125,7 @@ TEST_F(ProductsTest, BasicProductOperations) {
125125

126126
auto laptop = cache.find<SkuTag, std::string>("A1");
127127
ASSERT_NE(laptop, cache.end<SkuTag>());
128-
EXPECT_EQ(laptop->get().name, "Laptop");
128+
EXPECT_EQ(laptop->name, "Laptop");
129129
}
130130

131131
TEST_F(ProductsTest, ProductEviction) {

0 commit comments

Comments
 (0)