-
Notifications
You must be signed in to change notification settings - Fork 3.4k
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
Use pool allocator in QueryHeap #6943
base: master
Are you sure you want to change the base?
Changes from 48 commits
4e5bf05
8c7b80e
3bd897f
0723dc0
bbdac63
611a3c2
f62e917
5f166a5
8df282b
c578698
d1f04ab
233a756
53032e5
49f875c
7eb2d93
13448e4
f9358ed
1037256
6f04aa9
6d2fc45
abbe5e2
7337771
058c26e
c5aae51
4d940ab
3691f90
18b3c5f
9ef1911
a90e9dd
434cab4
a18ad91
69bc6c0
fb8182a
270f187
e9cdb31
ac05d36
e045dea
21f53ed
1436e96
f18791a
81d128b
fdd1ca0
70d67d0
8bd26dc
3096440
6090387
9ce059f
bff349f
d47012a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -116,8 +116,8 @@ class CellCustomizer | |
const std::vector<bool> &allowed_nodes, | ||
CellMetric &metric) const | ||
{ | ||
Heap heap_exemplar(graph.GetNumberOfNodes()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It used to work in a way that TBB copied |
||
HeapPtr heaps(heap_exemplar); | ||
const auto number_of_nodes = graph.GetNumberOfNodes(); | ||
HeapPtr heaps([number_of_nodes] { return Heap{number_of_nodes}; }); | ||
|
||
for (std::size_t level = 1; level < partition.GetNumberOfLevels(); ++level) | ||
{ | ||
|
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 |
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.
copy-paste from #6975
Just to make benchmark results more stable, I'll remove it before merge.