[PATCH] email update
[splice.git] / splice-bench.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <limits.h>
5 #include <string.h>
6 #include <getopt.h>
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <sys/poll.h>
10 #include <sys/stat.h>
11 #include <sys/types.h>
12 #include <sys/wait.h>
13 #include <sys/time.h>
14 #include <sys/mman.h>
15 #include <signal.h>
16 #include <netinet/in.h>
17 #include <arpa/inet.h>
18 #include <netdb.h>
19 #include <sched.h>
20
21 #include "splice.h"
22
23 static int nr_clients = 8;
24 static int net_port = 8888;
25 static int client_loops = 10;
26 static int bind_cpu;
27 static int write_to_null;
28 static int same_file;
29 static int splice_size = SPLICE_SIZE;
30 static char *filename = "splice-file";
31 static unsigned int max_client_run = 15;
32 static int run_rw = 1;
33 static int run_splice = 1;
34 static int run_mmap = 1;
35
36 static int nr_cpus;
37
38 static int usage(char *name)
39 {
40         fprintf(stderr, "Usage %s [options] [filename]:\n", name);
41         fprintf(stderr, "\t[-n] (number of clients]\n");
42         fprintf(stderr, "\t[-p] (port number)\n");
43         fprintf(stderr, "\t[-l] (number of loops)\n");
44         fprintf(stderr, "\t[-z] (write to /dev/null)\n");
45         fprintf(stderr, "\t[-s] (use 1 file for all)\n");
46         fprintf(stderr, "\t[-a] (set CPU affinity)\n");
47         fprintf(stderr, "\t[-b] (splice chunk size)\n");
48         fprintf(stderr, "\t[-t] (max client runtime in seconds)\n");
49         fprintf(stderr, "\t[-c] (clients to run (rw/mmap/splice)\n");
50         return 1;
51 }
52
53 static int parse_options(int argc, char *argv[])
54 {
55         int c, index = 1;
56
57         while ((c = getopt(argc, argv, "n:p:l:azsb:t:c:")) != -1) {
58                 switch (c) {
59                 case 'n':
60                         nr_clients = atoi(optarg);
61                         index++;
62                         break;
63                 case 'p':
64                         net_port = atoi(optarg);
65                         index++;
66                         break;
67                 case 'l':
68                         client_loops = atoi(optarg);
69                         index++;
70                         break;
71                 case 'a':
72                         bind_cpu = 1;
73                         index++;
74                         break;
75                 case 'z':
76                         write_to_null = 1;
77                         index++;
78                         break;
79                 case 's':
80                         same_file = 1;
81                         index++;
82                         break;
83                 case 'b':
84                         splice_size = atoi(optarg);
85                         index++;
86                         break;
87                 case 't':
88                         max_client_run = atoi(optarg);
89                         index++;
90                         break;
91                 case 'c':
92                         if (!strstr(optarg, "rw"))
93                                 run_rw = 0;
94                         if (!strstr(optarg, "splice"))
95                                 run_splice = 0;
96                         if (!strstr(optarg, "mmap"))
97                                 run_mmap = 0;
98                         index++;
99                         break;
100                 default:
101                         return -1;
102                 }
103         }
104
105         return index;
106 }
107
108 static int bind_to_cpu(int index)
109 {
110         cpu_set_t cpu_mask;
111         pid_t pid;
112         int cpu;
113
114         if (!bind_cpu || nr_cpus == 1)
115                 return 0;
116
117         cpu = index % nr_cpus;
118
119         CPU_ZERO(&cpu_mask);
120         CPU_SET((cpu), &cpu_mask);
121
122         pid = getpid();
123         if (sched_setaffinity(pid, sizeof(cpu_mask), &cpu_mask) == -1)
124                 return error("set affinity");
125
126         return 0;
127 }
128
129 static int accept_loop(int listen_sk)
130 {
131         struct sockaddr addr;
132         unsigned int len = sizeof(addr);
133         int sk;
134
135 again:
136         sk = accept(listen_sk, &addr, &len);
137         if (sk < 0)
138                 return error("accept");
139
140         /* read forever */
141         for (;;) {
142                 int ret = recv(sk, NULL, 128*1024*1024, MSG_TRUNC);
143                 if (ret > 0)
144                         continue;
145                 else if (!ret)
146                         break;
147                 if (errno == EAGAIN || errno == EINTR)
148                         continue;
149                 break;
150         }
151
152         close(sk);
153         goto again;
154 }
155
156 static int server(int offset)
157 {
158         struct sockaddr_in saddr_in;
159         struct sockaddr addr;
160         unsigned int len;
161         int sk, opt;
162
163         bind_to_cpu(offset);
164         nice(-20);
165
166         sk = socket(PF_INET, SOCK_STREAM, 0);
167         if (sk < 0)
168                 return error("socket");
169
170         opt = 1;
171         if (setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0)
172                 return error("setsockopt");
173
174         saddr_in.sin_addr.s_addr = htonl(INADDR_ANY);
175         saddr_in.sin_port = htons(net_port + offset);
176
177         if (bind(sk, (struct sockaddr *) &saddr_in, sizeof(saddr_in)) < 0)
178                 return error("bind");
179
180         if (listen(sk, 1) < 0)
181                 return error("listen");
182
183         len = sizeof(addr);
184         if (getsockname(sk, &addr, &len) < 0)
185                 return error("getsockname");
186
187         return accept_loop(sk);
188 }
189
190 static unsigned long mtime_since(struct timeval *s, struct timeval *e)
191 {
192         double sec, usec;
193
194         sec = e->tv_sec - s->tv_sec;
195         usec = e->tv_usec - s->tv_usec;
196         if (sec > 0 && usec < 0) {
197                 sec--;
198                 usec += 1000000;
199         }
200
201         sec *= (double) 1000;
202         usec /= (double) 1000;
203
204         return sec + usec;
205 }
206
207 static unsigned long mtime_since_now(struct timeval *s)
208 {
209         struct timeval t;
210
211         gettimeofday(&t, NULL);
212         return mtime_since(s, &t);
213 }
214
215 static int client_rw(int out_fd, int file_fd, int offset)
216 {
217         int loops = client_loops;
218         struct timeval start;
219         struct stat sb;
220         char *buf;
221         unsigned long long size;
222         unsigned long msecs;
223
224         if (fstat(file_fd, &sb) < 0)
225                 return error("fstat");
226
227         buf = malloc(splice_size);
228
229         gettimeofday(&start, NULL);
230 again:
231         if (lseek(file_fd, 0, SEEK_SET) < 0)
232                 return error("lseek");
233
234         size = sb.st_size;
235         while (size) {
236                 int this_len = min(size, (unsigned long long) splice_size);
237                 int ret = read(file_fd, buf, this_len);
238
239                 if (ret < 0)
240                         return error("read");
241
242                 size -= ret;
243                 while (ret) {
244                         int written = write(out_fd, buf, ret);
245
246                         if (written < 0)
247                                 return error("write");
248
249                         ret -= written;
250                 }
251         }
252
253         loops--;
254
255         if ((mtime_since_now(&start) < max_client_run * 1000) && loops)
256                 goto again;
257
258         free(buf);
259         size = sb.st_size >> 10;
260         size *= (client_loops - loops);
261         msecs = mtime_since_now(&start);
262         fprintf(stdout, "Client%d (rw): %Lu MiB/sec (%LuMiB in %lu msecs)\n", offset, size / (unsigned long long) msecs, size >> 10, msecs);
263         return 0;
264 }
265
266 static int client_mmap(int out_fd, int file_fd, int offset)
267 {
268         int loops = client_loops;
269         struct timeval start;
270         struct stat sb;
271         void *mmap_area, *buf;
272         unsigned long long size;
273         unsigned long msecs;
274
275         if (fstat(file_fd, &sb) < 0)
276                 return error("fstat");
277
278         mmap_area = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, file_fd, 0);
279         if (mmap_area == MAP_FAILED)
280                 return error("mmap");
281
282         if (madvise(mmap_area, sb.st_size, MADV_WILLNEED) < 0)
283                 return error("madvise");
284
285         gettimeofday(&start, NULL);
286 again:
287         buf = mmap_area;
288         size = sb.st_size;
289         while (size) {
290                 int this_len = min(size, (unsigned long long) splice_size);
291                 int ret = write(out_fd, buf, this_len);
292
293                 if (ret < 0)
294                         return error("write");
295
296                 buf += ret;
297                 size -= ret;
298         }
299
300         loops--;
301
302         if ((mtime_since_now(&start) < max_client_run * 1000) && loops)
303                 goto again;
304
305         size = sb.st_size >> 10;
306         size *= (client_loops - loops);
307         msecs = mtime_since_now(&start);
308         fprintf(stdout, "Client%d (mmap): %Lu MiB/sec (%LuMiB in %lu msecs)\n", offset, size / (unsigned long long) msecs, size >> 10, msecs);
309         munmap(mmap_area, sb.st_size);
310         return 0;
311
312 }
313
314 static int client_splice_loop(int out_fd, int fd, int *pfd, int offset)
315 {
316         struct timeval start;
317         unsigned long long size;
318         unsigned long msecs;
319         struct stat sb;
320         int loops = client_loops;
321         loff_t off;
322
323         if (fstat(fd, &sb) < 0)
324                 return error("fstat");
325
326         gettimeofday(&start, NULL);
327 again:
328         size = sb.st_size;
329         off = 0;
330
331         do {
332                 int ret = splice(fd, &off, pfd[1], NULL, min(size, (unsigned long long) splice_size), 0);
333
334                 if (ret <= 0)
335                         return error("splice-in");
336
337                 size -= ret;
338                 while (ret > 0) {
339                         int flags = size ? SPLICE_F_MORE : 0;
340                         int written = splice(pfd[0], NULL, out_fd, NULL, ret, flags);
341
342                         if (written <= 0)
343                                 return error("splice-out");
344
345                         ret -= written;
346                 }
347         } while (size);
348
349         loops--;
350
351         if ((mtime_since_now(&start) < max_client_run * 1000) && loops)
352                 goto again;
353
354         size = sb.st_size >> 10;
355         size *= (client_loops - loops);
356         msecs = mtime_since_now(&start);
357         fprintf(stdout, "Client%d (splice): %Lu MiB/sec (%LuMiB in %lu msecs)\n", offset, size / (unsigned long long) msecs, size >> 10, msecs);
358         return 0;
359 }
360
361 static int client_splice(int out_fd, int file_fd, int offset)
362 {
363         int pfd[2], ret;
364
365         if (pipe(pfd) < 0)
366                 return error("pipe");
367         
368         ret = client_splice_loop(out_fd, file_fd, pfd, offset);
369         close(pfd[0]);
370         close(pfd[1]);
371         return ret;
372 }
373
374 static int do_client(int out_fd, int file_fd, int offset)
375 {
376         int ret;
377
378         if (run_splice) {
379                 ret = client_splice(out_fd, file_fd, offset);
380                 if (ret)
381                         return ret;
382         }
383         if (run_mmap) {
384                 ret = client_mmap(out_fd, file_fd, offset);
385                 if (ret)
386                         return ret;
387         }
388         if (run_rw) {
389                 ret = client_rw(out_fd, file_fd, offset);
390                 if (ret)
391                         return ret;
392         }
393         return 0;
394 }
395
396 static int client_open_net(int offset)
397 {
398         int sk = socket(PF_INET, SOCK_STREAM, 0);
399         struct sockaddr_in s_to;
400         struct hostent *hp;
401
402         hp = gethostbyname("localhost");
403         if (!hp)
404                 return error("gethostbyname");
405
406         bzero((char *) &s_to, sizeof (s_to));
407         bcopy((char *) hp->h_addr, (char *) &(s_to.sin_addr), hp->h_length);
408         s_to.sin_family = hp->h_addrtype;
409         s_to.sin_port = htons(net_port + offset);
410
411         if (connect(sk, (struct sockaddr *)&s_to, sizeof(s_to)) < 0)
412                 return error("connect");
413
414         return sk;
415 }
416
417 static int client(int offset)
418 {
419         int file_fd, out_fd;
420         char fname[64];
421
422         bind_to_cpu(offset);
423         nice(-20);
424
425         if (!write_to_null)
426                 out_fd = client_open_net(offset);
427         else
428                 out_fd = open("/dev/null", O_WRONLY);
429
430         if (out_fd < 0)
431                 return error("socket");
432
433         sprintf(fname, "%s%d", filename, same_file ? 0 : offset);
434         file_fd = open(fname, O_RDONLY);
435         if (file_fd < 0)
436                 return error("open");
437
438         return do_client(out_fd, file_fd, offset);
439 }
440
441 int main(int argc, char *argv[])
442 {
443         pid_t *spids, *cpids;
444         int i, index;
445
446         index = parse_options(argc, argv);
447         if (index < 0)
448                 return usage(argv[0]);
449
450         if (index < argc)
451                 filename = argv[index];
452
453         spids = malloc(nr_clients * sizeof(pid_t));
454         cpids = malloc(nr_clients * sizeof(pid_t));
455         memset(spids, 0, nr_clients * sizeof(pid_t));
456         memset(cpids, 0, nr_clients * sizeof(pid_t));
457
458         nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);
459         if (nr_cpus < 0)
460                 return error("_SC_NPROCESSORS_ONLN");
461
462         /*
463          * fork servers
464          */
465         if (!write_to_null) {
466                 for (i = 0; i < nr_clients; i++) {
467                         pid_t pid = fork();
468
469                         if (pid)
470                                 spids[i] = pid;
471                         else {
472                                 server(i);
473                                 spids[i] = 0;
474                                 exit(0);
475                         }
476                 }
477                 sleep(1); /* should have servers started now */
478         }
479
480         /*
481          * fork clients
482          */
483         for (i = 0; i < nr_clients; i++) {
484                 pid_t pid = fork();
485
486                 if (pid)
487                         cpids[i] = pid;
488                 else {
489                         client(i);
490                         cpids[i] = 0;
491                         exit(0);
492                 }
493         }
494
495         /*
496          * wait for clients to exit
497          */
498         fprintf(stdout, "Waiting for clients\n");
499         for (i = 0; i < nr_clients; i++) {
500                 if (cpids[i]) {
501                         waitpid(cpids[i], NULL, 0);
502                         cpids[i] = 0;
503                 }
504         }
505
506         /*
507          * then kill servers
508          */
509         for (i = 0; i < nr_clients; i++) {
510                 if (spids[i]) {
511                         kill(spids[i], SIGKILL);
512                         waitpid(spids[i], NULL, 0);
513                         spids[i] = 0;
514                 }
515         }
516
517         return 0;
518 }