[PATCH] x86_64 version of rdtscll()
[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*1024U)
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_pipe_loops = SPLICE_PIPE_LOOPS;
38 #if 0
39 static int splice_loops = SPLICE_LOOPS;
40 #endif
41
42 static volatile long long *cycles, cycles_per_sec;
43
44 static struct timeval start_time;
45 static double start_cycles;
46 static double cpu_pct;
47
48 static void start_timing(const char *desc)
49 {
50         printf("%-20s: ", desc);
51         fflush(stdout);
52         gettimeofday(&start_time, NULL);
53         /*
54          * Give the lowprio cycles thread a chance to run and thus
55          * we get an accurate timestamp:
56          */
57         sched_yield();
58         start_cycles = (double)*cycles;
59 }
60
61 static double end_timing(unsigned long long bytes, double *rate)
62 {
63         static long long total;
64         struct timeval end_time;
65         double usecs;
66         double end_cycles, cpu_cycles;
67
68         gettimeofday(&end_time, NULL);
69         end_cycles = (double)*cycles;
70
71         usecs = (double) (end_time.tv_sec - start_time.tv_sec);
72         usecs *= 1000000.0;
73         usecs += (double) (end_time.tv_usec - start_time.tv_usec);
74         total += bytes;
75
76         cpu_cycles = end_cycles - start_cycles;
77         cpu_pct = 100.0 -
78                 cpu_cycles / cycles_per_sec / ( usecs / 1000000.0 ) * 100.0;
79
80         *rate = (double) bytes / usecs / (1024*1024) * 1000000;
81
82         printf("%.2fMB/s (%.1fMB total, %.2f%% CPU)\n", *rate,
83                 (double) total / (1024*1024),
84                 cpu_pct
85         );
86
87         return cpu_pct;
88 }
89
90 static void calibrate_loops(void)
91 {
92         long long l0, l1;
93         int i;
94
95         cycles_per_sec = 0;
96         printf("calibrating cycles: "); fflush(stdout);
97
98         /*
99          * Make sure we start on a precise timer IRQ boundary:
100          */
101         usleep(50000);
102
103         for (i = 0; i < 10; i++) {
104                 sched_yield();
105                 l0 = *cycles;
106                 usleep(200000);
107                 l1 = *cycles;
108                 cycles_per_sec = max(cycles_per_sec, l1-l0);
109         }
110         cycles_per_sec *= 5;
111
112         printf("%Ld cycles/sec\n", cycles_per_sec);
113 }
114
115 static int child(void)
116 {
117         static char buffer[BUFSIZE];
118         int sk;
119         double c1, c2, c3;
120         int fd;
121         struct sockaddr_in s_to;
122         struct hostent *hp;
123         double r1, r2, r3, r4, r5;
124         unsigned int i;
125         int pipefd[2];
126         loff_t off = 0;
127
128         r1 = r2 = r3 = r4 = r5 = 0;
129
130         sk = socket(PF_INET, SOCK_STREAM, 0);
131         if (!sk)
132                 return error("socket");
133         hp = gethostbyname (TARGET_HOSTNAME);
134         BUG_ON(!hp);
135         bzero ((char *) &s_to, sizeof (s_to));
136         bcopy ((char *) hp->h_addr, (char *) &(s_to.sin_addr), hp->h_length);
137         s_to.sin_family = hp->h_addrtype;
138         s_to.sin_port = htons(1111);
139
140         calibrate_loops();
141
142         fprintf(stdout, "BUFSIZE = %d\n", BUFSIZE);
143         fflush(stdout);
144
145         if (connect(sk, (struct sockaddr *)&s_to, sizeof(s_to)) < 0)
146                 return error("connect");
147
148         start_timing("Empty buffer");
149         for (i = 0; i < NR; i++)
150                 write(sk, buffer, BUFSIZE);
151         end_timing(NR*BUFSIZE, &r1);
152
153         fd = open("largefile", O_RDONLY);
154         if (fd < 0)
155                 return error("largefile");
156
157         start_timing("Read/write loop");
158         for (i = 0; i < NR; i++) {
159                 if (read(fd, buffer, BUFSIZE) != BUFSIZE)
160                         return error("largefile read");
161                 write(sk, buffer, BUFSIZE);
162         }
163         end_timing(NR*BUFSIZE, &r2);
164         close(fd);
165         close(sk);
166
167         start_timing("sendfile");
168 sendfile_again:
169         sk = socket(PF_INET, SOCK_STREAM, 0);
170         if (connect(sk, (struct sockaddr *)&s_to, sizeof(s_to)) < 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
189         start_timing("splice-pipe");
190 splice_pipe_again:
191         sk = socket(PF_INET, SOCK_STREAM, 0);
192         if (connect(sk, (struct sockaddr *)&s_to, sizeof(s_to)) < 0)
193                 return error("connect");
194
195         fd = open("largefile", O_RDONLY);
196         if (fd < 0)
197                 return error("largefile");
198         if (pipe(pipefd) < 0)
199                 return error("pipe");
200
201         i = NR*BUFSIZE;
202         off = 0;
203         do {
204                 int ret = splice(fd, &off, pipefd[1], NULL, min(i, BUFSIZE), SPLICE_F_NONBLOCK);
205                 if (ret <= 0)
206                         return error("splice-pipe-in");
207                 i -= ret;
208                 while (ret > 0) {
209                         int flags = i ? SPLICE_F_MORE : 0;
210                         int written = splice(pipefd[0], NULL, sk, NULL, ret, flags);
211                         if (written <= 0)
212                                 return error("splice-pipe-out");
213                         ret -= written;
214                 }
215         } while (i);
216
217         close(fd);
218         close(sk);
219         close(pipefd[0]);
220         close(pipefd[1]);
221         if (--splice_pipe_loops)
222                 goto splice_pipe_again;
223         c2 = end_timing(NR*BUFSIZE*SPLICE_LOOPS, &r4);
224
225         /*
226          * Direct splicing was disabled as being immediately available,
227          * it's reserved for sendfile emulation now.
228          */
229 #if 0
230         start_timing("splice");
231 splice_again:
232         sk = socket(PF_INET, SOCK_STREAM, 0);
233         if (connect(sk, (struct sockaddr *)&s_to, sizeof(s_to)) < 0)
234                 return error("connect");
235
236         fd = open("largefile", O_RDONLY);
237         if (fd < 0)
238                 return error("largefile");
239
240         i = NR*BUFSIZE;
241         off = 0;
242         do {
243                 int flags = BUFSIZE < i ? SPLICE_F_MORE : 0;
244                 int ret;
245
246                 ret = splice(fd, &off, sk, NULL, min(i, BUFSIZE), flags);
247
248                 if (ret <= 0)
249                         return error("splice");
250                 i -= ret;
251         } while (i);
252
253         close(fd);
254         close(sk);
255         if (--splice_loops)
256                 goto splice_again;
257         c3 = end_timing(NR*BUFSIZE*SPLICE_LOOPS, &r5);
258 #else
259         c3 = 0;
260 #endif
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 #if defined(__i386__)
314 #define rdtscll(val)                                    \
315 do {                                                    \
316         __asm__ __volatile__("rdtsc" : "=A" (val));     \
317 } while (0)
318 #elif defined(__x86_64__)
319 #define rdtscll(val)                                            \
320 do {                                                            \
321         uint64_t lo, hi;                                        \
322         __asm__ __volatile__("rdtsc" : "=a" (lo), "=d" (hi));   \
323         (val) = (hi << 32) | lo;                                \
324 } while (0)
325 #if 0
326 #elif defined(__ia64__)
327 #define rdtscll(val)                                    \
328 do {                                                    \
329         val = *__mm_clock_dev;                          \
330 } while (0)
331 #endif
332 #else
333 #define rdtscll(val) \
334         do { (val) = 0LL; } while (0)
335 #endif
336
337 /*
338  * Keep lowprio looping - to meausure the number of idle cycles
339  * available. It's tricky: we do a series of RDTSC calls, and
340  * if the delay to the last measurement was less than 500 cycles,
341  * we conclude that only this loop ran.
342  */
343 static void lowprio_cycle_soak_loop(void)
344 {
345         struct sched_param p = { sched_priority: 0 };
346         unsigned long long t0, t1, delta;
347
348         /*
349          * We are a nice +19 SCHED_BATCH task:
350          */
351         BUG_ON(sched_setscheduler(0, SCHED_BATCH, &p) != 0);
352         nice(40);
353
354         rdtscll(t0);
355         while (cycles >= 0) {
356                 rdtscll(t1);
357                 delta = t1-t0;
358                 if (delta < 500)
359                         *cycles += delta;
360                 t0 = t1;
361         }
362 }
363
364 int main(__attribute__((__unused__)) int argc, __attribute__((__unused__)) char **argv)
365 {
366         pid_t pid;
367
368         setup_shared_var();
369
370         signal(SIGCHLD, SIG_IGN);
371
372         pid = fork();
373         if (!pid) {
374                 lowprio_cycle_soak_loop();
375                 exit(0);
376         }
377
378         nice(-20);
379         child();
380         kill(pid, SIGHUP);
381         exit(0);
382 }