sched-messaging.c 6.9 KB

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