Skip to content

Commit 080bcfb

Browse files
committed
Full set of benches for talk
1 parent 706b462 commit 080bcfb

File tree

11 files changed

+668
-5
lines changed

11 files changed

+668
-5
lines changed

concurrency/follybenches/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ find_package(folly CONFIG REQUIRED)
99

1010
add_executable(mutex_bench mutex_bench.cc)
1111
add_executable(rwlock_bench rwlock_bench.cc)
12+
add_executable(sptr_bench sptr_bench.cc)
13+
add_executable(rcu_bench rcu_bench.cc)
14+
add_executable(hazptr_bench hazptr_bench.cc)
1215
target_link_libraries(mutex_bench PRIVATE Folly::folly Folly::follybenchmark gflags)
1316
target_link_libraries(rwlock_bench PRIVATE Folly::folly Folly::follybenchmark gflags)
17+
target_link_libraries(sptr_bench PRIVATE Folly::folly Folly::follybenchmark gflags)
18+
target_link_libraries(rcu_bench PRIVATE Folly::folly Folly::follybenchmark gflags)
19+
target_link_libraries(hazptr_bench PRIVATE Folly::folly Folly::follybenchmark gflags)
1420

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
#include <atomic>
2+
#include <chrono>
3+
#include <cstdint>
4+
#include <iostream>
5+
#include <mutex>
6+
#include <thread>
7+
#include <utility>
8+
#include <vector>
9+
#include <shared_mutex>
10+
11+
#include <folly/Benchmark.h>
12+
#include <folly/init/Init.h>
13+
#include <folly/synchronization/Rcu.h>
14+
#include <gflags/gflags.h>
15+
16+
std::atomic<int> current_value{1};
17+
18+
DEFINE_bool(verify, false, "Enable correctness checks");
19+
20+
DEFINE_int32(readers,
21+
std::thread::hardware_concurrency() > 1
22+
? int(std::thread::hardware_concurrency() - 1)
23+
: 1,
24+
"Number of reader threads");
25+
26+
DEFINE_int32(writers, 1, "Number of writer threads");
27+
28+
DEFINE_int32(writer_duration_ms, 2000,
29+
"Scenario duration (ms) for readers/writers");
30+
31+
DEFINE_int32(writer_pause_ns, 1,
32+
"Pause between writer updates (ns) outside critical section");
33+
34+
// ---------- API
35+
36+
struct alignas(std::hardware_destructive_interference_size) T : folly::hazptr_obj_base<T> {
37+
int x;
38+
T(int x): x(x) {}
39+
};
40+
41+
std::atomic<T*> src = nullptr;
42+
43+
template <typename Fn> requires std::invocable<Fn&, const T*>
44+
auto readAndAccess(Fn&& fn) {
45+
thread_local auto h = folly::make_hazard_pointer<>();
46+
T *srcptr = h.protect(src);
47+
return std::invoke(std::forward<Fn>(fn), srcptr);
48+
}
49+
50+
void update(std::unique_ptr<T> newptr) {
51+
T *oldptr = src.exchange(newptr.release(), std::memory_order_relaxed);
52+
if (oldptr)
53+
oldptr->retire();
54+
}
55+
56+
// ----------
57+
58+
struct alignas(std::hardware_destructive_interference_size) ScenarioStats {
59+
std::uint64_t reads = 0;
60+
std::uint64_t failedChecks = 0;
61+
};
62+
63+
void reset_state() {
64+
src.store(nullptr);
65+
current_value.store(1, std::memory_order_relaxed);
66+
}
67+
68+
ScenarioStats run_mutex_scenario(std::size_t numReaders,
69+
std::size_t numWriters) {
70+
reset_state();
71+
72+
std::atomic<bool> stop{false};
73+
std::vector<ScenarioStats> perReader(numReaders);
74+
75+
std::vector<std::thread> readerThreads;
76+
readerThreads.reserve(numReaders);
77+
78+
for (std::size_t i = 0; i < numReaders; ++i) {
79+
readerThreads.emplace_back([&, idx = i] {
80+
auto& local = perReader[idx];
81+
82+
while (!stop.load(std::memory_order_relaxed)) {
83+
int v = readAndAccess([&](const T* ptr) {
84+
int local_v = 0;
85+
86+
if (ptr) {
87+
local_v = ptr->x;
88+
89+
if (FLAGS_verify) {
90+
int cur = current_value.load(std::memory_order_acquire);
91+
if (local_v < 1 || local_v > cur) {
92+
++local.failedChecks;
93+
}
94+
}
95+
}
96+
97+
++local.reads;
98+
return local_v;
99+
});
100+
101+
folly::doNotOptimizeAway(v);
102+
}
103+
});
104+
}
105+
106+
std::vector<std::thread> writerThreads;
107+
writerThreads.reserve(numWriters);
108+
109+
auto pause = std::chrono::nanoseconds(FLAGS_writer_pause_ns);
110+
111+
for (std::size_t w = 0; w < numWriters; ++w) {
112+
writerThreads.emplace_back([&] {
113+
while (!stop.load(std::memory_order_relaxed)) {
114+
int next =
115+
current_value.fetch_add(1, std::memory_order_acq_rel);
116+
auto t = std::make_unique<T>(next);
117+
update(std::move(t));
118+
119+
if (pause.count() > 0) {
120+
std::this_thread::sleep_for(pause);
121+
}
122+
}
123+
});
124+
}
125+
126+
auto duration = std::chrono::milliseconds(FLAGS_writer_duration_ms);
127+
std::this_thread::sleep_for(duration);
128+
129+
stop.store(true, std::memory_order_relaxed);
130+
131+
for (auto& th : writerThreads)
132+
th.join();
133+
for (auto& th : readerThreads)
134+
th.join();
135+
136+
ScenarioStats total{};
137+
for (auto& r : perReader) {
138+
total.reads += r.reads;
139+
total.failedChecks += r.failedChecks;
140+
}
141+
142+
return total;
143+
}
144+
145+
BENCHMARK(MutexRW, iters) {
146+
ScenarioStats agg{};
147+
148+
const auto runs = iters;
149+
const std::size_t numReaders = static_cast<std::size_t>(FLAGS_readers);
150+
const std::size_t numWriters = static_cast<std::size_t>(FLAGS_writers);
151+
152+
for (std::uint32_t i = 0; i < runs; ++i) {
153+
auto stats = run_mutex_scenario(numReaders, numWriters);
154+
agg.reads += stats.reads;
155+
agg.failedChecks += stats.failedChecks;
156+
}
157+
158+
if (FLAGS_verify && agg.failedChecks != 0) {
159+
std::cerr << "[sptr] failed checks: " << agg.failedChecks << "\n";
160+
std::abort();
161+
}
162+
163+
const double total_ms =
164+
static_cast<double>(runs) * static_cast<double>(FLAGS_writer_duration_ms);
165+
const double total_sec = total_ms / 1000.0;
166+
167+
double rps_total = 0.0;
168+
double rps_per_reader = 0.0;
169+
170+
if (total_sec > 0.0) {
171+
rps_total = static_cast<double>(agg.reads) / total_sec;
172+
if (numReaders > 0) {
173+
rps_per_reader =
174+
static_cast<double>(agg.reads) / (numReaders * total_sec);
175+
}
176+
}
177+
178+
std::cout << "[sptr] bmk results\n";
179+
std::cout << "readers=" << numReaders << "\n";
180+
std::cout << "rps_per_reader=" << rps_per_reader << "\n";
181+
}
182+
183+
int main(int argc, char** argv) {
184+
folly::Init init(&argc, &argv);
185+
folly::runBenchmarks();
186+
}

