Initial commit
[splice.git] / splice-test4c.c
1 #include <assert.h>
2 #include <ctype.h>
3 #include <errno.h>
4 #include <fcntl.h>
5 #include <malloc.h>
6 #include <netdb.h>
7 #include <netinet/in.h>
8 #include <netinet/tcp.h>
9 #include <sched.h>
10 #include <signal.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <sys/mman.h>
15 #include <sys/sendfile.h>
16 #include <sys/socket.h>
17 #include <sys/time.h>
18 #include <sys/types.h>
19 #include <sys/wait.h>
20 #include <time.h>
21 #include <unistd.h>
22
23 #include "splice.h"
24
25 #define TARGET_HOSTNAME "localhost"
26
27 #define BYTES (128*1024*1024UL)
28 #define BUFSIZE (64*1024)
29
30 #define NR (BYTES/BUFSIZE)
31
32 #define SENDFILE_LOOPS 10
33 #define SPLICE_LOOPS 10
34 #define SPLICE_PIPE_LOOPS 10
35
36 static int sendfile_loops = SENDFILE_LOOPS;
37 static int splice_loops = SPLICE_LOOPS;
38 static int splice_pipe_loops = SPLICE_PIPE_LOOPS;
39
40 static volatile long long *cycles, cycles_per_sec;
41
42 static struct timeval start_time;
43 static double start_cycles;
44 static double cpu_pct;
45
46 static void start_timing(const char *desc)
47 {
48         printf("%-20s: ", desc);
49         fflush(stdout);
50         gettimeofday(&start_time, NULL);
51         /*
52          * Give the lowprio cycles thread a chance to run and thus
53          * we get an accurate timestamp:
54          */
55         sched_yield();
56         start_cycles = (double)*cycles;
57 }
58
59 static double end_timing(unsigned long long bytes, double *rate)
60 {
61         static long long total;
62         struct timeval end_time;
63         double usecs;
64         double end_cycles, cpu_cycles;
65
66         gettimeofday(&end_time, NULL);
67         end_cycles = (double)*cycles;
68
69         usecs = (double) (end_time.tv_sec - start_time.tv_sec);
70         usecs *= 1000000.0;
71         usecs += (double) (end_time.tv_usec - start_time.tv_usec);
72         total += bytes;
73
74         cpu_cycles = end_cycles - start_cycles;
75         cpu_pct = 100.0 -
76                 cpu_cycles / cycles_per_sec / ( usecs / 1000000.0 ) * 100.0;
77
78         *rate = (double) bytes / usecs / (1024*1024) * 1000000;
79
80         printf("%.2fMB/s (%.1fMB total, %.2f%% CPU)\n", *rate,
81                 (double) total / (1024*1024),
82                 cpu_pct
83         );
84
85         return cpu_pct;
86 }
87
88 static void calibrate_loops(void)
89 {
90         long long l0, l1;
91         int i;
92
93         cycles_per_sec = 0;
94         printf("calibrating cycles: "); fflush(stdout);
95
96         /*
97          * Make sure we start on a precise timer IRQ boundary:
98          */
99         usleep(50000);
100
101         for (i = 0; i < 10; i++) {
102                 sched_yield();
103                 l0 = *cycles;
104                 usleep(200000);
105                 l1 = *cycles;
106                 cycles_per_sec = max(cycles_per_sec, l1-l0);
107         }
108         cycles_per_sec *= 5;
109
110         printf("%Ld cycles/sec\n", cycles_per_sec);
111 }
112
113 static int child(struct sockaddr *addr, int len)
114 {
115         static char buffer[BUFSIZE];
116         int sk;
117         double c1, c2, c3;
118         int fd;
119         struct sockaddr_in s_to;
120         struct hostent *hp;
121         double r1, r2, r3, r4, r5;
122         int i, pipefd[2];
123         loff_t off = 0;
124
125         r1 = r2 = r3 = r4 = r5 = 0;
126
127         sk = socket(PF_INET, SOCK_STREAM, 0);
128         if (!sk)
129                 return error("socket");
130         hp = gethostbyname (TARGET_HOSTNAME);
131         BUG_ON(!hp);
132         bzero ((char *) &s_to, sizeof (s_to));
133         bcopy ((char *) hp->h_addr, (char *) &(s_to.sin_addr), hp->h_length);
134         s_to.sin_family = hp->h_addrtype;
135         s_to.sin_port = htons(1111);
136
137         calibrate_loops();
138
139         fprintf(stdout, "BUFSIZE = %d\n", BUFSIZE);
140         fflush(stdout);
141
142         if (getenv("EMPTY")) {
143                 if (connect(sk, (struct sockaddr *)&s_to, len) < 0)
144                         return error("connect");
145
146                 start_timing("Empty buffer");
147                 for (i = 0; i < NR; i++)
148                         write(sk, buffer, BUFSIZE);
149                 end_timing(NR*BUFSIZE, &r1);
150
151                 fd = open("largefile", O_RDONLY);
152                 if (fd < 0)
153                         return error("largefile");
154
155                 start_timing("Read/write loop");
156                 for (i = 0; i < NR; i++) {
157                         if (read(fd, buffer, BUFSIZE) != BUFSIZE)
158                                 return error("largefile read");
159                         write(sk, buffer, BUFSIZE);
160                 }
161                 end_timing(NR*BUFSIZE, &r2);
162                 close(fd);
163                 close(sk);
164         }
165
166         if (getenv("SF")) {
167                 start_timing("sendfile");
168 sendfile_again:
169                 sk = socket(PF_INET, SOCK_STREAM, 0);
170                 if (connect(sk, (struct sockaddr *)&s_to, len) < 0)
171                         return error("connect");
172
173                 fd = open("largefile", O_RDONLY);
174                 if (fd < 0)
175                         return error("largefile");
176
177                 i = NR*BUFSIZE;
178                 do {
179                         int ret = sendfile(sk, fd, NULL, i);
180                         i -= ret;
181                 } while (i);
182
183                 close(fd);
184                 close(sk);
185                 if (--sendfile_loops)
186                         goto sendfile_again;
187                 c1 = end_timing(NR*BUFSIZE*SENDFILE_LOOPS, &r3);
188         } else
189                 c1 = 0;
190
191         if (getenv("SPLICE_PIPE")) {
192                 start_timing("splice-pipe");
193 splice_pipe_again:
194                 sk = socket(PF_INET, SOCK_STREAM, 0);
195                 if (connect(sk, (struct sockaddr *)&s_to, len) < 0)
196                         return error("connect");
197
198                 fd = open("largefile", O_RDONLY);
199                 if (fd < 0)
200                         return error("largefile");
201                 if (pipe(pipefd) < 0)
202                         return error("pipe");
203
204                 i = NR*BUFSIZE;
205                 off = 0;
206                 do {
207                         int ret = splice(fd, &off, pipefd[1], NULL, min(i, BUFSIZE), SPLICE_F_NONBLOCK);
208                         if (ret <= 0)
209                                 return error("splice-pipe-in");
210                         i -= ret;
211                         while (ret > 0) {
212                                 int flags = i ? SPLICE_F_MORE : 0;
213                                 int written = splice(pipefd[0], NULL, sk, NULL, ret, flags);
214                                 if (written <= 0)
215                                         return error("splice-pipe-out");
216                                 ret -= written;
217                         }
218                 } while (i);
219
220                 close(fd);
221                 close(sk);
222                 close(pipefd[0]);
223                 close(pipefd[1]);
224                 if (--splice_pipe_loops)
225                         goto splice_pipe_again;
226                 c2 = end_timing(NR*BUFSIZE*SPLICE_LOOPS, &r4);
227         } else
228                 c2 = 0;
229
230         if (getenv("SPLICE")) {
231                 start_timing("splice");
232 splice_again:
233                 sk = socket(PF_INET, SOCK_STREAM, 0);
234                 if (connect(sk, (struct sockaddr *)&s_to, len) < 0)
235                         return error("connect");
236
237                 fd = open("largefile", O_RDONLY);
238                 if (fd < 0)
239                         return error("largefile");
240
241                 i = NR*BUFSIZE;
242                 off = 0;
243                 do {
244                         int flags = BUFSIZE < i ? SPLICE_F_MORE : 0;
245                         int ret;
246
247                         ret = splice(fd, &off, sk, NULL, min(i, BUFSIZE), flags);
248
249                         if (ret <= 0)
250                                 return error("splice");
251                         i -= ret;
252                 } while (i);
253
254                 close(fd);
255                 close(sk);
256                 if (--splice_loops)
257                         goto splice_again;
258                 c3 = end_timing(NR*BUFSIZE*SPLICE_LOOPS, &r5);
259         } else
260                 c3 = 0;
261
262         /*
263          * c1/r3 - sendfile
264          * c2/r4 - splice-pipe
265          * c3/r5 - splice
266          */
267
268         if (c1 && c2)
269                 printf("sendfile is %.2f%% more efficient than splice-pipe.\n",
270                         (c2 - c1) / c1 * 100.0 );
271         if (c1 && c3)
272                 printf("sendfile is %.2f%% more efficient than splice.\n",
273                         (c3 - c1) / c1 * 100.0 );
274         if (c2 && c3)
275                 printf("splice is %.2f%% more efficient splice-pipe.\n",
276                         (c2 - c3) / c3 * 100.0 );
277         if (r3 && r4)
278                 printf("sendfile is %.2f%% faster than splice-pipe.\n",
279                         (r3 - r4) / r4 * 100.0 );
280         if (r3 && r5)
281                 printf("sendfile is %.2f%% faster than splice.\n",
282                         (r3 - r5) / r5 * 100.0 );
283         if (r4 && r5)
284                 printf("splice is %.2f%% faster than splice-pipe.\n",
285                         (r5 - r4) / r4 * 100.0 );
286
287         return 0;
288 }
289
290
291 static void setup_shared_var(void)
292 {
293         char zerobuff [4096] = { 0, };
294         int ret, fd;
295
296         fd = creat(".tmp_mmap", 0700);
297         BUG_ON(fd == -1);
298         close(fd);
299
300         fd = open(".tmp_mmap", O_RDWR|O_CREAT|O_TRUNC);
301         BUG_ON(fd == -1);
302         ret = write(fd, zerobuff, 4096);
303         BUG_ON(ret != 4096);
304
305         cycles = (void *)mmap(0, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
306         BUG_ON(cycles == (void *)-1);
307
308         close(fd);
309 }
310
311 #define SCHED_BATCH 3
312
313 #ifdef __ia64__
314 # define rdtscll(val)                                   \
315 do {                                                    \
316         val = *__mm_clock_dev;                          \
317 } while (0)
318 #elif defined(__i386__)
319 # define rdtscll(val)                                   \
320 do {                                                    \
321         __asm__ __volatile__("rdtsc" : "=A" (val));     \
322 } while (0)
323 #else
324 # define rdtscll(val) \
325         do { (val) = 0LL; } while (0)
326 #endif
327
328 /*
329  * Keep lowprio looping - to meausure the number of idle cycles
330  * available. It's tricky: we do a series of RDTSC calls, and
331  * if the delay to the last measurement was less than 500 cycles,
332  * we conclude that only this loop ran.
333  */
334 static void lowprio_cycle_soak_loop(void)
335 {
336         struct sched_param p = { sched_priority: 0 };
337         unsigned long long t0, t1, delta;
338
339         /*
340          * We are a nice +19 SCHED_BATCH task:
341          */
342         BUG_ON(sched_setscheduler(0, SCHED_BATCH, &p) != 0);
343         nice(40);
344
345         rdtscll(t0);
346         while (cycles >= 0) {
347                 rdtscll(t1);
348                 delta = t1-t0;
349                 if (delta < 500)
350                         *cycles += delta;
351                 t0 = t1;
352         }
353 }
354
355 int main(int argc, char **argv)
356 {
357         unsigned int sk, len;
358         struct sockaddr addr;
359         pid_t pid;
360
361         setup_shared_var();
362
363         signal(SIGCHLD, SIG_IGN);
364         sk = socket(PF_INET, SOCK_STREAM, 0);
365         if (sk < 0) {
366                 perror("socket");
367                 exit(1);
368         }
369         if (listen(sk, 1) < 0) {
370                 perror("listen");
371                 exit(1);
372         }
373         len = sizeof(addr);
374         if (getsockname(sk, &addr, &len) < 0) {
375                 perror("getsockname");
376                 exit(1);
377         }
378         pid = fork();
379         if (!pid) {
380                 lowprio_cycle_soak_loop();
381                 exit(0);
382         }
383         nice(-20);
384         child(&addr, len);
385         kill(pid, SIGHUP);
386         exit(0);
387 }