1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * sched-pipe.c
5*4882a593Smuzhiyun *
6*4882a593Smuzhiyun * pipe: Benchmark for pipe()
7*4882a593Smuzhiyun *
8*4882a593Smuzhiyun * Based on pipe-test-1m.c by Ingo Molnar <mingo@redhat.com>
9*4882a593Smuzhiyun * http://people.redhat.com/mingo/cfs-scheduler/tools/pipe-test-1m.c
10*4882a593Smuzhiyun * Ported to perf by Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
11*4882a593Smuzhiyun */
12*4882a593Smuzhiyun #include <subcmd/parse-options.h>
13*4882a593Smuzhiyun #include "bench.h"
14*4882a593Smuzhiyun
15*4882a593Smuzhiyun #include <unistd.h>
16*4882a593Smuzhiyun #include <stdio.h>
17*4882a593Smuzhiyun #include <stdlib.h>
18*4882a593Smuzhiyun #include <signal.h>
19*4882a593Smuzhiyun #include <sys/wait.h>
20*4882a593Smuzhiyun #include <string.h>
21*4882a593Smuzhiyun #include <errno.h>
22*4882a593Smuzhiyun #include <assert.h>
23*4882a593Smuzhiyun #include <sys/time.h>
24*4882a593Smuzhiyun #include <sys/types.h>
25*4882a593Smuzhiyun #include <sys/syscall.h>
26*4882a593Smuzhiyun #include <linux/time64.h>
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun #include <pthread.h>
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun struct thread_data {
31*4882a593Smuzhiyun int nr;
32*4882a593Smuzhiyun int pipe_read;
33*4882a593Smuzhiyun int pipe_write;
34*4882a593Smuzhiyun pthread_t pthread;
35*4882a593Smuzhiyun };
36*4882a593Smuzhiyun
37*4882a593Smuzhiyun #define LOOPS_DEFAULT 1000000
38*4882a593Smuzhiyun static int loops = LOOPS_DEFAULT;
39*4882a593Smuzhiyun
40*4882a593Smuzhiyun /* Use processes by default: */
41*4882a593Smuzhiyun static bool threaded;
42*4882a593Smuzhiyun
43*4882a593Smuzhiyun static const struct option options[] = {
44*4882a593Smuzhiyun OPT_INTEGER('l', "loop", &loops, "Specify number of loops"),
45*4882a593Smuzhiyun OPT_BOOLEAN('T', "threaded", &threaded, "Specify threads/process based task setup"),
46*4882a593Smuzhiyun OPT_END()
47*4882a593Smuzhiyun };
48*4882a593Smuzhiyun
49*4882a593Smuzhiyun static const char * const bench_sched_pipe_usage[] = {
50*4882a593Smuzhiyun "perf bench sched pipe <options>",
51*4882a593Smuzhiyun NULL
52*4882a593Smuzhiyun };
53*4882a593Smuzhiyun
worker_thread(void * __tdata)54*4882a593Smuzhiyun static void *worker_thread(void *__tdata)
55*4882a593Smuzhiyun {
56*4882a593Smuzhiyun struct thread_data *td = __tdata;
57*4882a593Smuzhiyun int m = 0, i;
58*4882a593Smuzhiyun int ret;
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun for (i = 0; i < loops; i++) {
61*4882a593Smuzhiyun if (!td->nr) {
62*4882a593Smuzhiyun ret = read(td->pipe_read, &m, sizeof(int));
63*4882a593Smuzhiyun BUG_ON(ret != sizeof(int));
64*4882a593Smuzhiyun ret = write(td->pipe_write, &m, sizeof(int));
65*4882a593Smuzhiyun BUG_ON(ret != sizeof(int));
66*4882a593Smuzhiyun } else {
67*4882a593Smuzhiyun ret = write(td->pipe_write, &m, sizeof(int));
68*4882a593Smuzhiyun BUG_ON(ret != sizeof(int));
69*4882a593Smuzhiyun ret = read(td->pipe_read, &m, sizeof(int));
70*4882a593Smuzhiyun BUG_ON(ret != sizeof(int));
71*4882a593Smuzhiyun }
72*4882a593Smuzhiyun }
73*4882a593Smuzhiyun
74*4882a593Smuzhiyun return NULL;
75*4882a593Smuzhiyun }
76*4882a593Smuzhiyun
bench_sched_pipe(int argc,const char ** argv)77*4882a593Smuzhiyun int bench_sched_pipe(int argc, const char **argv)
78*4882a593Smuzhiyun {
79*4882a593Smuzhiyun struct thread_data threads[2], *td;
80*4882a593Smuzhiyun int pipe_1[2], pipe_2[2];
81*4882a593Smuzhiyun struct timeval start, stop, diff;
82*4882a593Smuzhiyun unsigned long long result_usec = 0;
83*4882a593Smuzhiyun int nr_threads = 2;
84*4882a593Smuzhiyun int t;
85*4882a593Smuzhiyun
86*4882a593Smuzhiyun /*
87*4882a593Smuzhiyun * why does "ret" exist?
88*4882a593Smuzhiyun * discarding returned value of read(), write()
89*4882a593Smuzhiyun * causes error in building environment for perf
90*4882a593Smuzhiyun */
91*4882a593Smuzhiyun int __maybe_unused ret, wait_stat;
92*4882a593Smuzhiyun pid_t pid, retpid __maybe_unused;
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun argc = parse_options(argc, argv, options, bench_sched_pipe_usage, 0);
95*4882a593Smuzhiyun
96*4882a593Smuzhiyun BUG_ON(pipe(pipe_1));
97*4882a593Smuzhiyun BUG_ON(pipe(pipe_2));
98*4882a593Smuzhiyun
99*4882a593Smuzhiyun gettimeofday(&start, NULL);
100*4882a593Smuzhiyun
101*4882a593Smuzhiyun for (t = 0; t < nr_threads; t++) {
102*4882a593Smuzhiyun td = threads + t;
103*4882a593Smuzhiyun
104*4882a593Smuzhiyun td->nr = t;
105*4882a593Smuzhiyun
106*4882a593Smuzhiyun if (t == 0) {
107*4882a593Smuzhiyun td->pipe_read = pipe_1[0];
108*4882a593Smuzhiyun td->pipe_write = pipe_2[1];
109*4882a593Smuzhiyun } else {
110*4882a593Smuzhiyun td->pipe_write = pipe_1[1];
111*4882a593Smuzhiyun td->pipe_read = pipe_2[0];
112*4882a593Smuzhiyun }
113*4882a593Smuzhiyun }
114*4882a593Smuzhiyun
115*4882a593Smuzhiyun
116*4882a593Smuzhiyun if (threaded) {
117*4882a593Smuzhiyun
118*4882a593Smuzhiyun for (t = 0; t < nr_threads; t++) {
119*4882a593Smuzhiyun td = threads + t;
120*4882a593Smuzhiyun
121*4882a593Smuzhiyun ret = pthread_create(&td->pthread, NULL, worker_thread, td);
122*4882a593Smuzhiyun BUG_ON(ret);
123*4882a593Smuzhiyun }
124*4882a593Smuzhiyun
125*4882a593Smuzhiyun for (t = 0; t < nr_threads; t++) {
126*4882a593Smuzhiyun td = threads + t;
127*4882a593Smuzhiyun
128*4882a593Smuzhiyun ret = pthread_join(td->pthread, NULL);
129*4882a593Smuzhiyun BUG_ON(ret);
130*4882a593Smuzhiyun }
131*4882a593Smuzhiyun
132*4882a593Smuzhiyun } else {
133*4882a593Smuzhiyun pid = fork();
134*4882a593Smuzhiyun assert(pid >= 0);
135*4882a593Smuzhiyun
136*4882a593Smuzhiyun if (!pid) {
137*4882a593Smuzhiyun worker_thread(threads + 0);
138*4882a593Smuzhiyun exit(0);
139*4882a593Smuzhiyun } else {
140*4882a593Smuzhiyun worker_thread(threads + 1);
141*4882a593Smuzhiyun }
142*4882a593Smuzhiyun
143*4882a593Smuzhiyun retpid = waitpid(pid, &wait_stat, 0);
144*4882a593Smuzhiyun assert((retpid == pid) && WIFEXITED(wait_stat));
145*4882a593Smuzhiyun }
146*4882a593Smuzhiyun
147*4882a593Smuzhiyun gettimeofday(&stop, NULL);
148*4882a593Smuzhiyun timersub(&stop, &start, &diff);
149*4882a593Smuzhiyun
150*4882a593Smuzhiyun switch (bench_format) {
151*4882a593Smuzhiyun case BENCH_FORMAT_DEFAULT:
152*4882a593Smuzhiyun printf("# Executed %d pipe operations between two %s\n\n",
153*4882a593Smuzhiyun loops, threaded ? "threads" : "processes");
154*4882a593Smuzhiyun
155*4882a593Smuzhiyun result_usec = diff.tv_sec * USEC_PER_SEC;
156*4882a593Smuzhiyun result_usec += diff.tv_usec;
157*4882a593Smuzhiyun
158*4882a593Smuzhiyun printf(" %14s: %lu.%03lu [sec]\n\n", "Total time",
159*4882a593Smuzhiyun diff.tv_sec,
160*4882a593Smuzhiyun (unsigned long) (diff.tv_usec / USEC_PER_MSEC));
161*4882a593Smuzhiyun
162*4882a593Smuzhiyun printf(" %14lf usecs/op\n",
163*4882a593Smuzhiyun (double)result_usec / (double)loops);
164*4882a593Smuzhiyun printf(" %14d ops/sec\n",
165*4882a593Smuzhiyun (int)((double)loops /
166*4882a593Smuzhiyun ((double)result_usec / (double)USEC_PER_SEC)));
167*4882a593Smuzhiyun break;
168*4882a593Smuzhiyun
169*4882a593Smuzhiyun case BENCH_FORMAT_SIMPLE:
170*4882a593Smuzhiyun printf("%lu.%03lu\n",
171*4882a593Smuzhiyun diff.tv_sec,
172*4882a593Smuzhiyun (unsigned long) (diff.tv_usec / USEC_PER_MSEC));
173*4882a593Smuzhiyun break;
174*4882a593Smuzhiyun
175*4882a593Smuzhiyun default:
176*4882a593Smuzhiyun /* reaching here is something disaster */
177*4882a593Smuzhiyun fprintf(stderr, "Unknown format:%d\n", bench_format);
178*4882a593Smuzhiyun exit(1);
179*4882a593Smuzhiyun break;
180*4882a593Smuzhiyun }
181*4882a593Smuzhiyun
182*4882a593Smuzhiyun return 0;
183*4882a593Smuzhiyun }
184