concurrency/follybenches/mutex_bench.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ DEFINE_int32(writers, 1, "Number of writer threads");
2626
DEFINE_int32(writer_duration_ms, 2000,
2727
"Scenario duration (ms) for readers/writers");
2828

29-
DEFINE_int32(writer_pause_ns, 0,
29+
DEFINE_int32(writer_pause_ns, 10,
3030
"Pause between writer updates (ns) outside critical section");
3131

3232
// ---------- API
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
set term pngcairo size 1500,600
2+
set grid
3+
set xlabel "Number of readers"
4+
set ylabel "RPS per reader"
5+
set title "Per-reader throughput vs readers"
6+
plot "-" using 1:2 with linespoints title "mutex", "-" using 1:2 with linespoints title "hazptr"
7+
2 3331060.0
8+
3 2635270.0
9+
4 1797800.0
10+
5 1119500.0
11+
6 821863.0
12+
7 611059.0
13+
8 489462.0
14+
9 444871.0
15+
10 383017.0
16+
e
17+
2 23909700.0
18+
3 21496400.0
19+
4 16914200.0
20+
5 14569300.0
21+
6 13386300.0
22+
7 11263700.0
23+
8 9654680.0
24+
9 8796290.0
25+
10 7869610.0
26+
e
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# ./run_rps.rb 2 10 mutex --cmd=./build/mutex_bench rcu --cmd=./build/rcu_bench | tee mutex_rcu.plot
2+
set term pngcairo size 1500,600
3+
set grid
4+
set xlabel "Number of readers"
5+
set ylabel "RPS per reader"
6+
set title "Per-reader throughput vs readers"
7+
plot "-" using 1:2 with linespoints title "mutex", "-" using 1:2 with linespoints title "rcu"
8+
2 3441110.0
9+
3 2678150.0
10+
4 1771880.0
11+
5 1133900.0
12+
6 766760.0
13+
7 581535.0
14+
8 480965.0
15+
9 415878.0
16+
10 376899.0
17+
e
18+
2 7485560.0
19+
3 7451880.0
20+
4 5957440.0
21+
5 5175660.0
22+
6 4629030.0
23+
7 4006320.0
24+
8 3541040.0
25+
9 3165250.0
26+
10 2832050.0
27+
e
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# ./run_rps.rb 2 10 mutex --cmd=./build/mutex_bench rwlock --cmd=./build/rwlock_bench | tee mutex_rwlock.plot
2+
set term pngcairo size 1500,600
3+
set grid
4+
set xlabel "Number of readers"
5+
set ylabel "RPS per reader"
6+
set title "Per-reader throughput vs readers"
7+
plot "-" using 1:2 with linespoints title "mutex", "-" using 1:2 with linespoints title "rwlock"
8+
2 3546940.0
9+
3 2801840.0
10+
4 1748030.0
11+
5 1139530.0
12+
6 803807.0
13+
7 577255.0
14+
8 481988.0
15+
9 409973.0
16+
10 361575.0
17+
e
18+
2 4731710.0
19+
3 2775370.0
20+
4 2158470.0
21+
5 1993900.0
22+
6 1785410.0
23+
7 1568050.0
24+
8 1398410.0
25+
9 1264550.0
26+
10 1166940.0
27+
e
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# ./run_rps.rb 2 10 mutex --cmd=./build/mutex_bench sptr --cmd=./build/sptr_bench | tee mutex_sptr.plot
2+
set term pngcairo size 1500,600
3+
set grid
4+
set xlabel "Number of readers"
5+
set ylabel "RPS per reader"
6+
set title "Per-reader throughput vs readers"
7+
plot "-" using 1:2 with linespoints title "mutex", "-" using 1:2 with linespoints title "sptr"
8+
2 3372940.0
9+
3 2709470.0
10+
4 1801860.0
11+
5 1084450.0
12+
6 801619.0
13+
7 620865.0
14+
8 506515.0
15+
9 415279.0
16+
10 395591.0
17+
e
18+
2 4365330.0
19+
3 2638190.0
20+
4 1736430.0
21+
5 1236500.0
22+
6 886423.0
23+
7 675467.0
24+
8 317831.0
25+
9 277913.0
26+
10 256474.0
27+
e

0 commit comments

Comments
 (0)