Skip to content

Instantly share code, notes, and snippets.

@frroossst
Created March 27, 2025 16:44
Show Gist options
  • Save frroossst/a2cfcd3dfd7256b9cbbe8b1802d2832a to your computer and use it in GitHub Desktop.
Save frroossst/a2cfcd3dfd7256b9cbbe8b1802d2832a to your computer and use it in GitHub Desktop.
/*
* Under high contention loads a futex should perform worse.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <linux/futex.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <sys/time.h>
#include <stdatomic.h>
// config
#define NUM_THREADS 64
#define ITERATIONS 1000000
#define LOW_CONTENTION_THREADS 2
#define HIGH_CONTENTION_THREADS 16
typedef enum {
SYNC_MUTEX,
SYNC_FUTEX
} SyncType;
// Shared benchmark state
typedef struct {
union {
pthread_mutex_t mutex;
atomic_int futex;
} lock;
int counter;
SyncType sync_type;
int thread_count;
} BenchmarkState;
// Futex wrapper functions
int futex_wait(atomic_int *futex, int expected) {
return syscall(SYS_futex, futex, FUTEX_WAIT_PRIVATE, expected, NULL, NULL, 0);
}
int futex_wake(atomic_int *futex) {
return syscall(SYS_futex, futex, FUTEX_WAKE_PRIVATE, 1, NULL, NULL, 0);
}
// Benchmark core logic
void *benchmark_thread(void *arg) {
BenchmarkState *state = (BenchmarkState *)arg;
for (int i = 0; i < ITERATIONS; i++) {
// Locking mechanism based on sync type
if (state->sync_type == SYNC_MUTEX) {
pthread_mutex_lock(&state->lock.mutex);
state->counter++;
pthread_mutex_unlock(&state->lock.mutex);
} else { // FUTEX
while (1) {
int expected = 0;
if (atomic_compare_exchange_weak(&state->lock.futex, &expected, 1)) {
// Critical section
state->counter++;
// Unlock futex
atomic_store(&state->lock.futex, 0);
futex_wake(&state->lock.futex);
break;
} else {
// Wait on futex
futex_wait(&state->lock.futex, 1);
}
}
}
}
return NULL;
}
// Benchmark runner
double run_benchmark(SyncType sync_type, int thread_count) {
BenchmarkState state;
pthread_t threads[NUM_THREADS];
struct timeval start, end;
// Initialize state
state.counter = 0;
state.sync_type = sync_type;
state.thread_count = thread_count;
if (sync_type == SYNC_MUTEX) {
pthread_mutex_init(&state.lock.mutex, NULL);
} else {
atomic_init(&state.lock.futex, 0);
}
// Start timing
gettimeofday(&start, NULL);
// Create threads
for (int i = 0; i < thread_count; i++) {
pthread_create(&threads[i], NULL, benchmark_thread, &state);
}
// Wait for threads to complete
for (int i = 0; i < thread_count; i++) {
pthread_join(threads[i], NULL);
}
// End timing
gettimeofday(&end, NULL);
// Calculate duration
double duration = (end.tv_sec - start.tv_sec) +
(end.tv_usec - start.tv_usec) / 1000000.0;
// Clean up
if (sync_type == SYNC_MUTEX) {
pthread_mutex_destroy(&state.lock.mutex);
}
return duration;
}
int main() {
printf("Futex vs Mutex Performance Benchmark\n");
printf("-----------------------------------\n");
// Low contention scenario
printf("Low Contention (%d threads):\n", LOW_CONTENTION_THREADS);
double mutex_low_time = run_benchmark(SYNC_MUTEX, LOW_CONTENTION_THREADS);
double futex_low_time = run_benchmark(SYNC_FUTEX, LOW_CONTENTION_THREADS);
printf(" Mutex Time: %.4f seconds\n", mutex_low_time);
printf(" Futex Time: %.4f seconds\n", futex_low_time);
printf(" Mutex Ops/s: %.0f\n", ITERATIONS * LOW_CONTENTION_THREADS / mutex_low_time);
printf(" Futex Ops/s: %.0f\n", ITERATIONS * LOW_CONTENTION_THREADS / futex_low_time);
// High contention scenario
printf("\nHigh Contention (%d threads):\n", HIGH_CONTENTION_THREADS);
double mutex_high_time = run_benchmark(SYNC_MUTEX, HIGH_CONTENTION_THREADS);
double futex_high_time = run_benchmark(SYNC_FUTEX, HIGH_CONTENTION_THREADS);
printf(" Mutex Time: %.4f seconds\n", mutex_high_time);
printf(" Futex Time: %.4f seconds\n", futex_high_time);
printf(" Mutex Ops/s: %.0f\n", ITERATIONS * HIGH_CONTENTION_THREADS / mutex_high_time);
printf(" Futex Ops/s: %.0f\n", ITERATIONS * HIGH_CONTENTION_THREADS / futex_high_time);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment