test/benchmark/sembench.c - ktap

Global variables defined

Data types defined

Functions defined

Macros defined

Source code

  1. /*
  2. * copyright Oracle 2007.  Licensed under GPLv2
  3. * To compile: gcc -Wall -o sembench sembench.c -lpthread
  4. *
  5. * usage: sembench -t thread count -w wakenum -r runtime -o op
  6. * op can be: 0 (ipc sem) 1 (nanosleep) 2 (futexes)
  7. *
  8. * example:
  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
  11. * futex locking.
  12. *
  13. */
  14. #define  _GNU_SOURCE
  15. #define _POSIX_C_SOURCE 199309
  16. #include <fcntl.h>
  17. #include <sched.h>
  18. #include <stdio.h>
  19. #include <stdlib.h>
  20. #include <sys/sem.h>
  21. #include <sys/ipc.h>
  22. #include <sys/types.h>
  23. #include <sys/mman.h>
  24. #include <pthread.h>
  25. #include <unistd.h>
  26. #include <string.h>
  27. #include <time.h>
  28. #include <sys/time.h>
  29. #include <sys/syscall.h>
  30. #include <errno.h>

  31. #define VERSION "0.2"

  32. /* futexes have been around since 2.5.something, but it still seems I
  33. * need to make my own syscall.  Sigh.
  34. */
  35. #define FUTEX_WAIT              0
  36. #define FUTEX_WAKE              1
  37. #define FUTEX_FD                2
  38. #define FUTEX_REQUEUE           3
  39. #define FUTEX_CMP_REQUEUE       4
  40. #define FUTEX_WAKE_OP           5
  41. static inline int futex (int *uaddr, int op, int val,
  42.              const struct timespec *timeout,
  43.              int *uaddr2, int val3)
  44. {
  45.     return syscall(__NR_futex, uaddr, op, val, timeout, uaddr2, val3);
  46. }

  47. static void smp_mb(void)
  48. {
  49.     __sync_synchronize();
  50. }

  51. static int all_done = 0;
  52. static int timeout_test = 0;

  53. #define SEMS_PERID 250

  54. struct sem_operations;

  55. struct lockinfo {
  56.     unsigned long id;
  57.     unsigned long index;
  58.     int data;
  59.     pthread_t tid;
  60.     struct lockinfo *next;
  61.     struct sem_operations *ops;
  62.     unsigned long ready;
  63. };

  64. struct sem_wakeup_info {
  65.     int wakeup_count;
  66.     struct sembuf sb[SEMS_PERID];
  67. };

  68. struct sem_operations {
  69.     void (*wait)(struct lockinfo *l);
  70.     int (*wake)(struct sem_wakeup_info *wi, int num_semids, int num);
  71.     void (*setup)(struct sem_wakeup_info **wi, int num_semids);
  72.     void (*cleanup)(int num_semids);
  73.     char *name;
  74. };

  75. int *semid_lookup = NULL;

  76. pthread_mutex_t worklist_mutex = PTHREAD_MUTEX_INITIALIZER;
  77. static unsigned long total_burns = 0;
  78. static unsigned long min_burns = ~0UL;
  79. static unsigned long max_burns = 0;

  80. /* currently running threads */
  81. static int thread_count = 0;

  82. struct lockinfo *worklist = NULL;
  83. static int workers_started = 0;

  84. /* total threads started */
  85. static int num_threads = 2048;

  86. static void worklist_add(struct lockinfo *l)
  87. {
  88.     smp_mb();
  89.     l->ready = 1;
  90. }

  91. static struct lockinfo *worklist_rm(void)
  92. {
  93.     static int last_index = 0;
  94.     int i;
  95.     struct lockinfo *l;

  96.     for (i = 0; i < num_threads; i++) {
  97.         int test = (last_index + i) % num_threads;

  98.         l = worklist + test;
  99.         smp_mb();
  100.         if (l->ready) {
  101.             l->ready = 0;
  102.             last_index = test;
  103.             return l;
  104.         }
  105.     }
  106.     return NULL;
  107. }

  108. /* ipc semaphore post& wait */
  109. void wait_ipc_sem(struct lockinfo *l)
  110. {
  111.     struct sembuf sb;
  112.     int ret;
  113.     struct timespec *tvp = NULL;
  114.     struct timespec tv = { 0, 1 };

  115.     sb.sem_num = l->index;
  116.     sb.sem_flg = 0;

  117.     sb.sem_op = -1;
  118.     l->data = 1;

  119.     if (timeout_test && (l->id % 5) == 0)
  120.         tvp = &tv;

  121.     worklist_add(l);
  122.     ret = semtimedop(semid_lookup[l->id], &sb, 1, tvp);

  123.     while(l->data != 0 && tvp) {
  124.         struct timespec tv2 = { 0, 500 };
  125.         nanosleep(&tv2, NULL);
  126.     }

  127.     if (l->data != 0) {
  128.         if (tvp)
  129.             return;
  130.         fprintf(stderr, "wakeup without data update\n");
  131.         exit(1);
  132.     }
  133.     if (ret) {
  134.         if (errno == EAGAIN && tvp)
  135.             return;
  136.         perror("semtimed op");
  137.         exit(1);
  138.     }
  139. }

  140. int ipc_wake_some(struct sem_wakeup_info *wi, int num_semids, int num)
  141. {
  142.     int i;
  143.     int ret;
  144.     struct lockinfo *l;
  145.     int found = 0;

  146.     for (i = 0; i < num_semids; i++) {
  147.         wi[i].wakeup_count = 0;
  148.     }
  149.     while(num > 0) {
  150.         struct sembuf *sb;
  151.         l = worklist_rm();
  152.         if (!l)
  153.             break;
  154.         if (l->data != 1)
  155.             fprintf(stderr, "warning, lockinfo data was %d\n",
  156.                 l->data);
  157.         l->data = 0;
  158.         sb = wi[l->id].sb + wi[l->id].wakeup_count;
  159.         sb->sem_num = l->index;
  160.         sb->sem_op = 1;
  161.         sb->sem_flg = IPC_NOWAIT;
  162.         wi[l->id].wakeup_count++;
  163.         found++;
  164.         num--;
  165.     }
  166.     if (!found)
  167.         return 0;
  168.     for (i = 0; i < num_semids; i++) {
  169.         int wakeup_total;
  170.         int cur;
  171.         int offset = 0;
  172.         if (!wi[i].wakeup_count)
  173.             continue;
  174.         wakeup_total = wi[i].wakeup_count;
  175.         while(wakeup_total > 0) {
  176.             cur = wakeup_total > 64 ? 64 : wakeup_total;
  177.             ret = semtimedop(semid_lookup[i], wi[i].sb + offset,
  178.                      cur, NULL);
  179.             if (ret) {
  180.                 perror("semtimedop");
  181.                 exit(1);
  182.             }
  183.             offset += cur;
  184.             wakeup_total -= cur;
  185.         }
  186.     }
  187.     return found;
  188. }

  189. void setup_ipc_sems(struct sem_wakeup_info **wi, int num_semids)
  190. {
  191.     int i;
  192.     *wi = malloc(sizeof(**wi) * num_semids);
  193.     semid_lookup = malloc(num_semids * sizeof(int));
  194.     for(i = 0; i < num_semids; i++) {
  195.         semid_lookup[i] = semget(IPC_PRIVATE, SEMS_PERID,
  196.                      IPC_CREAT | 0777);
  197.         if (semid_lookup[i] < 0) {
  198.             perror("semget");
  199.             exit(1);
  200.         }
  201.     }
  202.     sleep(10);
  203. }

  204. void cleanup_ipc_sems(int num)
  205. {
  206.     int i;
  207.     for (i = 0; i < num; i++) {
  208.         semctl(semid_lookup[i], 0, IPC_RMID);
  209.     }
  210. }

  211. struct sem_operations ipc_sem_ops = {
  212.     .wait = wait_ipc_sem,
  213.     .wake = ipc_wake_some,
  214.     .setup = setup_ipc_sems,
  215.     .cleanup = cleanup_ipc_sems,
  216.     .name = "ipc sem operations",
  217. };

  218. /* futex post & wait */
  219. void wait_futex_sem(struct lockinfo *l)
  220. {
  221.     int ret;
  222.     l->data = 1;
  223.     worklist_add(l);
  224.     while(l->data == 1) {
  225.         ret = futex(&l->data, FUTEX_WAIT, 1, NULL, NULL, 0);
  226.         /*
  227.         if (ret && ret != EWOULDBLOCK) {
  228.             perror("futex wait");
  229.             exit(1);
  230.         }*/
  231.     }
  232. }

  233. int futex_wake_some(struct sem_wakeup_info *wi, int num_semids, int num)
  234. {
  235.     int i;
  236.     int ret;
  237.     struct lockinfo *l;
  238.     int found = 0;

  239.     for (i = 0; i < num; i++) {
  240.         l = worklist_rm();
  241.         if (!l)
  242.             break;
  243.         if (l->data != 1)
  244.             fprintf(stderr, "warning, lockinfo data was %d\n",
  245.                 l->data);
  246.         l->data = 0;
  247.         ret = futex(&l->data, FUTEX_WAKE, 1, NULL, NULL, 0);
  248.         if (ret < 0) {
  249.             perror("futex wake");
  250.             exit(1);
  251.         }
  252.         found++;
  253.     }
  254.     return found;
  255. }

  256. void setup_futex_sems(struct sem_wakeup_info **wi, int num_semids)
  257. {
  258.     return;
  259. }

  260. void cleanup_futex_sems(int num)
  261. {
  262.     return;
  263. }

  264. struct sem_operations futex_sem_ops = {
  265.     .wait = wait_futex_sem,
  266.     .wake = futex_wake_some,
  267.     .setup = setup_futex_sems,
  268.     .cleanup = cleanup_futex_sems,
  269.     .name = "futex sem operations",
  270. };

  271. /* nanosleep sems here */
  272. void wait_nanosleep_sem(struct lockinfo *l)
  273. {
  274.     int ret;
  275.     struct timespec tv = { 0, 1000000 };
  276.     int count = 0;

  277.     l->data = 1;
  278.     worklist_add(l);
  279.     while(l->data) {
  280.         ret = nanosleep(&tv, NULL);
  281.         if (ret) {
  282.             perror("nanosleep");
  283.             exit(1);
  284.         }
  285.         count++;
  286.     }
  287. }

  288. int nanosleep_wake_some(struct sem_wakeup_info *wi, int num_semids, int num)
  289. {
  290.     int i;
  291.     struct lockinfo *l;

  292.     for (i = 0; i < num; i++) {
  293.         l = worklist_rm();
  294.         if (!l)
  295.             break;
  296.         if (l->data != 1)
  297.             fprintf(stderr, "warning, lockinfo data was %d\n",
  298.                 l->data);
  299.         l->data = 0;
  300.     }
  301.     return i;
  302. }

  303. void setup_nanosleep_sems(struct sem_wakeup_info **wi, int num_semids)
  304. {
  305.     return;
  306. }

  307. void cleanup_nanosleep_sems(int num)
  308. {
  309.     return;
  310. }

  311. struct sem_operations nanosleep_sem_ops = {
  312.     .wait = wait_nanosleep_sem,
  313.     .wake = nanosleep_wake_some,
  314.     .setup = setup_nanosleep_sems,
  315.     .cleanup = cleanup_nanosleep_sems,
  316.     .name = "nano sleep sem operations",
  317. };

  318. void *worker(void *arg)
  319. {
  320.     struct lockinfo *l = (struct lockinfo *)arg;
  321.     int burn_count = 0;
  322.     pthread_t tid = pthread_self();
  323.     size_t pagesize = getpagesize();
  324.     char *buf = malloc(pagesize);

  325.     if (!buf) {
  326.         perror("malloc");
  327.         exit(1);
  328.     }

  329.     l->tid = tid;
  330.     workers_started = 1;
  331.     smp_mb();

  332.     while(!all_done) {
  333.         l->ops->wait(l);
  334.         if (all_done)
  335.             break;
  336.         burn_count++;
  337.     }
  338.     pthread_mutex_lock(&worklist_mutex);
  339.     total_burns += burn_count;
  340.     if (burn_count < min_burns)
  341.         min_burns = burn_count;
  342.     if (burn_count > max_burns)
  343.         max_burns = burn_count;
  344.     thread_count--;
  345.     pthread_mutex_unlock(&worklist_mutex);
  346.     return (void *)0;
  347. }

  348. void print_usage(void)
  349. {
  350.     printf("usage: sembench [-t threads] [-w wake incr] [-r runtime]");
  351.     printf("                [-o num] (0=ipc, 1=nanosleep, 2=futex)\n");
  352.     exit(1);
  353. }

  354. #define NUM_OPERATIONS 3
  355. struct sem_operations *allops[NUM_OPERATIONS] = { &ipc_sem_ops,
  356.                         &nanosleep_sem_ops,
  357.                         &futex_sem_ops};

  358. int main(int ac, char **av) {
  359.     int ret;
  360.     int i;
  361.     int semid = 0;
  362.     int sem_num = 0;
  363.     int burn_count = 0;
  364.     struct sem_wakeup_info *wi = NULL;
  365.     struct timeval start;
  366.     struct timeval now;
  367.     int num_semids = 0;
  368.     int wake_num = 256;
  369.     int run_secs = 30;
  370.     int pagesize = getpagesize();
  371.     char *buf = malloc(pagesize);
  372.     struct sem_operations *ops = allops[0];
  373.     cpu_set_t cpu_mask;
  374.     cpu_set_t target_mask;
  375.     int target_cpu = 0;
  376.     int max_cpu = -1;

  377.     if (!buf) {
  378.         perror("malloc");
  379.         exit(1);
  380.     }
  381.     for (i = 1; i < ac; i++) {
  382.         if (strcmp(av[i], "-t") == 0) {
  383.             if (i == ac -1)
  384.                 print_usage();
  385.             num_threads = atoi(av[i+1]);
  386.             i++;
  387.         } else if (strcmp(av[i], "-w") == 0) {
  388.             if (i == ac -1)
  389.                 print_usage();
  390.             wake_num = atoi(av[i+1]);
  391.             i++;
  392.         } else if (strcmp(av[i], "-r") == 0) {
  393.             if (i == ac -1)
  394.                 print_usage();
  395.             run_secs = atoi(av[i+1]);
  396.             i++;
  397.         } else if (strcmp(av[i], "-o") == 0) {
  398.             int index;
  399.             if (i == ac -1)
  400.                 print_usage();
  401.             index = atoi(av[i+1]);
  402.             if (index >= NUM_OPERATIONS) {
  403.                 fprintf(stderr, "invalid operations %d\n",
  404.                     index);
  405.                 exit(1);
  406.             }
  407.             ops = allops[index];
  408.             i++;
  409.         } else if (strcmp(av[i], "-T") == 0) {
  410.             timeout_test = 1;
  411.         } else if (strcmp(av[i], "-h") == 0) {
  412.             print_usage();
  413.         }
  414.     }
  415.     num_semids = (num_threads + SEMS_PERID - 1) / SEMS_PERID;
  416.     ops->setup(&wi, num_semids);

  417.     ret = sched_getaffinity(0, sizeof(cpu_set_t), &cpu_mask);
  418.     if (ret) {
  419.         perror("sched_getaffinity");
  420.         exit(1);
  421.     }
  422.     for (i = 0; i < CPU_SETSIZE; i++)
  423.         if (CPU_ISSET(i, &cpu_mask))
  424.             max_cpu = i;
  425.     if (max_cpu == -1) {
  426.         fprintf(stderr, "sched_getaffinity returned empty mask\n");
  427.         exit(1);
  428.     }

  429.     CPU_ZERO(&target_mask);

  430.     worklist = malloc(sizeof(*worklist) * num_threads);
  431.     memset(worklist, 0, sizeof(*worklist) * num_threads);

  432.     for (i = 0; i < num_threads; i++) {
  433.         struct lockinfo *l;
  434.         pthread_t tid;
  435.         thread_count++;
  436.         l = worklist + i;
  437.         if (!l) {
  438.             perror("malloc");
  439.             exit(1);
  440.         }
  441.         l->id = semid;
  442.         l->index = sem_num++;
  443.         l->ops = ops;
  444.         if (sem_num >= SEMS_PERID) {
  445.             semid++;
  446.             sem_num = 0;
  447.         }
  448.         ret = pthread_create(&tid, NULL, worker, (void *)l);
  449.         if (ret) {
  450.             perror("pthread_create");
  451.             exit(1);
  452.         }

  453.         while (!CPU_ISSET(target_cpu, &cpu_mask)) {
  454.             target_cpu++;
  455.             if (target_cpu > max_cpu)
  456.                 target_cpu = 0;
  457.         }
  458.         CPU_SET(target_cpu, &target_mask);
  459.         ret = pthread_setaffinity_np(tid, sizeof(cpu_set_t),
  460.                          &target_mask);
  461.         CPU_CLR(target_cpu, &target_mask);
  462.         target_cpu++;

  463.         ret = pthread_detach(tid);
  464.         if (ret) {
  465.             perror("pthread_detach");
  466.             exit(1);
  467.         }
  468.     }
  469.     while(!workers_started) {
  470.         smp_mb();
  471.         usleep(200);
  472.     }
  473.     gettimeofday(&start, NULL);
  474.     //fprintf(stderr, "main loop going\n");
  475.     while(1) {
  476.         ops->wake(wi, num_semids, wake_num);
  477.         burn_count++;
  478.         gettimeofday(&now, NULL);
  479.         if (now.tv_sec - start.tv_sec >= run_secs)
  480.             break;
  481.     }
  482.     //fprintf(stderr, "all done\n");
  483.     all_done = 1;
  484.     while(thread_count > 0) {
  485.         ops->wake(wi, num_semids, wake_num);
  486.         usleep(200);
  487.     }
  488.     //printf("%d threads, waking %d at a time\n", num_threads, wake_num);
  489.     //printf("using %s\n", ops->name);
  490.     //printf("main thread burns: %d\n", burn_count);
  491.     //printf("worker burn count total %lu min %lu max %lu avg %lu\n",
  492.     //       total_burns, min_burns, max_burns, total_burns / num_threads);
  493.     printf("%d seconds: %lu worker burns per second\n",
  494.         (int)(now.tv_sec - start.tv_sec),
  495.         total_burns / (now.tv_sec - start.tv_sec));
  496.     ops->cleanup(num_semids);
  497.     return 0;
  498. }