Skip to content

Commit c3ddc35

Browse files
authored
Merge pull request #49 from msimberg/heap-config
Make heap segment limits and sizes configurable
2 parents 89e113a + 83b1711 commit c3ddc35

File tree

10 files changed

+698
-95
lines changed

10 files changed

+698
-95
lines changed

include/hwmalloc/heap.hpp

Lines changed: 50 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
* ghex-org
33
*
4-
* Copyright (c) 2014-2023, ETH Zurich
4+
* Copyright (c) 2014-2025, ETH Zurich
55
* All rights reserved.
66
*
77
* Please, refer to the LICENSE file in the root directory.
@@ -14,6 +14,7 @@
1414
#include <hwmalloc/fancy_ptr/void_ptr.hpp>
1515
#include <hwmalloc/fancy_ptr/const_void_ptr.hpp>
1616
#include <hwmalloc/fancy_ptr/unique_ptr.hpp>
17+
#include <hwmalloc/heap_config.hpp>
1718
#include <hwmalloc/allocator.hpp>
1819
#include <vector>
1920
#include <unordered_map>
@@ -56,6 +57,9 @@ class heap
5657
template<typename T>
5758
using unique_ptr = unique_ptr<T, block_type>;
5859

60+
// Note: sizes below are defaults and can be changed through heap_config and
61+
// environment variables.
62+
//
5963
// There are 5 size classes that the heap uses. For each size class it relies on a
6064
// fixed_size_heap. The size classes are:
6165
// - tiny: heaps with linearly increasing block sizes, each heap backed by 16KiB segments
@@ -98,78 +102,56 @@ class heap
98102
// : : m_huge_heaps: map
99103

100104
private:
101-
static constexpr std::size_t log2_c(std::size_t n) noexcept
105+
static std::size_t tiny_bucket_index(std::size_t n, std::size_t tiny_increment,
106+
std::size_t tiny_increment_shift) noexcept
102107
{
103-
return ((n < 2) ? 1 : 1 + log2_c(n >> 1));
108+
return ((n + tiny_increment - 1) >> tiny_increment_shift) - 1;
104109
}
105110

106-
static const std::size_t s_tiny_limit = (1u << 7); // 128
107-
static const std::size_t s_small_limit = (1u << 10); // 1024
108-
static const std::size_t s_large_limit = (1u << 16); // 65536
109-
110-
static const std::size_t s_bucket_shift = log2_c(s_tiny_limit) - 1;
111-
112-
static const std::size_t s_tiny_segment = 0x04000; // 16KiB
113-
static const std::size_t s_small_segment = 0x08000; // 32KiB
114-
static const std::size_t s_large_segment = 0x10000; // 64KiB
115-
116-
static const std::size_t s_tiny_increment_shift = 3;
117-
static const std::size_t s_tiny_increment = (1u << s_tiny_increment_shift); // = 8
118-
119-
static const std::size_t s_num_tiny_heaps = s_tiny_limit / s_tiny_increment;
120-
static const std::size_t s_num_small_heaps = log2_c(s_small_limit) - log2_c(s_tiny_limit);
121-
static const std::size_t s_num_large_heaps = log2_c(s_large_limit) - log2_c(s_small_limit);
122-
123-
static std::size_t tiny_bucket_index(std::size_t n) noexcept
111+
static std::size_t bucket_index(std::size_t n, std::size_t bucket_shift) noexcept
124112
{
125-
return ((n + s_tiny_increment - 1) >> s_tiny_increment_shift) - 1;
126-
}
127-
128-
static std::size_t bucket_index(std::size_t n) noexcept
129-
{
130-
return log2_c((n - 1) >> s_bucket_shift) - 1;
131-
}
132-
133-
static constexpr std::size_t round_to_pow_of_2(std::size_t n) noexcept
134-
{
135-
return 1u << log2_c(n - 1);
113+
return detail::log2_c((n - 1) >> bucket_shift) - 1;
136114
}
137115

