Created
May 23, 2024 19:43
-
-
Save andrealmeid/f0b8c93a3c7a5c50458247c47f7078e1 to your computer and use it in GitHub Desktop.
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
/* | |
* futex2_spin example, by André Almeida <andrealmeid@igalia.com> | |
* | |
* Partially inspired by Steven Rostedt benchmark from: | |
* https://lore.kernel.org/all/20231025235413.597287e1@gandalf.local.home/2-extend-sched.c | |
* | |
* gcc spin.c -o spin | |
*/ | |
#define _GNU_SOURCE | |
#include <err.h> | |
#include <errno.h> | |
#include <linux/futex.h> | |
#include <linux/sched.h> | |
#include <pthread.h> | |
#include <stdint.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <sys/mman.h> | |
#include <sys/time.h> | |
#include <sys/wait.h> | |
#include <unistd.h> | |
#define __NR_futex_wake 454 | |
#define __NR_futex_wait 455 | |
#define FUTEX2_SPIN 0x08 | |
#define STACK_SIZE (1024 * 1024) | |
#define FUTEX2_SIZE_U32 0x02 | |
#define FUTEX2_PRIVATE FUTEX_PRIVATE_FLAG | |
#define timeout_ns 90000000 | |
#define sec2usec(sec) (sec * 1000000ULL) | |
#define usec2sec(usec) (usec / 1000000ULL) | |
#define WAKE_WAIT_US 100 | |
#define CHILD_LOOPS 500000 | |
void *futex; | |
static unsigned long long get_time(void) | |
{ | |
struct timeval tv; | |
unsigned long long time; | |
gettimeofday(&tv, NULL); | |
time = sec2usec(tv.tv_sec); | |
time += tv.tv_usec; | |
return time; | |
} | |
static inline int futex2_wake(volatile void *uaddr, unsigned long mask, int nr, unsigned int flags) | |
{ | |
return syscall(__NR_futex_wake, uaddr, mask, nr, flags); | |
} | |
static inline int futex2_wait(volatile void *uaddr, unsigned long val, unsigned long mask, | |
unsigned int flags, struct timespec *timo, clockid_t clockid) | |
{ | |
return syscall(__NR_futex_wait, uaddr, val, mask, flags, timo, clockid); | |
} | |
void waiter_fn(unsigned int f, unsigned long long *total) | |
{ | |
struct timespec to; | |
unsigned int flags = FUTEX2_PRIVATE | FUTEX2_SIZE_U32; | |
flags |= f; | |
unsigned long long start, end, delta; | |
uint32_t child_pid = *(uint32_t *) futex; | |
clock_gettime(CLOCK_MONOTONIC, &to); | |
to.tv_nsec += timeout_ns; | |
if (to.tv_nsec >= 1000000000) { | |
to.tv_sec++; | |
to.tv_nsec -= 1000000000; | |
} | |
start = get_time(); | |
if (futex2_wait(futex, child_pid, ~0U, flags, &to, CLOCK_MONOTONIC)) | |
printf("waiter failed errno %d\n", errno); | |
end = get_time(); | |
delta = end - start; | |
*total = *total + delta; | |
} | |
int function(int n) | |
{ | |
return n + n; | |
} | |
static void *thread_fn(void *arg) | |
{ | |
int i, n = 2; | |
uint32_t *tid = (uint32_t *) arg; | |
*tid = gettid(); | |
for (i = 0; i < CHILD_LOOPS * 10; i++) | |
n = function(n); | |
futex2_wake(futex, ~0U, 1, FUTEX2_SIZE_U32 | FUTEX_PRIVATE_FLAG); | |
return NULL; | |
} | |
#define NR_RUNS 1000 | |
void run_test_pthread(unsigned int flags, uint32_t *child_pid) | |
{ | |
unsigned long long total_wait = 0; | |
pthread_t thread; | |
int i; | |
for (i = 0; i < NR_RUNS; i++) { | |
pthread_create(&thread, NULL, &thread_fn, child_pid); | |
usleep(WAKE_WAIT_US); | |
waiter_fn(flags, &total_wait); | |
pthread_join(thread, NULL); | |
} | |
printf("Total wait time: %llu usecs\n", total_wait); | |
} | |
int main() | |
{ | |
uint32_t child_pid = 0; | |
futex = &child_pid; | |
puts("Testing with FUTEX2_SPIN | FUTEX_WAIT"); | |
run_test_pthread(FUTEX2_SPIN, &child_pid); | |
sleep(1); | |
puts("Testing with FUTEX_WAIT"); | |
run_test_pthread(0, &child_pid); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment