synthesize.c 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Benchmark synthesis of perf events such as at the start of a 'perf
  4. * record'. Synthesis is done on the current process and the 'dummy' event
  5. * handlers are invoked that support dump_trace but otherwise do nothing.
  6. *
  7. * Copyright 2019 Google LLC.
  8. */
  9. #include <stdio.h>
  10. #include "bench.h"
  11. #include "../util/debug.h"
  12. #include "../util/session.h"
  13. #include "../util/stat.h"
  14. #include "../util/synthetic-events.h"
  15. #include "../util/target.h"
  16. #include "../util/thread_map.h"
  17. #include "../util/tool.h"
  18. #include "../util/util.h"
  19. #include <linux/atomic.h>
  20. #include <linux/err.h>
  21. #include <linux/time64.h>
  22. #include <subcmd/parse-options.h>
  23. static unsigned int min_threads = 1;
  24. static unsigned int max_threads = UINT_MAX;
  25. static unsigned int single_iterations = 10000;
  26. static unsigned int multi_iterations = 10;
  27. static bool run_st;
  28. static bool run_mt;
  29. static const struct option options[] = {
  30. OPT_BOOLEAN('s', "st", &run_st, "Run single threaded benchmark"),
  31. OPT_BOOLEAN('t', "mt", &run_mt, "Run multi-threaded benchmark"),
  32. OPT_UINTEGER('m', "min-threads", &min_threads,
  33. "Minimum number of threads in multithreaded bench"),
  34. OPT_UINTEGER('M', "max-threads", &max_threads,
  35. "Maximum number of threads in multithreaded bench"),
  36. OPT_UINTEGER('i', "single-iterations", &single_iterations,
  37. "Number of iterations used to compute single-threaded average"),
  38. OPT_UINTEGER('I', "multi-iterations", &multi_iterations,
  39. "Number of iterations used to compute multi-threaded average"),
  40. OPT_END()
  41. };
  42. static const char *const bench_usage[] = {
  43. "perf bench internals synthesize <options>",
  44. NULL
  45. };
  46. static atomic_t event_count;
  47. static int process_synthesized_event(const struct perf_tool *tool __maybe_unused,
  48. union perf_event *event __maybe_unused,
  49. struct perf_sample *sample __maybe_unused,
  50. struct machine *machine __maybe_unused)
  51. {
  52. atomic_inc(&event_count);
  53. return 0;
  54. }
  55. static int do_run_single_threaded(struct perf_session *session,
  56. struct perf_thread_map *threads,
  57. struct target *target, bool data_mmap)
  58. {
  59. const unsigned int nr_threads_synthesize = 1;
  60. struct timeval start, end, diff;
  61. u64 runtime_us;
  62. unsigned int i;
  63. double time_average, time_stddev, event_average, event_stddev;
  64. int err;
  65. struct stats time_stats, event_stats;
  66. init_stats(&time_stats);
  67. init_stats(&event_stats);
  68. for (i = 0; i < single_iterations; i++) {
  69. atomic_set(&event_count, 0);
  70. gettimeofday(&start, NULL);
  71. err = __machine__synthesize_threads(&session->machines.host,
  72. NULL,
  73. target, threads,
  74. process_synthesized_event,
  75. true, data_mmap,
  76. nr_threads_synthesize);
  77. if (err)
  78. return err;
  79. gettimeofday(&end, NULL);
  80. timersub(&end, &start, &diff);
  81. runtime_us = diff.tv_sec * USEC_PER_SEC + diff.tv_usec;
  82. update_stats(&time_stats, runtime_us);
  83. update_stats(&event_stats, atomic_read(&event_count));
  84. }
  85. time_average = avg_stats(&time_stats);
  86. time_stddev = stddev_stats(&time_stats);
  87. printf(" Average %ssynthesis took: %.3f usec (+- %.3f usec)\n",
  88. data_mmap ? "data " : "", time_average, time_stddev);
  89. event_average = avg_stats(&event_stats);
  90. event_stddev = stddev_stats(&event_stats);
  91. printf(" Average num. events: %.3f (+- %.3f)\n",
  92. event_average, event_stddev);
  93. printf(" Average time per event %.3f usec\n",
  94. time_average / event_average);
  95. return 0;
  96. }
  97. static int run_single_threaded(void)
  98. {
  99. struct perf_session *session;
  100. struct target target = {
  101. .pid = "self",
  102. };
  103. struct perf_thread_map *threads;
  104. int err;
  105. perf_set_singlethreaded();
  106. session = perf_session__new(NULL, NULL);
  107. if (IS_ERR(session)) {
  108. pr_err("Session creation failed.\n");
  109. return PTR_ERR(session);
  110. }
  111. threads = thread_map__new_by_pid(getpid());
  112. if (!threads) {
  113. pr_err("Thread map creation failed.\n");
  114. err = -ENOMEM;
  115. goto err_out;
  116. }
  117. puts(
  118. "Computing performance of single threaded perf event synthesis by\n"
  119. "synthesizing events on the perf process itself:");
  120. err = do_run_single_threaded(session, threads, &target, false);
  121. if (err)
  122. goto err_out;
  123. err = do_run_single_threaded(session, threads, &target, true);
  124. err_out:
  125. if (threads)
  126. perf_thread_map__put(threads);
  127. perf_session__delete(session);
  128. return err;
  129. }
  130. static int do_run_multi_threaded(struct target *target,
  131. unsigned int nr_threads_synthesize)
  132. {
  133. struct timeval start, end, diff;
  134. u64 runtime_us;
  135. unsigned int i;
  136. double time_average, time_stddev, event_average, event_stddev;
  137. int err;
  138. struct stats time_stats, event_stats;
  139. struct perf_session *session;
  140. init_stats(&time_stats);
  141. init_stats(&event_stats);
  142. for (i = 0; i < multi_iterations; i++) {
  143. session = perf_session__new(NULL, NULL);
  144. if (IS_ERR(session))
  145. return PTR_ERR(session);
  146. atomic_set(&event_count, 0);
  147. gettimeofday(&start, NULL);
  148. err = __machine__synthesize_threads(&session->machines.host,
  149. NULL,
  150. target, NULL,
  151. process_synthesized_event,
  152. true, false,
  153. nr_threads_synthesize);
  154. if (err) {
  155. perf_session__delete(session);
  156. return err;
  157. }
  158. gettimeofday(&end, NULL);
  159. timersub(&end, &start, &diff);
  160. runtime_us = diff.tv_sec * USEC_PER_SEC + diff.tv_usec;
  161. update_stats(&time_stats, runtime_us);
  162. update_stats(&event_stats, atomic_read(&event_count));
  163. perf_session__delete(session);
  164. }
  165. time_average = avg_stats(&time_stats);
  166. time_stddev = stddev_stats(&time_stats);
  167. printf(" Average synthesis took: %.3f usec (+- %.3f usec)\n",
  168. time_average, time_stddev);
  169. event_average = avg_stats(&event_stats);
  170. event_stddev = stddev_stats(&event_stats);
  171. printf(" Average num. events: %.3f (+- %.3f)\n",
  172. event_average, event_stddev);
  173. printf(" Average time per event %.3f usec\n",
  174. time_average / event_average);
  175. return 0;
  176. }
  177. static int run_multi_threaded(void)
  178. {
  179. struct target target = {
  180. .cpu_list = "0"
  181. };
  182. unsigned int nr_threads_synthesize;
  183. int err;
  184. if (max_threads == UINT_MAX)
  185. max_threads = sysconf(_SC_NPROCESSORS_ONLN);
  186. puts(
  187. "Computing performance of multi threaded perf event synthesis by\n"
  188. "synthesizing events on CPU 0:");
  189. for (nr_threads_synthesize = min_threads;
  190. nr_threads_synthesize <= max_threads;
  191. nr_threads_synthesize++) {
  192. if (nr_threads_synthesize == 1)
  193. perf_set_singlethreaded();
  194. else
  195. perf_set_multithreaded();
  196. printf(" Number of synthesis threads: %u\n",
  197. nr_threads_synthesize);
  198. err = do_run_multi_threaded(&target, nr_threads_synthesize);
  199. if (err)
  200. return err;
  201. }
  202. perf_set_singlethreaded();
  203. return 0;
  204. }
  205. int bench_synthesize(int argc, const char **argv)
  206. {
  207. int err = 0;
  208. argc = parse_options(argc, argv, options, bench_usage, 0);
  209. if (argc) {
  210. usage_with_options(bench_usage, options);
  211. exit(EXIT_FAILURE);
  212. }
  213. /*
  214. * If neither single threaded or multi-threaded are specified, default
  215. * to running just single threaded.
  216. */
  217. if (!run_st && !run_mt)
  218. run_st = true;
  219. if (run_st)
  220. err = run_single_threaded();
  221. if (!err && run_mt)
  222. err = run_multi_threaded();
  223. return err;
  224. }