138116
private:
117+
heap_config m_config;
139118
Context* m_context;
140119
std::size_t m_max_size;
141-
bool m_never_free;
142-
std::size_t m_num_reserve_segments;
143120
heap_vector m_tiny_heaps;
144121
heap_vector m_heaps;
145122
heap_map m_huge_heaps;
146123
std::mutex m_mutex;
147124

148125
public:
149-
heap(Context* context, bool never_free = false, std::size_t num_reserve_segments = 1)
150-
: m_context{context}
151-
, m_max_size(std::max(round_to_pow_of_2(s_large_limit * 2), s_large_limit))
152-
, m_never_free{never_free}
153-
, m_num_reserve_segments{num_reserve_segments}
154-
, m_tiny_heaps(s_tiny_limit / s_tiny_increment)
155-
, m_heaps(bucket_index(m_max_size) + 1)
126+
heap(Context* context, heap_config const& config = get_default_heap_config())
127+
: m_config{config}
128+
, m_context{context}
129+
, m_max_size(
130+
std::max(detail::round_to_pow_of_2(m_config.m_large_limit * 2), m_config.m_large_limit))
131+
, m_tiny_heaps(m_config.m_tiny_limit / m_config.m_tiny_increment)
132+
, m_heaps(bucket_index(m_max_size, m_config.m_bucket_shift) + 1)
156133
{
157134
for (std::size_t i = 0; i < m_tiny_heaps.size(); ++i)
158135
m_tiny_heaps[i] = std::make_unique<fixed_size_heap_type>(m_context,
159-
s_tiny_increment * (i + 1), s_tiny_segment, m_never_free, m_num_reserve_segments);
136+
m_config.m_tiny_increment * (i + 1), m_config.m_tiny_segment_size,
137+
m_config.m_never_free, m_config.m_num_reserve_segments);
160138

161-
for (std::size_t i = 0; i < s_num_small_heaps; ++i)
139+
for (std::size_t i = 0; i < m_config.m_num_small_heaps; ++i)
162140
m_heaps[i] = std::make_unique<fixed_size_heap_type>(m_context,
163-
(s_tiny_limit << (i + 1)), s_small_segment, m_never_free, m_num_reserve_segments);
141+
(m_config.m_tiny_limit << (i + 1)), m_config.m_small_segment_size,
142+
m_config.m_never_free, m_config.m_num_reserve_segments);
164143

165-
for (std::size_t i = 0; i < s_num_large_heaps; ++i)
166-
m_heaps[i + s_num_small_heaps] = std::make_unique<fixed_size_heap_type>(m_context,
167-
(s_small_limit << (i + 1)), s_large_segment, m_never_free, m_num_reserve_segments);
144+
for (std::size_t i = 0; i < m_config.m_num_large_heaps; ++i)
145+
m_heaps[i + m_config.m_num_small_heaps] = std::make_unique<fixed_size_heap_type>(
146+
m_context, (m_config.m_small_limit << (i + 1)), m_config.m_large_segment_size,
147+
m_config.m_never_free, m_config.m_num_reserve_segments);
168148

169-
for (std::size_t i = 0; i < m_heaps.size() - (s_num_small_heaps + s_num_large_heaps); ++i)
170-
m_heaps[i + s_num_small_heaps + s_num_large_heaps] =
171-
std::make_unique<fixed_size_heap_type>(m_context, (s_large_limit << (i + 1)),
172-
(s_large_limit << (i + 1)), m_never_free, m_num_reserve_segments);
149+
for (std::size_t i = 0;
150+
i < m_heaps.size() - (m_config.m_num_small_heaps + m_config.m_num_large_heaps); ++i)
151+
m_heaps[i + m_config.m_num_small_heaps + m_config.m_num_large_heaps] =
152+
std::make_unique<fixed_size_heap_type>(m_context,
153+
(m_config.m_large_limit << (i + 1)), (m_config.m_large_limit << (i + 1)),
154+
m_config.m_never_free, m_config.m_num_reserve_segments);
173155
}
174156

