-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Use pool allocator in QueryHeap #6943
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
SiarheiFedartsou
wants to merge
49
commits into
master
Choose a base branch
from
sf-pool-alloc
base: master
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
49 commits
Select commit
Hold shift + click to select a range
4e5bf05
Try to use boost::fast_pool_allocator in QueryHeap
SiarheiFedartsou 8c7b80e
wip
SiarheiFedartsou 3bd897f
wip
SiarheiFedartsou 0723dc0
wip
SiarheiFedartsou bbdac63
wip
SiarheiFedartsou 611a3c2
wip
SiarheiFedartsou f62e917
wip
SiarheiFedartsou 5f166a5
wip
SiarheiFedartsou 8df282b
wip
SiarheiFedartsou c578698
wip
SiarheiFedartsou d1f04ab
wip
SiarheiFedartsou 233a756
wip
SiarheiFedartsou 53032e5
wip
SiarheiFedartsou 49f875c
wip
SiarheiFedartsou 7eb2d93
wip
SiarheiFedartsou 13448e4
wip
SiarheiFedartsou f9358ed
wip
SiarheiFedartsou 1037256
wip
SiarheiFedartsou 6f04aa9
wip
SiarheiFedartsou 6d2fc45
wip
SiarheiFedartsou abbe5e2
wip
SiarheiFedartsou 7337771
wip
SiarheiFedartsou 058c26e
wip
SiarheiFedartsou c5aae51
wip
SiarheiFedartsou 4d940ab
wip
SiarheiFedartsou 3691f90
wip
SiarheiFedartsou 18b3c5f
wip
SiarheiFedartsou 9ef1911
wip
SiarheiFedartsou a90e9dd
wip
SiarheiFedartsou 434cab4
wip
SiarheiFedartsou a18ad91
wip
SiarheiFedartsou 69bc6c0
wip
SiarheiFedartsou fb8182a
wip
SiarheiFedartsou 270f187
wip
SiarheiFedartsou e9cdb31
wip
SiarheiFedartsou ac05d36
Update bench.cpp
SiarheiFedartsou e045dea
Use pool in std::unordered_map
SiarheiFedartsou 21f53ed
Use pool in std::unordered_map
SiarheiFedartsou 1436e96
wip
SiarheiFedartsou f18791a
wip
SiarheiFedartsou 81d128b
wip
SiarheiFedartsou fdd1ca0
wip
SiarheiFedartsou 70d67d0
wip
SiarheiFedartsou 8bd26dc
wip
SiarheiFedartsou 3096440
wip
SiarheiFedartsou 6090387
wip
SiarheiFedartsou 9ce059f
wip
SiarheiFedartsou bff349f
wip
SiarheiFedartsou d47012a
Merge branch 'master' into sf-pool-alloc
SiarheiFedartsou 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 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 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,158 @@ | ||
#pragma once | ||
|
||
#include <algorithm> | ||
#include <array> | ||
#include <bit> | ||
#include <boost/assert.hpp> | ||
#include <cstddef> | ||
#include <cstdlib> | ||
#include <memory> | ||
#include <mutex> | ||
#include <new> | ||
#include <vector> | ||
|
||
namespace osrm::util | ||
{ | ||
|
||
inline size_t align_up(size_t n, size_t alignment) | ||
{ | ||
return (n + alignment - 1) & ~(alignment - 1); | ||
} | ||
|
||
inline size_t get_next_power_of_two_exponent(size_t n) | ||
{ | ||
BOOST_ASSERT(n > 0); | ||
return (sizeof(size_t) * 8) - std::countl_zero(n - 1); | ||
} | ||
|
||
class MemoryPool | ||
{ | ||
private: | ||
constexpr static size_t MIN_CHUNK_SIZE_BYTES = 4096; | ||
|
||
public: | ||
static std::shared_ptr<MemoryPool> instance() | ||
{ | ||
static thread_local std::shared_ptr<MemoryPool> instance; | ||
if (!instance) | ||
{ | ||
instance = std::shared_ptr<MemoryPool>(new MemoryPool()); | ||
} | ||
return instance; | ||
} | ||
|
||
template <typename T> T *allocate(std::size_t items_count) | ||
{ | ||
static_assert(alignof(T) <= alignof(std::max_align_t), | ||
"Type is over-aligned for this allocator."); | ||
|
||
size_t free_list_index = get_next_power_of_two_exponent(items_count * sizeof(T)); | ||
auto &free_list = free_lists_[free_list_index]; | ||
if (free_list.empty()) | ||
{ | ||
size_t block_size_in_bytes = 1u << free_list_index; | ||
block_size_in_bytes = align_up(block_size_in_bytes, alignof(std::max_align_t)); | ||
// check if there is space in current memory chunk | ||
if (current_chunk_left_bytes_ < block_size_in_bytes) | ||
{ | ||
allocate_chunk(block_size_in_bytes); | ||
} | ||
|
||
free_list.push_back(current_chunk_ptr_); | ||
current_chunk_left_bytes_ -= block_size_in_bytes; | ||
current_chunk_ptr_ += block_size_in_bytes; | ||
} | ||
auto ptr = reinterpret_cast<T *>(free_list.back()); | ||
free_list.pop_back(); | ||
return ptr; | ||
} | ||
|
||
template <typename T> void deallocate(T *p, std::size_t n) noexcept | ||
{ | ||
size_t free_list_index = get_next_power_of_two_exponent(n * sizeof(T)); | ||
// NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion) | ||
free_lists_[free_list_index].push_back(reinterpret_cast<void *>(p)); | ||
} | ||
|
||
~MemoryPool() | ||
{ | ||
for (auto chunk : chunks_) | ||
{ | ||
// NOLINTNEXTLINE(cppcoreguidelines-no-malloc) | ||
std::free(chunk); | ||
} | ||
} | ||
|
||
private: | ||
MemoryPool() = default; | ||
MemoryPool(const MemoryPool &) = delete; | ||
MemoryPool &operator=(const MemoryPool &) = delete; | ||
|
||
void allocate_chunk(size_t bytes) | ||
{ | ||
auto chunk_size = std::max(bytes, MIN_CHUNK_SIZE_BYTES); | ||
// NOLINTNEXTLINE(cppcoreguidelines-no-malloc) | ||
void *chunk = std::malloc(chunk_size); | ||
if (!chunk) | ||
{ | ||
throw std::bad_alloc(); | ||
} | ||
chunks_.push_back(chunk); | ||
current_chunk_ptr_ = static_cast<uint8_t *>(chunk); | ||
current_chunk_left_bytes_ = chunk_size; | ||
} | ||
|
||
// we have 64 free lists, one for each possible power of two | ||
std::array<std::vector<void *>, sizeof(std::size_t) * 8> free_lists_; | ||
|
||
// list of allocated memory chunks, we don't free them until the pool is destroyed | ||
std::vector<void *> chunks_; | ||
|
||
uint8_t *current_chunk_ptr_ = nullptr; | ||
size_t current_chunk_left_bytes_ = 0; | ||
}; | ||
|
||
template <typename T> class PoolAllocator | ||
{ | ||
public: | ||
using value_type = T; | ||
|
||
PoolAllocator() noexcept : pool(MemoryPool::instance()){}; | ||
|
||
template <typename U> | ||
PoolAllocator(const PoolAllocator<U> &) noexcept : pool(MemoryPool::instance()) | ||
{ | ||
} | ||
|
||
template <typename U> struct rebind | ||
{ | ||
using other = PoolAllocator<U>; | ||
}; | ||
|
||
T *allocate(std::size_t n) { return pool->allocate<T>(n); } | ||
|
||
void deallocate(T *p, std::size_t n) noexcept { pool->deallocate<T>(p, n); } | ||
|
||
PoolAllocator(const PoolAllocator &) = default; | ||
PoolAllocator &operator=(const PoolAllocator &) = default; | ||
PoolAllocator(PoolAllocator &&) noexcept = default; | ||
PoolAllocator &operator=(PoolAllocator &&) noexcept = default; | ||
|
||
private: | ||
// using shared_ptr guarantees that memory pool won't be destroyed before all allocators using | ||
// it (important if there are static instances of PoolAllocator) | ||
std::shared_ptr<MemoryPool> pool; | ||
}; | ||
template <typename T, typename U> | ||
bool operator==(const PoolAllocator<T> &, const PoolAllocator<U> &) | ||
{ | ||
return true; | ||
} | ||
|
||
template <typename T, typename U> | ||
bool operator!=(const PoolAllocator<T> &, const PoolAllocator<U> &) | ||
{ | ||
return false; | ||
} | ||
|
||
} // namespace osrm::util |
This file contains 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 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,158 @@ | ||
#include "util/pool_allocator.hpp" | ||
#include "util/typedefs.hpp" | ||
#include <boost/test/unit_test.hpp> | ||
|
||
#include <unordered_map> | ||
|
||
BOOST_AUTO_TEST_SUITE(pool_allocator) | ||
|
||
using namespace osrm; | ||
using namespace osrm::util; | ||
|
||
BOOST_AUTO_TEST_CASE(test_align_up) | ||
{ | ||
BOOST_CHECK_EQUAL(align_up(5, 4), 8); | ||
BOOST_CHECK_EQUAL(align_up(9, 8), 16); | ||
BOOST_CHECK_EQUAL(align_up(17, 16), 32); | ||
BOOST_CHECK_EQUAL(align_up(4, 4), 4); | ||
BOOST_CHECK_EQUAL(align_up(8, 8), 8); | ||
BOOST_CHECK_EQUAL(align_up(16, 16), 16); | ||
BOOST_CHECK_EQUAL(align_up(32, 16), 32); | ||
BOOST_CHECK_EQUAL(align_up(0, 4), 0); | ||
BOOST_CHECK_EQUAL(align_up(0, 8), 0); | ||
BOOST_CHECK_EQUAL(align_up(0, 16), 0); | ||
BOOST_CHECK_EQUAL(align_up(1000000, 256), 1000192); | ||
BOOST_CHECK_EQUAL(align_up(999999, 512), 1000448); | ||
BOOST_CHECK_EQUAL(align_up(123456789, 1024), 123457536); | ||
BOOST_CHECK_EQUAL(align_up(0, 1), 0); | ||
BOOST_CHECK_EQUAL(align_up(5, 1), 5); | ||
BOOST_CHECK_EQUAL(align_up(123456, 1), 123456); | ||
} | ||
|
||
BOOST_AUTO_TEST_CASE(test_get_next_power_of_two_exponent) | ||
{ | ||
BOOST_CHECK_EQUAL(get_next_power_of_two_exponent(1), 0); | ||
BOOST_CHECK_EQUAL(get_next_power_of_two_exponent(2), 1); | ||
BOOST_CHECK_EQUAL(get_next_power_of_two_exponent(4), 2); | ||
BOOST_CHECK_EQUAL(get_next_power_of_two_exponent(8), 3); | ||
BOOST_CHECK_EQUAL(get_next_power_of_two_exponent(16), 4); | ||
BOOST_CHECK_EQUAL(get_next_power_of_two_exponent(3), 2); | ||
BOOST_CHECK_EQUAL(get_next_power_of_two_exponent(5), 3); | ||
BOOST_CHECK_EQUAL(get_next_power_of_two_exponent(9), 4); | ||
BOOST_CHECK_EQUAL(get_next_power_of_two_exponent(15), 4); | ||
BOOST_CHECK_EQUAL(get_next_power_of_two_exponent(17), 5); | ||
BOOST_CHECK_EQUAL(get_next_power_of_two_exponent(1), 0); | ||
BOOST_CHECK_EQUAL(get_next_power_of_two_exponent(SIZE_MAX), sizeof(size_t) * 8); | ||
} | ||
|
||
// in many of these tests we hope on address sanitizer to alert in the case if we are doing | ||
// something wrong | ||
BOOST_AUTO_TEST_CASE(smoke) | ||
{ | ||
PoolAllocator<int> pool; | ||
auto ptr = pool.allocate(1); | ||
*ptr = 42; | ||
BOOST_CHECK_NE(ptr, nullptr); | ||
pool.deallocate(ptr, 1); | ||
|
||
ptr = pool.allocate(2); | ||
*ptr = 42; | ||
*(ptr + 1) = 43; | ||
BOOST_CHECK_NE(ptr, nullptr); | ||
pool.deallocate(ptr, 2); | ||
} | ||
|
||
BOOST_AUTO_TEST_CASE(a_lot_of_items) | ||
{ | ||
PoolAllocator<int> pool; | ||
auto ptr = pool.allocate(2048); | ||
for (int i = 0; i < 2048; ++i) | ||
{ | ||
ptr[i] = i; | ||
} | ||
|
||
for (int i = 0; i < 2048; ++i) | ||
{ | ||
BOOST_CHECK_EQUAL(ptr[i], i); | ||
} | ||
|
||
pool.deallocate(ptr, 2048); | ||
} | ||
|
||
BOOST_AUTO_TEST_CASE(copy) | ||
{ | ||
PoolAllocator<int> pool; | ||
auto ptr = pool.allocate(1); | ||
*ptr = 42; | ||
BOOST_CHECK_NE(ptr, nullptr); | ||
pool.deallocate(ptr, 1); | ||
|
||
PoolAllocator<int> pool2(pool); | ||
ptr = pool2.allocate(1); | ||
*ptr = 42; | ||
BOOST_CHECK_NE(ptr, nullptr); | ||
pool2.deallocate(ptr, 1); | ||
} | ||
|
||
BOOST_AUTO_TEST_CASE(move) | ||
{ | ||
PoolAllocator<int> pool; | ||
auto ptr = pool.allocate(1); | ||
*ptr = 42; | ||
BOOST_CHECK_NE(ptr, nullptr); | ||
pool.deallocate(ptr, 1); | ||
|
||
PoolAllocator<int> pool2(std::move(pool)); | ||
ptr = pool2.allocate(1); | ||
*ptr = 42; | ||
BOOST_CHECK_NE(ptr, nullptr); | ||
pool2.deallocate(ptr, 1); | ||
} | ||
|
||
BOOST_AUTO_TEST_CASE(unordered_map) | ||
{ | ||
std::unordered_map<int, | ||
int, | ||
std::hash<int>, | ||
std::equal_to<int>, | ||
PoolAllocator<std::pair<const int, int>>> | ||
map; | ||
map[1] = 42; | ||
BOOST_CHECK_EQUAL(map[1], 42); | ||
|
||
map.clear(); | ||
|
||
map[2] = 43; | ||
|
||
BOOST_CHECK_EQUAL(map[2], 43); | ||
} | ||
|
||
BOOST_AUTO_TEST_CASE(alignment) | ||
{ | ||
PoolAllocator<char> pool_char; | ||
PoolAllocator<double> pool_double; | ||
|
||
auto ptr_char = pool_char.allocate(1); | ||
auto ptr_double = pool_double.allocate(1); | ||
|
||
BOOST_CHECK_NE(ptr_double, nullptr); | ||
BOOST_CHECK_EQUAL(reinterpret_cast<uintptr_t>(ptr_double) % alignof(double), 0); | ||
BOOST_CHECK_NE(ptr_char, nullptr); | ||
BOOST_CHECK_EQUAL(reinterpret_cast<uintptr_t>(ptr_char) % alignof(char), 0); | ||
|
||
pool_char.deallocate(ptr_char, 1); | ||
pool_double.deallocate(ptr_double, 1); | ||
|
||
ptr_char = pool_char.allocate(2); | ||
ptr_double = pool_double.allocate(1); | ||
|
||
BOOST_CHECK_NE(ptr_double, nullptr); | ||
BOOST_CHECK_EQUAL(reinterpret_cast<uintptr_t>(ptr_double) % alignof(double), 0); | ||
BOOST_CHECK_NE(ptr_char, nullptr); | ||
BOOST_CHECK_EQUAL(reinterpret_cast<uintptr_t>(ptr_char) % alignof(char), 0); | ||
|
||
pool_char.deallocate(ptr_char, 2); | ||
pool_double.deallocate(ptr_double, 1); | ||
} | ||
|
||
BOOST_AUTO_TEST_SUITE_END() |
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.
It used to work in a way that TBB copied
heap_exemplar
to each thread. QueryHeap started using thread local allocator, so here we had a situation when QueryHeap has PoolAllocator somewhere in its internals and this PoolAllocator has a pointer to its thread local memory pool, then we copy this QueryHeap to another thread and now new exemplar of QueryHeap has another PoolAllocator with a pointer to exactly the same memory pool, but now it is used on another thread!With such change we guarantee that each thread creates heap for itself on its own.