1 /*
2 * Copyright 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <audio_utils/mutex.h>
18
19 #include <android-base/logging.h>
20 #include <benchmark/benchmark.h>
21 #include <shared_mutex>
22 #include <thread>
23 #include <utils/RWLock.h>
24 #include <utils/Timers.h>
25
26 /*
27 On Pixel 7 U arm64-v8a
28
29 Note: to bump up the scheduler clock frequency, one can use the toybox uclampset:
30 $ adb shell uclampset -m 1024 /data/benchmarktest64/audio_mutex_benchmark/audio_mutex_benchmark
31
32 For simplicity these tests use the regular invocation:
33 $ atest audio_mutex_benchmark
34
35 Benchmark Time CPU Iteration
36 audio_mutex_benchmark:
37 #BM_atomic_add_equals<int32_t> 6.502025194439543 ns 6.47205015631869 ns 108145417
38 #BM_atomic_add_to_seq_cst<int16_t> 6.55807517340572 ns 6.526952655561198 ns 107217450
39 #BM_atomic_add_to_seq_cst<int32_t> 6.610803828172807 ns 6.58148248625125 ns 106355671
40 #BM_atomic_add_to_seq_cst<int64_t> 6.5568264443311595 ns 6.526632489003918 ns 107237292
41 #BM_atomic_add_to_seq_cst<float> 7.884542958080632 ns 7.8526649209116375 ns 89368018
42 #BM_atomic_add_to_seq_cst<double> 7.931010792308195 ns 7.893661616016361 ns 88487681
43 #BM_atomic_add_to_relaxed<int16_t> 5.167222836799001 ns 5.144664678496968 ns 136918225
44 #BM_atomic_add_to_relaxed<int32_t> 5.181042322951031 ns 5.15622768069756 ns 135684124
45 #BM_atomic_add_to_relaxed<int64_t> 5.16751983474899 ns 5.144558629227656 ns 138681351
46 #BM_atomic_add_to_relaxed<float> 7.7921119585599525 ns 7.741060701068997 ns 90441768
47 #BM_atomic_add_to_relaxed<double> 7.774451559752642 ns 7.737580743492468 ns 90244734
48 #BM_atomic_add_to_unordered<int16_t> 0.3535942390008131 ns 0.351996905 ns 1000000000
49 #BM_atomic_add_to_unordered<int32_t> 0.35363073799817357 ns 0.3519564250000009 ns 1000000000
50 #BM_atomic_add_to_unordered<int64_t> 0.35689860000275075 ns 0.35208711699999995 ns 1000000000
51 #BM_atomic_add_to_unordered<float> 0.7052556854655034 ns 0.7020281104213322 ns 997014156
52 #BM_atomic_add_to_unordered<double> 0.7050851735423606 ns 0.7020307730369924 ns 997136097
53 #BM_atomic_add_to_unordered<volatile_int16_t> 1.7630191837466263 ns 1.755060622823009 ns 398830899
54 #BM_atomic_add_to_unordered<volatile_int32_t> 1.7636458882248507 ns 1.7551169249266374 ns 398840618
55 #BM_atomic_add_to_unordered<volatile_int64_t> 1.762758401503814 ns 1.755028484468997 ns 398845420
56 #BM_atomic_add_to_unordered<volatile_float> 2.6616841096538084 ns 2.6491095463299206 ns 264227784
57 #BM_atomic_add_to_unordered<volatile_double> 2.659741383344485 ns 2.6476598391107227 ns 264613772
58 #BM_gettid 2.1159776035370936 ns 2.10614115284375 ns 332373750
59 #BM_systemTime 45.25256074688064 ns 45.040996499041846 ns 15560597
60 #BM_thread_8_variables 2.8218847925890063 ns 2.808269438152783 ns 249265931
61 #BM_thread_local_8_variables 2.8201526456300243 ns 2.808261059704455 ns 249274993
62 #BM_thread_detach_async 66004.9478946627 ns 36389.48824859354 ns 19019
63 #BM_thread_join_sync 226163.65853653837 ns 115567.64218708145 ns 11193
64 #BM_StdMutexLockUnlock 20.148828538610392 ns 20.05356471045582 ns 33531405
65 #BM_RWMutexReadLockUnlock 17.19880095571374 ns 17.12071584234365 ns 40958460
66 #BM_RWMutexWriteLockUnlock 19.816517996240673 ns 19.73653147918977 ns 35482330
67 #BM_SharedMutexReadLockUnlock 38.818405116741836 ns 38.64741636654499 ns 18109051
68 #BM_SharedMutexWriteLockUnlock 41.472496065276786 ns 41.27604196581944 ns 16957610
69 #BM_AudioUtilsMutexLockUnlock 31.710274563460512 ns 31.560500556055743 ns 22196515
70 #BM_AudioUtilsPIMutexLockUnlock 32.041340383783485 ns 31.906233969585948 ns 21938766
71 #BM_StdMutexInitializationLockUnlock 29.75497564252243 ns 29.621176282468877 ns 23639560
72 #BM_RWMutexInitializationReadLockUnlock 27.335134720127595 ns 27.211004896327836 ns 25715190
73 #BM_RWMutexInitializationWriteLockUnlock 30.29120801891917 ns 30.12894761884541 ns 23244772
74 #BM_SharedMutexInitializationReadLockUnlock 56.699768519426065 ns 56.433009417474274 ns 12406617
75 #BM_SharedMutexInitializationWriteLockUnlock 57.55622814765764 ns 57.303949058279805 ns 12223851
76 #BM_AudioUtilsMutexInitializationLockUnlock 43.02546297081612 ns 42.823799365616175 ns 16355083
77 #BM_AudioUtilsPIMutexInitializationLockUnlock 47.94813033517548 ns 47.722581549497065 ns 14667687
78 #BM_StdMutexBlockingConditionVariable/threads:2 26879.128794361706 ns 31117.70547138501 ns 37358
79 #BM_AudioUtilsMutexBlockingConditionVariable/threads:2 46786.98106298265 ns 51810.75161375347 ns 15182
80 #BM_AudioUtilsPIMutexBlockingConditionVariable/threads:2 48937.30165022134 ns 57147.57405000012 ns 20000
81 #BM_StdMutexScopedLockUnlock/threads:1 32.93981066359915 ns 32.76512553728174 ns 20414812
82 #BM_StdMutexScopedLockUnlock/threads:2 571.8094382500567 ns 1137.253613 ns 2000000
83 #BM_StdMutexScopedLockUnlock/threads:4 128.79479358731055 ns 404.5323853159404 ns 2347144
84 #BM_StdMutexScopedLockUnlock/threads:8 131.74327468115035 ns 517.3769108963708 ns 2534936
85 #BM_RWMutexScopedReadLockUnlock/threads:1 32.230652432400504 ns 32.08542964347514 ns 21723209
86 #BM_RWMutexScopedReadLockUnlock/threads:2 155.3515724990575 ns 307.43060200000036 ns 2000000
87 #BM_RWMutexScopedReadLockUnlock/threads:4 243.4595197344497 ns 961.0659840432863 ns 731980
88 #BM_RWMutexScopedReadLockUnlock/threads:8 253.54372642695628 ns 1952.4235494344487 ns 381920
89 #BM_RWMutexScopedWriteLockUnlock/threads:1 34.96165601863155 ns 34.794121014593 ns 20105476
90 #BM_RWMutexScopedWriteLockUnlock/threads:2 263.78236900200136 ns 514.6362324999996 ns 2000000
91 #BM_RWMutexScopedWriteLockUnlock/threads:4 527.032631925838 ns 1792.191760456376 ns 1008284
92 #BM_RWMutexScopedWriteLockUnlock/threads:8 483.5519374297695 ns 1863.0285003579795 ns 435784
93 #BM_SharedMutexScopedReadLockUnlock/threads:1 69.9000512364522 ns 69.56623691277518 ns 9561519
94 #BM_SharedMutexScopedReadLockUnlock/threads:2 351.76991433261804 ns 695.5628134433418 ns 1410542
95 #BM_SharedMutexScopedReadLockUnlock/threads:4 367.56704147658854 ns 1254.6058120544424 ns 771500
96 #BM_SharedMutexScopedReadLockUnlock/threads:8 378.72729057260494 ns 1560.9589011997377 ns 470768
97 #BM_SharedMutexScopedWriteLockUnlock/threads:1 71.71541684004359 ns 71.4271749213539 ns 8818020
98 #BM_SharedMutexScopedWriteLockUnlock/threads:2 277.63172079762904 ns 515.4668504883387 ns 1055008
99 #BM_SharedMutexScopedWriteLockUnlock/threads:4 2755.3780970431444 ns 6906.134785989446 ns 226344
100 #BM_SharedMutexScopedWriteLockUnlock/threads:8 3699.5667828023215 ns 12626.038175000023 ns 80000
101 #BM_AudioUtilsMutexScopedLockUnlock/threads:1 65.17313525653955 ns 64.8294116126777 ns 10795219
102 #BM_AudioUtilsMutexScopedLockUnlock/threads:2 446.8434749672579 ns 885.912395366048 ns 1181022
103 #BM_AudioUtilsMutexScopedLockUnlock/threads:4 333.62697061454804 ns 1070.9685904025976 ns 964928
104 #BM_AudioUtilsMutexScopedLockUnlock/threads:8 314.9844454113926 ns 1212.955095620149 ns 582304
105 #BM_AudioUtilsPIMutexScopedLockUnlock/threads:1 65.65269811876958 ns 65.34601386374699 ns 9144281
106 #BM_AudioUtilsPIMutexScopedLockUnlock/threads:2 5017.670372810213 ns 6042.387748137168 ns 2288452
107 #BM_AudioUtilsPIMutexScopedLockUnlock/threads:4 29737.776158677327 ns 42942.53487809585 ns 20672
108 #BM_AudioUtilsPIMutexScopedLockUnlock/threads:8 39162.52888815035 ns 49320.37778141362 ns 12224
109 #BM_StdMutexReverseScopedLockUnlock/threads:1 32.9542161313857 ns 32.80702810699402 ns 20720857
110 #BM_StdMutexReverseScopedLockUnlock/threads:2 60.71405982389486 ns 116.22175457775452 ns 5378730
111 #BM_StdMutexReverseScopedLockUnlock/threads:4 130.27275630605487 ns 426.97113497151395 ns 2300604
112 #BM_StdMutexReverseScopedLockUnlock/threads:8 146.68614021968642 ns 525.7952204915628 ns 1277872
113 #BM_AudioUtilsMutexReverseScopedLockUnlock/threads:1 64.96217937278401 ns 64.67648684147889 ns 9147495
114 #BM_AudioUtilsMutexReverseScopedLockUnlock/threads:2 315.65038050030125 ns 621.0967409999993 ns 2000000
115 #BM_AudioUtilsMutexReverseScopedLockUnlock/threads:4 248.41254045888044 ns 727.5389901889789 ns 1138312
116 #BM_AudioUtilsMutexReverseScopedLockUnlock/threads:8 327.52771167681186 ns 1223.1142029482817 ns 606184
117 #BM_AudioUtilsPIMutexReverseScopedLockUnlock/threads:1 65.70167857940193 ns 65.4193651777703 ns 9300777
118 #BM_AudioUtilsPIMutexReverseScopedLockUnlock/threads:2 2553.0187592521543 ns 3020.612901000003 ns 2000000
119 #BM_AudioUtilsPIMutexReverseScopedLockUnlock/threads:4 59069.56778749645 ns 74846.59661112927 ns 24728
120 #BM_AudioUtilsPIMutexReverseScopedLockUnlock/threads:8 28588.76596390947 ns 34629.68940916421 ns 19904
121 #BM_empty_while 0.3527790119987912 ns 0.3510390169999908 ns 1000000000
122
123 */
124
125 // ---
126
127 template<typename Integer>
BM_atomic_add_equals(benchmark::State & state)128 static void BM_atomic_add_equals(benchmark::State &state) {
129 Integer i = 10;
130 std::atomic<Integer> dst;
131 while (state.KeepRunning()) {
132 dst += i;
133 }
134 LOG(DEBUG) << __func__ << " " << dst.load();
135 }
136
137 BENCHMARK(BM_atomic_add_equals<int32_t>);
138
139 template <typename T>
BM_atomic_add_to(benchmark::State & state,std::memory_order order)140 static void BM_atomic_add_to(benchmark::State &state, std::memory_order order) {
141 int64_t i64 = 10;
142 std::atomic<T> dst;
143 while (state.KeepRunning()) {
144 android::audio_utils::atomic_add_to(dst, i64, order);
145 }
146 LOG(DEBUG) << __func__ << " " << dst.load();
147 }
148
149 // Avoid macro issues with the comma.
150 template <typename T>
BM_atomic_add_to_seq_cst(benchmark::State & state)151 static void BM_atomic_add_to_seq_cst(benchmark::State &state) {
152 BM_atomic_add_to<T>(state, std::memory_order_seq_cst);
153 }
154
155 BENCHMARK(BM_atomic_add_to_seq_cst<int16_t>);
156
157 BENCHMARK(BM_atomic_add_to_seq_cst<int32_t>);
158
159 BENCHMARK(BM_atomic_add_to_seq_cst<int64_t>);
160
161 BENCHMARK(BM_atomic_add_to_seq_cst<float>);
162
163 BENCHMARK(BM_atomic_add_to_seq_cst<double>);
164
165 template <typename T>
BM_atomic_add_to_relaxed(benchmark::State & state)166 static void BM_atomic_add_to_relaxed(benchmark::State &state) {
167 BM_atomic_add_to<T>(state, std::memory_order_relaxed);
168 }
169
170 BENCHMARK(BM_atomic_add_to_relaxed<int16_t>);
171
172 BENCHMARK(BM_atomic_add_to_relaxed<int32_t>);
173
174 BENCHMARK(BM_atomic_add_to_relaxed<int64_t>);
175
176 BENCHMARK(BM_atomic_add_to_relaxed<float>);
177
178 BENCHMARK(BM_atomic_add_to_relaxed<double>);
179
180 template <typename T>
BM_atomic_add_to_unordered(benchmark::State & state)181 static void BM_atomic_add_to_unordered(benchmark::State &state) {
182 int64_t i64 = 10;
183 android::audio_utils::unordered_atomic<T> dst;
184 while (state.KeepRunning()) {
185 android::audio_utils::atomic_add_to(dst, i64, std::memory_order_relaxed);
186 }
187 LOG(DEBUG) << __func__ << " " << dst.load();
188 }
189
190 BENCHMARK(BM_atomic_add_to_unordered<int16_t>);
191
192 BENCHMARK(BM_atomic_add_to_unordered<int32_t>);
193
194 BENCHMARK(BM_atomic_add_to_unordered<int64_t>);
195
196 BENCHMARK(BM_atomic_add_to_unordered<float>);
197
198 BENCHMARK(BM_atomic_add_to_unordered<double>);
199
200 // type aliases to allow for macro parsing.
201 using volatile_int16_t = volatile int16_t;
202 using volatile_int32_t = volatile int32_t;
203 using volatile_int64_t = volatile int64_t;
204 using volatile_float = volatile float;
205 using volatile_double = volatile double;
206
207 BENCHMARK(BM_atomic_add_to_unordered<volatile_int16_t>);
208
209 BENCHMARK(BM_atomic_add_to_unordered<volatile_int32_t>);
210
211 BENCHMARK(BM_atomic_add_to_unordered<volatile_int64_t>);
212
213 BENCHMARK(BM_atomic_add_to_unordered<volatile_float>);
214
215 BENCHMARK(BM_atomic_add_to_unordered<volatile_double>);
216
217 // Benchmark gettid(). The mutex class uses this to get the linux thread id.
BM_gettid(benchmark::State & state)218 static void BM_gettid(benchmark::State &state) {
219 int32_t value = 0;
220 while (state.KeepRunning()) {
221 value ^= android::audio_utils::gettid_wrapper(); // ensure the return value used.
222 }
223 ALOGD("%s: value:%d", __func__, value);
224 }
225
226 BENCHMARK(BM_gettid);
227
228 // Benchmark systemTime(). The mutex class uses this for timing.
BM_systemTime(benchmark::State & state)229 static void BM_systemTime(benchmark::State &state) {
230 int64_t value = 0;
231 while (state.KeepRunning()) {
232 value ^= systemTime();
233 }
234 ALOGD("%s: value:%lld", __func__, (long long)value);
235 }
236
237 BENCHMARK(BM_systemTime);
238
239 // Benchmark access to 8 thread local storage variables by compiler built_in __thread.
240 __thread volatile int tls_value1 = 1;
241 __thread volatile int tls_value2 = 2;
242 __thread volatile int tls_value3 = 3;
243 __thread volatile int tls_value4 = 4;
244 __thread volatile int tls_value5 = 5;
245 __thread volatile int tls_value6 = 6;
246 __thread volatile int tls_value7 = 7;
247 __thread volatile int tls_value8 = 8;
248
BM_thread_8_variables(benchmark::State & state)249 static void BM_thread_8_variables(benchmark::State &state) {
250 while (state.KeepRunning()) {
251 tls_value1 ^= tls_value1 ^ tls_value2 ^ tls_value3 ^ tls_value4 ^
252 tls_value5 ^ tls_value6 ^ tls_value7 ^ tls_value8;
253 }
254 ALOGD("%s: value:%d", __func__, tls_value1);
255 }
256
257 BENCHMARK(BM_thread_8_variables);
258
259 // Benchmark access to 8 thread local storage variables through the
260 // the C/C++ 11 standard thread_local.
261 thread_local volatile int tlsa_value1 = 1;
262 thread_local volatile int tlsa_value2 = 2;
263 thread_local volatile int tlsa_value3 = 3;
264 thread_local volatile int tlsa_value4 = 4;
265 thread_local volatile int tlsa_value5 = 5;
266 thread_local volatile int tlsa_value6 = 6;
267 thread_local volatile int tlsa_value7 = 7;
268 thread_local volatile int tlsa_value8 = 8;
269
BM_thread_local_8_variables(benchmark::State & state)270 static void BM_thread_local_8_variables(benchmark::State &state) {
271 while (state.KeepRunning()) {
272 tlsa_value1 ^= tlsa_value1 ^ tlsa_value2 ^ tlsa_value3 ^ tlsa_value4 ^
273 tlsa_value5 ^ tlsa_value6 ^ tlsa_value7 ^ tlsa_value8;
274 }
275 ALOGD("%s: value:%d", __func__, tlsa_value1);
276 }
277
278 BENCHMARK(BM_thread_local_8_variables);
279
BM_thread_detach_async(benchmark::State & state)280 static void BM_thread_detach_async(benchmark::State &state) {
281 while (state.KeepRunning()) {
282 std::thread([](){}).detach();
283 }
284 }
285
286 BENCHMARK(BM_thread_detach_async);
287
BM_thread_join_sync(benchmark::State & state)288 static void BM_thread_join_sync(benchmark::State &state) {
289 while (state.KeepRunning()) {
290 std::thread([](){}).join();
291 }
292 }
293
294 BENCHMARK(BM_thread_join_sync);
295
296 // ---
297
298 // std::mutex is the reference mutex that we compare against.
299 // It is based on Bionic pthread_mutex* support.
300
301 // RWLock is a specialized Android mutex class based on
302 // Bionic pthread_rwlock* which in turn is based on the
303 // original ART shared reader mutex.
304
305 // Test shared read lock performance.
306 class RWReadMutex : private android::RWLock {
307 public:
lock()308 void lock() { readLock(); }
try_lock()309 bool try_lock() { return tryReadLock() == 0; }
310 using android::RWLock::unlock;
311 };
312
313 // Test exclusive write lock performance.
314 class RWWriteMutex : private android::RWLock {
315 public:
lock()316 void lock() { writeLock(); }
try_lock()317 bool try_lock() { return tryWriteLock() == 0; }
318 using android::RWLock::unlock;
319 };
320
321 // std::shared_mutex lock/unlock behavior is default exclusive.
322 // We subclass to create the shared reader equivalent.
323 //
324 // Unfortunately std::shared_mutex implementation can contend on an internal
325 // mutex with multiple readers (even with no writers), resulting in worse lock performance
326 // than other shared mutexes.
327 // This is due to the portability desire in the original reference implementation:
328 // https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2406.html#shared_mutex_imp
329 class SharedReadMutex : private std::shared_mutex {
330 public:
lock()331 void lock() { std::shared_mutex::lock_shared(); }
try_lock()332 bool try_lock() { return std::shared_mutex::try_lock_shared(); }
unlock()333 void unlock() { std::shared_mutex::unlock_shared(); }
334 };
335
336 // audio_utils mutex is designed to have mutex order checking, statistics,
337 // deadlock detection, and priority inheritance capabilities,
338 // so it is higher overhead than just the std::mutex that it is based upon.
339 //
340 // audio_utils mutex without priority inheritance.
341 class AudioMutex : public android::audio_utils::mutex {
342 public:
AudioMutex()343 AudioMutex() : android::audio_utils::mutex(false /* priority_inheritance */) {}
344 };
345
346 // audio_utils mutex with priority inheritance.
347 class AudioPIMutex : public android::audio_utils::mutex {
348 public:
AudioPIMutex()349 AudioPIMutex() : android::audio_utils::mutex(true /* priority_inheritance */) {}
350 };
351
352 template <typename Mutex>
MutexLockUnlock(benchmark::State & state)353 void MutexLockUnlock(benchmark::State& state) {
354 Mutex m;
355 while (state.KeepRunning()) {
356 m.lock();
357 m.unlock();
358 }
359 }
360
BM_StdMutexLockUnlock(benchmark::State & state)361 static void BM_StdMutexLockUnlock(benchmark::State &state) {
362 MutexLockUnlock<std::mutex>(state);
363 }
364
365 // Benchmark repeated mutex lock/unlock from a single thread
366 // using the mutex.
367 BENCHMARK(BM_StdMutexLockUnlock);
368
BM_RWMutexReadLockUnlock(benchmark::State & state)369 static void BM_RWMutexReadLockUnlock(benchmark::State& state) {
370 MutexLockUnlock<RWReadMutex>(state);
371 }
372
373 BENCHMARK(BM_RWMutexReadLockUnlock);
374
BM_RWMutexWriteLockUnlock(benchmark::State & state)375 static void BM_RWMutexWriteLockUnlock(benchmark::State& state) {
376 MutexLockUnlock<RWWriteMutex>(state);
377 }
378
379 BENCHMARK(BM_RWMutexWriteLockUnlock);
380
BM_SharedMutexReadLockUnlock(benchmark::State & state)381 static void BM_SharedMutexReadLockUnlock(benchmark::State& state) {
382 MutexLockUnlock<SharedReadMutex>(state);
383 }
384
385 BENCHMARK(BM_SharedMutexReadLockUnlock);
386
BM_SharedMutexWriteLockUnlock(benchmark::State & state)387 static void BM_SharedMutexWriteLockUnlock(benchmark::State& state) {
388 MutexLockUnlock<std::shared_mutex>(state);
389 }
390
391 BENCHMARK(BM_SharedMutexWriteLockUnlock);
392
BM_AudioUtilsMutexLockUnlock(benchmark::State & state)393 static void BM_AudioUtilsMutexLockUnlock(benchmark::State &state) {
394 MutexLockUnlock<AudioMutex>(state);
395 }
396
397 BENCHMARK(BM_AudioUtilsMutexLockUnlock);
398
BM_AudioUtilsPIMutexLockUnlock(benchmark::State & state)399 static void BM_AudioUtilsPIMutexLockUnlock(benchmark::State &state) {
400 MutexLockUnlock<AudioPIMutex>(state);
401 }
402
403 BENCHMARK(BM_AudioUtilsPIMutexLockUnlock);
404
405 // ---
406
407 template <typename Mutex>
MutexInitializationLockUnlock(benchmark::State & state)408 void MutexInitializationLockUnlock(benchmark::State& state) {
409 while (state.KeepRunning()) {
410 Mutex m;
411 m.lock();
412 m.unlock();
413 }
414 }
415
BM_StdMutexInitializationLockUnlock(benchmark::State & state)416 static void BM_StdMutexInitializationLockUnlock(benchmark::State &state) {
417 MutexInitializationLockUnlock<std::mutex>(state);
418 }
419
420 // Benchmark repeated mutex creation then lock/unlock from a single thread
421 // using the mutex.
422 BENCHMARK(BM_StdMutexInitializationLockUnlock);
423
BM_RWMutexInitializationReadLockUnlock(benchmark::State & state)424 static void BM_RWMutexInitializationReadLockUnlock(benchmark::State& state) {
425 MutexInitializationLockUnlock<RWReadMutex>(state);
426 }
427
428 BENCHMARK(BM_RWMutexInitializationReadLockUnlock);
429
BM_RWMutexInitializationWriteLockUnlock(benchmark::State & state)430 static void BM_RWMutexInitializationWriteLockUnlock(benchmark::State& state) {
431 MutexInitializationLockUnlock<RWWriteMutex>(state);
432 }
433
434 BENCHMARK(BM_RWMutexInitializationWriteLockUnlock);
435
BM_SharedMutexInitializationReadLockUnlock(benchmark::State & state)436 static void BM_SharedMutexInitializationReadLockUnlock(benchmark::State& state) {
437 MutexInitializationLockUnlock<SharedReadMutex>(state);
438 }
439
440 BENCHMARK(BM_SharedMutexInitializationReadLockUnlock);
441
BM_SharedMutexInitializationWriteLockUnlock(benchmark::State & state)442 static void BM_SharedMutexInitializationWriteLockUnlock(benchmark::State& state) {
443 MutexInitializationLockUnlock<std::shared_mutex>(state);
444 }
445
446 BENCHMARK(BM_SharedMutexInitializationWriteLockUnlock);
447
BM_AudioUtilsMutexInitializationLockUnlock(benchmark::State & state)448 static void BM_AudioUtilsMutexInitializationLockUnlock(benchmark::State &state) {
449 MutexInitializationLockUnlock<AudioMutex>(state);
450 }
451
452 BENCHMARK(BM_AudioUtilsMutexInitializationLockUnlock);
453
BM_AudioUtilsPIMutexInitializationLockUnlock(benchmark::State & state)454 static void BM_AudioUtilsPIMutexInitializationLockUnlock(benchmark::State &state) {
455 MutexInitializationLockUnlock<AudioPIMutex>(state);
456 }
457
458 BENCHMARK(BM_AudioUtilsPIMutexInitializationLockUnlock);
459
460 // ---
461
462 static constexpr size_t THREADS = 2;
463
464 template <typename Mutex, typename UniqueLock, typename ConditionVariable>
465 class MutexBlockingConditionVariable {
466 Mutex m;
467 ConditionVariable cv[THREADS];
468 bool wake[THREADS];
469
470 public:
run(benchmark::State & state)471 void run(benchmark::State& state) {
472 const size_t local = state.thread_index();
473 const size_t remote = (local + 1) % THREADS;
474 if (local == 0) wake[local] = true;
475 for (auto _ : state) {
476 UniqueLock ul(m);
477 cv[local].wait(ul, [&]{ return wake[local]; });
478 wake[remote] = true;
479 wake[local] = false;
480 cv[remote].notify_one();
481 }
482 UniqueLock ul(m);
483 wake[remote] = true;
484 cv[remote].notify_one();
485 }
486 };
487
488 MutexBlockingConditionVariable<std::mutex,
489 std::unique_lock<std::mutex>,
490 std::condition_variable> CvStd;
491
BM_StdMutexBlockingConditionVariable(benchmark::State & state)492 static void BM_StdMutexBlockingConditionVariable(benchmark::State &state) {
493 CvStd.run(state);
494 }
495
496 // Benchmark 2 threads that use condition variables to wake each other up,
497 // where only one thread is active at a given time. This benchmark
498 // uses mutex, unique_lock, and condition_variable.
499 BENCHMARK(BM_StdMutexBlockingConditionVariable)->Threads(THREADS);
500
501 MutexBlockingConditionVariable<AudioMutex,
502 android::audio_utils::unique_lock<AudioMutex>,
503 android::audio_utils::condition_variable> CvAu;
504
BM_AudioUtilsMutexBlockingConditionVariable(benchmark::State & state)505 static void BM_AudioUtilsMutexBlockingConditionVariable(benchmark::State &state) {
506 CvAu.run(state);
507 }
508
509 BENCHMARK(BM_AudioUtilsMutexBlockingConditionVariable)->Threads(THREADS);
510
511 MutexBlockingConditionVariable<AudioPIMutex,
512 android::audio_utils::unique_lock<AudioPIMutex>,
513 android::audio_utils::condition_variable> CvAuPI;
514
BM_AudioUtilsPIMutexBlockingConditionVariable(benchmark::State & state)515 static void BM_AudioUtilsPIMutexBlockingConditionVariable(benchmark::State &state) {
516 CvAuPI.run(state);
517 }
518
519 BENCHMARK(BM_AudioUtilsPIMutexBlockingConditionVariable)->Threads(THREADS);
520
521 // ---
522
523 // Benchmark scoped_lock where two threads try to obtain the
524 // same 2 locks with the same initial acquisition order.
525 // This uses std::scoped_lock.
526
527 static constexpr size_t THREADS_SCOPED = 8;
528
529 template <typename Mutex, typename ScopedLock>
530 class MutexScopedLockUnlock {
531 const bool reverse_;
532 Mutex m1_, m2_;
533 int counter_ = 0;
534
535 public:
MutexScopedLockUnlock(bool reverse=false)536 MutexScopedLockUnlock(bool reverse = false) : reverse_(reverse) {}
537
run(benchmark::State & state)538 void run(benchmark::State& state) {
539 const size_t index = state.thread_index();
540 for (auto _ : state) {
541 if (!reverse_ || index & 1) {
542 ScopedLock s1(m1_, m2_);
543 ++counter_;
544 } else {
545 ScopedLock s1(m2_, m1_);
546 ++counter_;
547 }
548 }
549 }
550 };
551
552 MutexScopedLockUnlock<std::mutex,
553 std::scoped_lock<std::mutex, std::mutex>> ScopedStd;
554
BM_StdMutexScopedLockUnlock(benchmark::State & state)555 static void BM_StdMutexScopedLockUnlock(benchmark::State &state) {
556 ScopedStd.run(state);
557 }
558
559 BENCHMARK(BM_StdMutexScopedLockUnlock)->ThreadRange(1, THREADS_SCOPED);
560
561 MutexScopedLockUnlock<RWReadMutex,
562 std::scoped_lock<RWReadMutex, RWReadMutex>> ScopedRwRead;
563
BM_RWMutexScopedReadLockUnlock(benchmark::State & state)564 static void BM_RWMutexScopedReadLockUnlock(benchmark::State &state) {
565 ScopedRwRead.run(state);
566 }
567
568 BENCHMARK(BM_RWMutexScopedReadLockUnlock)->ThreadRange(1, THREADS_SCOPED);
569
570 MutexScopedLockUnlock<RWWriteMutex,
571 std::scoped_lock<RWWriteMutex, RWWriteMutex>> ScopedRwWrite;
572
BM_RWMutexScopedWriteLockUnlock(benchmark::State & state)573 static void BM_RWMutexScopedWriteLockUnlock(benchmark::State &state) {
574 ScopedRwWrite.run(state);
575 }
576
577 BENCHMARK(BM_RWMutexScopedWriteLockUnlock)->ThreadRange(1, THREADS_SCOPED);
578
579 MutexScopedLockUnlock<SharedReadMutex,
580 std::scoped_lock<SharedReadMutex, SharedReadMutex>> ScopedSharedRead;
581
BM_SharedMutexScopedReadLockUnlock(benchmark::State & state)582 static void BM_SharedMutexScopedReadLockUnlock(benchmark::State &state) {
583 ScopedSharedRead.run(state);
584 }
585
586 BENCHMARK(BM_SharedMutexScopedReadLockUnlock)->ThreadRange(1, THREADS_SCOPED);
587
588 MutexScopedLockUnlock<std::shared_mutex,
589 std::scoped_lock<std::shared_mutex, std::shared_mutex>> ScopedSharedWrite;
590
BM_SharedMutexScopedWriteLockUnlock(benchmark::State & state)591 static void BM_SharedMutexScopedWriteLockUnlock(benchmark::State &state) {
592 ScopedSharedWrite.run(state);
593 }
594
595 BENCHMARK(BM_SharedMutexScopedWriteLockUnlock)->ThreadRange(1, THREADS_SCOPED);
596
597 MutexScopedLockUnlock<AudioMutex,
598 android::audio_utils::scoped_lock<
599 AudioMutex, AudioMutex>> ScopedAu;
600
BM_AudioUtilsMutexScopedLockUnlock(benchmark::State & state)601 static void BM_AudioUtilsMutexScopedLockUnlock(benchmark::State &state) {
602 ScopedAu.run(state);
603 }
604
605 BENCHMARK(BM_AudioUtilsMutexScopedLockUnlock)->ThreadRange(1, THREADS_SCOPED);
606
607 MutexScopedLockUnlock<AudioPIMutex,
608 android::audio_utils::scoped_lock<
609 AudioPIMutex, AudioPIMutex>> ScopedAuPI;
610
BM_AudioUtilsPIMutexScopedLockUnlock(benchmark::State & state)611 static void BM_AudioUtilsPIMutexScopedLockUnlock(benchmark::State &state) {
612 ScopedAuPI.run(state);
613 }
614
615 BENCHMARK(BM_AudioUtilsPIMutexScopedLockUnlock)->ThreadRange(1, THREADS_SCOPED);
616
617 MutexScopedLockUnlock<std::mutex,
618 std::scoped_lock<std::mutex, std::mutex>> ReverseScopedStd(true);
619
BM_StdMutexReverseScopedLockUnlock(benchmark::State & state)620 static void BM_StdMutexReverseScopedLockUnlock(benchmark::State &state) {
621 ReverseScopedStd.run(state);
622 }
623
624 // Benchmark scoped_lock with odd thread having reversed scoped_lock mutex acquisition order.
625 // This uses std::scoped_lock.
626 BENCHMARK(BM_StdMutexReverseScopedLockUnlock)->ThreadRange(1, THREADS_SCOPED);
627
628 MutexScopedLockUnlock<AudioMutex,
629 android::audio_utils::scoped_lock<
630 AudioMutex, AudioMutex>> ReverseScopedAu(true);
631
BM_AudioUtilsMutexReverseScopedLockUnlock(benchmark::State & state)632 static void BM_AudioUtilsMutexReverseScopedLockUnlock(benchmark::State &state) {
633 ReverseScopedAu.run(state);
634 }
635
636 BENCHMARK(BM_AudioUtilsMutexReverseScopedLockUnlock)->ThreadRange(1, THREADS_SCOPED);
637
638 MutexScopedLockUnlock<AudioPIMutex,
639 android::audio_utils::scoped_lock<
640 AudioPIMutex, AudioPIMutex>> ReverseScopedAuPI(true);
641
BM_AudioUtilsPIMutexReverseScopedLockUnlock(benchmark::State & state)642 static void BM_AudioUtilsPIMutexReverseScopedLockUnlock(benchmark::State &state) {
643 ReverseScopedAuPI.run(state);
644 }
645
646 BENCHMARK(BM_AudioUtilsPIMutexReverseScopedLockUnlock)->ThreadRange(1, THREADS_SCOPED);
647
BM_empty_while(benchmark::State & state)648 static void BM_empty_while(benchmark::State &state) {
649
650 while (state.KeepRunning()) {
651 ;
652 }
653 ALOGD("%s", android::audio_utils::mutex::all_stats_to_string().c_str());
654 }
655
656 // Benchmark to see the cost of doing nothing.
657 BENCHMARK(BM_empty_while);
658
659 BENCHMARK_MAIN();
660