Absolutely perfect. This is a top-tier benchmarking test of pthread_mutex_t
vs pthread_rwlock_t
under a realistic read-heavy scenario. Let’s now transform this into an elite Obsidian note under [[asymmetric_pthreads/06_mutex_vs_rwlock_under_load]]
🧠⚙️
🧵 06_mutex_vs_rwlock_under_load
⚔️ Benchmark Battle: How well does a
mutex
hold up against arwlock
when 90% of threads just want to read?
🔍 Objective
title: Goal
To compare the performance of a traditional `pthread_mutex_t` versus a `pthread_rwlock_t` when:
- 🧠 1000 threads compete for access
- 🔁 90% are *readers*, 10% are *writers*
- ⚙️ Each thread performs 100 iterations
⚒️ The Code Setup
#define THREAD_COUNT 1000
#define READ_RATIO 90
#define ITERATIONS 100
-
📊
READ_RATIO = 90
simulates a read-heavy workload. -
👥
THREAD_COUNT = 1000
simulates a congested environment. -
🔁 Each thread runs the logic in a tight loop
ITERATIONS
times.
📦 struct s_args
typedef struct s_args {
int index;
int is_reader;
int use_rwlock;
} t_args;
Used to:
-
Tag threads as readers or writers
-
Specify whether to benchmark the
mutex
orrwlock
logic
🧠 Thread Logic: thread_fn
title: Reader vs Writer Behavior
collapse: open
- 🔐 If using `rwlock`:
- `reader` → `pthread_rwlock_rdlock` → safe concurrent reads
- `writer` → `pthread_rwlock_wrlock` → exclusive access
- 🔒 If using `mutex`:
- All threads must `pthread_mutex_lock`, even for reads
if (args->use_rwlock) {
if (args->is_reader)
pthread_rwlock_rdlock();
else
pthread_rwlock_wrlock();
...
pthread_rwlock_unlock();
} else {
pthread_mutex_lock();
...
pthread_mutex_unlock();
}
🕒 Timing Measurement
clock_gettime(CLOCK_MONOTONIC, &start);
// launch + join all threads
clock_gettime(CLOCK_MONOTONIC, &end);
-
Uses
CLOCK_MONOTONIC
for nanosecond precision. -
Final result is printed as:
mutex ->time: 142004500, ns
rwlock ->time: 59004100, ns
📈 Expected Outcome
title: Realistic Performance Insights
- `rwlock` should outperform `mutex` in read-heavy loads ✅
- `mutex` becomes a bottleneck even for readers ❌
- `rwlock` allows **true parallelism for readers** ✨
- Writers still serialize — but overall throughput is much higher
🧠 Learnings
title: System Design Takeaways
- Always benchmark in **context** — `rwlock` shines when reads dominate
- Don't use `rwlock` if writes are frequent — performance may degrade
- `mutex` is simpler but **can throttle scale** due to unnecessary exclusivity
💣 Warnings
title: Trap Zones
- ❗ Be careful with `pthread_rwlock_rdlock()` starvation — writers can starve
- ❗ `pthread_rwlock_t` is heavier than `mutex` — memory + performance tradeoff
- ❗ Use with real benchmarking — results may vary based on CPU architecture, load, thread scheduling
🧪 Bonus: Possible Improvements
- Add histogram for latency across threads
- Track contention count
- Experiment with WRITE_RATIO = 50
- Pin threads to CPUs with `pthread_setaffinity_np`
- Add yield points (`sched_yield()`) to observe context switch behavior
🔚 Final Verdict
title: Summary
- This test shows **why `rwlock` exists** — not as a replacement, but as an optimization tool.
- In `read-heavy` systems (caches, DB replicas, config services), it's **a clear win**.
- But as always: **test in your own system context** 🔬
📎 Obsidian Links
- Related: [[asymmetric_pthreads/07_shared_counter_with_mutex]]
- Related: [[asymmetric_pthreads/13_spinlock_and_compare_swap]]
- Advanced: [[asymmetric_pthreads/36_mutexattr_recursive_deadlock_fix]]
Let me know if you’d like to run multiple variations of this benchmark (e.g. 10/90 write-heavy, 50/50, or full writer overload)!