dispenso
A library for task parallelism
 
Loading...
Searching...
No Matches
pool_allocator.cpp
1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
9
10namespace dispenso {
11
12template <bool kThreadSafe>
14 size_t chunkSize,
15 size_t allocSize,
16 std::function<void*(size_t)> allocFunc,
17 std::function<void(void*)> deallocFunc)
18 : chunkSize_(chunkSize),
19 allocSize_(allocSize),
20 chunksPerAlloc_(allocSize / chunkSize),
21 allocFunc_(std::move(allocFunc)),
22 deallocFunc_(std::move(deallocFunc)) {
23 // Start off with at least enough space to store at least one set of chunks.
24 chunks_.reserve(chunksPerAlloc_);
25}
26
27template <bool kThreadSafe>
29 while (true) {
30 uint32_t allocId = 0;
31 if (kThreadSafe) {
32 allocId = backingAllocLock_.fetch_or(1, std::memory_order_acquire);
33 }
34
35 if (allocId == 0) {
36 if (chunks_.empty()) {
37 char* buffer;
38 if (backingAllocs2_.empty()) {
39 buffer = reinterpret_cast<char*>(allocFunc_(allocSize_));
40 } else {
41 buffer = backingAllocs2_.back();
42 backingAllocs2_.pop_back();
43 }
44 backingAllocs_.push_back(buffer);
45 // Push n-1 values into the chunks_ buffer, and then return the nth.
46 for (size_t i = 0; i < chunksPerAlloc_ - 1; ++i) {
47 chunks_.push_back(buffer);
48 buffer += chunkSize_;
49 }
50 if (kThreadSafe) {
51 backingAllocLock_.store(0, std::memory_order_release);
52 }
53 return buffer;
54 }
55 char* back = chunks_.back();
56 chunks_.pop_back();
57 if (kThreadSafe) {
58 backingAllocLock_.store(0, std::memory_order_release);
59 }
60 return back;
61 } else {
62 std::this_thread::yield();
63 }
64 }
65}
66
67template <bool kThreadSafe>
69 // For now do not release any memory back to the deallocFunc until destruction.
70 // TODO(bbudge): Consider cases where we haven't gotten below some threshold of ready chunks
71 // in a while. In that case, we could begin tracking allocations, and try to assemble entire
72 // starting allocations, possibly deferring a small amount to each alloc call. This would be
73 // slower, but would ensure we don't get into a situation where we need a bunch of memory up
74 // front, and then never again.
75
76 while (true) {
77 uint32_t allocId = 0;
78 if (kThreadSafe) {
79 allocId = backingAllocLock_.fetch_or(1, std::memory_order_acquire);
80 }
81 if (allocId == 0) {
82 chunks_.push_back(ptr);
83 if (kThreadSafe) {
84 backingAllocLock_.store(0, std::memory_order_release);
85 }
86 break;
87 }
88 }
89}
90
91template <bool kThreadSafe>
93 chunks_.clear();
94 if (backingAllocs2_.size() < backingAllocs_.size()) {
95 std::swap(backingAllocs2_, backingAllocs_);
96 }
97 for (char* ba : backingAllocs_) {
98 backingAllocs2_.push_back(ba);
99 }
100 backingAllocs_.clear();
101}
102
103template <bool kThreadSafe>
105 for (char* backing : backingAllocs_) {
106 deallocFunc_(backing);
107 }
108 for (char* backing : backingAllocs2_) {
109 deallocFunc_(backing);
110 }
111}
112
113template class PoolAllocatorT<false>;
114template class PoolAllocatorT<true>;
115
116} // namespace dispenso
DISPENSO_DLL_ACCESS char * alloc()
DISPENSO_DLL_ACCESS void clear()
DISPENSO_DLL_ACCESS ~PoolAllocatorT()
DISPENSO_DLL_ACCESS PoolAllocatorT(size_t chunkSize, size_t allocSize, std::function< void *(size_t)> allocFunc, std::function< void(void *)> deallocFunc)
DISPENSO_DLL_ACCESS void dealloc(char *ptr)
detail::OpResult< T > OpResult
Definition pipeline.h:29