Skip to content

Instantly share code, notes, and snippets.

@mrbid
Last active January 1, 2024 21:32
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 mrbid/3040f54eb6942ed53daa044a9c055dbb to your computer and use it in GitHub Desktop.
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.
/*
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