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