Skip to content

Commit 73706ea

Browse files
authored
Merge pull request #204 from igchor/mt_benchmark
Add MT benchmarks
2 parents 57ae985 + e577673 commit 73706ea

File tree

6 files changed

+333
-4
lines changed

6 files changed

+333
-4
lines changed

.github/workflows/benchmarks.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ jobs:
3535
-DCMAKE_CXX_COMPILER=${{matrix.compiler.cxx}}
3636
-DUMF_BUILD_SHARED_LIBRARY=${{matrix.shared_library}}
3737
-DUMF_BUILD_BENCHMARKS=ON
38+
-DUMF_BUILD_BENCHMARKS_MT=ON
3839
-DUMF_BUILD_TESTS=OFF
3940
-DUMF_FORMAT_CODE_STYLE=OFF
4041
-DUMF_DEVELOPER_MODE=OFF
@@ -62,3 +63,7 @@ jobs:
6263
fi
6364
cat $LOG
6465
echo "[ PASSED ] The CI benchmark job PASSED."
66+
67+
- name: Run MT benchmarks
68+
working-directory: ${{github.workspace}}/build
69+
run: ./benchmark/multithread_bench

CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ option(UMF_BUILD_LIBUMF_POOL_JEMALLOC "Build the libumf_pool_jemalloc static lib
1313
option(UMF_BUILD_LIBUMF_POOL_SCALABLE "Build the libumf_pool_scalable static library" OFF)
1414
option(UMF_BUILD_TESTS "Build UMF tests" ON)
1515
option(UMF_BUILD_BENCHMARKS "Build UMF benchmarks" OFF)
16+
option(UMF_BUILD_BENCHMARKS_MT "Build UMF multithreaded benchmarks" OFF)
1617
option(UMF_ENABLE_POOL_TRACKING "Build UMF with pool tracking" ON)
1718
option(UMF_DEVELOPER_MODE "Enable developer checks, treats warnings as errors" OFF)
1819
option(UMF_FORMAT_CODE_STYLE "Format UMF code with clang-format" OFF)
@@ -26,7 +27,8 @@ option(USE_MSAN "Enable MemorySanitizer checks" OFF)
2627
# CMake will set up a strict C build, without C++ support.
2728
set(OPTIONS_REQUIRING_CXX
2829
"UMF_BUILD_TESTS"
29-
"UMF_BUILD_LIBUMF_POOL_DISJOINT")
30+
"UMF_BUILD_LIBUMF_POOL_DISJOINT"
31+
"UMF_BUILD_BENCHMARKS_MT")
3032
foreach(option_name ${OPTIONS_REQUIRING_CXX})
3133
if(${option_name})
3234
enable_language(CXX)

README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,14 +84,18 @@ Required packages:
8484
For development and contributions:
8585
- clang-format-15.0 (can be installed with `python -m pip install clang-format==15.0.7`)
8686

87-
For building tests and Disjoint Pool:
87+
For building tests, multithreaded benchmarks and Disjoint Pool:
8888
- C++ compiler with C++17 support
8989

9090
### Benchmark
9191

