Skip to content

Commit 2bd7146

Browse files
committed
add bench
1 parent e6e3e4c commit 2bd7146

File tree

8 files changed

+267
-38
lines changed

8 files changed

+267
-38
lines changed

bench/bench_async_mutex.cpp

+152
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
//
2+
// Created by konstantin on 26.07.24.
3+
//
4+
5+
#include <chrono>
6+
#include <coroutine>
7+
#include <condition_variable>
8+
#include <latch>
9+
#include <thread>
10+
#include <vector>
11+
12+
#include <benchmark/benchmark.h>
13+
#include <components/async_mutex/async_mutex.h>
14+
15+
using namespace std::chrono_literals;
16+
17+
static constexpr size_t CountIteration = 5;
18+
static constexpr size_t CountRepetitions = 10;
19+
static constexpr size_t MaxCountThreads = 8;
20+
static constexpr size_t CountIterationsInThread = 100'00;
21+
22+
struct TestAsyncMutex final {
23+
NComponents::AsyncMutex mutex;
24+
size_t number{};
25+
26+
std::latch latch;
27+
const size_t count_iterations;
28+
29+
explicit TestAsyncMutex(size_t n, size_t count_iterations)
30+
: latch(n), count_iterations(count_iterations) {}
31+
32+
NComponents::ResumableNoOwn run() {
33+
{
34+
std::unique_lock lock(cv_wait);
35+
while (!wait_flag) cv.wait(lock);
36+
}
37+
for (size_t i = 0; i < count_iterations; i++) {
38+
co_await mutex.lock();
39+
number += 1;
40+
mutex.unlock();
41+
42+
co_await mutex.lock();
43+
number += 1;
44+
mutex.unlock();
45+
}
46+
47+
latch.count_down();
48+
}
49+
50+
NComponents::ResumableNoOwn StartAll() {
51+
std::unique_lock lock(cv_wait);
52+
wait_flag.store(true);
53+
cv.notify_all();
54+
55+
co_await mutex.lock();
56+
mutex.unlock();
57+
}
58+
59+
void Wait() { latch.wait(); }
60+
61+
private:
62+
std::mutex cv_wait;
63+
std::condition_variable cv;
64+
std::atomic<bool> wait_flag{};
65+
};
66+
67+
struct TestStdMutex final {
68+
std::mutex mutex;
69+
size_t number{};
70+
71+
std::latch latch;
72+
const size_t count_iterations;
73+
74+
explicit TestStdMutex(size_t n, size_t count_iterations)
75+
: latch(n), count_iterations(count_iterations) {}
76+
77+
void run() {
78+
{
79+
std::unique_lock lock(cv_wait);
80+
while (!wait_flag) cv.wait(lock);
81+
}
82+
for (size_t i = 0; i < count_iterations; i++) {
83+
mutex.lock();
84+
number += 1;
85+
mutex.unlock();
86+
87+
mutex.lock();
88+
number += 1;
89+
mutex.unlock();
90+
}
91+
92+
latch.count_down();
93+
}
94+
95+
void StartAll() {
96+
std::unique_lock lock(cv_wait);
97+
wait_flag.store(true);
98+
cv.notify_all();
99+
100+
mutex.lock();
101+
mutex.unlock();
102+
}
103+
104+
void Wait() { latch.wait(); }
105+
106+
private:
107+
std::mutex cv_wait;
108+
std::condition_variable cv;
109+
std::atomic<bool> wait_flag{};
110+
};
111+
112+
static void BenchAsyncMutex(benchmark::State& state) {
113+
for (auto _ : state) {
114+
115+
116+
TestAsyncMutex worker(MaxCountThreads, CountIterationsInThread);
117+
{
118+
std::vector<std::jthread> workers;
119+
for (size_t i = 0; i < MaxCountThreads; i++) {
120+
workers.emplace_back(&TestAsyncMutex::run, &worker);
121+
}
122+
123+
worker.StartAll();
124+
worker.Wait();
125+
}
126+
}
127+
}
128+
BENCHMARK(BenchAsyncMutex)
129+
->Name("AsyncMutex")
130+
->Repetitions(CountRepetitions)
131+
->Iterations(CountIteration)
132+
->Unit(benchmark::kMillisecond);
133+
134+
static void BenchStdMutex(benchmark::State& state) {
135+
for (auto _ : state) {
136+
TestStdMutex worker(MaxCountThreads, CountIterationsInThread);
137+
{
138+
std::vector<std::jthread> workers;
139+
for (size_t i = 0; i < MaxCountThreads; i++) {
140+
workers.emplace_back(&TestStdMutex::run, &worker);
141+
}
142+
143+
worker.StartAll();
144+
worker.Wait();
145+
}
146+
}
147+
}
148+
BENCHMARK(BenchStdMutex)
149+
->Name("StdMutex")
150+
->Repetitions(CountRepetitions)
151+
->Iterations(CountIteration)
152+
->Unit(benchmark::kMillisecond);

