Last active
January 1, 2024 21:32
-
-
Save mrbid/3040f54eb6942ed53daa044a9c055dbb to your computer and use it in GitHub Desktop.
IsAtomic - This is a test to see if the default variable type on the executing CPU is atomic or not.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
James William Fletcher (github.com/mrbid) | |
February 2022 | |
This is a test to see if the default variable | |
type on the executing CPU is atomic or not. | |
It is notable that lock-free atomic increments are | |
significantly slower than non-atomic increments. | |
compile: gcc -std=gnu99 isatomic.c -lpthread -Ofast -lm -o isatomic | |
*/ | |
#include <stdio.h> // printf | |
#include <string.h> // memset | |
#include <pthread.h> // pthread_ | |
#include <stdatomic.h> // atomic_ | |
#include <x86intrin.h> // __rdtsc | |
#include <sys/time.h> // gettimeofday | |
#include <locale.h> // setlocale | |
#include <stdint.h> // uint64_t | |
#define VAR_TYPE unsigned short | |
#define VAR_TYPE_ATOMIC atomic_ushort | |
// #define VAR_TYPE unsigned int | |
// #define VAR_TYPE_ATOMIC atomic_uint | |
#define NUM_THREADS 4 | |
#define NUM_THREAD_ITERATIONS 10000 | |
pthread_t tid[NUM_THREADS]; | |
/// regular | |
VAR_TYPE shared = 0; | |
VAR_TYPE private[NUM_THREADS] = {0}; | |
void *increment_thread(void *arg) | |
{ | |
for(unsigned int i = 0; i < NUM_THREAD_ITERATIONS; i++) | |
{ | |
(*(VAR_TYPE*)arg)++; | |
shared++; | |
} | |
return 0; | |
} | |
/// atomic | |
VAR_TYPE_ATOMIC ashared = 0; | |
VAR_TYPE aprivate[NUM_THREADS] = {0}; | |
void *increment_atomic_thread(void *arg) | |
{ | |
for(unsigned int i = 0; i < NUM_THREAD_ITERATIONS; i++) | |
{ | |
(*(VAR_TYPE*)arg)++; | |
ashared++; | |
} | |
return 0; | |
} | |
/// utils | |
uint64_t microtime() | |
{ | |
struct timeval tv; | |
struct timezone tz; | |
memset(&tz, 0, sizeof(struct timezone)); | |
gettimeofday(&tv, &tz); | |
return 1000000 * tv.tv_sec + tv.tv_usec; | |
} | |
/// main | |
int main() | |
{ | |
setlocale(LC_NUMERIC, ""); | |
printf("\n"); | |
// regular | |
printf(":: regular\n"); | |
uint64_t stm = microtime(); | |
uint64_t st = __rdtsc(); | |
for(int i = 0; i < NUM_THREADS; i++) | |
{ | |
if(pthread_create(&tid[i], NULL, increment_thread, &private[i]) != 0) | |
printf("failed to create thread %i\n", i); | |
} | |
for(int i = 0; i < NUM_THREADS; i++) | |
{ | |
if(pthread_join(tid[i], NULL) != 0) | |
printf("failed to join thread %i\n", i); | |
} | |
const uint64_t tt = __rdtsc()-st; | |
const uint64_t ttm = microtime()-stm; | |
printf("time taken: %'lu cycles\n", tt); | |
printf("time taken: %'lu μs\n", ttm); | |
VAR_TYPE private_final = 0; | |
for(int i = 0; i < NUM_THREADS; i++) | |
private_final += private[i]; | |
printf("SHARED/FINAL: %i/%i\n", shared, private_final); | |
if(shared != private_final) | |
printf("!! default type is not atomic\n"); | |
else | |
printf("!! default type is atomic\n"); | |
printf("\n"); | |
// atomic | |
printf(":: atomic "); | |
if(atomic_is_lock_free(&ashared) == 1) | |
printf("[is lock free: YES]\n"); | |
else | |
printf("[is lock free: NO]\n"); | |
uint64_t astm = microtime(); | |
uint64_t ast = __rdtsc(); | |
for(int i = 0; i < NUM_THREADS; i++) | |
{ | |
if(pthread_create(&tid[i], NULL, increment_atomic_thread, &aprivate[i]) != 0) | |
printf("failed to create thread %i\n", i); | |
} | |
for(int i = 0; i < NUM_THREADS; i++) | |
{ | |
if(pthread_join(tid[i], NULL) != 0) | |
printf("failed to join thread %i\n", i); | |
} | |
const uint64_t att = __rdtsc()-ast; | |
const uint64_t attm = microtime()-astm; | |
printf("time taken: %'lu cycles\n", att); | |
printf("time taken: %'lu μs\n", attm); | |
private_final = 0; | |
for(int i = 0; i < NUM_THREADS; i++) | |
private_final += aprivate[i]; | |
printf("SHARED/FINAL: %i/%i\n", ashared, private_final); | |
if(ashared != private_final) | |
printf("!! atomic type is not atomic\n"); | |
else | |
printf("!! atomic type is atomic\n"); | |
printf("\n"); | |
// benchmark | |
printf(":: benchmark\n"); | |
const uint64_t bt1 = att / tt; | |
const uint64_t bt2 = attm / ttm; | |
printf("Atomic is %'lux slower.\n", (bt1+bt2)/2); | |
// done | |
printf("\n"); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment