sched-messaging.c 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. /*
  2. *
  3. * sched-messaging.c
  4. *
  5. * messaging: Benchmark for scheduler and IPC mechanisms
  6. *
  7. * Based on hackbench by Rusty Russell <rusty@rustcorp.com.au>
  8. * Ported to perf by Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
  9. *
  10. */
  11. #include "../perf.h"
  12. #include "../util/util.h"
  13. #include <subcmd/parse-options.h>
  14. #include "../builtin.h"
  15. #include "bench.h"
  16. /* Test groups of 20 processes spraying to 20 receivers */
  17. #include <pthread.h>
  18. #include <stdio.h>
  19. #include <stdlib.h>
  20. #include <string.h>
  21. #include <errno.h>
  22. #include <unistd.h>
  23. #include <sys/types.h>
  24. #include <sys/socket.h>
  25. #include <sys/wait.h>
  26. #include <sys/time.h>
  27. #include <poll.h>
  28. #include <limits.h>
  29. #include <err.h>
  30. #include <linux/time64.h>
  31. #define DATASIZE 100
  32. static bool use_pipes = false;
  33. static unsigned int nr_loops = 100;
  34. static bool thread_mode = false;
  35. static unsigned int num_groups = 10;
  36. struct sender_context {
  37. unsigned int num_fds;
  38. int ready_out;
  39. int wakefd;
  40. int out_fds[0];
  41. };
  42. struct receiver_context {
  43. unsigned int num_packets;
  44. int in_fds[2];
  45. int ready_out;
  46. int wakefd;
  47. };
  48. static void fdpair(int fds[2])
  49. {
  50. if (use_pipes) {
  51. if (pipe(fds) == 0)
  52. return;
  53. } else {
  54. if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0)
  55. return;
  56. }
  57. err(EXIT_FAILURE, use_pipes ? "pipe()" : "socketpair()");
  58. }
  59. /* Block until we're ready to go */
  60. static void ready(int ready_out, int wakefd)
  61. {
  62. char dummy;
  63. struct pollfd pollfd = { .fd = wakefd, .events = POLLIN };
  64. /* Tell them we're ready. */
  65. if (write(ready_out, &dummy, 1) != 1)
  66. err(EXIT_FAILURE, "CLIENT: ready write");
  67. /* Wait for "GO" signal */
  68. if (poll(&pollfd, 1, -1) != 1)
  69. err(EXIT_FAILURE, "poll");
  70. }
  71. /* Sender sprays nr_loops messages down each file descriptor */
  72. static void *sender(struct sender_context *ctx)
  73. {
  74. char data[DATASIZE];
  75. unsigned int i, j;
  76. ready(ctx->ready_out, ctx->wakefd);
  77. /* Now pump to every receiver. */
  78. for (i = 0; i < nr_loops; i++) {
  79. for (j = 0; j < ctx->num_fds; j++) {
  80. int ret, done = 0;
  81. again:
  82. ret = write(ctx->out_fds[j], data + done,
  83. sizeof(data)-done);
  84. if (ret < 0)
  85. err(EXIT_FAILURE, "SENDER: write");
  86. done += ret;
  87. if (done < DATASIZE)
  88. goto again;
  89. }
  90. }
  91. return NULL;
  92. }
  93. /* One receiver per fd */
  94. static void *receiver(struct receiver_context* ctx)
  95. {
  96. unsigned int i;
  97. if (!thread_mode)
  98. close(ctx->in_fds[1]);
  99. /* Wait for start... */
  100. ready(ctx->ready_out, ctx->wakefd);
  101. /* Receive them all */
  102. for (i = 0; i < ctx->num_packets; i++) {
  103. char data[DATASIZE];
  104. int ret, done = 0;
  105. again:
  106. ret = read(ctx->in_fds[0], data + done, DATASIZE - done);
  107. if (ret < 0)
  108. err(EXIT_FAILURE, "SERVER: read");
  109. done += ret;
  110. if (done < DATASIZE)
  111. goto again;
  112. }
  113. return NULL;
  114. }
  115. static pthread_t create_worker(void *ctx, void *(*func)(void *))
  116. {
  117. pthread_attr_t attr;
  118. pthread_t childid;
  119. int ret;
  120. if (!thread_mode) {
  121. /* process mode */
  122. /* Fork the receiver. */
  123. switch (fork()) {
  124. case -1:
  125. err(EXIT_FAILURE, "fork()");
  126. break;
  127. case 0:
  128. (*func) (ctx);
  129. exit(0);
  130. break;
  131. default:
  132. break;
  133. }
  134. return (pthread_t)0;
  135. }
  136. if (pthread_attr_init(&attr) != 0)
  137. err(EXIT_FAILURE, "pthread_attr_init:");
  138. #ifndef __ia64__
  139. if (pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN) != 0)
  140. err(EXIT_FAILURE, "pthread_attr_setstacksize");
  141. #endif
  142. ret = pthread_create(&childid, &attr, func, ctx);
  143. if (ret != 0)
  144. err(EXIT_FAILURE, "pthread_create failed");
  145. return childid;
  146. }
  147. static void reap_worker(pthread_t id)
  148. {
  149. int proc_status;
  150. void *thread_status;
  151. if (!thread_mode) {
  152. /* process mode */
  153. wait(&proc_status);
  154. if (!WIFEXITED(proc_status))
  155. exit(1);
  156. } else {
  157. pthread_join(id, &thread_status);
  158. }
  159. }
  160. /* One group of senders and receivers */
  161. static unsigned int group(pthread_t *pth,
  162. unsigned int num_fds,
  163. int ready_out,
  164. int wakefd)
  165. {
  166. unsigned int i;
  167. struct sender_context *snd_ctx = malloc(sizeof(struct sender_context)
  168. + num_fds * sizeof(int));
  169. if (!snd_ctx)
  170. err(EXIT_FAILURE, "malloc()");
  171. for (i = 0; i < num_fds; i++) {
  172. int fds[2];
  173. struct receiver_context *ctx = malloc(sizeof(*ctx));
  174. if (!ctx)
  175. err(EXIT_FAILURE, "malloc()");
  176. /* Create the pipe between client and server */
  177. fdpair(fds);
  178. ctx->num_packets = num_fds * nr_loops;
  179. ctx->in_fds[0] = fds[0];
  180. ctx->in_fds[1] = fds[1];
  181. ctx->ready_out = ready_out;
  182. ctx->wakefd = wakefd;
  183. pth[i] = create_worker(ctx, (void *)receiver);
  184. snd_ctx->out_fds[i] = fds[1];
  185. if (!thread_mode)
  186. close(fds[0]);
  187. }
  188. /* Now we have all the fds, fork the senders */
  189. for (i = 0; i < num_fds; i++) {
  190. snd_ctx->ready_out = ready_out;
  191. snd_ctx->wakefd = wakefd;
  192. snd_ctx->num_fds = num_fds;
  193. pth[num_fds+i] = create_worker(snd_ctx, (void *)sender);
  194. }
  195. /* Close the fds we have left */
  196. if (!thread_mode)
  197. for (i = 0; i < num_fds; i++)
  198. close(snd_ctx->out_fds[i]);
  199. /* Return number of children to reap */
  200. return num_fds * 2;
  201. }
  202. static const struct option options[] = {
  203. OPT_BOOLEAN('p', "pipe", &use_pipes,
  204. "Use pipe() instead of socketpair()"),
  205. OPT_BOOLEAN('t', "thread", &thread_mode,
  206. "Be multi thread instead of multi process"),
  207. OPT_UINTEGER('g', "group", &num_groups, "Specify number of groups"),
  208. OPT_UINTEGER('l', "nr_loops", &nr_loops, "Specify the number of loops to run (default: 100)"),
  209. OPT_END()
  210. };
  211. static const char * const bench_sched_message_usage[] = {
  212. "perf bench sched messaging <options>",
  213. NULL
  214. };
  215. int bench_sched_messaging(int argc, const char **argv,
  216. const char *prefix __maybe_unused)
  217. {
  218. unsigned int i, total_children;
  219. struct timeval start, stop, diff;
  220. unsigned int num_fds = 20;
  221. int readyfds[2], wakefds[2];
  222. char dummy;
  223. pthread_t *pth_tab;
  224. argc = parse_options(argc, argv, options,
  225. bench_sched_message_usage, 0);
  226. pth_tab = malloc(num_fds * 2 * num_groups * sizeof(pthread_t));
  227. if (!pth_tab)
  228. err(EXIT_FAILURE, "main:malloc()");
  229. fdpair(readyfds);
  230. fdpair(wakefds);
  231. total_children = 0;
  232. for (i = 0; i < num_groups; i++)
  233. total_children += group(pth_tab+total_children, num_fds,
  234. readyfds[1], wakefds[0]);
  235. /* Wait for everyone to be ready */
  236. for (i = 0; i < total_children; i++)
  237. if (read(readyfds[0], &dummy, 1) != 1)
  238. err(EXIT_FAILURE, "Reading for readyfds");
  239. gettimeofday(&start, NULL);
  240. /* Kick them off */
  241. if (write(wakefds[1], &dummy, 1) != 1)
  242. err(EXIT_FAILURE, "Writing to start them");
  243. /* Reap them all */
  244. for (i = 0; i < total_children; i++)
  245. reap_worker(pth_tab[i]);
  246. gettimeofday(&stop, NULL);
  247. timersub(&stop, &start, &diff);
  248. switch (bench_format) {
  249. case BENCH_FORMAT_DEFAULT:
  250. printf("# %d sender and receiver %s per group\n",
  251. num_fds, thread_mode ? "threads" : "processes");
  252. printf("# %d groups == %d %s run\n\n",
  253. num_groups, num_groups * 2 * num_fds,
  254. thread_mode ? "threads" : "processes");
  255. printf(" %14s: %lu.%03lu [sec]\n", "Total time",
  256. diff.tv_sec,
  257. (unsigned long) (diff.tv_usec / USEC_PER_MSEC));
  258. break;
  259. case BENCH_FORMAT_SIMPLE:
  260. printf("%lu.%03lu\n", diff.tv_sec,
  261. (unsigned long) (diff.tv_usec / USEC_PER_MSEC));
  262. break;
  263. default:
  264. /* reaching here is something disaster */
  265. fprintf(stderr, "Unknown format:%d\n", bench_format);
  266. exit(1);
  267. break;
  268. }
  269. free(pth_tab);
  270. return 0;
  271. }