175157
heap(heap const&) = delete;
@@ -194,20 +176,22 @@ class heap
194176

195177
pointer allocate(std::size_t size, std::size_t numa_node)
196178
{
197-
if (size <= s_tiny_limit)
198-
return {m_tiny_heaps[tiny_bucket_index(size)]->allocate(numa_node)};
179+
if (size <= m_config.m_tiny_limit)
180+
return {m_tiny_heaps[tiny_bucket_index(size, m_config.m_tiny_increment,
181+
m_config.m_tiny_increment_shift)]
182+
->allocate(numa_node)};
199183
else if (size <= m_max_size)
200-
return {m_heaps[bucket_index(size)]->allocate(numa_node)};
184+
return {m_heaps[bucket_index(size, m_config.m_bucket_shift)]->allocate(numa_node)};
201185
else
202186
{
203187
fixed_size_heap_type* h;
204188
{
205189
std::lock_guard<std::mutex> lock(m_mutex);
206-
const auto s = round_to_pow_of_2(size);
190+
const auto s = detail::round_to_pow_of_2(size);
207191
auto& u_ptr = m_huge_heaps[s];
208192
if (!u_ptr)
209-
u_ptr = std::make_unique<fixed_size_heap_type>(m_context, s, s, m_never_free,
210-
m_num_reserve_segments);
193+
u_ptr = std::make_unique<fixed_size_heap_type>(m_context, s, s,
194+
m_config.m_never_free, m_config.m_num_reserve_segments);
211195
h = u_ptr.get();
212196
}
213197
return {h->allocate(numa_node)};
@@ -223,20 +207,23 @@ class heap
223207
#if HWMALLOC_ENABLE_DEVICE
224208
pointer allocate(std::size_t size, std::size_t numa_node, int device_id)
225209
{
226-
if (size <= s_tiny_limit)
227-
return {m_tiny_heaps[tiny_bucket_index(size)]->allocate(numa_node, device_id)};
210+
if (size <= m_config.m_tiny_limit)
211+
return {m_tiny_heaps[tiny_bucket_index(size, m_config.m_tiny_increment,
212+
m_config.m_tiny_increment_shift)]
213+
->allocate(numa_node, device_id)};
228214
else if (size <= m_max_size)
229-
return {m_heaps[bucket_index(size)]->allocate(numa_node, device_id)};
215+
return {m_heaps[bucket_index(size, m_config.m_bucket_shift)]->allocate(numa_node,
216+
device_id)};
230217
else
231218
{
232219
fixed_size_heap_type* h;
233220
{
234221
std::lock_guard<std::mutex> lock(m_mutex);
235-
const auto s = round_to_pow_of_2(size);
222+
const auto s = detail::round_to_pow_of_2(size);
236223
auto& u_ptr = m_huge_heaps[s];
237224
if (!u_ptr)
238-
u_ptr = std::make_unique<fixed_size_heap_type>(m_context, s, s, m_never_free,
239-
m_num_reserve_segments);
225+
u_ptr = std::make_unique<fixed_size_heap_type>(m_context, s, s,
226+
m_config.m_never_free, m_config.m_num_reserve_segments);
240227
h = u_ptr.get();
241228
}
242229
return {h->allocate(numa_node, device_id)};
@@ -293,29 +280,4 @@ class heap
293280
}
294281
};
295282

296-
template<typename Context>
297-
const std::size_t heap<Context>::s_tiny_limit;
298-
template<typename Context>
299-
const std::size_t heap<Context>::s_small_limit;
300-
template<typename Context>
301-
const std::size_t heap<Context>::s_large_limit;
302-
template<typename Context>
303-
const std::size_t heap<Context>::s_bucket_shift;
304-
template<typename Context>
305-
const std::size_t heap<Context>::s_tiny_segment;
306-
template<typename Context>
307-
const std::size_t heap<Context>::s_small_segment;
308-
template<typename Context>
309-
const std::size_t heap<Context>::s_large_segment;
310-
template<typename Context>
311-
const std::size_t heap<Context>::s_tiny_increment_shift;
312-
template<typename Context>
313-
const std::size_t heap<Context>::s_tiny_increment;
314-
template<typename Context>
315-
const std::size_t heap<Context>::s_num_tiny_heaps;
316-
template<typename Context>
317-
const std::size_t heap<Context>::s_num_small_heaps;
318-
template<typename Context>
319-
const std::size_t heap<Context>::s_num_large_heaps;
320-
321283
} // namespace hwmalloc

include/hwmalloc/heap_config.hpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* ghex-org
3+
*
4+
* Copyright (c) 2014-2025, ETH Zurich
5+
* All rights reserved.
6+
*
7+
* Please, refer to the LICENSE file in the root directory.
8+
* SPDX-License-Identifier: BSD-3-Clause
9+
*/
10+
#pragma once
11+
12+
#include <cstddef>
13+
14+
namespace hwmalloc
15+
{
16+
namespace detail
17+
{
18+
inline constexpr std::size_t
19+
log2_c(std::size_t n) noexcept
20+
{
21+
return ((n < 2) ? 1 : 1 + log2_c(n >> 1));
22+
}
23+
24+
inline constexpr std::size_t
25+
round_to_pow_of_2(std::size_t n) noexcept
26+
{
27+
return 1u << log2_c(n - 1);
28+
}
29+
} // namespace detail
30+
31+
struct heap_config
32+
{
33+
static constexpr bool never_free_default = false;
34+
static constexpr std::size_t num_reserve_segments_default = 16u;
35+
static constexpr std::size_t tiny_limit_default = 128u; // 128B
36+
static constexpr std::size_t small_limit_default = 4096u; // 4KiB
37+
static constexpr std::size_t large_limit_default = 2097152u; // 2MiB
38+
static constexpr std::size_t tiny_segment_size_default = 65536u; // 64KiB
39+
static constexpr std::size_t small_segment_size_default = 65536u; // 64KiB
40+
static constexpr std::size_t large_segment_size_default = 2097152u; // 2MiB
41+
42+
bool m_never_free;
43+
std::size_t m_num_reserve_segments;
44+
std::size_t m_tiny_limit;
45+
std::size_t m_small_limit;
46+
std::size_t m_large_limit;
47+
std::size_t m_bucket_shift = detail::log2_c(m_tiny_limit) - 1;
48+
std::size_t m_tiny_segment_size;
49+
std::size_t m_small_segment_size;
50+
std::size_t m_large_segment_size;
51+
static constexpr std::size_t m_tiny_increment_shift = 3;
52+
static constexpr std::size_t m_tiny_increment = (1u << m_tiny_increment_shift);
53+
std::size_t m_num_tiny_heaps = m_tiny_limit / m_tiny_increment;
54+
std::size_t m_num_small_heaps = detail::log2_c(m_small_limit) - detail::log2_c(m_tiny_limit);
55+
std::size_t m_num_large_heaps = detail::log2_c(m_large_limit) - detail::log2_c(m_small_limit);
56+
57+
heap_config(bool never_free, std::size_t num_reserve_segments, std::size_t tiny_limit,
58+
std::size_t small_limit, std::size_t large_limit, std::size_t tiny_segment_size,
59+
std::size_t small_segment_size, std::size_t large_segment_size);
60+
};
61+
62+
heap_config const& get_default_heap_config();
63+
} // namespace hwmalloc

src/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
target_sources(hwmalloc PRIVATE heap_config.cpp)
2+
13
if (NUMA_LIBRARY)
24
target_sources(hwmalloc PRIVATE numa.cpp)
35
else()

0 commit comments

Comments
 (0)