123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331 |
- /*
- *
- * sched-messaging.c
- *
- * messaging: Benchmark for scheduler and IPC mechanisms
- *
- * Based on hackbench by Rusty Russell <rusty@rustcorp.com.au>
- * Ported to perf by Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
- *
- */
- #include "../perf.h"
- #include "../util/util.h"
- #include "../util/parse-options.h"
- #include "../builtin.h"
- #include "bench.h"
- /* Test groups of 20 processes spraying to 20 receivers */
- #include <pthread.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <errno.h>
- #include <unistd.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <sys/wait.h>
- #include <sys/time.h>
- #include <poll.h>
- #include <limits.h>
- #include <err.h>
- #define DATASIZE 100
- static bool use_pipes = false;
- static unsigned int nr_loops = 100;
- static bool thread_mode = false;
- static unsigned int num_groups = 10;
- struct sender_context {
- unsigned int num_fds;
- int ready_out;
- int wakefd;
- int out_fds[0];
- };
- struct receiver_context {
- unsigned int num_packets;
- int in_fds[2];
- int ready_out;
- int wakefd;
- };
- static void fdpair(int fds[2])
- {
- if (use_pipes) {
- if (pipe(fds) == 0)
- return;
- } else {
- if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0)
- return;
- }
- err(EXIT_FAILURE, use_pipes ? "pipe()" : "socketpair()");
- }
- /* Block until we're ready to go */
- static void ready(int ready_out, int wakefd)
- {
- char dummy;
- struct pollfd pollfd = { .fd = wakefd, .events = POLLIN };
- /* Tell them we're ready. */
- if (write(ready_out, &dummy, 1) != 1)
- err(EXIT_FAILURE, "CLIENT: ready write");
- /* Wait for "GO" signal */
- if (poll(&pollfd, 1, -1) != 1)
- err(EXIT_FAILURE, "poll");
- }
- /* Sender sprays nr_loops messages down each file descriptor */
- static void *sender(struct sender_context *ctx)
- {
- char data[DATASIZE];
- unsigned int i, j;
- ready(ctx->ready_out, ctx->wakefd);
- /* Now pump to every receiver. */
- for (i = 0; i < nr_loops; i++) {
- for (j = 0; j < ctx->num_fds; j++) {
- int ret, done = 0;
- again:
- ret = write(ctx->out_fds[j], data + done,
- sizeof(data)-done);
- if (ret < 0)
- err(EXIT_FAILURE, "SENDER: write");
- done += ret;
- if (done < DATASIZE)
- goto again;
- }
- }
- return NULL;
- }
- /* One receiver per fd */
- static void *receiver(struct receiver_context* ctx)
- {
- unsigned int i;
- if (!thread_mode)
- close(ctx->in_fds[1]);
- /* Wait for start... */
- ready(ctx->ready_out, ctx->wakefd);
- /* Receive them all */
- for (i = 0; i < ctx->num_packets; i++) {
- char data[DATASIZE];
- int ret, done = 0;
- again:
- ret = read(ctx->in_fds[0], data + done, DATASIZE - done);
- if (ret < 0)
- err(EXIT_FAILURE, "SERVER: read");
- done += ret;
- if (done < DATASIZE)
- goto again;
- }
- return NULL;
- }
- static pthread_t create_worker(void *ctx, void *(*func)(void *))
- {
- pthread_attr_t attr;
- pthread_t childid;
- int ret;
- if (!thread_mode) {
- /* process mode */
- /* Fork the receiver. */
- switch (fork()) {
- case -1:
- err(EXIT_FAILURE, "fork()");
- break;
- case 0:
- (*func) (ctx);
- exit(0);
- break;
- default:
- break;
- }
- return (pthread_t)0;
- }
- if (pthread_attr_init(&attr) != 0)
- err(EXIT_FAILURE, "pthread_attr_init:");
- #ifndef __ia64__
- if (pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN) != 0)
- err(EXIT_FAILURE, "pthread_attr_setstacksize");
- #endif
- ret = pthread_create(&childid, &attr, func, ctx);
- if (ret != 0)
- err(EXIT_FAILURE, "pthread_create failed");
- return childid;
- }
- static void reap_worker(pthread_t id)
- {
- int proc_status;
- void *thread_status;
- if (!thread_mode) {
- /* process mode */
- wait(&proc_status);
- if (!WIFEXITED(proc_status))
- exit(1);
- } else {
- pthread_join(id, &thread_status);
- }
- }
- /* One group of senders and receivers */
- static unsigned int group(pthread_t *pth,
- unsigned int num_fds,
- int ready_out,
- int wakefd)
- {
- unsigned int i;
- struct sender_context *snd_ctx = malloc(sizeof(struct sender_context)
- + num_fds * sizeof(int));
- if (!snd_ctx)
- err(EXIT_FAILURE, "malloc()");
- for (i = 0; i < num_fds; i++) {
- int fds[2];
- struct receiver_context *ctx = malloc(sizeof(*ctx));
- if (!ctx)
- err(EXIT_FAILURE, "malloc()");
- /* Create the pipe between client and server */
- fdpair(fds);
- ctx->num_packets = num_fds * nr_loops;
- ctx->in_fds[0] = fds[0];
- ctx->in_fds[1] = fds[1];
- ctx->ready_out = ready_out;
- ctx->wakefd = wakefd;
- pth[i] = create_worker(ctx, (void *)receiver);
- snd_ctx->out_fds[i] = fds[1];
- if (!thread_mode)
- close(fds[0]);
- }
- /* Now we have all the fds, fork the senders */
- for (i = 0; i < num_fds; i++) {
- snd_ctx->ready_out = ready_out;
- snd_ctx->wakefd = wakefd;
- snd_ctx->num_fds = num_fds;
- pth[num_fds+i] = create_worker(snd_ctx, (void *)sender);
- }
- /* Close the fds we have left */
- if (!thread_mode)
- for (i = 0; i < num_fds; i++)
- close(snd_ctx->out_fds[i]);
- /* Return number of children to reap */
- return num_fds * 2;
- }
- static const struct option options[] = {
- OPT_BOOLEAN('p', "pipe", &use_pipes,
- "Use pipe() instead of socketpair()"),
- OPT_BOOLEAN('t', "thread", &thread_mode,
- "Be multi thread instead of multi process"),
- OPT_UINTEGER('g', "group", &num_groups, "Specify number of groups"),
- OPT_UINTEGER('l', "nr_loops", &nr_loops, "Specify the number of loops to run (default: 100)"),
- OPT_END()
- };
- static const char * const bench_sched_message_usage[] = {
- "perf bench sched messaging <options>",
- NULL
- };
- int bench_sched_messaging(int argc, const char **argv,
- const char *prefix __maybe_unused)
- {
- unsigned int i, total_children;
- struct timeval start, stop, diff;
- unsigned int num_fds = 20;
- int readyfds[2], wakefds[2];
- char dummy;
- pthread_t *pth_tab;
- argc = parse_options(argc, argv, options,
- bench_sched_message_usage, 0);
- pth_tab = malloc(num_fds * 2 * num_groups * sizeof(pthread_t));
- if (!pth_tab)
- err(EXIT_FAILURE, "main:malloc()");
- fdpair(readyfds);
- fdpair(wakefds);
- total_children = 0;
- for (i = 0; i < num_groups; i++)
- total_children += group(pth_tab+total_children, num_fds,
- readyfds[1], wakefds[0]);
- /* Wait for everyone to be ready */
- for (i = 0; i < total_children; i++)
- if (read(readyfds[0], &dummy, 1) != 1)
- err(EXIT_FAILURE, "Reading for readyfds");
- gettimeofday(&start, NULL);
- /* Kick them off */
- if (write(wakefds[1], &dummy, 1) != 1)
- err(EXIT_FAILURE, "Writing to start them");
- /* Reap them all */
- for (i = 0; i < total_children; i++)
- reap_worker(pth_tab[i]);
- gettimeofday(&stop, NULL);
- timersub(&stop, &start, &diff);
- switch (bench_format) {
- case BENCH_FORMAT_DEFAULT:
- printf("# %d sender and receiver %s per group\n",
- num_fds, thread_mode ? "threads" : "processes");
- printf("# %d groups == %d %s run\n\n",
- num_groups, num_groups * 2 * num_fds,
- thread_mode ? "threads" : "processes");
- printf(" %14s: %lu.%03lu [sec]\n", "Total time",
- diff.tv_sec,
- (unsigned long) (diff.tv_usec/1000));
- break;
- case BENCH_FORMAT_SIMPLE:
- printf("%lu.%03lu\n", diff.tv_sec,
- (unsigned long) (diff.tv_usec/1000));
- break;
- default:
- /* reaching here is something disaster */
- fprintf(stderr, "Unknown format:%d\n", bench_format);
- exit(1);
- break;
- }
- free(pth_tab);
- return 0;
- }
|