Skip to content

Commit 8950beb

Browse files
committed
add memusage stat to os_provider and use it in benchmarks
1 parent c2024af commit 8950beb

File tree

4 files changed

+206
-23
lines changed

4 files changed

+206
-23
lines changed

benchmark/benchmark.hpp

+75-6
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,14 @@ class provider_allocator : public allocator_interface {
173173
return argPos;
174174
}
175175

176+
void preBench(::benchmark::State &state) override {
177+
provider.preBench(state);
178+
}
179+
180+
void postBench(::benchmark::State &state) override {
181+
provider.postBench(state);
182+
}
183+
176184
void TearDown(::benchmark::State &state) override {
177185
provider.TearDown(state);
178186
}
@@ -204,13 +212,18 @@ template <typename Pool> class pool_allocator : public allocator_interface {
204212
return argPos;
205213
}
206214

215+
void preBench(::benchmark::State &state) override { pool.preBench(state); }
216+
void postBench(::benchmark::State &state) override {
217+
pool.postBench(state);
218+
}
219+
207220
void TearDown(::benchmark::State &state) override { pool.TearDown(state); }
208221

209-
virtual void *benchAlloc(size_t size) override {
222+
void *benchAlloc(size_t size) override {
210223
return umfPoolMalloc(pool.pool, size);
211224
}
212225

213-
virtual void benchFree(void *ptr, [[maybe_unused]] size_t size) override {
226+
void benchFree(void *ptr, [[maybe_unused]] size_t size) override {
214227
umfPoolFree(pool.pool, ptr);
215228
}
216229

@@ -241,7 +254,7 @@ struct benchmark_interface : public benchmark::Fixture {
241254
allocator.TearDown(state);
242255
}
243256

244-
virtual void bench(::benchmark::State &state) = 0;
257+
void bench([[maybe_unused]] ::benchmark::State &state){};
245258

246259
virtual std::vector<std::string> argsName() {
247260
auto s = Size::argsName();
@@ -260,6 +273,9 @@ struct benchmark_interface : public benchmark::Fixture {
260273
benchmark->ArgNames(bench->argsName())->Name(bench->name());
261274
}
262275

276+
void custom_counters(::benchmark::State &state) {
277+
allocator.custom_counters(state);
278+
}
263279
std::vector<Size> alloc_sizes;
264280
Allocator allocator;
265281
};
@@ -282,7 +298,8 @@ class multiple_malloc_free_benchmark : public benchmark_interface<Size, Alloc> {
282298

283299
vector2d<alloc_data> allocations;
284300
std::vector<unsigned> iters;
285-
301+
std::vector<size_t> memused;
302+
std::vector<size_t> peakmemory;
286303
vector2d<next_alloc_data> next;
287304
std::vector<std::vector<next_alloc_data>::const_iterator> next_iter;
288305
int64_t iterations;
@@ -302,6 +319,12 @@ class multiple_malloc_free_benchmark : public benchmark_interface<Size, Alloc> {
302319
allocations.resize(state.threads());
303320
next.resize(state.threads());
304321
next_iter.resize(state.threads());
322+
memused.resize(state.threads());
323+
peakmemory.resize(state.threads());
324+
for (int i = 0; i < state.threads(); i++) {
325+
memused[i] = 0;
326+
peakmemory[i] = 0;
327+
}
305328

306329
#ifndef WIN32
307330
// Ensure that system malloc does not have memory pooled on the heap
@@ -323,13 +346,49 @@ class multiple_malloc_free_benchmark : public benchmark_interface<Size, Alloc> {
323346
waitForAllThreads(state);
324347
// prepare workload for actual benchmark.
325348
freeAllocs(state);
349+
// reset peak memory before real benchmark
350+
for (auto &i : peakmemory) {
351+
i = 0;
352+
}
353+
326354
prealloc(state);
327355
prepareWorkload(state);
356+
waitForAllThreads(state);
357+
base::allocator.preBench(state);
328358
}
329359

330360
void TearDown(::benchmark::State &state) override {
361+
base::allocator.postBench(state);
331362
auto tid = state.thread_index();
363+
if (tid == 0) {
364+
size_t current_memory_allocated = 0;
365+
for (int i = 0; i < state.threads(); i++) {
366+
current_memory_allocated += memused[i];
367+
}
368+
size_t peak_memory_allocated =
369+
*std::max_element(peakmemory.begin(), peakmemory.end());
370+
371+
size_t peak_memory_used = state.counters["peak_memory_use"];
372+
size_t current_memory_used = state.counters["current_memory_use"];
373+
374+
if (peak_memory_used != 0) {
375+
state.counters["peak_memory_fragmentation"] =
376+
100.0 * (peak_memory_used - peak_memory_allocated) /
377+
peak_memory_used;
378+
}
379+
380+
if (current_memory_used != 0) {
381+
state.counters["current_memory_fragmentation"] =
382+
100.0 * (current_memory_used - current_memory_allocated) /
383+
current_memory_used;
384+
}
385+
386+
state.counters["current_memory_allocated"] =
387+
current_memory_allocated;
388+
state.counters["peak_memory_allocated"] = peak_memory_allocated;
389+
}
332390

391+
waitForAllThreads(state);
333392
freeAllocs(state);
334393
waitForAllThreads(state);
335394
if (tid == 0) {
@@ -342,20 +401,24 @@ class multiple_malloc_free_benchmark : public benchmark_interface<Size, Alloc> {
342401
base::TearDown(state);
343402
}
344403

345-
void bench(benchmark::State &state) override {
404+
void bench(benchmark::State &state) {
346405
auto tid = state.thread_index();
347406
auto &allocation = allocations[tid];
407+
auto &memuse = memused[tid];
408+
auto &peak = peakmemory[tid];
348409
for (int i = 0; i < allocsPerIterations; i++) {
349410
auto &n = *next_iter[tid]++;
350411
auto &alloc = allocation[n.offset];
351412
base::allocator.benchFree(alloc.ptr, alloc.size);
352-
413+
memuse -= alloc.size;
353414
alloc.size = n.size;
354415
alloc.ptr = base::allocator.benchAlloc(alloc.size);
355416

356417
if (alloc.ptr == NULL) {
357418
state.SkipWithError("allocation failed");
358419
}
420+
memuse += alloc.size;
421+
peak = std::max(peak, memuse);
359422
}
360423
}
361424

@@ -376,7 +439,10 @@ class multiple_malloc_free_benchmark : public benchmark_interface<Size, Alloc> {
376439
auto tid = state.thread_index();
377440
auto &i = allocations[tid];
378441
i.resize(max_allocs);
442+
auto &memuse = memused[tid];
443+
auto &peak = peakmemory[tid];
379444
auto sizeGenerator = base::alloc_sizes[tid];
445+
380446
for (size_t j = 0; j < max_allocs; j++) {
381447
auto size = sizeGenerator.nextSize();
382448
i[j].ptr = base::allocator.benchAlloc(size);
@@ -385,6 +451,8 @@ class multiple_malloc_free_benchmark : public benchmark_interface<Size, Alloc> {
385451
return;
386452
}
387453
i[j].size = size;
454+
memuse += size;
455+
peak = std::max(peak, memuse);
388456
}
389457
}
390458

@@ -394,6 +462,7 @@ class multiple_malloc_free_benchmark : public benchmark_interface<Size, Alloc> {
394462
for (auto &j : i) {
395463
if (j.ptr != NULL) {
396464
base::allocator.benchFree(j.ptr, j.size);
465+
memused[tid] -= j.size;
397466
j.ptr = NULL;
398467
j.size = 0;
399468
}

benchmark/benchmark_umf.hpp

+52-14
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ struct provider_interface {
3030
using params_ptr = std::unique_ptr<void, void (*)(void *)>;
3131

3232
umf_memory_provider_handle_t provider = NULL;
33-
virtual void SetUp(::benchmark::State &state) {
33+
void SetUp(::benchmark::State &state) {
3434
if (state.thread_index() != 0) {
3535
return;
3636
}
@@ -42,7 +42,26 @@ struct provider_interface {
4242
}
4343
}
4444

45-
virtual void TearDown([[maybe_unused]] ::benchmark::State &state) {
45+
void preBench([[maybe_unused]] ::benchmark::State &state) {
46+
if (state.thread_index() != 0) {
47+
return;
48+
}
49+
umfCtlExec("umf.provider.by_handle.stats.reset", provider, NULL);
50+
}
51+
52+
void postBench([[maybe_unused]] ::benchmark::State &state) {
53+
if (state.thread_index() != 0) {
54+
return;
55+
}
56+
size_t arg;
57+
umfCtlGet("umf.provider.by_handle.stats.peak_memory", provider, &arg);
58+
state.counters["peak_memory_use"] = arg;
59+
umfCtlGet("umf.provider.by_handle.stats.allocated_memory", provider,
60+
&arg);
61+
state.counters["current_memory_use"] = arg;
62+
}
63+
64+
void TearDown([[maybe_unused]] ::benchmark::State &state) {
4665
if (state.thread_index() != 0) {
4766
return;
4867
}
@@ -53,9 +72,7 @@ struct provider_interface {
5372
}
5473

5574
virtual umf_memory_provider_ops_t *
56-
getOps([[maybe_unused]] ::benchmark::State &state) {
57-
return nullptr;
58-
}
75+
getOps([[maybe_unused]] ::benchmark::State &state) = 0;
5976

6077
virtual params_ptr getParams([[maybe_unused]] ::benchmark::State &state) {
6178
return {nullptr, [](void *) {}};
@@ -68,7 +85,7 @@ template <typename T,
6885
struct pool_interface {
6986
using params_ptr = std::unique_ptr<void, void (*)(void *)>;
7087

71-
virtual void SetUp(::benchmark::State &state) {
88+
void SetUp(::benchmark::State &state) {
7289
provider.SetUp(state);
7390
if (state.thread_index() != 0) {
7491
return;
@@ -80,7 +97,22 @@ struct pool_interface {
8097
state.SkipWithError("umfPoolCreate() failed");
8198
}
8299
}
83-
virtual void TearDown([[maybe_unused]] ::benchmark::State &state) {
100+
101+
void preBench([[maybe_unused]] ::benchmark::State &state) {
102+
provider.preBench(state);
103+
if (state.thread_index() != 0) {
104+
return;
105+
}
106+
}
107+
108+
void postBench([[maybe_unused]] ::benchmark::State &state) {
109+
provider.postBench(state);
110+
if (state.thread_index() != 0) {
111+
return;
112+
}
113+
}
114+
115+
void TearDown([[maybe_unused]] ::benchmark::State &state) {
84116
if (state.thread_index() != 0) {
85117
return;
86118
}
@@ -93,15 +125,17 @@ struct pool_interface {
93125
if (pool) {
94126
umfPoolDestroy(pool);
95127
}
128+
129+
provider.TearDown(state);
96130
};
97131

98132
virtual umf_memory_pool_ops_t *
99-
getOps([[maybe_unused]] ::benchmark::State &state) {
100-
return nullptr;
101-
}
133+
getOps([[maybe_unused]] ::benchmark::State &state) = 0;
134+
102135
virtual params_ptr getParams([[maybe_unused]] ::benchmark::State &state) {
103136
return {nullptr, [](void *) {}};
104137
}
138+
105139
T provider;
106140
umf_memory_pool_handle_t pool;
107141
};
@@ -110,6 +144,8 @@ class allocator_interface {
110144
public:
111145
virtual unsigned SetUp([[maybe_unused]] ::benchmark::State &state,
112146
[[maybe_unused]] unsigned argPos) = 0;
147+
virtual void preBench([[maybe_unused]] ::benchmark::State &state) = 0;
148+
virtual void postBench([[maybe_unused]] ::benchmark::State &state) = 0;
113149
virtual void TearDown([[maybe_unused]] ::benchmark::State &state) = 0;
114150
virtual void *benchAlloc(size_t size) = 0;
115151
virtual void benchFree(void *ptr, [[maybe_unused]] size_t size) = 0;
@@ -121,7 +157,9 @@ struct glibc_malloc : public allocator_interface {
121157
unsigned argPos) override {
122158
return argPos;
123159
}
124-
void TearDown([[maybe_unused]] ::benchmark::State &state) override{};
160+
void preBench([[maybe_unused]] ::benchmark::State &state) override {}
161+
void postBench([[maybe_unused]] ::benchmark::State &state) override {}
162+
void TearDown([[maybe_unused]] ::benchmark::State &state) override {}
125163
void *benchAlloc(size_t size) override { return malloc(size); }
126164
void benchFree(void *ptr, [[maybe_unused]] size_t size) override {
127165
free(ptr);
@@ -163,7 +201,7 @@ struct fixed_provider : public provider_interface {
163201
char *mem = NULL;
164202
const size_t size = 1024 * 1024 * 1024; // 1GB
165203
public:
166-
virtual void SetUp(::benchmark::State &state) override {
204+
void SetUp(::benchmark::State &state) {
167205
if (state.thread_index() != 0) {
168206
return;
169207
}
@@ -175,7 +213,7 @@ struct fixed_provider : public provider_interface {
175213
provider_interface::SetUp(state);
176214
}
177215

178-
virtual void TearDown(::benchmark::State &state) override {
216+
void TearDown(::benchmark::State &state) {
179217
if (state.thread_index() != 0) {
180218
return;
181219
}
@@ -295,7 +333,7 @@ struct jemalloc_pool : public pool_interface<Provider> {
295333
#ifdef UMF_POOL_SCALABLE_ENABLED
296334
template <typename Provider>
297335
struct scalable_pool : public pool_interface<Provider> {
298-
virtual umf_memory_pool_ops_t *
336+
umf_memory_pool_ops_t *
299337
getOps([[maybe_unused]] ::benchmark::State &state) override {
300338
return umfScalablePoolOps();
301339
}

0 commit comments

Comments
 (0)