92-
A simple micro benchmark based on [ubench](https://github.com/sheredom/ubench.h).
92+
UMF comes with a single-threaded micro benchmark based on [ubench](https://github.com/sheredom/ubench.h).
9393
In order to build the benchmark, the `UMF_BUILD_BENCHMARKS` CMake configuration flag has to be turned `ON`.
9494

95+
UMF also provides multithreaded benchmarks that can be enabled by setting both
96+
`UMF_BUILD_BENCHMARKS` and `UMF_BUILD_BENCHMARKS_MT` CMake
97+
configuration flags to `ON`. Multithreaded benchmarks require a C++ support.
98+
9599
### Windows
96100

97101
Generating Visual Studio Project. EXE and binaries will be in **build/bin/{build_config}**

benchmark/CMakeLists.txt

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
include(FindThreads)
66

77
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
8-
message(WARNING "The ubench SHOULD NOT be run in the Debug build type!")
8+
message(WARNING "The benchmarks SHOULD NOT be run in the Debug build type!")
99
endif()
1010

1111
if(UMF_BUILD_LIBUMF_POOL_DISJOINT)
@@ -34,18 +34,44 @@ target_link_libraries(ubench
3434
${CMAKE_THREAD_LIBS_INIT}
3535
m)
3636

37+
if (UMF_BUILD_BENCHMARKS_MT)
38+
add_executable(multithread_bench multithread.cpp)
39+
target_link_libraries(multithread_bench
40+
umf
41+
${LIBS_OPTIONAL}
42+
pthread
43+
m)
44+
target_include_directories(multithread_bench PRIVATE ${UMF_CMAKE_SOURCE_DIR}/include/)
45+
endif()
46+
3747
if (UMF_BUILD_OS_MEMORY_PROVIDER)
3848
target_compile_definitions(ubench PRIVATE UMF_BUILD_OS_MEMORY_PROVIDER=1)
49+
50+
if (UMF_BUILD_BENCHMARKS_MT)
51+
target_compile_definitions(multithread_bench PRIVATE UMF_BUILD_OS_MEMORY_PROVIDER=1)
52+
endif()
3953
endif()
4054

4155
if (UMF_BUILD_LIBUMF_POOL_DISJOINT)
4256
target_compile_definitions(ubench PRIVATE UMF_BUILD_LIBUMF_POOL_DISJOINT=1)
57+
58+
if (UMF_BUILD_BENCHMARKS_MT)
59+
target_compile_definitions(multithread_bench PRIVATE UMF_BUILD_LIBUMF_POOL_DISJOINT=1)
60+
endif()
4361
endif()
4462

4563
if (UMF_BUILD_LIBUMF_POOL_JEMALLOC)
4664
target_compile_definitions(ubench PRIVATE UMF_BUILD_LIBUMF_POOL_JEMALLOC=1)
65+
66+
if (UMF_BUILD_BENCHMARKS_MT)
67+
target_compile_definitions(multithread_bench PRIVATE UMF_BUILD_LIBUMF_POOL_JEMALLOC=1)
68+
endif()
4769
endif()
4870

4971
if (UMF_BUILD_LIBUMF_POOL_SCALABLE)
5072
target_compile_definitions(ubench PRIVATE UMF_BUILD_LIBUMF_POOL_SCALABLE=1)
73+
74+
if (UMF_BUILD_BENCHMARKS_MT)
75+
target_compile_definitions(multithread_bench PRIVATE UMF_BUILD_LIBUMF_POOL_SCALABLE=1)
76+
endif()
5177
endif()

benchmark/multithread.cpp

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
/*
2+
*
3+
* Copyright (C) 2024 Intel Corporation
4+
*
5+
* Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT.
6+
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7+
*
8+
*/
9+
10+
#include "multithread.hpp"
11+
12+
#include <umf/memory_pool.h>
13+
#include <umf/pools/pool_disjoint.h>
14+
#include <umf/pools/pool_jemalloc.h>
15+
#include <umf/pools/pool_scalable.h>
16+
#include <umf/providers/provider_os_memory.h>
17+
18+
#include <iostream>
19+
#include <memory>
20+
#include <numeric>
21+
22+
struct bench_params {
23+
// bench_params() = default;
24+
size_t n_repeats = 5;
25+
size_t n_iterations = 50000;
26+
size_t n_threads = 20;
27+
size_t alloc_size = 64;
28+
};
29+
30+
using poolCreateExtParams = std::tuple<umf_memory_pool_ops_t *, void *,
31+
umf_memory_provider_ops_t *, void *>;
32+
33+
static auto poolCreateExtUnique(poolCreateExtParams params) {
34+
umf_memory_pool_handle_t hPool;
35+
auto [pool_ops, pool_params, provider_ops, provider_params] = params;
36+
37+
umf_memory_provider_handle_t provider = nullptr;
38+
auto ret =
39+
umfMemoryProviderCreate(provider_ops, provider_params, &provider);
40+
if (ret != UMF_RESULT_SUCCESS) {
41+
std::cerr << "provider create failed" << std::endl;
42+
abort();
43+
}
44+
45+
ret = umfPoolCreate(pool_ops, provider, pool_params,
46+
UMF_POOL_CREATE_FLAG_OWN_PROVIDER, &hPool);
47+
if (ret != UMF_RESULT_SUCCESS) {
48+
std::cerr << "pool create failed" << std::endl;
49+
abort();
50+
}
51+
52+
return std::shared_ptr<umf_memory_pool_t>(hPool, &umfPoolDestroy);
53+
}
54+
55+
static void mt_alloc_free(poolCreateExtParams params,
56+
const bench_params &bench = bench_params()) {
57+
auto pool = poolCreateExtUnique(params);
58+
59+
std::vector<std::vector<void *>> allocs(bench.n_threads);
60+
std::vector<size_t> numFailures(bench.n_threads);
61+
for (auto &v : allocs) {
62+
v.reserve(bench.n_iterations);
63+
}
64+
65+
auto values = umf_bench::measure<std::chrono::milliseconds>(
66+
bench.n_repeats, bench.n_threads,
67+
[&, pool = pool.get()](auto thread_id) {
68+
for (int i = 0; i < bench.n_iterations; i++) {
69+
allocs[thread_id].push_back(
70+
umfPoolMalloc(pool, bench.alloc_size));
71+
if (!allocs[thread_id].back()) {
72+
numFailures[thread_id]++;
73+
}
74+
}
75+
76+
for (int i = 0; i < bench.n_iterations; i++) {
77+
umfPoolFree(pool, allocs[thread_id][i]);
78+
}
79+
80+
// clear the vector as this function might be called multiple times
81+
allocs[thread_id].clear();
82+
});
83+
84+
std::cout << "mean: " << umf_bench::mean(values)
85+
<< " [ms] std_dev: " << umf_bench::std_dev(values) << " [ms]"
86+
<< " (total alloc failures: "
87+
<< std::accumulate(numFailures.begin(), numFailures.end(), 0)
88+
<< " out of "
89+
<< bench.n_iterations * bench.n_repeats * bench.n_threads << ")"
90+
<< std::endl;
91+
}
92+
93+
int main() {
94+
#if defined(UMF_BUILD_OS_MEMORY_PROVIDER)
95+
auto osParams = umfOsMemoryProviderParamsDefault();
96+
#endif
97+
98+
#if defined(UMF_BUILD_OS_MEMORY_PROVIDER) && \
99+
defined(UMF_BUILD_LIBUMF_POOL_SCALABLE)
100+
101+
// Increase iterations for scalable pool since it runs much faster than the remaining
102+
// ones.
103+
bench_params params;
104+
params.n_iterations *= 20;
105+
106+
std::cout << "scalable_pool mt_alloc_free: ";
107+
mt_alloc_free(poolCreateExtParams{umfScalablePoolOps(), nullptr,
108+
umfOsMemoryProviderOps(), &osParams},
109+
params);
110+
#else
111+
std::cout << "skipping scalable_pool mt_alloc_free" << std::endl;
112+
#endif
113+
114+
#if defined(UMF_BUILD_OS_MEMORY_PROVIDER) && \
115+
defined(UMF_BUILD_LIBUMF_POOL_JEMALLOC)
116+
std::cout << "jemalloc_pool mt_alloc_free: ";
117+
mt_alloc_free(poolCreateExtParams{umfJemallocPoolOps(), nullptr,
118+
umfOsMemoryProviderOps(), &osParams});
119+
#else
120+
std::cout << "skipping jemalloc_pool mt_alloc_free" << std::endl;
121+
#endif
122+
123+
#if defined(UMF_BUILD_OS_MEMORY_PROVIDER) && \
124+
defined(UMF_BUILD_LIBUMF_POOL_DISJOINT)
125+
auto disjointParams = umfDisjointPoolParamsDefault();
126+
127+
std::cout << "disjoint_pool mt_alloc_free: ";
128+
mt_alloc_free(poolCreateExtParams{umfDisjointPoolOps(), &disjointParams,
129+
umfOsMemoryProviderOps(), &osParams});
130+
#else
131+
std::cout << "skipping disjoint_pool mt_alloc_free" << std::endl;
132+
#endif
133+
134+
return 0;
135+
}

0 commit comments

Comments
 (0)