Skip to content

Instantly share code, notes, and snippets.

@wjlroe
Last active April 30, 2019 14:35
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 wjlroe/a9e0949a784b96a8afdfecad79090c7d to your computer and use it in GitHub Desktop.
Save wjlroe/a9e0949a784b96a8afdfecad79090c7d to your computer and use it in GitHub Desktop.
Playing with C11's stdatomics like atomic_compare_exchange and atomic_fetch_add for thread-safe global counters
#include <stdatomic.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#define NUM_THREADS 1000
pthread_t threads[NUM_THREADS];
#define INC_TIMES 500
int global_counter = 0;
_Atomic int atomic_global_counter = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void* run_naive(void *arg) {
for (int i = 0; i < INC_TIMES; i++) {
global_counter++;
}
}
void* run_mutex(void *arg) {
for (int i = 0; i < INC_TIMES; i++) {
pthread_mutex_lock(&mutex);
global_counter++;
pthread_mutex_unlock(&mutex);
}
}
void* run_cas(void *arg) {
int old, new;
for (int i = 0; i < INC_TIMES; i++) {
do {
old = atomic_global_counter;
new = atomic_global_counter + 1;
} while(!atomic_compare_exchange_strong(&atomic_global_counter, &old, new));
}
}
void* run_atomic_add(void *arg) {
for (int i = 0; i < INC_TIMES; i++) {
atomic_fetch_add(&atomic_global_counter, 1);
}
}
double run_with_threads(void * (run_method)(void *)) {
global_counter = 0;
atomic_global_counter = 0;
clock_t start, end;
double cpu_time_used;
start = clock();
for (int i = 0; i < NUM_THREADS; i++) {
int err = pthread_create(&(threads[i]), NULL, run_method, NULL);
if (err != 0) {
printf("\nThread creation failed: %s", strerror(err));
}
}
for (int i = 0; i < NUM_THREADS; i++) {
pthread_join(threads[i], NULL);
}
end = clock();
cpu_time_used = ((double) (end - start)) / CLOCKS_PER_SEC;
return cpu_time_used;
}
int main() {
int desired_value = NUM_THREADS * INC_TIMES;
double naive_time = run_with_threads(&run_naive);
if (global_counter != desired_value) {
printf("!!! [Naive version]Counter (%d) does not equal expected value (%d)\n", global_counter, desired_value);
} else {
printf("Naive version worked\n");
}
double mutex_locked_time = run_with_threads(&run_mutex);
if (global_counter != desired_value) {
printf("!!! [Mutex version]Counter (%d) does not equal expected value (%d)\n", global_counter, desired_value);
} else {
printf("Mutex version worked\n");
}
double cas_time = run_with_threads(&run_cas);
global_counter = atomic_global_counter;
if (global_counter != desired_value) {
printf("!!! [CAS version]Counter (%d) does not equal expected value (%d)\n", global_counter, desired_value);
} else {
printf("CAS version worked\n");
}
double atomic_add_time = run_with_threads(&run_atomic_add);
global_counter = atomic_global_counter;
if (global_counter != desired_value) {
printf("!!! [Atomic_fetch_add version]Counter (%d) does not equal expected value (%d)\n", global_counter, desired_value);
} else {
printf("Atomic_fetch_add version worked\n");
}
printf("\n");
printf("Naive: %f\n", naive_time);
printf("Mutex: %f\n", mutex_locked_time);
printf("CAS: %f\n", cas_time);
printf("Atomic_fetch_add: %f\n", atomic_add_time);
printf("\n");
printf("Done\n");
return 0;
}
@wjlroe
Copy link
Author

wjlroe commented Apr 30, 2019

Representative output:

!!! [Naive version]Counter (495387) does not equal expected value (500000)
Mutex version worked
CAS version worked
Atomic_fetch_add version worked

Naive:            0.027296
Mutex:            0.318031
CAS:              0.269807
Atomic_fetch_add: 0.033671

Done

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment