-
Notifications
You must be signed in to change notification settings - Fork 350
lru multiindex container prototype #1048
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
hzhdlrp
wants to merge
16
commits into
userver-framework:develop
Choose a base branch
from
hzhdlrp:lru_miltiindex_prototype
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
f62b90d
lru container prototype
hzhdlrp 7e4a7d1
lru with boost::intrusive::list
hzhdlrp f9dbf6d
some comments
hzhdlrp f404549
some comments
hzhdlrp b4e6710
self pointer as key, member_hook instead of base_hook
hzhdlrp 084b91e
naming issues
hzhdlrp aa2b5f9
itegrated multi-index lru to userver
hzhdlrp 5e03471
deleted unnecessary implementations
hzhdlrp d3fdf10
run cmake error fix
hzhdlrp 9817ece
GetPointerToSelf instead of self pointer member
hzhdlrp 7328c7e
refactoring structure to make it more in userver-style
hzhdlrp c84759a
added my branch to githubCI
hzhdlrp 76653b8
compatibility with older c++ versions
hzhdlrp fc4bf4a
changed namespace naming
hzhdlrp b541dcb
fix
hzhdlrp b60a70d
fix
hzhdlrp File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| include_guard(GLOBAL) | ||
|
|
||
| if(userver_multiindex_lru_FOUND) | ||
| return() | ||
| endif() | ||
|
|
||
| find_package(userver REQUIRED COMPONENTS core) | ||
|
|
||
| find_package(Boost REQUIRED) | ||
|
|
||
| set(userver_multiindex_lru_FOUND TRUE) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| project(userver-multi-index-lru CXX) | ||
|
|
||
| find_package(Boost REQUIRED) | ||
|
|
||
| userver_module( | ||
| multi-index-lru | ||
| SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" | ||
| UTEST_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/*_test.cpp" | ||
| UBENCH_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/*_benchmark.cpp" | ||
| DEPENDS core | ||
| ) | ||
|
|
||
| target_include_directories(userver-multi-index-lru | ||
| PUBLIC | ||
| ${Boost_INCLUDE_DIRS} | ||
| "${CMAKE_CURRENT_SOURCE_DIR}/tests/" | ||
| ) | ||
|
|
||
| _userver_directory_install( | ||
| COMPONENT multi-index-lru | ||
| FILES "${USERVER_ROOT_DIR}/cmake/modules/Findboost.cmake" | ||
| DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/userver/modules" | ||
| ) |
193 changes: 193 additions & 0 deletions
193
libraries/multi_index_lru/include/userver/multi_index_lru/lru_boost_list_container.h
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,193 @@ | ||
| #pragma once | ||
|
|
||
| #include <boost/intrusive/link_mode.hpp> | ||
| #include <boost/intrusive/list.hpp> | ||
| #include <boost/intrusive/list_hook.hpp> | ||
| #include <boost/multi_index/hashed_index.hpp> | ||
| #include <boost/multi_index/mem_fun.hpp> | ||
|
|
||
| USERVER_NAMESPACE_BEGIN | ||
|
|
||
| namespace multi_index_lru { | ||
| using namespace boost::multi_index; | ||
|
|
||
| namespace impl { | ||
| template<typename Value> | ||
| struct ValueWithHook | ||
| { | ||
| Value value; | ||
| mutable boost::intrusive::list_member_hook<> list_hook; | ||
| const ValueWithHook *GetPointerToSelf() const { return this; }; | ||
|
|
||
| explicit ValueWithHook(const Value& val) : value(val) {} | ||
|
|
||
| explicit ValueWithHook(Value&& val) : value(std::move(val)) {} | ||
|
|
||
| ValueWithHook() = delete; | ||
| ValueWithHook(const ValueWithHook&) = delete; | ||
| ValueWithHook(ValueWithHook &&) = delete; | ||
|
|
||
| ValueWithHook &operator=(const ValueWithHook&) = delete; | ||
| ValueWithHook &operator=(ValueWithHook&&) = delete; | ||
|
|
||
| operator Value&() { return value; } | ||
| operator const Value&() const { return value; } | ||
|
|
||
| Value* operator->() { return &value; } | ||
| const Value* operator->() const { return &value; } | ||
|
|
||
| Value& get() { return value; } | ||
| const Value& get() const { return value; } | ||
|
|
||
| using boost_list = boost::intrusive::list< | ||
| ValueWithHook, | ||
| boost::intrusive::member_hook< | ||
| ValueWithHook, | ||
| boost::intrusive::list_member_hook<>, | ||
| &ValueWithHook::list_hook | ||
| > | ||
| >; | ||
|
|
||
| void push_back_to_list(boost_list &lst) const { | ||
| lst.push_back(const_cast<ValueWithHook&>(*this)); | ||
| } | ||
|
|
||
| void splice_in_list(boost_list &lst) const { | ||
| lst.splice(lst.end(), lst, lst.iterator_to(const_cast<ValueWithHook&>(*this))); | ||
| } | ||
| }; | ||
|
|
||
| struct internal_ptr_tag {}; | ||
| } // namespace impl | ||
|
|
||
| template< | ||
| typename Value, | ||
| typename IndexSpecifierList, | ||
| typename Allocator = std::allocator<impl::ValueWithHook<Value>> | ||
| > | ||
| class LRUCacheContainer { | ||
| private: | ||
| using CacheItem = impl::ValueWithHook<Value>; | ||
| using List = boost::intrusive::list< | ||
| CacheItem, | ||
| boost::intrusive::member_hook< | ||
| CacheItem, | ||
| boost::intrusive::list_member_hook<>, | ||
| &CacheItem::list_hook | ||
| > | ||
| >; | ||
|
|
||
| using ExtendedIndexSpecifierList = typename boost::mpl::push_back< | ||
| IndexSpecifierList, | ||
| hashed_unique< | ||
| tag<impl::internal_ptr_tag>, | ||
| const_mem_fun<CacheItem, const CacheItem*, &CacheItem::GetPointerToSelf> | ||
| > | ||
| >::type; | ||
|
|
||
| using Container = multi_index_container< | ||
| CacheItem, | ||
| ExtendedIndexSpecifierList, | ||
| Allocator | ||
| >; | ||
|
|
||
| Container container; | ||
| size_t max_size; | ||
|
|
||
| List usage_list; | ||
|
|
||
| public: | ||
| using value_type = Value; | ||
| using cache_item_type = CacheItem; | ||
|
|
||
| LRUCacheContainer(size_t max_size) : max_size(max_size) {} | ||
|
|
||
| template<typename... Args> | ||
| bool emplace(Args&&... args) { | ||
| if (container.size() >= max_size) { | ||
| evict_lru(); | ||
| } | ||
|
|
||
| auto result = container.emplace(std::forward<Args>(args)...); | ||
|
|
||
| auto &value = *result.first; | ||
| if (result.second) { | ||
| value.push_back_to_list(usage_list); | ||
| } else { | ||
| value.splice_in_list(usage_list); | ||
| } | ||
| return result.second; | ||
| } | ||
|
|
||
| bool insert(const Value& value) { | ||
| return emplace(value); | ||
| } | ||
|
|
||
| bool insert(Value&& value) { | ||
| return emplace(std::move(value)); | ||
| } | ||
|
|
||
| template<typename Tag, typename Key> | ||
| typename Container::template index<Tag>::type::iterator find(const Key& key) { | ||
| auto& primary_index = container.template get<Tag>(); | ||
| auto it = primary_index.find(key); | ||
|
|
||
| if (it != primary_index.end()) { | ||
| it->splice_in_list(usage_list); | ||
| } | ||
|
|
||
| return it; | ||
| } | ||
|
|
||
| template<typename Tag, typename Key> | ||
| bool contains(const Key& key) { | ||
| return this->template find<Tag, Key>(key) != container.template get<Tag>().end(); | ||
| } | ||
|
|
||
| template<typename Tag, typename Key> | ||
| bool erase(const Key& key) { | ||
| auto& primary_index = container.template get<Tag>(); | ||
| auto it = primary_index.find(key); | ||
| if (it != primary_index.end()) { | ||
| usage_list.erase(usage_list.iterator_to(*it)); | ||
| } | ||
| return container.template get<Tag>().erase(key) > 0; | ||
| } | ||
|
|
||
| template<typename Tag> | ||
| auto& get() { | ||
| return container.template get<Tag>(); | ||
| } | ||
|
|
||
| template<typename Tag> | ||
| const auto& get() const { | ||
| return container.template get<Tag>(); | ||
| } | ||
|
|
||
| size_t size() const { return container.size(); } | ||
| bool empty() const { return container.empty(); } | ||
| size_t capacity() const { return max_size; } | ||
|
|
||
| void set_capacity(size_t new_capacity) { | ||
| max_size = new_capacity; | ||
| while (container.size() > max_size) { | ||
| evict_lru(); | ||
| } | ||
| } | ||
|
|
||
| void clear() { | ||
| container.clear(); | ||
| } | ||
|
|
||
| private: | ||
| void evict_lru() { | ||
| if (!usage_list.empty()) { | ||
| CacheItem *ptr_to_erase = &*usage_list.begin(); | ||
| usage_list.erase(usage_list.begin()); | ||
| container.template get<impl::internal_ptr_tag>().erase(ptr_to_erase); | ||
| } | ||
| } | ||
| }; | ||
| } // namespace multi_index_lru | ||
|
|
||
| USERVER_NAMESPACE_END |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| project-name: userver-lib-multi-index-lru | ||
|
|
||
| description: multi index lru cache | ||
|
|
||
| maintainers: | ||
| - Common components | ||
|
|
||
| libraries: | ||
| - userver-core |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| #include "benchmarks/lru_basic_benchmarks.h" | ||
| #include "benchmarks/lru_google_benchmarks.h" | ||
|
|
||
| // #define LRU_CONTAINER_DEBUG__ | ||
| #include <userver/multi_index_lru/lru_boost_list_container.h> | ||
|
|
||
| using namespace USERVER_NAMESPACE; | ||
|
|
||
| int main() { | ||
| benchmarks::simple_benchmark<multi_index_lru::LRUCacheContainer>("boost_list_output.txt"); | ||
| benchmarks::google_benchmark<multi_index_lru::LRUCacheContainer>(); | ||
|
|
||
| benchmarks::google_benchmark_init("google_output.txt"); | ||
| benchmarks::google_benchmark_run(); | ||
| return 0; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| #include "tests/lru_basic_tests.h" | ||
|
|
||
| // #define LRU_CONTAINER_DEBUG__ | ||
| #include <userver/multi_index_lru/lru_boost_list_container.h> | ||
|
|
||
| using namespace USERVER_NAMESPACE; | ||
|
|
||
| int main() { | ||
| test_lru_users<multi_index_lru::LRUCacheContainer>(); | ||
| test_lru_products<multi_index_lru::LRUCacheContainer>(); | ||
| std::cout << "all tests success" << std::endl; | ||
| return 0; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| #include <userver/multi_index_lru/lru_boost_list_container.h> | ||
|
|
||
| namespace userver::multi_index_lru { const char* multi_index_lru_version = "1.0"; } |
60 changes: 60 additions & 0 deletions
60
libraries/multi_index_lru/tests/benchmarks/benchmarks_resourses.h
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| #pragma once | ||
|
|
||
| #include <iostream> | ||
| #include <string> | ||
| #include <fstream> | ||
| #include <vector> | ||
| #include <random> | ||
| #include <chrono> | ||
| #include <iomanip> | ||
|
|
||
| #include "../lru_container_concept.h" | ||
|
|
||
| USERVER_NAMESPACE_BEGIN | ||
|
|
||
| namespace benchmarks { | ||
|
|
||
| const std::vector<long long> CACHE_SIZES = {1000, 10000, 100000}; | ||
| const size_t OPERATIONS_NUMBER = 100000; | ||
| const int MAX_ID_SIZE = 50000; | ||
|
|
||
| struct id_tag {}; | ||
| struct email_tag {}; | ||
| struct name_tag {}; | ||
|
|
||
| struct User { | ||
| int id; | ||
| std::string email; | ||
| std::string name; | ||
|
|
||
| bool operator==(const User& other) const { | ||
| return id == other.id && email == other.email && name == other.name; | ||
| } | ||
| }; | ||
|
|
||
| namespace generator { | ||
| std::random_device rd; | ||
| std::mt19937 gen(rd()); | ||
| std::uniform_real_distribution<double> action_dist(0.0, 1.0); | ||
| std::uniform_int_distribution<int> id_dist(0, MAX_ID_SIZE); | ||
|
|
||
| User generate_user() { | ||
| std::string email = "email" + std::to_string(id_dist(gen)); | ||
| std::string name = "name" + std::to_string(id_dist(gen)); | ||
| return User{id_dist(gen), email, name}; | ||
| } | ||
|
|
||
| int generate_id() { | ||
| return id_dist(gen); | ||
| } | ||
|
|
||
| std::string generate_name() { | ||
| return "name" + std::to_string(id_dist(gen)); | ||
| } | ||
|
|
||
| std::string generate_email() { | ||
| return "email" + std::to_string(id_dist(gen)); | ||
| } | ||
| } // generator | ||
| } // benchmarks | ||
| USERVER_NAMESPACE_END |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi! Can you please add some documentation on the feature? The description says it is "from" boost - is it a copy-paste of implementation? Or maybe some adapted version of it? Or a wrapper?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'll add some docs later. Prototype with proper support of CMake goes first.
I'm discussing the PR with the author in Telegram