Skip to content

Instantly share code, notes, and snippets.

@guowangy
Last active April 13, 2022 02:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save guowangy/459085e5be6bfeda101c591d2d2304c5 to your computer and use it in GitHub Desktop.
Save guowangy/459085e5be6bfeda101c591d2d2304c5 to your computer and use it in GitHub Desktop.
Adaptive pthread_mutex benchmark. It can measure different threads and critical sections (length represented by num of pause instruction)
all:
gcc -O2 -lpthread -lm mutex-bench.c -o mutex-bench
clean:
rm -rf mutex-bench
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <stdbool.h>
#include <math.h>
#define VERBOSE 0
#define MAX_THREAD_NUM 512
#define TEST_DURATION 1
#define REPEAT 10
#define pause() __asm volatile ("pause" ::: )
double calc_avg(double *array, size_t n) {
double sum = 0.0;
for (int i = 0; i < n; i++)
sum += array[i];
return (sum / n);
}
double calc_rsd(double *array, size_t n) {
double avg = calc_avg(array, n);
double std = 0.0;
for (int i = 0; i < n; i++)
std += pow(array[i] - avg, 2);
std = sqrt(std / n);
return (std / avg);
}
volatile unsigned long long count;
volatile bool running;
struct worker_params {
pthread_mutex_t *lock;
int critcal_pause;
};
void* worker(void *lock_arg) {
struct worker_params *params = (struct worker_params *) lock_arg;
pthread_mutex_t *lock = params->lock;
int npause = params->critcal_pause;
while (running) {
pthread_mutex_lock(lock);
for (int j = 0; j < npause; j++)
pause();
count++;
pthread_mutex_unlock(lock);
}
return NULL;
}
double do_bench(int thread_num, int npause) {
struct timespec start, stop;
double duration;
struct worker_params params;
double *tps = (double *) malloc(REPEAT * sizeof(double));
pthread_t *tids = (pthread_t *) malloc(thread_num * sizeof(pthread_t));
pthread_mutex_t mutex;
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP);
pthread_mutex_init(&mutex, &attr);
for (int j = 0; j < REPEAT; j++) {
tps[j] = 0.0;
count = 0;
params.lock = &mutex;
params.critcal_pause = npause;
running = true;
// create thread to start benchmark
clock_gettime(CLOCK_MONOTONIC, &start);
for (int i = 0; i < thread_num; i++)
pthread_create(&tids[i], NULL, worker, &params);
// execute at least 5 seconds
sleep(TEST_DURATION);
// stop and get total count
running = false;
pthread_mutex_lock(&mutex);
unsigned long long total_count = count;
clock_gettime(CLOCK_MONOTONIC, &stop);
pthread_mutex_unlock(&mutex);
// clean up threads
for (int i = 0; i < thread_num; i++)
pthread_join(tids[i], NULL);
duration = (double)(stop.tv_sec - start.tv_sec) + (double)((stop.tv_nsec - start.tv_nsec)) * 1.e-9;
tps[j] = (total_count) / duration;
#if VERBOSE
printf("tps: %f\n", tps[j]);
#endif
}
double ret = calc_avg(tps, REPEAT);
double rsd = calc_rsd(tps, REPEAT);
printf("[pause: %3d, thread: %3d] -- Throughput: %10.0f ops/s\tRSD: %2.2f%%\n", npause, thread_num, ret, rsd * 100);
free(tids);
free(tps);
return ret;
}
#define BENCH(npause) \
do_bench(1, npause); \
do_bench(4, npause); \
do_bench(16, npause); \
do_bench(64, npause); \
do_bench(128, npause);
void main(void) {
BENCH(0);
BENCH(1);
BENCH(4);
BENCH(16);
BENCH(64);
BENCH(128);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment