futex-lock-pi.c 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Copyright (C) 2015 Davidlohr Bueso.
  4. */
  5. /* For the CLR_() macros */
  6. #include <string.h>
  7. #include <pthread.h>
  8. #include <signal.h>
  9. #include "../util/stat.h"
  10. #include <subcmd/parse-options.h>
  11. #include <linux/compiler.h>
  12. #include <linux/kernel.h>
  13. #include <errno.h>
  14. #include "bench.h"
  15. #include "futex.h"
  16. #include "cpumap.h"
  17. #include <err.h>
  18. #include <stdlib.h>
  19. #include <sys/time.h>
  20. struct worker {
  21. int tid;
  22. u_int32_t *futex;
  23. pthread_t thread;
  24. unsigned long ops;
  25. };
  26. static u_int32_t global_futex = 0;
  27. static struct worker *worker;
  28. static unsigned int nsecs = 10;
  29. static bool silent = false, multi = false;
  30. static bool done = false, fshared = false;
  31. static unsigned int nthreads = 0;
  32. static int futex_flag = 0;
  33. struct timeval start, end, runtime;
  34. static pthread_mutex_t thread_lock;
  35. static unsigned int threads_starting;
  36. static struct stats throughput_stats;
  37. static pthread_cond_t thread_parent, thread_worker;
  38. static const struct option options[] = {
  39. OPT_UINTEGER('t', "threads", &nthreads, "Specify amount of threads"),
  40. OPT_UINTEGER('r', "runtime", &nsecs, "Specify runtime (in seconds)"),
  41. OPT_BOOLEAN( 'M', "multi", &multi, "Use multiple futexes"),
  42. OPT_BOOLEAN( 's', "silent", &silent, "Silent mode: do not display data/details"),
  43. OPT_BOOLEAN( 'S', "shared", &fshared, "Use shared futexes instead of private ones"),
  44. OPT_END()
  45. };
  46. static const char * const bench_futex_lock_pi_usage[] = {
  47. "perf bench futex lock-pi <options>",
  48. NULL
  49. };
  50. static void print_summary(void)
  51. {
  52. unsigned long avg = avg_stats(&throughput_stats);
  53. double stddev = stddev_stats(&throughput_stats);
  54. printf("%sAveraged %ld operations/sec (+- %.2f%%), total secs = %d\n",
  55. !silent ? "\n" : "", avg, rel_stddev_stats(stddev, avg),
  56. (int) runtime.tv_sec);
  57. }
  58. static void toggle_done(int sig __maybe_unused,
  59. siginfo_t *info __maybe_unused,
  60. void *uc __maybe_unused)
  61. {
  62. /* inform all threads that we're done for the day */
  63. done = true;
  64. gettimeofday(&end, NULL);
  65. timersub(&end, &start, &runtime);
  66. }
  67. static void *workerfn(void *arg)
  68. {
  69. struct worker *w = (struct worker *) arg;
  70. unsigned long ops = w->ops;
  71. pthread_mutex_lock(&thread_lock);
  72. threads_starting--;
  73. if (!threads_starting)
  74. pthread_cond_signal(&thread_parent);
  75. pthread_cond_wait(&thread_worker, &thread_lock);
  76. pthread_mutex_unlock(&thread_lock);
  77. do {
  78. int ret;
  79. again:
  80. ret = futex_lock_pi(w->futex, NULL, futex_flag);
  81. if (ret) { /* handle lock acquisition */
  82. if (!silent)
  83. warn("thread %d: Could not lock pi-lock for %p (%d)",
  84. w->tid, w->futex, ret);
  85. if (done)
  86. break;
  87. goto again;
  88. }
  89. usleep(1);
  90. ret = futex_unlock_pi(w->futex, futex_flag);
  91. if (ret && !silent)
  92. warn("thread %d: Could not unlock pi-lock for %p (%d)",
  93. w->tid, w->futex, ret);
  94. ops++; /* account for thread's share of work */
  95. } while (!done);
  96. w->ops = ops;
  97. return NULL;
  98. }
  99. static void create_threads(struct worker *w, pthread_attr_t thread_attr,
  100. struct cpu_map *cpu)
  101. {
  102. cpu_set_t cpuset;
  103. unsigned int i;
  104. threads_starting = nthreads;
  105. for (i = 0; i < nthreads; i++) {
  106. worker[i].tid = i;
  107. if (multi) {
  108. worker[i].futex = calloc(1, sizeof(u_int32_t));
  109. if (!worker[i].futex)
  110. err(EXIT_FAILURE, "calloc");
  111. } else
  112. worker[i].futex = &global_futex;
  113. CPU_ZERO(&cpuset);
  114. CPU_SET(cpu->map[i % cpu->nr], &cpuset);
  115. if (pthread_attr_setaffinity_np(&thread_attr, sizeof(cpu_set_t), &cpuset))
  116. err(EXIT_FAILURE, "pthread_attr_setaffinity_np");
  117. if (pthread_create(&w[i].thread, &thread_attr, workerfn, &worker[i]))
  118. err(EXIT_FAILURE, "pthread_create");
  119. }
  120. }
  121. int bench_futex_lock_pi(int argc, const char **argv)
  122. {
  123. int ret = 0;
  124. unsigned int i;
  125. struct sigaction act;
  126. pthread_attr_t thread_attr;
  127. struct cpu_map *cpu;
  128. argc = parse_options(argc, argv, options, bench_futex_lock_pi_usage, 0);
  129. if (argc)
  130. goto err;
  131. cpu = cpu_map__new(NULL);
  132. if (!cpu)
  133. err(EXIT_FAILURE, "calloc");
  134. sigfillset(&act.sa_mask);
  135. act.sa_sigaction = toggle_done;
  136. sigaction(SIGINT, &act, NULL);
  137. if (!nthreads)
  138. nthreads = cpu->nr;
  139. worker = calloc(nthreads, sizeof(*worker));
  140. if (!worker)
  141. err(EXIT_FAILURE, "calloc");
  142. if (!fshared)
  143. futex_flag = FUTEX_PRIVATE_FLAG;
  144. printf("Run summary [PID %d]: %d threads doing pi lock/unlock pairing for %d secs.\n\n",
  145. getpid(), nthreads, nsecs);
  146. init_stats(&throughput_stats);
  147. pthread_mutex_init(&thread_lock, NULL);
  148. pthread_cond_init(&thread_parent, NULL);
  149. pthread_cond_init(&thread_worker, NULL);
  150. threads_starting = nthreads;
  151. pthread_attr_init(&thread_attr);
  152. gettimeofday(&start, NULL);
  153. create_threads(worker, thread_attr, cpu);
  154. pthread_attr_destroy(&thread_attr);
  155. pthread_mutex_lock(&thread_lock);
  156. while (threads_starting)
  157. pthread_cond_wait(&thread_parent, &thread_lock);
  158. pthread_cond_broadcast(&thread_worker);
  159. pthread_mutex_unlock(&thread_lock);
  160. sleep(nsecs);
  161. toggle_done(0, NULL, NULL);
  162. for (i = 0; i < nthreads; i++) {
  163. ret = pthread_join(worker[i].thread, NULL);
  164. if (ret)
  165. err(EXIT_FAILURE, "pthread_join");
  166. }
  167. /* cleanup & report results */
  168. pthread_cond_destroy(&thread_parent);
  169. pthread_cond_destroy(&thread_worker);
  170. pthread_mutex_destroy(&thread_lock);
  171. for (i = 0; i < nthreads; i++) {
  172. unsigned long t = worker[i].ops/runtime.tv_sec;
  173. update_stats(&throughput_stats, t);
  174. if (!silent)
  175. printf("[thread %3d] futex: %p [ %ld ops/sec ]\n",
  176. worker[i].tid, worker[i].futex, t);
  177. if (multi)
  178. free(worker[i].futex);
  179. }
  180. print_summary();
  181. free(worker);
  182. return ret;
  183. err:
  184. usage_with_options(bench_futex_lock_pi_usage, options);
  185. exit(EXIT_FAILURE);
  186. }