diff options
author | Andy Hung <hunga@google.com> | 2024-03-12 19:10:28 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2024-03-12 19:10:28 +0000 |
commit | 8f6208583277d35a6870db3266f70cbae9815010 (patch) | |
tree | 9011977e14675324f9e1695e551613d21c478868 | |
parent | 6b25d8a969826b9e95630b1ad7e3e85c537959b5 (diff) | |
parent | 4f1e68ef9cc743b54e19751d262e1270ab15df2e (diff) | |
download | media-8f6208583277d35a6870db3266f70cbae9815010.tar.gz |
Merge "audio mutex: Add additional tests and benchmarks" into main
-rw-r--r-- | audio_utils/benchmarks/audio_mutex_benchmark.cpp | 403 | ||||
-rw-r--r-- | audio_utils/include/audio_utils/mutex.h | 25 | ||||
-rw-r--r-- | audio_utils/tests/audio_mutex_tests.cpp | 43 |
3 files changed, 380 insertions, 91 deletions
diff --git a/audio_utils/benchmarks/audio_mutex_benchmark.cpp b/audio_utils/benchmarks/audio_mutex_benchmark.cpp index 0280706b..a58aec31 100644 --- a/audio_utils/benchmarks/audio_mutex_benchmark.cpp +++ b/audio_utils/benchmarks/audio_mutex_benchmark.cpp @@ -16,8 +16,11 @@ #include <audio_utils/mutex.h> +#include <android-base/logging.h> #include <benchmark/benchmark.h> +#include <shared_mutex> #include <thread> +#include <utils/RWLock.h> #include <utils/Timers.h> /* @@ -29,48 +32,139 @@ $ adb shell uclampset -m 1024 /data/benchmarktest64/audio_mutex_benchmark/audio_ For simplicity these tests use the regular invocation: $ atest audio_mutex_benchmark -PI disabled Benchmark Time CPU Iteration audio_mutex_benchmark: - #BM_gettid 2.114707270012698 ns 2.1060503125712704 ns 316574041 - #BM_systemTime 45.21805864916908 ns 45.026921817247256 ns 15550399 - #BM_thread_8_variables 2.8218655987348464 ns 2.8084059508955304 ns 249245377 - #BM_thread_local_8_variables 2.8193214063325636 ns 2.808094924629991 ns 249270437 - #BM_StdMutexLockUnlock 18.284456262132316 ns 18.198265636345102 ns 38452720 - #BM_AudioUtilsMutexLockUnlock 65.64444199789075 ns 65.27982033200155 ns 10727119 - #BM_StdMutexInitializationLockUnlock 27.511005854186497 ns 27.400197055351832 ns 25576570 - #BM_AudioUtilsMutexInitializationLockUnlock 80.13035874526041 ns 79.7832739530702 ns 8771433 - #BM_StdMutexBlockingConditionVariable/threads:2 20162.129644197645 ns 23610.966860300927 ns 49578 - #BM_AudioUtilsMutexBlockingConditionVariable/threads:2 15137.825614929703 ns 17290.483328991908 ns 53746 - #BM_StdMutexScopedLockUnlock/threads:2 237.3259685000022 ns 471.24025449999993 ns 2000000 - #BM_AudioUtilsMutexScopedLockUnlock/threads:2 1166.2574495961544 ns 2286.2857883742913 ns 696078 - #BM_StdMutexReverseScopedLockUnlock/threads:2 78.13460285213144 ns 147.4075991604524 ns 4367114 - #BM_AudioUtilsMutexReverseScopedLockUnlock/threads:2 652.4407601321186 ns 1260.665055303518 ns 729610 - #BM_empty_while 0.3525216479999926 ns 0.35102758499999887 ns 1000000000 - -PI enabled -Benchmark Time CPU Iteration -audio_mutex_benchmark: - #BM_gettid 2.1172475906830703 ns 2.1060892051984452 ns 320818635 - #BM_systemTime 45.199132456479795 ns 45.00638566260978 ns 15548269 - #BM_thread_8_variables 2.8225443366713554 ns 2.808487087931257 ns 249225013 - #BM_thread_local_8_variables 2.821986069144024 ns 2.8082593311342396 ns 249257908 - #BM_StdMutexLockUnlock 18.40590257629023 ns 18.181680287238002 ns 38568989 - #BM_AudioUtilsMutexLockUnlock 67.93231825441279 ns 67.0807303508343 ns 10434985 - #BM_StdMutexInitializationLockUnlock 27.68553624974349 ns 27.411678887121656 ns 25531919 - #BM_AudioUtilsMutexInitializationLockUnlock 87.67312115717117 ns 86.83834669971819 ns 8058524 - #BM_StdMutexBlockingConditionVariable/threads:2 17272.46593515828 ns 19906.88809450987 ns 78468 - #BM_AudioUtilsMutexBlockingConditionVariable/threads:2 32005.869201264206 ns 37545.03395758123 ns 44320 - #BM_StdMutexScopedLockUnlock/threads:2 486.46992430399723 ns 956.4308063689062 ns 1080374 - #BM_AudioUtilsMutexScopedLockUnlock/threads:2 2054.297687080403 ns 2381.4833355667856 ns 462662 - #BM_StdMutexReverseScopedLockUnlock/threads:2 82.48454541925697 ns 156.5002537866742 ns 3502942 - #BM_AudioUtilsMutexReverseScopedLockUnlock/threads:2 7202.760827499759 ns 8601.367344999997 ns 200000 - #BM_empty_while 0.3630271000000107 ns 0.3611862729999995 ns 1000000000 + #BM_atomic_add_equals<int32_t> 6.508700118072382 ns 6.471633177192451 ns 108110486 + #BM_atomic_add_to_seq_cst<int16_t> 6.557658152513349 ns 6.526665108542128 ns 107252873 + #BM_atomic_add_to_seq_cst<int32_t> 6.61304199453549 ns 6.58175539524565 ns 106351923 + #BM_atomic_add_to_seq_cst<int64_t> 6.557521711571485 ns 6.5265363568644625 ns 107250668 + #BM_atomic_add_to_seq_cst<float> 7.895243222524512 ns 7.858297243207844 ns 89394951 + #BM_atomic_add_to_seq_cst<double> 7.931688495474578 ns 7.893971885098797 ns 88653486 + #BM_atomic_add_to_relaxed<int16_t> 5.140386288993005 ns 5.116383769230237 ns 135131188 + #BM_atomic_add_to_relaxed<int32_t> 5.181670175781189 ns 5.157418005923224 ns 135724804 + #BM_atomic_add_to_relaxed<int64_t> 5.161260548149761 ns 5.136776648952849 ns 135135216 + #BM_atomic_add_to_relaxed<float> 7.786417198158838 ns 7.749791796134465 ns 90646732 + #BM_atomic_add_to_relaxed<double> 7.760358404716961 ns 7.723992286938152 ns 90644677 + #BM_gettid 2.116039491081284 ns 2.106033253650779 ns 332358395 + #BM_systemTime 43.074033150581585 ns 42.8699911242381 ns 16328739 + #BM_thread_8_variables 2.8214796173366734 ns 2.8081271094521703 ns 249273547 + #BM_thread_local_8_variables 2.819987500327649 ns 2.808149311074747 ns 249278495 + #BM_StdMutexLockUnlock 18.155770972784783 ns 18.070903999828232 ns 38747264 + #BM_RWMutexReadLockUnlock 16.12456214871892 ns 16.04901684644192 ns 43612414 + #BM_RWMutexWriteLockUnlock 19.14824893658628 ns 19.05893391346091 ns 36725255 + #BM_SharedMutexReadLockUnlock 39.54155074347332 ns 39.35497456828369 ns 17788418 + #BM_SharedMutexWriteLockUnlock 41.58785205766037 ns 41.39323040198865 ns 16911078 + #BM_AudioUtilsMutexLockUnlock 66.56918230215399 ns 66.25544975244046 ns 10562911 + #BM_AudioUtilsPIMutexLockUnlock 67.02589961630612 ns 66.70819768056897 ns 10493090 + #BM_StdMutexInitializationLockUnlock 29.544903877103074 ns 29.406544528057406 ns 23801319 + #BM_RWMutexInitializationReadLockUnlock 26.91749522594829 ns 26.802654591541785 ns 26123567 + #BM_RWMutexInitializationWriteLockUnlock 30.20599678894913 ns 30.06422812747118 ns 23284596 + #BM_SharedMutexInitializationReadLockUnlock 58.070478136125395 ns 57.79511704041489 ns 12111671 + #BM_SharedMutexInitializationWriteLockUnlock 59.36722820827075 ns 59.08875400469678 ns 11843905 + #BM_AudioUtilsMutexInitializationLockUnlock 85.04952357479699 ns 84.65093492146583 ns 8269839 + #BM_AudioUtilsPIMutexInitializationLockUnlock 83.32953114993384 ns 82.9411400506946 ns 8440765 + #BM_StdMutexBlockingConditionVariable/threads:2 20067.186478012434 ns 25402.779402102544 ns 54792 + #BM_AudioUtilsMutexBlockingConditionVariable/threads:2 48417.40553370931 ns 58220.13591731267 ns 23220 + #BM_AudioUtilsPIMutexBlockingConditionVariable/threads:2 48724.90563264992 ns 59858.82489342454 ns 15482 + #BM_StdMutexScopedLockUnlock/threads:1 33.58821991644139 ns 33.41913176098606 ns 16058919 + #BM_StdMutexScopedLockUnlock/threads:2 356.67886764843007 ns 707.8318856903202 ns 4625680 + #BM_StdMutexScopedLockUnlock/threads:4 130.45108549886208 ns 447.1268742499998 ns 4000000 + #BM_StdMutexScopedLockUnlock/threads:8 139.0823761208755 ns 541.9088026721488 ns 1362200 + #BM_RWMutexScopedReadLockUnlock/threads:1 32.33613871803748 ns 32.194204614295046 ns 21710272 + #BM_RWMutexScopedReadLockUnlock/threads:2 160.47792160732033 ns 319.3012639397403 ns 2095986 + #BM_RWMutexScopedReadLockUnlock/threads:4 217.21087383931467 ns 861.2673855686197 ns 839892 + #BM_RWMutexScopedReadLockUnlock/threads:8 232.19586516883186 ns 1831.4409709220026 ns 491368 + #BM_RWMutexScopedWriteLockUnlock/threads:1 33.49908180449042 ns 33.34195684310611 ns 21010780 + #BM_RWMutexScopedWriteLockUnlock/threads:2 286.096410842338 ns 564.599202114389 ns 2485068 + #BM_RWMutexScopedWriteLockUnlock/threads:4 451.7913123512162 ns 1601.6332793492106 ns 1931432 + #BM_RWMutexScopedWriteLockUnlock/threads:8 417.50240217790537 ns 1678.8585405353656 ns 794072 + #BM_SharedMutexScopedReadLockUnlock/threads:1 67.65354544884363 ns 67.37498338520537 ns 9133426 + #BM_SharedMutexScopedReadLockUnlock/threads:2 370.22816132765433 ns 735.4710534035784 ns 1322608 + #BM_SharedMutexScopedReadLockUnlock/threads:4 298.7991937078523 ns 1015.8674764877635 ns 991824 + #BM_SharedMutexScopedReadLockUnlock/threads:8 359.17200914091643 ns 1500.1318202480697 ns 615960 + #BM_SharedMutexScopedWriteLockUnlock/threads:1 73.40224842642553 ns 73.06218848168656 ns 8616869 + #BM_SharedMutexScopedWriteLockUnlock/threads:2 502.8427941278981 ns 909.1756670594543 ns 599122 + #BM_SharedMutexScopedWriteLockUnlock/threads:4 2322.7325028106275 ns 6083.585590040707 ns 313436 + #BM_SharedMutexScopedWriteLockUnlock/threads:8 4948.555700826256 ns 15412.772486815033 ns 373152 + #BM_AudioUtilsMutexScopedLockUnlock/threads:1 147.60580533538862 ns 146.97151308638587 ns 4062848 + #BM_AudioUtilsMutexScopedLockUnlock/threads:2 5409.319112352385 ns 10729.084861761592 ns 728090 + #BM_AudioUtilsMutexScopedLockUnlock/threads:4 630.9403610213494 ns 1866.9171243841429 ns 579688 + #BM_AudioUtilsMutexScopedLockUnlock/threads:8 612.9153996947896 ns 2167.0654441098654 ns 417104 + #BM_AudioUtilsPIMutexScopedLockUnlock/threads:1 148.94249680999073 ns 148.3061023465011 ns 4387722 + #BM_AudioUtilsPIMutexScopedLockUnlock/threads:2 3537.898640072271 ns 4287.604650248743 ns 356196 + #BM_AudioUtilsPIMutexScopedLockUnlock/threads:4 13969.834843789307 ns 19572.29615170118 ns 28688 + #BM_AudioUtilsPIMutexScopedLockUnlock/threads:8 30652.264078729862 ns 40000.50360617244 ns 23848 + #BM_StdMutexReverseScopedLockUnlock/threads:1 31.34740304135938 ns 31.200396418488175 ns 21854682 + #BM_StdMutexReverseScopedLockUnlock/threads:2 54.06016658620641 ns 103.2554157873692 ns 5317694 + #BM_StdMutexReverseScopedLockUnlock/threads:4 169.8661622311813 ns 592.4042833246494 ns 3209096 + #BM_StdMutexReverseScopedLockUnlock/threads:8 156.65913206788008 ns 604.623918327717 ns 1742672 + #BM_AudioUtilsMutexReverseScopedLockUnlock/threads:1 147.51456839840807 ns 146.73295356311675 ns 4395816 + #BM_AudioUtilsMutexReverseScopedLockUnlock/threads:2 2425.8992549948744 ns 4812.346055000001 ns 200000 + #BM_AudioUtilsMutexReverseScopedLockUnlock/threads:4 453.8639331349259 ns 1256.0567649999934 ns 400000 + #BM_AudioUtilsMutexReverseScopedLockUnlock/threads:8 635.5625220561735 ns 2294.725433768965 ns 356872 + #BM_AudioUtilsPIMutexReverseScopedLockUnlock/threads:1 148.7079480412097 ns 148.0359150267745 ns 4188943 + #BM_AudioUtilsPIMutexReverseScopedLockUnlock/threads:2 14037.435207752424 ns 17829.977469499998 ns 2000000 + #BM_AudioUtilsPIMutexReverseScopedLockUnlock/threads:4 20098.127750043204 ns 26126.68207500001 ns 40000 + #BM_AudioUtilsPIMutexReverseScopedLockUnlock/threads:8 28805.264783022852 ns 38780.66452074406 ns 16776 + #BM_empty_while 0.352701456999057 ns 0.35104016500000057 ns 1000000000 */ // --- +template<typename Integer> +static void BM_atomic_add_equals(benchmark::State &state) { + Integer i = 10; + std::atomic<Integer> dst; + while (state.KeepRunning()) { + dst += i; + } + LOG(DEBUG) << __func__ << " " << dst.load(); +} + +BENCHMARK(BM_atomic_add_equals<int32_t>); + +template <typename T> +static void BM_atomic_add_to(benchmark::State &state, std::memory_order order) { + int64_t i64 = 10; + std::atomic<T> dst; + while (state.KeepRunning()) { + android::audio_utils::atomic_add_to(dst, i64, order); + } + LOG(DEBUG) << __func__ << " " << dst.load(); +} + +// Avoid macro issues with the comma. +template <typename T> +static void BM_atomic_add_to_seq_cst(benchmark::State &state) { + BM_atomic_add_to<T>(state, std::memory_order_seq_cst); +} + +BENCHMARK(BM_atomic_add_to_seq_cst<int16_t>); + +BENCHMARK(BM_atomic_add_to_seq_cst<int32_t>); + +BENCHMARK(BM_atomic_add_to_seq_cst<int64_t>); + +BENCHMARK(BM_atomic_add_to_seq_cst<float>); + +BENCHMARK(BM_atomic_add_to_seq_cst<double>); + +template <typename T> +static void BM_atomic_add_to_relaxed(benchmark::State &state) { + BM_atomic_add_to<T>(state, std::memory_order_relaxed); +} + +BENCHMARK(BM_atomic_add_to_relaxed<int16_t>); + +BENCHMARK(BM_atomic_add_to_relaxed<int32_t>); + +BENCHMARK(BM_atomic_add_to_relaxed<int64_t>); + +BENCHMARK(BM_atomic_add_to_relaxed<float>); + +BENCHMARK(BM_atomic_add_to_relaxed<double>); + // Benchmark gettid(). The mutex class uses this to get the linux thread id. static void BM_gettid(benchmark::State &state) { int32_t value = 0; @@ -136,6 +230,60 @@ BENCHMARK(BM_thread_local_8_variables); // --- +// std::mutex is the reference mutex that we compare against. +// It is based on Bionic pthread_mutex* support. + +// RWLock is a specialized Android mutex class based on +// Bionic pthread_rwlock* which in turn is based on the +// original ART shared reader mutex. + +// Test shared read lock performance. +class RWReadMutex : private android::RWLock { +public: + void lock() { readLock(); } + bool try_lock() { return tryReadLock() == 0; } + using android::RWLock::unlock; +}; + +// Test exclusive write lock performance. +class RWWriteMutex : private android::RWLock { +public: + void lock() { writeLock(); } + bool try_lock() { return tryWriteLock() == 0; } + using android::RWLock::unlock; +}; + +// std::shared_mutex lock/unlock behavior is default exclusive. +// We subclass to create the shared reader equivalent. +// +// Unfortunately std::shared_mutex implementation can contend on an internal +// mutex with multiple readers (even with no writers), resulting in worse lock performance +// than other shared mutexes. +// This is due to the portability desire in the original reference implementation: +// https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2406.html#shared_mutex_imp +class SharedReadMutex : private std::shared_mutex { +public: + void lock() { std::shared_mutex::lock_shared(); } + bool try_lock() { return std::shared_mutex::try_lock_shared(); } + void unlock() { std::shared_mutex::unlock_shared(); } +}; + +// audio_utils mutex is designed to have mutex order checking, statistics, +// deadlock detection, and priority inheritance capabilities, +// so it is higher overhead than just the std::mutex that it is based upon. +// +// audio_utils mutex without priority inheritance. +class AudioMutex : public android::audio_utils::mutex { +public: + AudioMutex() : android::audio_utils::mutex(false /* priority_inheritance */) {} +}; + +// audio_utils mutex with priority inheritance. +class AudioPIMutex : public android::audio_utils::mutex { +public: + AudioPIMutex() : android::audio_utils::mutex(true /* priority_inheritance */) {} +}; + template <typename Mutex> void MutexLockUnlock(benchmark::State& state) { Mutex m; @@ -150,18 +298,45 @@ static void BM_StdMutexLockUnlock(benchmark::State &state) { } // Benchmark repeated mutex lock/unlock from a single thread -// using std::mutex. +// using the mutex. BENCHMARK(BM_StdMutexLockUnlock); +static void BM_RWMutexReadLockUnlock(benchmark::State& state) { + MutexLockUnlock<RWReadMutex>(state); +} + +BENCHMARK(BM_RWMutexReadLockUnlock); + +static void BM_RWMutexWriteLockUnlock(benchmark::State& state) { + MutexLockUnlock<RWWriteMutex>(state); +} + +BENCHMARK(BM_RWMutexWriteLockUnlock); + +static void BM_SharedMutexReadLockUnlock(benchmark::State& state) { + MutexLockUnlock<SharedReadMutex>(state); +} + +BENCHMARK(BM_SharedMutexReadLockUnlock); + +static void BM_SharedMutexWriteLockUnlock(benchmark::State& state) { + MutexLockUnlock<std::shared_mutex>(state); +} + +BENCHMARK(BM_SharedMutexWriteLockUnlock); + static void BM_AudioUtilsMutexLockUnlock(benchmark::State &state) { - MutexLockUnlock<android::audio_utils::mutex>(state); + MutexLockUnlock<AudioMutex>(state); } -// Benchmark repeated mutex lock/unlock from a single thread -// using audio_utils::mutex. This will differ when priority inheritance -// is used or not. BENCHMARK(BM_AudioUtilsMutexLockUnlock); +static void BM_AudioUtilsPIMutexLockUnlock(benchmark::State &state) { + MutexLockUnlock<AudioPIMutex>(state); +} + +BENCHMARK(BM_AudioUtilsPIMutexLockUnlock); + // --- template <typename Mutex> @@ -178,18 +353,45 @@ static void BM_StdMutexInitializationLockUnlock(benchmark::State &state) { } // Benchmark repeated mutex creation then lock/unlock from a single thread -// using std::mutex. +// using the mutex. BENCHMARK(BM_StdMutexInitializationLockUnlock); +static void BM_RWMutexInitializationReadLockUnlock(benchmark::State& state) { + MutexInitializationLockUnlock<RWReadMutex>(state); +} + +BENCHMARK(BM_RWMutexInitializationReadLockUnlock); + +static void BM_RWMutexInitializationWriteLockUnlock(benchmark::State& state) { + MutexInitializationLockUnlock<RWWriteMutex>(state); +} + +BENCHMARK(BM_RWMutexInitializationWriteLockUnlock); + +static void BM_SharedMutexInitializationReadLockUnlock(benchmark::State& state) { + MutexInitializationLockUnlock<SharedReadMutex>(state); +} + +BENCHMARK(BM_SharedMutexInitializationReadLockUnlock); + +static void BM_SharedMutexInitializationWriteLockUnlock(benchmark::State& state) { + MutexInitializationLockUnlock<std::shared_mutex>(state); +} + +BENCHMARK(BM_SharedMutexInitializationWriteLockUnlock); + static void BM_AudioUtilsMutexInitializationLockUnlock(benchmark::State &state) { - MutexInitializationLockUnlock<android::audio_utils::mutex>(state); + MutexInitializationLockUnlock<AudioMutex>(state); } -// Benchmark repeated mutex creation then lock/unlock from a single thread -// using audio_utils::mutex. This will differ when priority inheritance -// is used or not. BENCHMARK(BM_AudioUtilsMutexInitializationLockUnlock); +static void BM_AudioUtilsPIMutexInitializationLockUnlock(benchmark::State &state) { + MutexInitializationLockUnlock<AudioPIMutex>(state); +} + +BENCHMARK(BM_AudioUtilsPIMutexInitializationLockUnlock); + // --- static constexpr size_t THREADS = 2; @@ -228,10 +430,10 @@ static void BM_StdMutexBlockingConditionVariable(benchmark::State &state) { // Benchmark 2 threads that use condition variables to wake each other up, // where only one thread is active at a given time. This benchmark -// uses std::mutex, std::unique_lock, and std::condition_variable. +// uses mutex, unique_lock, and condition_variable. BENCHMARK(BM_StdMutexBlockingConditionVariable)->Threads(THREADS); -MutexBlockingConditionVariable<android::audio_utils::mutex, +MutexBlockingConditionVariable<AudioMutex, android::audio_utils::unique_lock, android::audio_utils::condition_variable> CvAu; @@ -239,15 +441,26 @@ static void BM_AudioUtilsMutexBlockingConditionVariable(benchmark::State &state) CvAu.run(state); } -// Benchmark 2 threads that use condition variables to wake each other up, -// where only one thread is active at a given time. This benchmark -// uses audio_utils::mutex, audio_utils::unique_lock, and audio_utils::condition_variable. -// -// Priority inheritance mutexes will affect the performance of this benchmark. BENCHMARK(BM_AudioUtilsMutexBlockingConditionVariable)->Threads(THREADS); +MutexBlockingConditionVariable<AudioPIMutex, + android::audio_utils::unique_lock, + android::audio_utils::condition_variable> CvAuPI; + +static void BM_AudioUtilsPIMutexBlockingConditionVariable(benchmark::State &state) { + CvAuPI.run(state); +} + +BENCHMARK(BM_AudioUtilsPIMutexBlockingConditionVariable)->Threads(THREADS); + // --- +// Benchmark scoped_lock where two threads try to obtain the +// same 2 locks with the same initial acquisition order. +// This uses std::scoped_lock. + +static constexpr size_t THREADS_SCOPED = 8; + template <typename Mutex, typename ScopedLock> class MutexScopedLockUnlock { const bool reverse_; @@ -278,25 +491,63 @@ static void BM_StdMutexScopedLockUnlock(benchmark::State &state) { ScopedStd.run(state); } -// Benchmark scoped_lock where two threads try to obtain the -// same 2 locks with the same initial acquisition order. -// This uses std::mutex, std::scoped_lock. -BENCHMARK(BM_StdMutexScopedLockUnlock)->Threads(THREADS); +BENCHMARK(BM_StdMutexScopedLockUnlock)->ThreadRange(1, THREADS_SCOPED); + +MutexScopedLockUnlock<RWReadMutex, + std::scoped_lock<RWReadMutex, RWReadMutex>> ScopedRwRead; -MutexScopedLockUnlock<android::audio_utils::mutex, +static void BM_RWMutexScopedReadLockUnlock(benchmark::State &state) { + ScopedRwRead.run(state); +} + +BENCHMARK(BM_RWMutexScopedReadLockUnlock)->ThreadRange(1, THREADS_SCOPED); + +MutexScopedLockUnlock<RWWriteMutex, + std::scoped_lock<RWWriteMutex, RWWriteMutex>> ScopedRwWrite; + +static void BM_RWMutexScopedWriteLockUnlock(benchmark::State &state) { + ScopedRwWrite.run(state); +} + +BENCHMARK(BM_RWMutexScopedWriteLockUnlock)->ThreadRange(1, THREADS_SCOPED); + +MutexScopedLockUnlock<SharedReadMutex, + std::scoped_lock<SharedReadMutex, SharedReadMutex>> ScopedSharedRead; + +static void BM_SharedMutexScopedReadLockUnlock(benchmark::State &state) { + ScopedSharedRead.run(state); +} + +BENCHMARK(BM_SharedMutexScopedReadLockUnlock)->ThreadRange(1, THREADS_SCOPED); + +MutexScopedLockUnlock<std::shared_mutex, + std::scoped_lock<std::shared_mutex, std::shared_mutex>> ScopedSharedWrite; + +static void BM_SharedMutexScopedWriteLockUnlock(benchmark::State &state) { + ScopedSharedWrite.run(state); +} + +BENCHMARK(BM_SharedMutexScopedWriteLockUnlock)->ThreadRange(1, THREADS_SCOPED); + +MutexScopedLockUnlock<AudioMutex, android::audio_utils::scoped_lock< - android::audio_utils::mutex, android::audio_utils::mutex>> ScopedAu; + AudioMutex, AudioMutex>> ScopedAu; static void BM_AudioUtilsMutexScopedLockUnlock(benchmark::State &state) { ScopedAu.run(state); } -// Benchmark scoped_lock where two threads try to obtain the -// same 2 locks with the same initial acquisition order. -// This uses audio_utils::mutex and audio_utils::scoped_lock. -// -// Priority inheritance mutexes will affect the performance of this benchmark. -BENCHMARK(BM_AudioUtilsMutexScopedLockUnlock)->Threads(THREADS); +BENCHMARK(BM_AudioUtilsMutexScopedLockUnlock)->ThreadRange(1, THREADS_SCOPED); + +MutexScopedLockUnlock<AudioPIMutex, + android::audio_utils::scoped_lock< + AudioPIMutex, AudioPIMutex>> ScopedAuPI; + +static void BM_AudioUtilsPIMutexScopedLockUnlock(benchmark::State &state) { + ScopedAuPI.run(state); +} + +BENCHMARK(BM_AudioUtilsPIMutexScopedLockUnlock)->ThreadRange(1, THREADS_SCOPED); MutexScopedLockUnlock<std::mutex, std::scoped_lock<std::mutex, std::mutex>> ReverseScopedStd(true); @@ -306,22 +557,28 @@ static void BM_StdMutexReverseScopedLockUnlock(benchmark::State &state) { } // Benchmark scoped_lock with odd thread having reversed scoped_lock mutex acquisition order. -// This uses std::mutex, std::scoped_lock. -BENCHMARK(BM_StdMutexReverseScopedLockUnlock)->Threads(THREADS); +// This uses std::scoped_lock. +BENCHMARK(BM_StdMutexReverseScopedLockUnlock)->ThreadRange(1, THREADS_SCOPED); -MutexScopedLockUnlock<android::audio_utils::mutex, +MutexScopedLockUnlock<AudioMutex, android::audio_utils::scoped_lock< - android::audio_utils::mutex, android::audio_utils::mutex>> ReverseScopedAu(true); + AudioMutex, AudioMutex>> ReverseScopedAu(true); static void BM_AudioUtilsMutexReverseScopedLockUnlock(benchmark::State &state) { ReverseScopedAu.run(state); } -// Benchmark scoped_lock with odd thread having reversed scoped_lock mutex acquisition order. -// This uses audio_utils::mutex and audio_utils::scoped_lock. -// -// Priority inheritance mutexes will affect the performance of this benchmark. -BENCHMARK(BM_AudioUtilsMutexReverseScopedLockUnlock)->Threads(THREADS); +BENCHMARK(BM_AudioUtilsMutexReverseScopedLockUnlock)->ThreadRange(1, THREADS_SCOPED); + +MutexScopedLockUnlock<AudioPIMutex, + android::audio_utils::scoped_lock< + AudioPIMutex, AudioPIMutex>> ReverseScopedAuPI(true); + +static void BM_AudioUtilsPIMutexReverseScopedLockUnlock(benchmark::State &state) { + ReverseScopedAuPI.run(state); +} + +BENCHMARK(BM_AudioUtilsPIMutexReverseScopedLockUnlock)->ThreadRange(1, THREADS_SCOPED); static void BM_empty_while(benchmark::State &state) { diff --git a/audio_utils/include/audio_utils/mutex.h b/audio_utils/include/audio_utils/mutex.h index 63e99fb2..a473b611 100644 --- a/audio_utils/include/audio_utils/mutex.h +++ b/audio_utils/include/audio_utils/mutex.h @@ -394,12 +394,21 @@ public: * prior to C++23 support of atomic<float> atomic<double> accumulation. */ template <typename AccumulateType, typename ValueType> -void atomic_add_to(std::atomic<AccumulateType> &dst, ValueType src) { +requires std::is_floating_point<AccumulateType>::value +void atomic_add_to(std::atomic<AccumulateType> &dst, ValueType src, + std::memory_order order = std::memory_order_seq_cst) { static_assert(std::atomic<AccumulateType>::is_always_lock_free); AccumulateType expected; do { expected = dst; - } while (!dst.compare_exchange_weak(expected, expected + src)); + } while (!dst.compare_exchange_weak(expected, expected + src, order)); +} + +template <typename AccumulateType, typename ValueType> +requires std::is_integral<AccumulateType>::value +void atomic_add_to(std::atomic<AccumulateType> &dst, ValueType src, + std::memory_order order = std::memory_order_seq_cst) { + dst.fetch_add(src, order); } /** @@ -416,6 +425,8 @@ template <typename CounterType, typename AccumulatorType> struct mutex_stat { static_assert(std::is_floating_point_v<AccumulatorType>); static_assert(std::is_integral_v<CounterType>); + static_assert(std::atomic<CounterType>::is_always_lock_free); + static_assert(std::atomic<AccumulatorType>::is_always_lock_free); std::atomic<CounterType> locks = 0; // number of times locked std::atomic<CounterType> unlocks = 0; // number of times unlocked std::atomic<CounterType> waits = 0; // number of locks that waitedwa @@ -1013,7 +1024,15 @@ public: // We use composition here. // No copy/move ctors as the member std::mutex has it deleted. + + // Constructor selects priority inheritance based on the platform default. mutex_impl(typename Attributes::order_t order = Attributes::order_default_) + : mutex_impl(mutex_get_enable_flag(), order) + {} + + // Constructor selects priority inheritance based on input argument. + mutex_impl(bool priority_inheritance, + typename Attributes::order_t order = Attributes::order_default_) : order_(order) , stat_{get_mutex_stat_array()[static_cast<size_t>(order)]} { @@ -1021,7 +1040,7 @@ public: "mutex order %u is equal to or greater than order limit:%zu", order, Attributes::order_size_); - if (!mutex_get_enable_flag()) return; + if (!priority_inheritance) return; pthread_mutexattr_t attr; int ret = pthread_mutexattr_init(&attr); diff --git a/audio_utils/tests/audio_mutex_tests.cpp b/audio_utils/tests/audio_mutex_tests.cpp index 5cc860d8..29c967e2 100644 --- a/audio_utils/tests/audio_mutex_tests.cpp +++ b/audio_utils/tests/audio_mutex_tests.cpp @@ -370,9 +370,14 @@ TEST(audio_mutex_tests, OrderDetection) { EXPECT_EQ(0, nil2.second.load()); } +// The following tests are evaluated for the android::audio_utils::mutex +// Non-Priority Inheritance and Priority Inheritance cases. + +class MutexTestSuite : public testing::TestWithParam<bool> {}; + // Test that the mutex aborts on recursion (if the abort flag is set). -TEST(audio_mutex_tests, FatalRecursiveMutex) +TEST_P(MutexTestSuite, FatalRecursiveMutex) NO_THREAD_SAFETY_ANALYSIS { if (!android::audio_utils::AudioMutexAttributes::abort_on_recursion_check_ || !audio_utils::mutex_get_enable_flag()) { @@ -382,8 +387,9 @@ NO_THREAD_SAFETY_ANALYSIS { using Mutex = android::audio_utils::mutex; using LockGuard = android::audio_utils::lock_guard; + const bool priority_inheritance = GetParam(); - Mutex m; + Mutex m(priority_inheritance); LockGuard lg(m); // Can't lock ourselves again. @@ -392,7 +398,7 @@ NO_THREAD_SAFETY_ANALYSIS { // Test that the mutex aborts on lock order inversion (if the abort flag is set). -TEST(audio_mutex_tests, FatalLockOrder) +TEST_P(MutexTestSuite, FatalLockOrder) NO_THREAD_SAFETY_ANALYSIS { if (!android::audio_utils::AudioMutexAttributes::abort_on_order_check_ || !audio_utils::mutex_get_enable_flag()) { @@ -402,9 +408,10 @@ NO_THREAD_SAFETY_ANALYSIS { using Mutex = android::audio_utils::mutex; using LockGuard = android::audio_utils::lock_guard; + const bool priority_inheritance = GetParam(); - Mutex m1{(android::audio_utils::AudioMutexAttributes::order_t)1}; - Mutex m2{(android::audio_utils::AudioMutexAttributes::order_t)2}; + Mutex m1{priority_inheritance, (android::audio_utils::AudioMutexAttributes::order_t)1}; + Mutex m2{priority_inheritance, (android::audio_utils::AudioMutexAttributes::order_t)2}; LockGuard lg2(m2); // m1 must be locked before m2 as 1 < 2. @@ -413,7 +420,7 @@ NO_THREAD_SAFETY_ANALYSIS { // Test that the mutex aborts on lock order inversion (if the abort flag is set). -TEST(audio_mutex_tests, UnexpectedUnlock) +TEST_P(MutexTestSuite, UnexpectedUnlock) NO_THREAD_SAFETY_ANALYSIS { if (!android::audio_utils::AudioMutexAttributes::abort_on_invalid_unlock_ || !audio_utils::mutex_get_enable_flag()) { @@ -422,16 +429,19 @@ NO_THREAD_SAFETY_ANALYSIS { } using Mutex = android::audio_utils::mutex; + const bool priority_inheritance = GetParam(); - Mutex m1{(android::audio_utils::AudioMutexAttributes::order_t)1}; + Mutex m1{priority_inheritance, (android::audio_utils::AudioMutexAttributes::order_t)1}; ASSERT_DEATH(m1.unlock(), ".*mutex unlock.*"); } -TEST(audio_mutex_tests, TimedLock) { +TEST_P(MutexTestSuite, TimedLock) { using ConditionVariable = android::audio_utils::condition_variable; using Mutex = android::audio_utils::mutex; using UniqueLock = android::audio_utils::unique_lock; - Mutex m, m1; + const bool priority_inheritance = GetParam(); + + Mutex m{priority_inheritance}, m1{priority_inheritance}; ConditionVariable cv; bool quit = false; // GUARDED_BY(m) @@ -492,10 +502,11 @@ TEST(audio_mutex_tests, TimedLock) { // Test the deadlock detection algorithm for a single wait chain // (no cycle). -TEST(audio_mutex_tests, DeadlockDetection) { +TEST_P(MutexTestSuite, DeadlockDetection) { using Mutex = android::audio_utils::mutex; using UniqueLock = android::audio_utils::unique_lock; using ConditionVariable = android::audio_utils::condition_variable; + const bool priority_inheritance = GetParam(); // order checked below. constexpr size_t kOrder1 = 1; @@ -503,11 +514,11 @@ TEST(audio_mutex_tests, DeadlockDetection) { constexpr size_t kOrder3 = 3; static_assert(Mutex::attributes_t::order_size_ > kOrder3); - Mutex m1{static_cast<Mutex::attributes_t::order_t>(kOrder1)}; - Mutex m2{static_cast<Mutex::attributes_t::order_t>(kOrder2)}; - Mutex m3{static_cast<Mutex::attributes_t::order_t>(kOrder3)}; - Mutex m4; - Mutex m; + Mutex m1{priority_inheritance, static_cast<Mutex::attributes_t::order_t>(kOrder1)}; + Mutex m2{priority_inheritance, static_cast<Mutex::attributes_t::order_t>(kOrder2)}; + Mutex m3{priority_inheritance, static_cast<Mutex::attributes_t::order_t>(kOrder3)}; + Mutex m4{priority_inheritance}; + Mutex m{priority_inheritance}; ConditionVariable cv; bool quit = false; // GUARDED_BY(m) std::atomic<pid_t> tid1{}, tid2{}, tid3{}, tid4{}; @@ -593,5 +604,7 @@ TEST(audio_mutex_tests, DeadlockDetection) { t1.join(); } +INSTANTIATE_TEST_SUITE_P(Mutex_NonPI_and_PI, MutexTestSuite, testing::Values(false, true)); + } // namespace android |