xref: /aosp_15_r20/bionic/benchmarks/atomic_benchmark.cpp (revision 8d67ca893c1523eb926b9080dbe4e2ffd2a27ba1)
1*8d67ca89SAndroid Build Coastguard Worker /*
2*8d67ca89SAndroid Build Coastguard Worker  * Copyright (C) 2017 The Android Open Source Project
3*8d67ca89SAndroid Build Coastguard Worker  *
4*8d67ca89SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*8d67ca89SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*8d67ca89SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*8d67ca89SAndroid Build Coastguard Worker  *
8*8d67ca89SAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*8d67ca89SAndroid Build Coastguard Worker  *
10*8d67ca89SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*8d67ca89SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*8d67ca89SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*8d67ca89SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*8d67ca89SAndroid Build Coastguard Worker  * limitations under the License.
15*8d67ca89SAndroid Build Coastguard Worker  */
16*8d67ca89SAndroid Build Coastguard Worker 
17*8d67ca89SAndroid Build Coastguard Worker // Our goal is to measure the cost of various C++ atomic operations.
18*8d67ca89SAndroid Build Coastguard Worker // Android doesn't really control those. But since some of these operations can be quite
19*8d67ca89SAndroid Build Coastguard Worker // expensive, this may be useful input for development of higher level code.
20*8d67ca89SAndroid Build Coastguard Worker // Expected mappings from C++ atomics to hardware primitives can be found at
21*8d67ca89SAndroid Build Coastguard Worker // http://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html .
22*8d67ca89SAndroid Build Coastguard Worker 
23*8d67ca89SAndroid Build Coastguard Worker #include <atomic>
24*8d67ca89SAndroid Build Coastguard Worker #include <mutex>
25*8d67ca89SAndroid Build Coastguard Worker 
26*8d67ca89SAndroid Build Coastguard Worker #include <benchmark/benchmark.h>
27*8d67ca89SAndroid Build Coastguard Worker #include "util.h"
28*8d67ca89SAndroid Build Coastguard Worker 
29*8d67ca89SAndroid Build Coastguard Worker // We time atomic operations separated by a volatile (not atomic!) increment.  This ensures
30*8d67ca89SAndroid Build Coastguard Worker // that the compiler emits memory instructions (e.g. load or store) prior to any fence or the
31*8d67ca89SAndroid Build Coastguard Worker // like.  That in turn ensures that the CPU has outstanding memory operations when the fence
32*8d67ca89SAndroid Build Coastguard Worker // is executed.
33*8d67ca89SAndroid Build Coastguard Worker 
34*8d67ca89SAndroid Build Coastguard Worker // In most respects, we compute best case values. Since there is only one thread, there are no
35*8d67ca89SAndroid Build Coastguard Worker // coherence misses.
36*8d67ca89SAndroid Build Coastguard Worker 
37*8d67ca89SAndroid Build Coastguard Worker // We assume that the compiler is not smart enough to optimize away fences in a single-threaded
38*8d67ca89SAndroid Build Coastguard Worker // program. If that changes, we'll need to add a second thread.
39*8d67ca89SAndroid Build Coastguard Worker 
40*8d67ca89SAndroid Build Coastguard Worker // We increment the counter this way to avoid -Wdeprecated-volatile warnings.
41*8d67ca89SAndroid Build Coastguard Worker static volatile unsigned counter;
42*8d67ca89SAndroid Build Coastguard Worker #define INC_COUNTER() counter = counter + 1
43*8d67ca89SAndroid Build Coastguard Worker 
44*8d67ca89SAndroid Build Coastguard Worker std::atomic<int> test_loc(0);
45*8d67ca89SAndroid Build Coastguard Worker 
46*8d67ca89SAndroid Build Coastguard Worker static volatile unsigned sink;
47*8d67ca89SAndroid Build Coastguard Worker 
48*8d67ca89SAndroid Build Coastguard Worker static std::mutex mtx;
49*8d67ca89SAndroid Build Coastguard Worker 
BM_atomic_empty(benchmark::State & state)50*8d67ca89SAndroid Build Coastguard Worker void BM_atomic_empty(benchmark::State& state) {
51*8d67ca89SAndroid Build Coastguard Worker   while (state.KeepRunning()) {
52*8d67ca89SAndroid Build Coastguard Worker     INC_COUNTER();
53*8d67ca89SAndroid Build Coastguard Worker   }
54*8d67ca89SAndroid Build Coastguard Worker }
55*8d67ca89SAndroid Build Coastguard Worker BIONIC_BENCHMARK(BM_atomic_empty);
56*8d67ca89SAndroid Build Coastguard Worker 
BM_atomic_load_relaxed(benchmark::State & state)57*8d67ca89SAndroid Build Coastguard Worker static void BM_atomic_load_relaxed(benchmark::State& state) {
58*8d67ca89SAndroid Build Coastguard Worker   unsigned result = 0;
59*8d67ca89SAndroid Build Coastguard Worker   while (state.KeepRunning()) {
60*8d67ca89SAndroid Build Coastguard Worker     result += test_loc.load(std::memory_order_relaxed);
61*8d67ca89SAndroid Build Coastguard Worker     INC_COUNTER();
62*8d67ca89SAndroid Build Coastguard Worker   }
63*8d67ca89SAndroid Build Coastguard Worker   sink = result;
64*8d67ca89SAndroid Build Coastguard Worker }
65*8d67ca89SAndroid Build Coastguard Worker BIONIC_BENCHMARK(BM_atomic_load_relaxed);
66*8d67ca89SAndroid Build Coastguard Worker 
BM_atomic_load_acquire(benchmark::State & state)67*8d67ca89SAndroid Build Coastguard Worker static void BM_atomic_load_acquire(benchmark::State& state) {
68*8d67ca89SAndroid Build Coastguard Worker   unsigned result = 0;
69*8d67ca89SAndroid Build Coastguard Worker   while (state.KeepRunning()) {
70*8d67ca89SAndroid Build Coastguard Worker     result += test_loc.load(std::memory_order_acquire);
71*8d67ca89SAndroid Build Coastguard Worker     INC_COUNTER();
72*8d67ca89SAndroid Build Coastguard Worker   }
73*8d67ca89SAndroid Build Coastguard Worker   sink = result;
74*8d67ca89SAndroid Build Coastguard Worker }
75*8d67ca89SAndroid Build Coastguard Worker BIONIC_BENCHMARK(BM_atomic_load_acquire);
76*8d67ca89SAndroid Build Coastguard Worker 
BM_atomic_store_release(benchmark::State & state)77*8d67ca89SAndroid Build Coastguard Worker static void BM_atomic_store_release(benchmark::State& state) {
78*8d67ca89SAndroid Build Coastguard Worker   int i = counter;
79*8d67ca89SAndroid Build Coastguard Worker   while (state.KeepRunning()) {
80*8d67ca89SAndroid Build Coastguard Worker     test_loc.store(++i, std::memory_order_release);
81*8d67ca89SAndroid Build Coastguard Worker     INC_COUNTER();
82*8d67ca89SAndroid Build Coastguard Worker   }
83*8d67ca89SAndroid Build Coastguard Worker }
84*8d67ca89SAndroid Build Coastguard Worker BIONIC_BENCHMARK(BM_atomic_store_release);
85*8d67ca89SAndroid Build Coastguard Worker 
BM_atomic_store_seq_cst(benchmark::State & state)86*8d67ca89SAndroid Build Coastguard Worker static void BM_atomic_store_seq_cst(benchmark::State& state) {
87*8d67ca89SAndroid Build Coastguard Worker   int i = counter;
88*8d67ca89SAndroid Build Coastguard Worker   while (state.KeepRunning()) {
89*8d67ca89SAndroid Build Coastguard Worker     test_loc.store(++i, std::memory_order_seq_cst);
90*8d67ca89SAndroid Build Coastguard Worker     INC_COUNTER();
91*8d67ca89SAndroid Build Coastguard Worker   }
92*8d67ca89SAndroid Build Coastguard Worker }
93*8d67ca89SAndroid Build Coastguard Worker BIONIC_BENCHMARK(BM_atomic_store_seq_cst);
94*8d67ca89SAndroid Build Coastguard Worker 
BM_atomic_fetch_add_relaxed(benchmark::State & state)95*8d67ca89SAndroid Build Coastguard Worker static void BM_atomic_fetch_add_relaxed(benchmark::State& state) {
96*8d67ca89SAndroid Build Coastguard Worker   unsigned result = 0;
97*8d67ca89SAndroid Build Coastguard Worker   while (state.KeepRunning()) {
98*8d67ca89SAndroid Build Coastguard Worker     result += test_loc.fetch_add(1, std::memory_order_relaxed);
99*8d67ca89SAndroid Build Coastguard Worker     INC_COUNTER();
100*8d67ca89SAndroid Build Coastguard Worker   }
101*8d67ca89SAndroid Build Coastguard Worker   sink = result;
102*8d67ca89SAndroid Build Coastguard Worker }
103*8d67ca89SAndroid Build Coastguard Worker BIONIC_BENCHMARK(BM_atomic_fetch_add_relaxed);
104*8d67ca89SAndroid Build Coastguard Worker 
BM_atomic_fetch_add_seq_cst(benchmark::State & state)105*8d67ca89SAndroid Build Coastguard Worker static void BM_atomic_fetch_add_seq_cst(benchmark::State& state) {
106*8d67ca89SAndroid Build Coastguard Worker   unsigned result = 0;
107*8d67ca89SAndroid Build Coastguard Worker   while (state.KeepRunning()) {
108*8d67ca89SAndroid Build Coastguard Worker     result += test_loc.fetch_add(1, std::memory_order_seq_cst);
109*8d67ca89SAndroid Build Coastguard Worker     INC_COUNTER();
110*8d67ca89SAndroid Build Coastguard Worker   }
111*8d67ca89SAndroid Build Coastguard Worker   sink = result;
112*8d67ca89SAndroid Build Coastguard Worker }
113*8d67ca89SAndroid Build Coastguard Worker BIONIC_BENCHMARK(BM_atomic_fetch_add_seq_cst);
114*8d67ca89SAndroid Build Coastguard Worker 
115*8d67ca89SAndroid Build Coastguard Worker // The fence benchmarks include a relaxed load to make it much harder to optimize away
116*8d67ca89SAndroid Build Coastguard Worker // the fence.
117*8d67ca89SAndroid Build Coastguard Worker 
BM_atomic_acquire_fence(benchmark::State & state)118*8d67ca89SAndroid Build Coastguard Worker static void BM_atomic_acquire_fence(benchmark::State& state) {
119*8d67ca89SAndroid Build Coastguard Worker   unsigned result = 0;
120*8d67ca89SAndroid Build Coastguard Worker   while (state.KeepRunning()) {
121*8d67ca89SAndroid Build Coastguard Worker     result += test_loc.load(std::memory_order_relaxed);
122*8d67ca89SAndroid Build Coastguard Worker     std::atomic_thread_fence(std::memory_order_acquire);
123*8d67ca89SAndroid Build Coastguard Worker     INC_COUNTER();
124*8d67ca89SAndroid Build Coastguard Worker   }
125*8d67ca89SAndroid Build Coastguard Worker   sink = result;
126*8d67ca89SAndroid Build Coastguard Worker }
127*8d67ca89SAndroid Build Coastguard Worker BIONIC_BENCHMARK(BM_atomic_acquire_fence);
128*8d67ca89SAndroid Build Coastguard Worker 
BM_atomic_seq_cst_fence(benchmark::State & state)129*8d67ca89SAndroid Build Coastguard Worker static void BM_atomic_seq_cst_fence(benchmark::State& state) {
130*8d67ca89SAndroid Build Coastguard Worker   unsigned result = 0;
131*8d67ca89SAndroid Build Coastguard Worker   while (state.KeepRunning()) {
132*8d67ca89SAndroid Build Coastguard Worker     result += test_loc.load(std::memory_order_relaxed);
133*8d67ca89SAndroid Build Coastguard Worker     std::atomic_thread_fence(std::memory_order_seq_cst);
134*8d67ca89SAndroid Build Coastguard Worker     INC_COUNTER();
135*8d67ca89SAndroid Build Coastguard Worker   }
136*8d67ca89SAndroid Build Coastguard Worker   sink = result;
137*8d67ca89SAndroid Build Coastguard Worker }
138*8d67ca89SAndroid Build Coastguard Worker BIONIC_BENCHMARK(BM_atomic_seq_cst_fence);
139*8d67ca89SAndroid Build Coastguard Worker 
140*8d67ca89SAndroid Build Coastguard Worker // For comparison, also throw in a critical section version:
141*8d67ca89SAndroid Build Coastguard Worker 
BM_atomic_fetch_add_cs(benchmark::State & state)142*8d67ca89SAndroid Build Coastguard Worker static void BM_atomic_fetch_add_cs(benchmark::State& state) {
143*8d67ca89SAndroid Build Coastguard Worker   unsigned result = 0;
144*8d67ca89SAndroid Build Coastguard Worker   while (state.KeepRunning()) {
145*8d67ca89SAndroid Build Coastguard Worker     {
146*8d67ca89SAndroid Build Coastguard Worker       std::lock_guard<std::mutex> _(mtx);
147*8d67ca89SAndroid Build Coastguard Worker       INC_COUNTER();
148*8d67ca89SAndroid Build Coastguard Worker       result += counter;
149*8d67ca89SAndroid Build Coastguard Worker     }
150*8d67ca89SAndroid Build Coastguard Worker   }
151*8d67ca89SAndroid Build Coastguard Worker   sink = result;
152*8d67ca89SAndroid Build Coastguard Worker }
153*8d67ca89SAndroid Build Coastguard Worker BIONIC_BENCHMARK(BM_atomic_fetch_add_cs);
154