bench/meson.build

+15-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
bench_inc = include_directories('.')
22

3-
bench_source_files = [
3+
bench_pool_source_files = [
44
'main.cpp',
55
'bench_pool.cpp',
66
]
77

8+
bench_mutex_source_files = [
9+
'main.cpp',
10+
'bench_async_mutex.cpp',
11+
]
12+
813
bench_dependencies = dependency(
914
[
1015
'benchmark',
@@ -13,8 +18,16 @@ bench_dependencies = dependency(
1318

1419
bench_pool = executable(
1520
'bench_pool',
16-
sources : bench_source_files,
21+
sources : bench_pool_source_files,
1722
dependencies : bench_dependencies,
1823
include_directories : [bench_inc, root_src_dir],
1924
link_with : [executor, fiber, go]
2025
)
26+
27+
bench_async_mutex = executable(
28+
'bench_async_mutex',
29+
sources : bench_mutex_source_files,
30+
dependencies : bench_dependencies,
31+
include_directories : [bench_inc, root_src_dir],
32+
link_with : [components]
33+
)

bench/readme.md

+24-1
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,27 @@
2626
| DistributedPool_task_100000/iterations:10/repeats:5_mean | 127 ms | 59.3 ms | 5 |
2727
| DistributedPool_task_100000/iterations:10/repeats:5_median | 123 ms | 59.3 ms | 5 |
2828
| DistributedPool_task_100000/iterations:10/repeats:5_stddev | 8.96 ms| 0.477 ms| 5 |
29-
| DistributedPool_task_100000/iterations:10/repeats:5_cv | 7.05 % | 0.80 % | 5 |
29+
| DistributedPool_task_100000/iterations:10/repeats:5_cv | 7.05 % | 0.80 % | 5 |
30+
31+
### Mutex
32+
33+
| Benchmark | Time | CPU | Iterations |
34+
|-----------------------------------------------|----------|---------|------------|
35+
| AsyncMutex/iterations:10/repeats:5 | 29.0 ms | 0.243 ms| 10 |
36+
| AsyncMutex/iterations:10/repeats:5 | 29.1 ms | 0.189 ms| 10 |
37+
| AsyncMutex/iterations:10/repeats:5 | 29.7 ms | 0.227 ms| 10 |
38+
| AsyncMutex/iterations:10/repeats:5 | 28.3 ms | 0.223 ms| 10 |
39+
| AsyncMutex/iterations:10/repeats:5 | 28.7 ms | 0.221 ms| 10 |
40+
| AsyncMutex/iterations:10/repeats:5_mean | 29.0 ms | 0.221 ms| 5 |
41+
| AsyncMutex/iterations:10/repeats:5_median | 29.0 ms | 0.223 ms| 5 |
42+
| AsyncMutex/iterations:10/repeats:5_stddev | 0.522 ms | 0.020 ms| 5 |
43+
| AsyncMutex/iterations:10/repeats:5_cv | 1.80 % | 8.91 % | 5 |
44+
| StdMutex/iterations:10/repeats:5 | 154 ms | 0.205 ms| 10 |
45+
| StdMutex/iterations:10/repeats:5 | 156 ms | 0.187 ms| 10 |
46+
| StdMutex/iterations:10/repeats:5 | 157 ms | 0.182 ms| 10 |
47+
| StdMutex/iterations:10/repeats:5 | 158 ms | 0.193 ms| 10 |
48+
| StdMutex/iterations:10/repeats:5 | 159 ms | 0.190 ms| 10 |
49+
| StdMutex/iterations:10/repeats:5_mean | 157 ms | 0.191 ms| 5 |
50+
| StdMutex/iterations:10/repeats:5_median | 157 ms | 0.190 ms| 5 |
51+
| StdMutex/iterations:10/repeats:5_stddev | 1.90 ms | 0.009 ms| 5 |
52+
| StdMutex/iterations:10/repeats:5_cv | 1.21 % | 4.54 % | 5 |

src/components/async_mutex/async_mutex_coro_impl.cpp

+35-18
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,49 @@
11
//
22
// Created by konstantin on 19.07.24.
33
//
4+
#include "async_mutex_coro_impl.h"
5+
46
#include <iostream>
57
#include <syncstream>
68

7-
#include "async_mutex_coro_impl.h"
9+
#include <components/async_mutex/config.h>
810

911
namespace NComponents {
1012

1113
bool AsyncMutexCoroImpl::TryLock() {
1214
if (lock_flag) {
13-
std::osyncstream(std::cout)
14-
<< "[AsyncMutexCoroImpl::TryLock][thread_id=" << std::this_thread::get_id()
15-
<< "] lock_flag was locked. Need park." << std::endl;
15+
if (ENABLE_DEBUG) {
16+
std::osyncstream(std::cout)
17+
<< "[AsyncMutexCoroImpl::TryLock][thread_id="
18+
<< std::this_thread::get_id()
19+
<< "] lock_flag was locked. Need park." << std::endl;
20+
}
21+
1622
return false;
1723
}
1824

19-
std::osyncstream(std::cout)
20-
<< "[AsyncMutexCoroImpl::TryLock][thread_id=" << std::this_thread::get_id()
21-
<< "] lock lock_flag." << std::endl;
25+
if (ENABLE_DEBUG) {
26+
std::osyncstream(std::cout)
27+
<< "[AsyncMutexCoroImpl::TryLock][thread_id="
28+
<< std::this_thread::get_id() << "] lock lock_flag." << std::endl;
29+
}
2230
lock_flag = true;
2331
return true;
2432
}
2533

2634
void AsyncMutexCoroImpl::Unlock() {
2735
std::unique_lock lock(spinlock);
2836

29-
std::osyncstream(std::cout)
30-
<< "[AsyncMutexCoroImpl::Unlock][thread_id=" << std::this_thread::get_id()
31-
<< "] call" << std::endl;
32-
37+
if (ENABLE_DEBUG) {
38+
std::osyncstream(std::cout)
39+
<< "[AsyncMutexCoroImpl::Unlock][thread_id="
40+
<< std::this_thread::get_id() << "] call" << std::endl;
41+
}
3342
if (!waiters.empty()) {
34-
std::cout << "[AsyncMutexCoroImpl::Unlock] Waiters size=" << waiters.size()
35-
<< ", wake up first" << std::endl;
43+
if (ENABLE_DEBUG) {
44+
std::cout << "[AsyncMutexCoroImpl::Unlock] Waiters size="
45+
<< waiters.size() << ", wake up first" << std::endl;
46+
}
3647
MutexAwaiter waiter = std::move(waiters.front());
3748
waiters.pop_front();
3849
lock.unlock();
@@ -41,17 +52,23 @@ void AsyncMutexCoroImpl::Unlock() {
4152
return;
4253
}
4354

44-
std::cout << "[AsyncMutexCoroImpl::Unlock] Waiters empty. Set lock_flag to false"
45-
<< std::endl;
55+
if (ENABLE_DEBUG) {
56+
std::cout << "[AsyncMutexCoroImpl::Unlock] Waiters empty. Set "
57+
"lock_flag to false"
58+
<< std::endl;
59+
}
4660
lock_flag = false;
4761
}
4862

4963
void AsyncMutexCoroImpl::ParkAwaiter(MutexAwaiter* awaiter) {
5064
assert(awaiter);
5165

52-
std::osyncstream(std::cout)
53-
<< "[AsyncMutexCoroImpl::ParkAwaiter][thead_id=" << std::this_thread::get_id()
54-
<< "] add waiter, new size=" << waiters.size() + 1 << std::endl;
66+
if (ENABLE_DEBUG) {
67+
std::osyncstream(std::cout)
68+
<< "[AsyncMutexCoroImpl::ParkAwaiter][thead_id="
69+
<< std::this_thread::get_id()
70+
<< "] add waiter, new size=" << waiters.size() + 1 << std::endl;
71+
}
5572

5673
waiters.emplace_back(std::move(*awaiter));
5774
waiters.back().ReleaseLock();

src/components/async_mutex/config.h

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
//
2+
// Created by konstantin on 26.07.24.
3+
//
4+
5+
#pragma once
6+
7+
namespace NComponents {
8+
9+
static constexpr bool ENABLE_DEBUG = false;
10+
11+
}

0 commit comments

Comments
 (0)