xref: /aosp_15_r20/system/media/audio_utils/benchmarks/audio_mutex_benchmark.cpp (revision b9df5ad1c9ac98a7fefaac271a55f7ae3db05414)
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