2 * copyright Oracle 2007. Licensed under GPLv2
3 * To compile: gcc -Wall -o sembench sembench.c -lpthread
5 * usage: sembench -t thread count -w wakenum -r runtime -o op
6 * op can be: 0 (ipc sem) 1 (nanosleep) 2 (futexes)
9 * sembench -t 1024 -w 512 -r 60 -o 2
10 * runs 1024 threads, waking up 512 at a time, running for 60 seconds using
15 #define _POSIX_C_SOURCE 199309
22 #include <sys/types.h>
29 #include <sys/syscall.h>
34 /* futexes have been around since 2.5.something, but it still seems I
35 * need to make my own syscall. Sigh.
40 #define FUTEX_REQUEUE 3
41 #define FUTEX_CMP_REQUEUE 4
42 #define FUTEX_WAKE_OP 5
43 static inline int futex (int *uaddr, int op, int val,
44 const struct timespec *timeout,
45 int *uaddr2, int val3)
47 return syscall(__NR_futex, uaddr, op, val, timeout, uaddr2, val3);
50 static void smp_mb(void)
55 static int all_done = 0;
56 static int timeout_test = 0;
58 #define SEMS_PERID 250
60 struct sem_operations;
67 struct lockinfo *next;
68 struct sem_operations *ops;
72 struct sem_wakeup_info {
74 struct sembuf sb[SEMS_PERID];
77 struct sem_operations {
78 void (*wait)(struct lockinfo *l);
79 int (*wake)(struct sem_wakeup_info *wi, int num_semids, int num);
80 void (*setup)(struct sem_wakeup_info **wi, int num_semids);
81 void (*cleanup)(int num_semids);
85 int *semid_lookup = NULL;
87 pthread_mutex_t worklist_mutex = PTHREAD_MUTEX_INITIALIZER;
88 static unsigned long total_burns = 0;
89 static unsigned long min_burns = ~0UL;
90 static unsigned long max_burns = 0;
92 /* currently running threads */
93 static int thread_count = 0;
95 struct lockinfo *worklist = NULL;
96 static int workers_started = 0;
98 /* total threads started */
99 static int num_threads = 2048;
101 static void worklist_add(struct lockinfo *l)
107 static struct lockinfo *worklist_rm(void)
109 static int last_index = 0;
113 for (i = 0; i < num_threads; i++) {
114 int test = (last_index + i) % num_threads;
127 /* ipc semaphore post& wait */
128 void wait_ipc_sem(struct lockinfo *l)
132 struct timespec *tvp = NULL;
133 struct timespec tv = { 0, 1 };
135 sb.sem_num = l->index;
141 if (timeout_test && (l->id % 5) == 0)
145 ret = semtimedop(semid_lookup[l->id], &sb, 1, tvp);
147 while(l->data != 0 && tvp) {
148 struct timespec tv2 = { 0, 500 };
149 nanosleep(&tv2, NULL);
155 fprintf(stderr, "wakeup without data update\n");
159 if (errno == EAGAIN && tvp)
161 perror("semtimed op");
166 int ipc_wake_some(struct sem_wakeup_info *wi, int num_semids, int num)
173 for (i = 0; i < num_semids; i++) {
174 wi[i].wakeup_count = 0;
182 fprintf(stderr, "warning, lockinfo data was %d\n",
185 sb = wi[l->id].sb + wi[l->id].wakeup_count;
186 sb->sem_num = l->index;
188 sb->sem_flg = IPC_NOWAIT;
189 wi[l->id].wakeup_count++;
195 for (i = 0; i < num_semids; i++) {
199 if (!wi[i].wakeup_count)
201 wakeup_total = wi[i].wakeup_count;
202 while(wakeup_total > 0) {
203 cur = wakeup_total > 64 ? 64 : wakeup_total;
204 ret = semtimedop(semid_lookup[i], wi[i].sb + offset,
207 perror("semtimedop");
217 void setup_ipc_sems(struct sem_wakeup_info **wi, int num_semids)
220 *wi = malloc(sizeof(**wi) * num_semids);
221 semid_lookup = malloc(num_semids * sizeof(int));
222 for(i = 0; i < num_semids; i++) {
223 semid_lookup[i] = semget(IPC_PRIVATE, SEMS_PERID,
225 if (semid_lookup[i] < 0) {
233 void cleanup_ipc_sems(int num)
236 for (i = 0; i < num; i++) {
237 semctl(semid_lookup[i], 0, IPC_RMID);
241 struct sem_operations ipc_sem_ops = {
242 .wait = wait_ipc_sem,
243 .wake = ipc_wake_some,
244 .setup = setup_ipc_sems,
245 .cleanup = cleanup_ipc_sems,
246 .name = "ipc sem operations",
249 /* futex post & wait */
250 void wait_futex_sem(struct lockinfo *l)
255 while(l->data == 1) {
256 ret = futex(&l->data, FUTEX_WAIT, 1, NULL, NULL, 0);
258 if (ret && ret != EWOULDBLOCK) {
259 perror("futex wait");
265 int futex_wake_some(struct sem_wakeup_info *wi, int num_semids, int num)
272 for (i = 0; i < num; i++) {
277 fprintf(stderr, "warning, lockinfo data was %d\n",
280 ret = futex(&l->data, FUTEX_WAKE, 1, NULL, NULL, 0);
282 perror("futex wake");
290 void setup_futex_sems(struct sem_wakeup_info **wi, int num_semids)
295 void cleanup_futex_sems(int num)
300 struct sem_operations futex_sem_ops = {
301 .wait = wait_futex_sem,
302 .wake = futex_wake_some,
303 .setup = setup_futex_sems,
304 .cleanup = cleanup_futex_sems,
305 .name = "futex sem operations",
308 /* nanosleep sems here */
309 void wait_nanosleep_sem(struct lockinfo *l)
312 struct timespec tv = { 0, 1000000 };
318 ret = nanosleep(&tv, NULL);
327 int nanosleep_wake_some(struct sem_wakeup_info *wi, int num_semids, int num)
332 for (i = 0; i < num; i++) {
337 fprintf(stderr, "warning, lockinfo data was %d\n",
344 void setup_nanosleep_sems(struct sem_wakeup_info **wi, int num_semids)
349 void cleanup_nanosleep_sems(int num)
354 struct sem_operations nanosleep_sem_ops = {
355 .wait = wait_nanosleep_sem,
356 .wake = nanosleep_wake_some,
357 .setup = setup_nanosleep_sems,
358 .cleanup = cleanup_nanosleep_sems,
359 .name = "nano sleep sem operations",
362 void *worker(void *arg)
364 struct lockinfo *l = (struct lockinfo *)arg;
366 pthread_t tid = pthread_self();
367 size_t pagesize = getpagesize();
368 char *buf = malloc(pagesize);
385 pthread_mutex_lock(&worklist_mutex);
386 total_burns += burn_count;
387 if (burn_count < min_burns)
388 min_burns = burn_count;
389 if (burn_count > max_burns)
390 max_burns = burn_count;
392 pthread_mutex_unlock(&worklist_mutex);
396 void print_usage(void)
398 printf("usage: sembench [-t threads] [-w wake incr] [-r runtime]");
399 printf(" [-o num] (0=ipc, 1=nanosleep, 2=futex)\n");
403 #define NUM_OPERATIONS 3
404 struct sem_operations *allops[NUM_OPERATIONS] = { &ipc_sem_ops,
408 int main(int ac, char **av) {
414 struct sem_wakeup_info *wi = NULL;
415 struct timeval start;
420 int pagesize = getpagesize();
421 char *buf = malloc(pagesize);
422 struct sem_operations *ops = allops[0];
424 cpu_set_t target_mask;
432 for (i = 1; i < ac; i++) {
433 if (strcmp(av[i], "-t") == 0) {
436 num_threads = atoi(av[i+1]);
438 } else if (strcmp(av[i], "-w") == 0) {
441 wake_num = atoi(av[i+1]);
443 } else if (strcmp(av[i], "-r") == 0) {
446 run_secs = atoi(av[i+1]);
448 } else if (strcmp(av[i], "-o") == 0) {
452 index = atoi(av[i+1]);
453 if (index >= NUM_OPERATIONS) {
454 fprintf(stderr, "invalid operations %d\n",
460 } else if (strcmp(av[i], "-T") == 0) {
462 } else if (strcmp(av[i], "-h") == 0) {
466 num_semids = (num_threads + SEMS_PERID - 1) / SEMS_PERID;
467 ops->setup(&wi, num_semids);
469 ret = sched_getaffinity(0, sizeof(cpu_set_t), &cpu_mask);
471 perror("sched_getaffinity");
474 for (i = 0; i < CPU_SETSIZE; i++)
475 if (CPU_ISSET(i, &cpu_mask))
478 fprintf(stderr, "sched_getaffinity returned empty mask\n");
482 CPU_ZERO(&target_mask);
484 worklist = malloc(sizeof(*worklist) * num_threads);
485 memset(worklist, 0, sizeof(*worklist) * num_threads);
487 for (i = 0; i < num_threads; i++) {
497 l->index = sem_num++;
499 if (sem_num >= SEMS_PERID) {
503 ret = pthread_create(&tid, NULL, worker, (void *)l);
505 perror("pthread_create");
509 while (!CPU_ISSET(target_cpu, &cpu_mask)) {
511 if (target_cpu > max_cpu)
514 CPU_SET(target_cpu, &target_mask);
515 ret = pthread_setaffinity_np(tid, sizeof(cpu_set_t),
517 CPU_CLR(target_cpu, &target_mask);
520 ret = pthread_detach(tid);
522 perror("pthread_detach");
526 while(!workers_started) {
530 gettimeofday(&start, NULL);
531 fprintf(stderr, "main loop going\n");
533 ops->wake(wi, num_semids, wake_num);
535 gettimeofday(&now, NULL);
536 if (now.tv_sec - start.tv_sec >= run_secs)
539 fprintf(stderr, "all done\n");
541 while(thread_count > 0) {
542 ops->wake(wi, num_semids, wake_num);
545 printf("%d threads, waking %d at a time\n", num_threads, wake_num);
546 printf("using %s\n", ops->name);
547 printf("main thread burns: %d\n", burn_count);
548 printf("worker burn count total %lu min %lu max %lu avg %lu\n",
549 total_burns, min_burns, max_burns, total_burns / num_threads);
550 printf("run time %d seconds %lu worker burns per second\n",
551 (int)(now.tv_sec - start.tv_sec),
552 total_burns / (now.tv_sec - start.tv_sec));
553 ops->cleanup(num_semids);