Skip to content

Commit f13e6f9

Browse files
committed
perf: add comprehensive ThreadSafePoolAllocator benchmarks
- add single-threaded baseline benchmark - add multithreaded scaling tests (2, 4, 8 threads) - add contention benchmarks with high allocation rates - add new/delete multithreaded comparison - add bulk operations benchmark across threads - use UseRealTime() for accurate multithreaded measurements - refactor existing benchmarks to remove redundant comments Signed-off-by: NotKeira <github.rxs06@accounts.keira.boo>
1 parent 5db56db commit f13e6f9

5 files changed

Lines changed: 175 additions & 18 deletions

File tree

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ if (BUILD_BENCHMARKS)
8787
benchmarks/bench_pool.cpp
8888
benchmarks/bench_stack.cpp
8989
benchmarks/bench_freelist.cpp
90+
benchmarks/bench_threadsafe_pool.cpp
9091
)
9192

9293
target_link_libraries(alloc_benchmarks PRIVATE

benchmarks/bench_freelist.cpp

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
#include <benchmark/benchmark.h>
22
#include "freelist_allocator.h"
33
#include <vector>
4-
#include <random>
54

65
using namespace fast_alloc;
76

8-
// Benchmark free list allocator vs malloc
97
static void BM_FreeListAllocator_FirstFit(benchmark::State& state)
108
{
11-
constexpr std::size_t allocator_size = 1024 * 1024; // 1MB
9+
constexpr std::size_t allocator_size = 1024 * 1024;
1210
FreeListAllocator allocator(allocator_size, FreeListStrategy::FirstFit);
1311

1412
for (auto _ : state)
@@ -54,13 +52,12 @@ static void BM_Malloc_Compare(benchmark::State& state)
5452

5553
BENCHMARK(BM_Malloc_Compare);
5654

57-
// Benchmark variable-sized allocations
5855
static void BM_FreeListAllocator_VariableSizes(benchmark::State& state)
5956
{
6057
constexpr std::size_t allocator_size = 1024 * 1024;
6158
FreeListAllocator allocator(allocator_size, FreeListStrategy::FirstFit);
6259

63-
std::vector<std::size_t> sizes = {16, 32, 64, 128, 256, 512};
60+
const std::vector<std::size_t> sizes = {16, 32, 64, 128, 256, 512};
6461
std::size_t size_idx = 0;
6562

6663
for (auto _ : state)
@@ -77,7 +74,6 @@ static void BM_FreeListAllocator_VariableSizes(benchmark::State& state)
7774

7875
BENCHMARK(BM_FreeListAllocator_VariableSizes);
7976

80-
// Benchmark fragmentation handling
8177
static void BM_FreeListAllocator_Fragmentation(benchmark::State& state)
8278
{
8379
constexpr std::size_t allocator_size = 1024 * 1024;
@@ -90,21 +86,18 @@ static void BM_FreeListAllocator_Fragmentation(benchmark::State& state)
9086
std::vector<void*> ptrs;
9187
ptrs.reserve(num_allocs);
9288

93-
// Allocate many blocks
9489
for (std::size_t i = 0; i < num_allocs; ++i)
9590
{
9691
ptrs.push_back(allocator.allocate(1024));
9792
}
9893

99-
// Free every other block to create fragmentation
10094
for (std::size_t i = 1; i < num_allocs; i += 2)
10195
{
10296
allocator.deallocate(ptrs[i]);
10397
ptrs[i] = nullptr;
10498
}
10599
state.ResumeTiming();
106100

107-
// Benchmark allocation in fragmented state
108101
void* ptr = allocator.allocate(512);
109102
benchmark::DoNotOptimize(ptr);
110103
allocator.deallocate(ptr);

benchmarks/bench_pool.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
using namespace fast_alloc;
66

7-
// Benchmark pool allocator vs new/delete
87
static void BM_PoolAllocator_Allocate(benchmark::State& state)
98
{
109
constexpr std::size_t block_size = 64;
@@ -38,7 +37,6 @@ static void BM_NewDelete_Allocate(benchmark::State& state)
3837

3938
BENCHMARK(BM_NewDelete_Allocate);
4039

41-
// Benchmark bulk allocations
4240
static void BM_PoolAllocator_BulkAllocate(benchmark::State& state)
4341
{
4442
const std::size_t num_allocs = state.range(0);

benchmarks/bench_stack.cpp

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,9 @@
33

44
using namespace fast_alloc;
55

6-
// Benchmark stack allocator vs malloc
76
static void BM_StackAllocator_Allocate(benchmark::State& state)
87
{
9-
constexpr std::size_t stack_size = 1024 * 1024; // 1MB
8+
constexpr std::size_t stack_size = 1024 * 1024;
109
StackAllocator stack(stack_size);
1110

1211
for (auto _ : state)
@@ -35,23 +34,20 @@ static void BM_Malloc_Allocate(benchmark::State& state)
3534

3635
BENCHMARK(BM_Malloc_Allocate);
3736

38-
// Benchmark frame-based allocation pattern
3937
static void BM_StackAllocator_FramePattern(benchmark::State& state)
4038
{
41-
constexpr std::size_t stack_size = 1024 * 1024; // 1MB
39+
constexpr std::size_t stack_size = 1024 * 1024;
4240
const std::size_t allocs_per_frame = state.range(0);
4341
StackAllocator stack(stack_size);
4442

4543
for (auto _ : state)
4644
{
47-
// Simulate frame allocations
4845
for (std::size_t i = 0; i < allocs_per_frame; ++i)
4946
{
5047
void* ptr = stack.allocate(64);
5148
benchmark::DoNotOptimize(ptr);
5249
}
5350

54-
// Reset at end of frame
5551
stack.reset();
5652
}
5753

@@ -88,7 +84,6 @@ static void BM_Malloc_FramePattern(benchmark::State& state)
8884

8985
BENCHMARK(BM_Malloc_FramePattern)->Arg(10)->Arg(100)->Arg(1000);
9086

91-
// Benchmark aligned allocations
9287
static void BM_StackAllocator_AlignedAllocate(benchmark::State& state)
9388
{
9489
constexpr std::size_t stack_size = 1024 * 1024;
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
#include <benchmark/benchmark.h>
2+
#include "threadsafe_pool_allocator.h"
3+
#include <vector>
4+
#include <thread>
5+
6+
using namespace fast_alloc;
7+
8+
static void BM_ThreadSafePoolAllocator_SingleThread(benchmark::State& state)
9+
{
10+
constexpr std::size_t block_size = 64;
11+
constexpr std::size_t block_count = 10000;
12+
ThreadSafePoolAllocator pool(block_size, block_count);
13+
14+
for (auto _ : state)
15+
{
16+
void* ptr = pool.allocate();
17+
benchmark::DoNotOptimize(ptr);
18+
pool.deallocate(ptr);
19+
}
20+
21+
state.SetItemsProcessed(state.iterations());
22+
}
23+
24+
BENCHMARK(BM_ThreadSafePoolAllocator_SingleThread);
25+
26+
static void BM_ThreadSafePoolAllocator_MultiThread(benchmark::State& state)
27+
{
28+
constexpr std::size_t block_size = 64;
29+
constexpr std::size_t block_count = 10000;
30+
ThreadSafePoolAllocator pool(block_size, block_count);
31+
32+
const int num_threads = state.range(0);
33+
34+
for (auto _ : state)
35+
{
36+
std::vector<std::thread> threads;
37+
threads.reserve(num_threads);
38+
39+
for (int i = 0; i < num_threads; ++i)
40+
{
41+
threads.emplace_back([&pool]()
42+
{
43+
void* ptr = pool.allocate();
44+
benchmark::DoNotOptimize(ptr);
45+
if (ptr) pool.deallocate(ptr);
46+
});
47+
}
48+
49+
for (auto& thread : threads)
50+
{
51+
thread.join();
52+
}
53+
}
54+
55+
state.SetItemsProcessed(state.iterations() * num_threads);
56+
}
57+
58+
BENCHMARK(BM_ThreadSafePoolAllocator_MultiThread)->Arg(2)->Arg(4)->Arg(8)->UseRealTime();
59+
60+
static void BM_ThreadSafePoolAllocator_Contention(benchmark::State& state)
61+
{
62+
constexpr std::size_t block_size = 64;
63+
constexpr std::size_t block_count = 1000;
64+
ThreadSafePoolAllocator pool(block_size, block_count);
65+
66+
const int num_threads = state.range(0);
67+
68+
for (auto _ : state)
69+
{
70+
std::vector<std::thread> threads;
71+
threads.reserve(num_threads);
72+
73+
for (int i = 0; i < num_threads; ++i)
74+
{
75+
threads.emplace_back([&pool]()
76+
{
77+
constexpr int operations = 100;
78+
for (int j = 0; j < operations; ++j)
79+
{
80+
if (void* ptr = pool.allocate()) pool.deallocate(ptr);
81+
}
82+
});
83+
}
84+
85+
for (auto& thread : threads)
86+
{
87+
thread.join();
88+
}
89+
}
90+
91+
state.SetItemsProcessed(state.iterations() * num_threads * 100);
92+
}
93+
94+
BENCHMARK(BM_ThreadSafePoolAllocator_Contention)->Arg(2)->Arg(4)->Arg(8)->UseRealTime();
95+
96+
static void BM_NewDelete_MultiThread(benchmark::State& state)
97+
{
98+
const int num_threads = state.range(0);
99+
100+
for (auto _ : state)
101+
{
102+
std::vector<std::thread> threads;
103+
threads.reserve(num_threads);
104+
105+
for (int i = 0; i < num_threads; ++i)
106+
{
107+
threads.emplace_back([]()
108+
{
109+
constexpr std::size_t block_size = 64;
110+
void* ptr = operator new(block_size);
111+
benchmark::DoNotOptimize(ptr);
112+
operator delete(ptr);
113+
});
114+
}
115+
116+
for (auto& thread : threads)
117+
{
118+
thread.join();
119+
}
120+
}
121+
122+
state.SetItemsProcessed(state.iterations() * num_threads);
123+
}
124+
125+
BENCHMARK(BM_NewDelete_MultiThread)->Arg(2)->Arg(4)->Arg(8)->UseRealTime();
126+
127+
static void BM_ThreadSafePoolAllocator_BulkOperations(benchmark::State& state)
128+
{
129+
constexpr std::size_t block_size = 64;
130+
constexpr std::size_t block_count = 10000;
131+
ThreadSafePoolAllocator pool(block_size, block_count);
132+
133+
const std::size_t operations_per_thread = state.range(0);
134+
constexpr int num_threads = 4;
135+
136+
for (auto _ : state)
137+
{
138+
std::vector<std::thread> threads;
139+
threads.reserve(num_threads);
140+
141+
for (int i = 0; i < num_threads; ++i)
142+
{
143+
threads.emplace_back([&pool, operations_per_thread]()
144+
{
145+
std::vector<void*> ptrs;
146+
ptrs.reserve(operations_per_thread);
147+
148+
for (std::size_t j = 0; j < operations_per_thread; ++j)
149+
{
150+
void* ptr = pool.allocate();
151+
if (ptr) ptrs.push_back(ptr);
152+
}
153+
154+
for (void* ptr : ptrs)
155+
{
156+
pool.deallocate(ptr);
157+
}
158+
});
159+
}
160+
161+
for (auto& thread : threads)
162+
{
163+
thread.join();
164+
}
165+
}
166+
167+
state.SetItemsProcessed(state.iterations() * num_threads * operations_per_thread);
168+
}
169+
170+
BENCHMARK(BM_ThreadSafePoolAllocator_BulkOperations)->Arg(100)->Arg(500)->Arg(1000)->UseRealTime();

0 commit comments

Comments
 (0)