14992df3eSRobert Watson /*-
24992df3eSRobert Watson * Copyright (c) 2005 Robert N. M. Watson
34992df3eSRobert Watson * All rights reserved.
44992df3eSRobert Watson *
54992df3eSRobert Watson * Redistribution and use in source and binary forms, with or without
64992df3eSRobert Watson * modification, are permitted provided that the following conditions
74992df3eSRobert Watson * are met:
84992df3eSRobert Watson * 1. Redistributions of source code must retain the above copyright
94992df3eSRobert Watson * notice, this list of conditions and the following disclaimer.
104992df3eSRobert Watson * 2. Redistributions in binary form must reproduce the above copyright
114992df3eSRobert Watson * notice, this list of conditions and the following disclaimer in the
124992df3eSRobert Watson * documentation and/or other materials provided with the distribution.
134992df3eSRobert Watson *
144992df3eSRobert Watson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
154992df3eSRobert Watson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
164992df3eSRobert Watson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
174992df3eSRobert Watson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
184992df3eSRobert Watson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
194992df3eSRobert Watson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
204992df3eSRobert Watson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
214992df3eSRobert Watson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
224992df3eSRobert Watson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
234992df3eSRobert Watson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
244992df3eSRobert Watson * SUCH DAMAGE.
254992df3eSRobert Watson */
264992df3eSRobert Watson
274992df3eSRobert Watson #include <sys/types.h>
284992df3eSRobert Watson #include <sys/socket.h>
29cd94bb1eSSergey Kandaurov #include <sys/stdint.h>
304992df3eSRobert Watson #include <sys/time.h>
314992df3eSRobert Watson #include <sys/utsname.h>
324992df3eSRobert Watson #include <sys/wait.h>
334992df3eSRobert Watson
344992df3eSRobert Watson #include <netinet/in.h>
354992df3eSRobert Watson
364992df3eSRobert Watson #include <err.h>
374992df3eSRobert Watson #include <errno.h>
384992df3eSRobert Watson #include <pthread.h>
394992df3eSRobert Watson #include <signal.h>
404992df3eSRobert Watson #include <stdio.h>
414992df3eSRobert Watson #include <stdlib.h>
424992df3eSRobert Watson #include <string.h>
434992df3eSRobert Watson #include <unistd.h>
444992df3eSRobert Watson
454992df3eSRobert Watson /*
464992df3eSRobert Watson * juggle is a simple IPC/context switch performance test, which works on
474992df3eSRobert Watson * pairs of file descriptors of various types. In various runs, it considers
484992df3eSRobert Watson * the cost of bouncing a message synchronously across the descriptor pair,
494992df3eSRobert Watson * either in the same thread, two different threads, or two different
504992df3eSRobert Watson * processes. Timing measurements for each series of I/O's are reported, but
514992df3eSRobert Watson * the first measurement in each series discarded as "warmup" on the IPC
524992df3eSRobert Watson * primitive. Variations on the test permit for pipelining, or the insertion
534992df3eSRobert Watson * of more than one packet into the stream at a time, intended to permit
544992df3eSRobert Watson * greater parallelism, hopefully allowing performance numbers to reflect
554992df3eSRobert Watson * use of available parallelism, and/or intelligence in context switching to
564992df3eSRobert Watson * avoid premature switching when multiple messages are queued.
574992df3eSRobert Watson */
584992df3eSRobert Watson
594992df3eSRobert Watson /*
604992df3eSRobert Watson * The UDP test uses UDP over the loopback interface. Two arbitrary but
614992df3eSRobert Watson * fixed port numbers.
624992df3eSRobert Watson */
634992df3eSRobert Watson #define UDP_PORT1 2020
644992df3eSRobert Watson #define UDP_PORT2 2021
654992df3eSRobert Watson
664992df3eSRobert Watson /*
674992df3eSRobert Watson * Size of each message. Must be smaller than the socket buffer or pipe
684992df3eSRobert Watson * buffer maximum size, as we want to send it atomically without blocking.
694992df3eSRobert Watson * If pipelining is in use, must be able to fit PIPELINE_MAX of these
704992df3eSRobert Watson * messages into the send queue.
714992df3eSRobert Watson */
724992df3eSRobert Watson #define MESSAGELEN 128
734992df3eSRobert Watson
744992df3eSRobert Watson /*
754992df3eSRobert Watson * Number of message cycles -- into fd1, out of fd2, into fd2, and out of
764992df3eSRobert Watson * fd1. By counting in cycles, we allow the master thread or process to
774992df3eSRobert Watson * perform timing without explicitly synchronizing with the secondary thread
784992df3eSRobert Watson * or process.
794992df3eSRobert Watson */
804992df3eSRobert Watson #define NUMCYCLES 1024
814992df3eSRobert Watson
824992df3eSRobert Watson /*
834992df3eSRobert Watson * Number of times to run each test.
844992df3eSRobert Watson */
854992df3eSRobert Watson #define LOOPS 10
864992df3eSRobert Watson
874992df3eSRobert Watson /*
884992df3eSRobert Watson * Number of in-flight messages per cycle. I adjusting this value, be
894992df3eSRobert Watson * careful not to exceed the socket/etc buffer depth, or messages may be lost
904992df3eSRobert Watson * or result in blocking.
914992df3eSRobert Watson */
924992df3eSRobert Watson #define PIPELINE_MAX 4
934992df3eSRobert Watson
944992df3eSRobert Watson static int
udp_create(int * fd1p,int * fd2p)954992df3eSRobert Watson udp_create(int *fd1p, int *fd2p)
964992df3eSRobert Watson {
974992df3eSRobert Watson struct sockaddr_in sin1, sin2;
984992df3eSRobert Watson int sock1, sock2;
994992df3eSRobert Watson
1004992df3eSRobert Watson sock1 = socket(PF_INET, SOCK_DGRAM, 0);
1014992df3eSRobert Watson if (sock1 == -1)
1024992df3eSRobert Watson return (-1);
1034992df3eSRobert Watson
1044992df3eSRobert Watson sock2 = socket(PF_INET, SOCK_DGRAM, 0);
1054992df3eSRobert Watson if (sock2 == -1) {
1064992df3eSRobert Watson close(sock1);
1074992df3eSRobert Watson return (-1);
1084992df3eSRobert Watson }
1094992df3eSRobert Watson
1104992df3eSRobert Watson bzero(&sin1, sizeof(sin1));
1114992df3eSRobert Watson sin1.sin_len = sizeof(sin1);
1124992df3eSRobert Watson sin1.sin_family = AF_INET;
1134992df3eSRobert Watson sin1.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
1144992df3eSRobert Watson sin1.sin_port = htons(UDP_PORT1);
1154992df3eSRobert Watson
1164992df3eSRobert Watson bzero(&sin2, sizeof(sin2));
1174992df3eSRobert Watson sin2.sin_len = sizeof(sin2);
1184992df3eSRobert Watson sin2.sin_family = AF_INET;
1194992df3eSRobert Watson sin2.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
1204992df3eSRobert Watson sin2.sin_port = htons(UDP_PORT2);
1214992df3eSRobert Watson
1224992df3eSRobert Watson if (bind(sock1, (struct sockaddr *) &sin1, sizeof(sin1)) < 0) {
1234992df3eSRobert Watson close(sock1);
1244992df3eSRobert Watson close(sock2);
1254992df3eSRobert Watson return (-1);
1264992df3eSRobert Watson }
1274992df3eSRobert Watson
1284992df3eSRobert Watson if (bind(sock2, (struct sockaddr *) &sin2, sizeof(sin2)) < 0) {
1294992df3eSRobert Watson close(sock1);
1304992df3eSRobert Watson close(sock2);
1314992df3eSRobert Watson return (-1);
1324992df3eSRobert Watson }
1334992df3eSRobert Watson
1344992df3eSRobert Watson if (connect(sock1, (struct sockaddr *) &sin2, sizeof(sin2)) < 0) {
1354992df3eSRobert Watson close(sock1);
1364992df3eSRobert Watson close(sock2);
1374992df3eSRobert Watson return (-1);
1384992df3eSRobert Watson }
1394992df3eSRobert Watson
1404992df3eSRobert Watson if (connect(sock2, (struct sockaddr *) &sin1, sizeof(sin1)) < 0) {
1414992df3eSRobert Watson close(sock1);
1424992df3eSRobert Watson close(sock2);
1434992df3eSRobert Watson return (-1);
1444992df3eSRobert Watson }
1454992df3eSRobert Watson
1464992df3eSRobert Watson *fd1p = sock1;
1474992df3eSRobert Watson *fd2p = sock2;
1484992df3eSRobert Watson
1494992df3eSRobert Watson return (0);
1504992df3eSRobert Watson }
1514992df3eSRobert Watson
1524992df3eSRobert Watson static int
pipe_create(int * fd1p,int * fd2p)1534992df3eSRobert Watson pipe_create(int *fd1p, int *fd2p)
1544992df3eSRobert Watson {
1554992df3eSRobert Watson int fds[2];
1564992df3eSRobert Watson
1574992df3eSRobert Watson if (pipe(fds) < 0)
1584992df3eSRobert Watson return (-1);
1594992df3eSRobert Watson
1604992df3eSRobert Watson *fd1p = fds[0];
1614992df3eSRobert Watson *fd2p = fds[1];
1624992df3eSRobert Watson
1634992df3eSRobert Watson return (0);
1644992df3eSRobert Watson }
1654992df3eSRobert Watson
1664992df3eSRobert Watson static int
socketpairdgram_create(int * fd1p,int * fd2p)1674992df3eSRobert Watson socketpairdgram_create(int *fd1p, int *fd2p)
1684992df3eSRobert Watson {
1694992df3eSRobert Watson int fds[2];
1704992df3eSRobert Watson
1714992df3eSRobert Watson if (socketpair(PF_LOCAL, SOCK_DGRAM, 0, fds) < 0)
1724992df3eSRobert Watson return (-1);
1734992df3eSRobert Watson
1744992df3eSRobert Watson *fd1p = fds[0];
1754992df3eSRobert Watson *fd2p = fds[1];
1764992df3eSRobert Watson
1774992df3eSRobert Watson return (0);
1784992df3eSRobert Watson }
1794992df3eSRobert Watson
1804992df3eSRobert Watson static int
socketpairstream_create(int * fd1p,int * fd2p)1814992df3eSRobert Watson socketpairstream_create(int *fd1p, int *fd2p)
1824992df3eSRobert Watson {
1834992df3eSRobert Watson int fds[2];
1844992df3eSRobert Watson
1854992df3eSRobert Watson if (socketpair(PF_LOCAL, SOCK_STREAM, 0, fds) < 0)
1864992df3eSRobert Watson return (-1);
1874992df3eSRobert Watson
1884992df3eSRobert Watson *fd1p = fds[0];
1894992df3eSRobert Watson *fd2p = fds[1];
1904992df3eSRobert Watson
1914992df3eSRobert Watson return (0);
1924992df3eSRobert Watson }
1934992df3eSRobert Watson
1944992df3eSRobert Watson static int
message_send(int s)1954992df3eSRobert Watson message_send(int s)
1964992df3eSRobert Watson {
1974992df3eSRobert Watson u_char buffer[MESSAGELEN];
1984992df3eSRobert Watson ssize_t len;
1994992df3eSRobert Watson
2004992df3eSRobert Watson bzero(buffer, sizeof(buffer));
2014992df3eSRobert Watson
2024992df3eSRobert Watson len = write(s, buffer, sizeof(buffer));
2034992df3eSRobert Watson if (len == -1)
2044992df3eSRobert Watson return (-1);
2054992df3eSRobert Watson if (len != sizeof(buffer)) {
2064992df3eSRobert Watson errno = EMSGSIZE;
2074992df3eSRobert Watson return (-1);
2084992df3eSRobert Watson }
2094992df3eSRobert Watson return (0);
2104992df3eSRobert Watson }
2114992df3eSRobert Watson
2124992df3eSRobert Watson static int
message_recv(int s)2134992df3eSRobert Watson message_recv(int s)
2144992df3eSRobert Watson {
2154992df3eSRobert Watson u_char buffer[MESSAGELEN];
2164992df3eSRobert Watson ssize_t len;
2174992df3eSRobert Watson
2184992df3eSRobert Watson len = read(s, buffer, sizeof(buffer));
2194992df3eSRobert Watson if (len == -1)
2204992df3eSRobert Watson return (-1);
2214992df3eSRobert Watson if (len != sizeof(buffer)) {
2224992df3eSRobert Watson errno = EMSGSIZE;
2234992df3eSRobert Watson return (-1);
2244992df3eSRobert Watson }
2254992df3eSRobert Watson return (0);
2264992df3eSRobert Watson }
2274992df3eSRobert Watson
2284992df3eSRobert Watson /*
2294992df3eSRobert Watson * Juggle messages between two file descriptors in a single thread/process,
2304992df3eSRobert Watson * so simply a measure of IPC performance.
2314992df3eSRobert Watson */
2324992df3eSRobert Watson static struct timespec
juggle(int fd1,int fd2,int pipeline)2334992df3eSRobert Watson juggle(int fd1, int fd2, int pipeline)
2344992df3eSRobert Watson {
2354992df3eSRobert Watson struct timespec tstart, tfinish;
2364992df3eSRobert Watson int i, j;
2374992df3eSRobert Watson
2384992df3eSRobert Watson if (clock_gettime(CLOCK_REALTIME, &tstart) < 0)
2394992df3eSRobert Watson err(-1, "juggle: clock_gettime");
2404992df3eSRobert Watson
2414992df3eSRobert Watson for (i = 0; i < NUMCYCLES; i++) {
2424992df3eSRobert Watson
2434992df3eSRobert Watson for (j = 0; j < pipeline; j++) {
2444992df3eSRobert Watson if (message_send(fd1) < 0)
2454992df3eSRobert Watson err(-1, "message_send fd1");
2464992df3eSRobert Watson }
2474992df3eSRobert Watson
2484992df3eSRobert Watson for (j = 0; j < pipeline; j++) {
2494992df3eSRobert Watson if (message_recv(fd2) < 0)
2504992df3eSRobert Watson err(-1, "message_recv fd2");
2514992df3eSRobert Watson
2524992df3eSRobert Watson if (message_send(fd2) < 0)
2534992df3eSRobert Watson err(-1, "message_send fd2");
2544992df3eSRobert Watson }
2554992df3eSRobert Watson
2564992df3eSRobert Watson for (j = 0; j < pipeline; j++) {
2574992df3eSRobert Watson if (message_recv(fd1) < 0)
2584992df3eSRobert Watson err(-1, "message_recv fd1");
2594992df3eSRobert Watson }
2604992df3eSRobert Watson }
2614992df3eSRobert Watson
2624992df3eSRobert Watson if (clock_gettime(CLOCK_REALTIME, &tfinish) < 0)
2634992df3eSRobert Watson err(-1, "juggle: clock_gettime");
2644992df3eSRobert Watson
2656040822cSAlan Somers timespecsub(&tfinish, &tstart, &tfinish);
2664992df3eSRobert Watson
2674992df3eSRobert Watson return (tfinish);
2684992df3eSRobert Watson }
2694992df3eSRobert Watson
2704992df3eSRobert Watson /*
2714992df3eSRobert Watson * Juggle messages between two file descriptors in two threads, so measure
2724992df3eSRobert Watson * the cost of IPC and the cost of a thread context switch.
2734992df3eSRobert Watson *
2744992df3eSRobert Watson * In order to avoid measuring thread creation time, we make use of a
2754992df3eSRobert Watson * condition variable to decide when both threads are ready to begin
2764992df3eSRobert Watson * juggling.
2774992df3eSRobert Watson */
2784992df3eSRobert Watson static int threaded_child_ready;
2794992df3eSRobert Watson static pthread_mutex_t threaded_mtx;
2804992df3eSRobert Watson static pthread_cond_t threaded_cond;
2814992df3eSRobert Watson static int threaded_pipeline;
2824992df3eSRobert Watson
2834992df3eSRobert Watson static void *
juggling_thread(void * arg)2844992df3eSRobert Watson juggling_thread(void *arg)
2854992df3eSRobert Watson {
2864992df3eSRobert Watson int fd2, i, j;
2874992df3eSRobert Watson
2884992df3eSRobert Watson fd2 = *(int *)arg;
2894992df3eSRobert Watson
2902949bd10SRuslan Ermilov if (pthread_mutex_lock(&threaded_mtx) != 0)
2914992df3eSRobert Watson err(-1, "juggling_thread: pthread_mutex_lock");
2924992df3eSRobert Watson
2934992df3eSRobert Watson threaded_child_ready = 1;
2944992df3eSRobert Watson
2952949bd10SRuslan Ermilov if (pthread_cond_signal(&threaded_cond) != 0)
2964992df3eSRobert Watson err(-1, "juggling_thread: pthread_cond_signal");
2974992df3eSRobert Watson
2982949bd10SRuslan Ermilov if (pthread_mutex_unlock(&threaded_mtx) != 0)
2994992df3eSRobert Watson err(-1, "juggling_thread: pthread_mutex_unlock");
3004992df3eSRobert Watson
3014992df3eSRobert Watson for (i = 0; i < NUMCYCLES; i++) {
3024992df3eSRobert Watson for (j = 0; j < threaded_pipeline; j++) {
3034992df3eSRobert Watson if (message_recv(fd2) < 0)
3044992df3eSRobert Watson err(-1, "message_recv fd2");
3054992df3eSRobert Watson
3064992df3eSRobert Watson if (message_send(fd2) < 0)
3074992df3eSRobert Watson err(-1, "message_send fd2");
3084992df3eSRobert Watson }
3094992df3eSRobert Watson }
3104992df3eSRobert Watson
3114992df3eSRobert Watson return (NULL);
3124992df3eSRobert Watson }
3134992df3eSRobert Watson
3144992df3eSRobert Watson static struct timespec
thread_juggle(int fd1,int fd2,int pipeline)3154992df3eSRobert Watson thread_juggle(int fd1, int fd2, int pipeline)
3164992df3eSRobert Watson {
3174992df3eSRobert Watson struct timespec tstart, tfinish;
3184992df3eSRobert Watson pthread_t thread;
3194992df3eSRobert Watson int i, j;
3204992df3eSRobert Watson
3214992df3eSRobert Watson threaded_pipeline = pipeline;
3224992df3eSRobert Watson
3232949bd10SRuslan Ermilov if (pthread_mutex_init(&threaded_mtx, NULL) != 0)
3244992df3eSRobert Watson err(-1, "thread_juggle: pthread_mutex_init");
3254992df3eSRobert Watson
3262949bd10SRuslan Ermilov if (pthread_create(&thread, NULL, juggling_thread, &fd2) != 0)
3274992df3eSRobert Watson err(-1, "thread_juggle: pthread_create");
3284992df3eSRobert Watson
3292949bd10SRuslan Ermilov if (pthread_mutex_lock(&threaded_mtx) != 0)
3304992df3eSRobert Watson err(-1, "thread_juggle: pthread_mutex_lock");
3314992df3eSRobert Watson
3324992df3eSRobert Watson while (!threaded_child_ready) {
3332949bd10SRuslan Ermilov if (pthread_cond_wait(&threaded_cond, &threaded_mtx) != 0)
3344992df3eSRobert Watson err(-1, "thread_juggle: pthread_cond_wait");
3354992df3eSRobert Watson }
3364992df3eSRobert Watson
3372949bd10SRuslan Ermilov if (pthread_mutex_unlock(&threaded_mtx) != 0)
3384992df3eSRobert Watson err(-1, "thread_juggle: pthread_mutex_unlock");
3394992df3eSRobert Watson
3404992df3eSRobert Watson if (clock_gettime(CLOCK_REALTIME, &tstart) < 0)
3414992df3eSRobert Watson err(-1, "thread_juggle: clock_gettime");
3424992df3eSRobert Watson
3434992df3eSRobert Watson for (i = 0; i < NUMCYCLES; i++) {
3444992df3eSRobert Watson for (j = 0; j < pipeline; j++) {
3454992df3eSRobert Watson if (message_send(fd1) < 0)
3464992df3eSRobert Watson err(-1, "message_send fd1");
3474992df3eSRobert Watson }
3484992df3eSRobert Watson
3494992df3eSRobert Watson for (j = 0; j < pipeline; j++) {
3504992df3eSRobert Watson if (message_recv(fd1) < 0)
3514992df3eSRobert Watson err(-1, "message_recv fd1");
3524992df3eSRobert Watson }
3534992df3eSRobert Watson }
3544992df3eSRobert Watson
3554992df3eSRobert Watson if (clock_gettime(CLOCK_REALTIME, &tfinish) < 0)
3564992df3eSRobert Watson err(-1, "thread_juggle: clock_gettime");
3574992df3eSRobert Watson
3582949bd10SRuslan Ermilov if (pthread_join(thread, NULL) != 0)
3594992df3eSRobert Watson err(-1, "thread_juggle: pthread_join");
3604992df3eSRobert Watson
3616040822cSAlan Somers timespecsub(&tfinish, &tstart, &tfinish);
3624992df3eSRobert Watson
3634992df3eSRobert Watson return (tfinish);
3644992df3eSRobert Watson }
3654992df3eSRobert Watson
3664992df3eSRobert Watson /*
3674992df3eSRobert Watson * Juggle messages between two file descriptors in two processes, so measure
3684992df3eSRobert Watson * the cost of IPC and the cost of a process context switch.
3694992df3eSRobert Watson *
3704992df3eSRobert Watson * Since we can't use a mutex between the processes, we simply do an extra
3714992df3eSRobert Watson * write on the child to let the parent know that it's ready to start.
3724992df3eSRobert Watson */
3734992df3eSRobert Watson static struct timespec
process_juggle(int fd1,int fd2,int pipeline)3744992df3eSRobert Watson process_juggle(int fd1, int fd2, int pipeline)
3754992df3eSRobert Watson {
3764992df3eSRobert Watson struct timespec tstart, tfinish;
3774992df3eSRobert Watson pid_t pid, ppid, wpid;
3784992df3eSRobert Watson int error, i, j;
3794992df3eSRobert Watson
3804992df3eSRobert Watson ppid = getpid();
3814992df3eSRobert Watson
3824992df3eSRobert Watson pid = fork();
3834992df3eSRobert Watson if (pid < 0)
3844992df3eSRobert Watson err(-1, "process_juggle: fork");
3854992df3eSRobert Watson
3864992df3eSRobert Watson if (pid == 0) {
3874992df3eSRobert Watson if (message_send(fd2) < 0) {
3884992df3eSRobert Watson error = errno;
3894992df3eSRobert Watson kill(ppid, SIGTERM);
3904992df3eSRobert Watson errno = error;
3914992df3eSRobert Watson err(-1, "process_juggle: child: message_send");
3924992df3eSRobert Watson }
3934992df3eSRobert Watson
3944992df3eSRobert Watson for (i = 0; i < NUMCYCLES; i++) {
3954992df3eSRobert Watson for (j = 0; j < pipeline; j++) {
3964992df3eSRobert Watson if (message_send(fd2) < 0)
3974992df3eSRobert Watson err(-1, "message_send fd2");
3984992df3eSRobert Watson
3994992df3eSRobert Watson if (message_recv(fd2) < 0)
4004992df3eSRobert Watson err(-1, "message_recv fd2");
4014992df3eSRobert Watson }
4024992df3eSRobert Watson }
4034992df3eSRobert Watson
4044992df3eSRobert Watson exit(0);
4054992df3eSRobert Watson } else {
4064992df3eSRobert Watson if (message_recv(fd1) < 0) {
4074992df3eSRobert Watson error = errno;
4084992df3eSRobert Watson kill(pid, SIGTERM);
4094992df3eSRobert Watson errno = error;
4104992df3eSRobert Watson err(-1, "process_juggle: parent: message_recv");
4114992df3eSRobert Watson }
4124992df3eSRobert Watson
4134992df3eSRobert Watson if (clock_gettime(CLOCK_REALTIME, &tstart) < 0)
4144992df3eSRobert Watson err(-1, "process_juggle: clock_gettime");
4154992df3eSRobert Watson
4164992df3eSRobert Watson for (i = 0; i < NUMCYCLES; i++) {
4174992df3eSRobert Watson for (j = 0; j < pipeline; j++) {
4184992df3eSRobert Watson if (message_send(fd1) < 0) {
4194992df3eSRobert Watson error = errno;
4204992df3eSRobert Watson kill(pid, SIGTERM);
4214992df3eSRobert Watson errno = error;
4224992df3eSRobert Watson err(-1, "message_send fd1");
4234992df3eSRobert Watson }
4244992df3eSRobert Watson }
4254992df3eSRobert Watson
4264992df3eSRobert Watson for (j = 0; j < pipeline; j++) {
4274992df3eSRobert Watson if (message_recv(fd1) < 0) {
4284992df3eSRobert Watson error = errno;
4294992df3eSRobert Watson kill(pid, SIGTERM);
4304992df3eSRobert Watson errno = error;
4314992df3eSRobert Watson err(-1, "message_recv fd1");
4324992df3eSRobert Watson }
4334992df3eSRobert Watson }
4344992df3eSRobert Watson }
4354992df3eSRobert Watson
4364992df3eSRobert Watson if (clock_gettime(CLOCK_REALTIME, &tfinish) < 0)
4374992df3eSRobert Watson err(-1, "process_juggle: clock_gettime");
4384992df3eSRobert Watson }
4394992df3eSRobert Watson
4404992df3eSRobert Watson wpid = waitpid(pid, NULL, 0);
4414992df3eSRobert Watson if (wpid < 0)
4424992df3eSRobert Watson err(-1, "process_juggle: waitpid");
4434992df3eSRobert Watson if (wpid != pid)
4444992df3eSRobert Watson errx(-1, "process_juggle: waitpid: pid != wpid");
4454992df3eSRobert Watson
4466040822cSAlan Somers timespecsub(&tfinish, &tstart, &tfinish);
4474992df3eSRobert Watson
4484992df3eSRobert Watson return (tfinish);
4494992df3eSRobert Watson }
4504992df3eSRobert Watson
4514992df3eSRobert Watson /*
4524992df3eSRobert Watson * When we print out results for larger pipeline sizes, we scale back by the
4534992df3eSRobert Watson * depth of the pipeline. This generally means dividing by the pipeline
4544992df3eSRobert Watson * depth. Except when it means dividing by zero.
4554992df3eSRobert Watson */
4564992df3eSRobert Watson static void
scale_timespec(struct timespec * ts,int p)4574992df3eSRobert Watson scale_timespec(struct timespec *ts, int p)
4584992df3eSRobert Watson {
4594992df3eSRobert Watson
4604992df3eSRobert Watson if (p == 0)
4614992df3eSRobert Watson return;
4624992df3eSRobert Watson
4634992df3eSRobert Watson ts->tv_sec /= p;
4644992df3eSRobert Watson ts->tv_nsec /= p;
4654992df3eSRobert Watson }
4664992df3eSRobert Watson
4674992df3eSRobert Watson static const struct ipctype {
4684992df3eSRobert Watson int (*it_create)(int *fd1p, int *fd2p);
4694992df3eSRobert Watson const char *it_name;
4704992df3eSRobert Watson } ipctypes[] = {
4714992df3eSRobert Watson { pipe_create, "pipe" },
4724992df3eSRobert Watson { udp_create, "udp" },
4734992df3eSRobert Watson { socketpairdgram_create, "socketpairdgram" },
4744992df3eSRobert Watson { socketpairstream_create, "socketpairstream" },
4754992df3eSRobert Watson };
4764992df3eSRobert Watson static const int ipctypes_len = (sizeof(ipctypes) / sizeof(struct ipctype));
4774992df3eSRobert Watson
4784992df3eSRobert Watson int
main(int argc,char * argv[])4794992df3eSRobert Watson main(int argc, char *argv[])
4804992df3eSRobert Watson {
4814992df3eSRobert Watson struct timespec juggle_results[LOOPS], process_results[LOOPS];
4824992df3eSRobert Watson struct timespec thread_results[LOOPS];
4834992df3eSRobert Watson int fd1, fd2, i, j, p;
4844992df3eSRobert Watson struct utsname uts;
4854992df3eSRobert Watson
4867f9f1e42SRobert Watson printf("version, juggle.c %s\n", "$FreeBSD$");
4874992df3eSRobert Watson
4884992df3eSRobert Watson if (uname(&uts) < 0)
4894992df3eSRobert Watson err(-1, "utsname");
4904992df3eSRobert Watson printf("sysname, %s\n", uts.sysname);
4914992df3eSRobert Watson printf("nodename, %s\n", uts.nodename);
4924992df3eSRobert Watson printf("release, %s\n", uts.release);
4934992df3eSRobert Watson printf("version, %s\n", uts.version);
4944992df3eSRobert Watson printf("machine, %s\n", uts.machine);
4954992df3eSRobert Watson printf("\n");
4964992df3eSRobert Watson
4974992df3eSRobert Watson printf("MESSAGELEN, %d\n", MESSAGELEN);
4984992df3eSRobert Watson printf("NUMCYCLES, %d\n", NUMCYCLES);
4994992df3eSRobert Watson printf("LOOPS, %d\n", LOOPS);
5004992df3eSRobert Watson printf("PIPELINE_MAX, %d\n", PIPELINE_MAX);
5014992df3eSRobert Watson printf("\n\n");
5024992df3eSRobert Watson
5034992df3eSRobert Watson printf("ipctype, test, pipeline_depth");
5044992df3eSRobert Watson for (j = 0; j < LOOPS; j++)
5054992df3eSRobert Watson printf(", data%d", j);
5064992df3eSRobert Watson printf("\n");
5074992df3eSRobert Watson fflush(stdout);
5084992df3eSRobert Watson for (p = 0; p < PIPELINE_MAX + 1; p++) {
5094992df3eSRobert Watson for (i = 0; i < ipctypes_len; i++) {
5104992df3eSRobert Watson if (ipctypes[i].it_create(&fd1, &fd2) < 0)
5114992df3eSRobert Watson err(-1, "main: %s", ipctypes[i].it_name);
5124992df3eSRobert Watson
5134992df3eSRobert Watson /*
5144992df3eSRobert Watson * For each test, do one uncounted warmup, then LOOPS
5154992df3eSRobert Watson * runs of the actual test.
5164992df3eSRobert Watson */
5174992df3eSRobert Watson juggle(fd1, fd2, p);
5184992df3eSRobert Watson for (j = 0; j < LOOPS; j++)
5194992df3eSRobert Watson juggle_results[j] = juggle(fd1, fd2, p);
5204992df3eSRobert Watson process_juggle(fd1, fd2, p);
5214992df3eSRobert Watson for (j = 0; j < LOOPS; j++)
5224992df3eSRobert Watson process_results[j] = process_juggle(fd1, fd2,
5234992df3eSRobert Watson p);
5244992df3eSRobert Watson thread_juggle(fd1, fd2, p);
5254992df3eSRobert Watson for (j = 0; j < LOOPS; j++)
5264992df3eSRobert Watson thread_results[j] = thread_juggle(fd1, fd2,
5274992df3eSRobert Watson p);
5284992df3eSRobert Watson for (j = 0; j < LOOPS; j++) {
5294992df3eSRobert Watson thread_results[j].tv_sec = 0;
5304992df3eSRobert Watson thread_results[j].tv_nsec = 0;
5314992df3eSRobert Watson }
5324992df3eSRobert Watson close(fd1);
5334992df3eSRobert Watson close(fd2);
5344992df3eSRobert Watson }
5354992df3eSRobert Watson /*
5364992df3eSRobert Watson * When printing results for the round, normalize the results
5374992df3eSRobert Watson * with respect to the pipeline depth. We're doing p times
5384992df3eSRobert Watson * as much work, and are we taking p times as long?
5394992df3eSRobert Watson */
5404992df3eSRobert Watson for (i = 0; i < ipctypes_len; i++) {
5414992df3eSRobert Watson printf("%s, juggle, %d, ", ipctypes[i].it_name, p);
5424992df3eSRobert Watson for (j = 0; j < LOOPS; j++) {
5434992df3eSRobert Watson if (j != 0)
5444992df3eSRobert Watson printf(", ");
5454992df3eSRobert Watson scale_timespec(&juggle_results[j], p);
546cd94bb1eSSergey Kandaurov printf("%jd.%09lu",
547cd94bb1eSSergey Kandaurov (intmax_t)juggle_results[j].tv_sec,
5484992df3eSRobert Watson juggle_results[j].tv_nsec);
5494992df3eSRobert Watson }
5504992df3eSRobert Watson printf("\n");
5514992df3eSRobert Watson printf("%s, process_juggle, %d, ",
5524992df3eSRobert Watson ipctypes[i].it_name, p);
5534992df3eSRobert Watson for (j = 0; j < LOOPS; j++) {
5544992df3eSRobert Watson if (j != 0)
5554992df3eSRobert Watson printf(", ");
5564992df3eSRobert Watson scale_timespec(&process_results[j], p);
557cd94bb1eSSergey Kandaurov printf("%jd.%09lu",
558cd94bb1eSSergey Kandaurov (intmax_t)process_results[j].tv_sec,
5594992df3eSRobert Watson process_results[j].tv_nsec);
5604992df3eSRobert Watson }
5614992df3eSRobert Watson printf("\n");
5624992df3eSRobert Watson printf("%s, thread_juggle, %d, ",
5634992df3eSRobert Watson ipctypes[i].it_name, p);
5644992df3eSRobert Watson for (j = 0; j < LOOPS; j++) {
5654992df3eSRobert Watson if (j != 0)
5664992df3eSRobert Watson printf(", ");
5674992df3eSRobert Watson scale_timespec(&thread_results[j], p);
568cd94bb1eSSergey Kandaurov printf("%jd.%09lu",
569cd94bb1eSSergey Kandaurov (intmax_t)thread_results[j].tv_sec,
5704992df3eSRobert Watson thread_results[j].tv_nsec);
5714992df3eSRobert Watson }
5724992df3eSRobert Watson printf("\n");
5734992df3eSRobert Watson }
5744992df3eSRobert Watson fflush(stdout);
5754992df3eSRobert Watson }
5764992df3eSRobert Watson return (0);
5774992df3eSRobert Watson }
578