summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndy Hung <hunga@google.com>2024-03-12 19:10:28 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2024-03-12 19:10:28 +0000
commit8f6208583277d35a6870db3266f70cbae9815010 (patch)
tree9011977e14675324f9e1695e551613d21c478868
parent6b25d8a969826b9e95630b1ad7e3e85c537959b5 (diff)
parent4f1e68ef9cc743b54e19751d262e1270ab15df2e (diff)
downloadmedia-8f6208583277d35a6870db3266f70cbae9815010.tar.gz
Merge "audio mutex: Add additional tests and benchmarks" into main
-rw-r--r--audio_utils/benchmarks/audio_mutex_benchmark.cpp403
-rw-r--r--audio_utils/include/audio_utils/mutex.h25
-rw-r--r--audio_utils/tests/audio_mutex_tests.cpp43
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