--- /dev/null
+/*
+ * Splice stdin to net
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include "splice.h"
+
+static struct timeval start_time;
+static unsigned long long kb_sent;
+static unsigned long iters;
+
+static unsigned long mtime_since(struct timeval *s, struct timeval *e)
+{
+ double sec, usec;
+
+ sec = e->tv_sec - s->tv_sec;
+ usec = e->tv_usec - s->tv_usec;
+ if (sec > 0 && usec < 0) {
+ sec--;
+ usec += 1000000;
+ }
+
+ sec *= (double) 1000;
+ usec /= (double) 1000;
+
+ return sec + usec;
+}
+
+static unsigned long mtime_since_now(struct timeval *s)
+{
+ struct timeval t;
+
+ gettimeofday(&t, NULL);
+ return mtime_since(s, &t);
+}
+
+void show_rate(int sig)
+{
+ unsigned long msecs = mtime_since_now(&start_time);
+
+ printf("Throughput: %LuMiB/sec (%Lu MiB in %lu msecs)\n", kb_sent / msecs, kb_sent, msecs);
+ printf("avg put: %Lu\n", kb_sent / iters);
+}
+
+int main(int argc, char *argv[])
+{
+ struct sockaddr_in addr;
+ unsigned short port;
+ int fd, pfd[2], ffd, ret;
+ unsigned long long b_sent = 0;
+ int bla = 1;
+
+ if (argc < 4) {
+ printf("%s: file target port\n", argv[0]);
+ return 1;
+ }
+
+ port = atoi(argv[3]);
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+
+ if (inet_aton(argv[2], &addr.sin_addr) != 1) {
+ struct hostent *hent = gethostbyname(argv[2]);
+
+ if (!hent) {
+ perror("gethostbyname");
+ return 1;
+ }
+
+ memcpy(&addr.sin_addr, hent->h_addr, 4);
+ }
+
+ printf("Connecting to %s/%d\n", argv[2], port);
+
+ fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (fd < 0) {
+ perror("socket");
+ return 1;
+ }
+
+ if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ perror("connect");
+ return 1;
+ }
+
+ if (pipe(pfd) < 0) {
+ perror("pipe");
+ return 1;
+ }
+
+ ffd = open(argv[1], O_RDWR);
+ if (ffd < 0) {
+ perror("open input");
+ return 1;
+ }
+
+ signal(SIGINT, show_rate);
+ gettimeofday(&start_time, NULL);
+
+ do {
+ if (kb_sent >= 128*1024) {
+ b_sent += 40000;
+ ret = ftruncate(ffd, b_sent);
+ printf("trunc file a little %d\n", ret);
+ bla = 0;
+ }
+
+ ret = splice(ffd, NULL, pfd[1], NULL, SPLICE_SIZE, 0x02);
+
+ if (!bla)
+ printf("spliced %d\n", ret);
+
+ if (ret < 0) {
+ perror("splice");
+ break;
+ } else if (!ret) {
+ break;
+ }
+ b_sent += ret;
+ kb_sent += ret >> 10;
+ iters++;
+ while (ret > 0) {
+ int flags = 0;
+ int written = splice(pfd[0], NULL, fd, NULL, ret, flags);
+ if (written < 0) {
+ perror("splice-out");
+ break;
+ } else if (!written)
+ break;
+ ret -= written;
+ }
+ } while (kb_sent < 512 * 1024 && bla);
+
+ show_rate(0);
+ close(fd);
+ return 0;
+}
--- /dev/null
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <malloc.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <sched.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/sendfile.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "splice.h"
+
+#define TARGET_HOSTNAME "localhost"
+
+#define BYTES (128*1024*1024UL)
+#define BUFSIZE (64*1024)
+
+#define NR (BYTES/BUFSIZE)
+
+#define SENDFILE_LOOPS 10
+#define SPLICE_LOOPS 10
+#define SPLICE_PIPE_LOOPS 10
+
+static int sendfile_loops = SENDFILE_LOOPS;
+static int splice_loops = SPLICE_LOOPS;
+static int splice_pipe_loops = SPLICE_PIPE_LOOPS;
+
+static volatile long long *cycles, cycles_per_sec;
+
+static struct timeval start_time;
+static double start_cycles;
+static double cpu_pct;
+
+static void start_timing(const char *desc)
+{
+ printf("%-20s: ", desc);
+ fflush(stdout);
+ gettimeofday(&start_time, NULL);
+ /*
+ * Give the lowprio cycles thread a chance to run and thus
+ * we get an accurate timestamp:
+ */
+ sched_yield();
+ start_cycles = (double)*cycles;
+}
+
+static double end_timing(unsigned long long bytes, double *rate)
+{
+ static long long total;
+ struct timeval end_time;
+ double usecs;
+ double end_cycles, cpu_cycles;
+
+ gettimeofday(&end_time, NULL);
+ end_cycles = (double)*cycles;
+
+ usecs = (double) (end_time.tv_sec - start_time.tv_sec);
+ usecs *= 1000000.0;
+ usecs += (double) (end_time.tv_usec - start_time.tv_usec);
+ total += bytes;
+
+ cpu_cycles = end_cycles - start_cycles;
+ cpu_pct = 100.0 -
+ cpu_cycles / cycles_per_sec / ( usecs / 1000000.0 ) * 100.0;
+
+ *rate = (double) bytes / usecs / (1024*1024) * 1000000;
+
+ printf("%.2fMB/s (%.1fMB total, %.2f%% CPU)\n", *rate,
+ (double) total / (1024*1024),
+ cpu_pct
+ );
+
+ return cpu_pct;
+}
+
+static void calibrate_loops(void)
+{
+ long long l0, l1;
+ int i;
+
+ cycles_per_sec = 0;
+ printf("calibrating cycles: "); fflush(stdout);
+
+ /*
+ * Make sure we start on a precise timer IRQ boundary:
+ */
+ usleep(50000);
+
+ for (i = 0; i < 10; i++) {
+ sched_yield();
+ l0 = *cycles;
+ usleep(200000);
+ l1 = *cycles;
+ cycles_per_sec = max(cycles_per_sec, l1-l0);
+ }
+ cycles_per_sec *= 5;
+
+ printf("%Ld cycles/sec\n", cycles_per_sec);
+}
+
+static int child(struct sockaddr *addr, int len)
+{
+ static char buffer[BUFSIZE];
+ int sk;
+ double c1, c2, c3;
+ int fd;
+ struct sockaddr_in s_to;
+ struct hostent *hp;
+ double r1, r2, r3, r4, r5;
+ int i, pipefd[2];
+ loff_t off = 0;
+
+ r1 = r2 = r3 = r4 = r5 = 0;
+
+ sk = socket(PF_INET, SOCK_STREAM, 0);
+ if (!sk)
+ return error("socket");
+ hp = gethostbyname (TARGET_HOSTNAME);
+ BUG_ON(!hp);
+ bzero ((char *) &s_to, sizeof (s_to));
+ bcopy ((char *) hp->h_addr, (char *) &(s_to.sin_addr), hp->h_length);
+ s_to.sin_family = hp->h_addrtype;
+ s_to.sin_port = htons(1111);
+
+ calibrate_loops();
+
+ fprintf(stdout, "BUFSIZE = %d\n", BUFSIZE);
+ fflush(stdout);
+
+ if (getenv("EMPTY")) {
+ if (connect(sk, (struct sockaddr *)&s_to, len) < 0)
+ return error("connect");
+
+ start_timing("Empty buffer");
+ for (i = 0; i < NR; i++)
+ write(sk, buffer, BUFSIZE);
+ end_timing(NR*BUFSIZE, &r1);
+
+ fd = open("largefile", O_RDONLY);
+ if (fd < 0)
+ return error("largefile");
+
+ start_timing("Read/write loop");
+ for (i = 0; i < NR; i++) {
+ if (read(fd, buffer, BUFSIZE) != BUFSIZE)
+ return error("largefile read");
+ write(sk, buffer, BUFSIZE);
+ }
+ end_timing(NR*BUFSIZE, &r2);
+ close(fd);
+ close(sk);
+ }
+
+ if (getenv("SF")) {
+ start_timing("sendfile");
+sendfile_again:
+ sk = socket(PF_INET, SOCK_STREAM, 0);
+ if (connect(sk, (struct sockaddr *)&s_to, len) < 0)
+ return error("connect");
+
+ fd = open("largefile", O_RDONLY);
+ if (fd < 0)
+ return error("largefile");
+
+ i = NR*BUFSIZE;
+ do {
+ int ret = sendfile(sk, fd, NULL, i);
+ i -= ret;
+ } while (i);
+
+ close(fd);
+ close(sk);
+ if (--sendfile_loops)
+ goto sendfile_again;
+ c1 = end_timing(NR*BUFSIZE*SENDFILE_LOOPS, &r3);
+ } else
+ c1 = 0;
+
+ if (getenv("SPLICE_PIPE")) {
+ start_timing("splice-pipe");
+splice_pipe_again:
+ sk = socket(PF_INET, SOCK_STREAM, 0);
+ if (connect(sk, (struct sockaddr *)&s_to, len) < 0)
+ return error("connect");
+
+ fd = open("largefile", O_RDONLY);
+ if (fd < 0)
+ return error("largefile");
+ if (pipe(pipefd) < 0)
+ return error("pipe");
+
+ i = NR*BUFSIZE;
+ off = 0;
+ do {
+ int ret = splice(fd, &off, pipefd[1], NULL, min(i, BUFSIZE), SPLICE_F_NONBLOCK);
+ if (ret <= 0)
+ return error("splice-pipe-in");
+ i -= ret;
+ while (ret > 0) {
+ int flags = i ? SPLICE_F_MORE : 0;
+ int written = splice(pipefd[0], NULL, sk, NULL, ret, flags);
+ if (written <= 0)
+ return error("splice-pipe-out");
+ ret -= written;
+ }
+ } while (i);
+
+ close(fd);
+ close(sk);
+ close(pipefd[0]);
+ close(pipefd[1]);
+ if (--splice_pipe_loops)
+ goto splice_pipe_again;
+ c2 = end_timing(NR*BUFSIZE*SPLICE_LOOPS, &r4);
+ } else
+ c2 = 0;
+
+ if (getenv("SPLICE")) {
+ start_timing("splice");
+splice_again:
+ sk = socket(PF_INET, SOCK_STREAM, 0);
+ if (connect(sk, (struct sockaddr *)&s_to, len) < 0)
+ return error("connect");
+
+ fd = open("largefile", O_RDONLY);
+ if (fd < 0)
+ return error("largefile");
+
+ i = NR*BUFSIZE;
+ off = 0;
+ do {
+ int flags = BUFSIZE < i ? SPLICE_F_MORE : 0;
+ int ret;
+
+ ret = splice(fd, &off, sk, NULL, min(i, BUFSIZE), flags);
+
+ if (ret <= 0)
+ return error("splice");
+ i -= ret;
+ } while (i);
+
+ close(fd);
+ close(sk);
+ if (--splice_loops)
+ goto splice_again;
+ c3 = end_timing(NR*BUFSIZE*SPLICE_LOOPS, &r5);
+ } else
+ c3 = 0;
+
+ /*
+ * c1/r3 - sendfile
+ * c2/r4 - splice-pipe
+ * c3/r5 - splice
+ */
+
+ if (c1 && c2)
+ printf("sendfile is %.2f%% more efficient than splice-pipe.\n",
+ (c2 - c1) / c1 * 100.0 );
+ if (c1 && c3)
+ printf("sendfile is %.2f%% more efficient than splice.\n",
+ (c3 - c1) / c1 * 100.0 );
+ if (c2 && c3)
+ printf("splice is %.2f%% more efficient splice-pipe.\n",
+ (c2 - c3) / c3 * 100.0 );
+ if (r3 && r4)
+ printf("sendfile is %.2f%% faster than splice-pipe.\n",
+ (r3 - r4) / r4 * 100.0 );
+ if (r3 && r5)
+ printf("sendfile is %.2f%% faster than splice.\n",
+ (r3 - r5) / r5 * 100.0 );
+ if (r4 && r5)
+ printf("splice is %.2f%% faster than splice-pipe.\n",
+ (r5 - r4) / r4 * 100.0 );
+
+ return 0;
+}
+
+
+static void setup_shared_var(void)
+{
+ char zerobuff [4096] = { 0, };
+ int ret, fd;
+
+ fd = creat(".tmp_mmap", 0700);
+ BUG_ON(fd == -1);
+ close(fd);
+
+ fd = open(".tmp_mmap", O_RDWR|O_CREAT|O_TRUNC);
+ BUG_ON(fd == -1);
+ ret = write(fd, zerobuff, 4096);
+ BUG_ON(ret != 4096);
+
+ cycles = (void *)mmap(0, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+ BUG_ON(cycles == (void *)-1);
+
+ close(fd);
+}
+
+#define SCHED_BATCH 3
+
+#ifdef __ia64__
+# define rdtscll(val) \
+do { \
+ val = *__mm_clock_dev; \
+} while (0)
+#elif defined(__i386__)
+# define rdtscll(val) \
+do { \
+ __asm__ __volatile__("rdtsc" : "=A" (val)); \
+} while (0)
+#else
+# define rdtscll(val) \
+ do { (val) = 0LL; } while (0)
+#endif
+
+/*
+ * Keep lowprio looping - to meausure the number of idle cycles
+ * available. It's tricky: we do a series of RDTSC calls, and
+ * if the delay to the last measurement was less than 500 cycles,
+ * we conclude that only this loop ran.
+ */
+static void lowprio_cycle_soak_loop(void)
+{
+ struct sched_param p = { sched_priority: 0 };
+ unsigned long long t0, t1, delta;
+
+ /*
+ * We are a nice +19 SCHED_BATCH task:
+ */
+ BUG_ON(sched_setscheduler(0, SCHED_BATCH, &p) != 0);
+ nice(40);
+
+ rdtscll(t0);
+ while (cycles >= 0) {
+ rdtscll(t1);
+ delta = t1-t0;
+ if (delta < 500)
+ *cycles += delta;
+ t0 = t1;
+ }
+}
+
+int main(int argc, char **argv)
+{
+ unsigned int sk, len;
+ struct sockaddr addr;
+ pid_t pid;
+
+ setup_shared_var();
+
+ signal(SIGCHLD, SIG_IGN);
+ sk = socket(PF_INET, SOCK_STREAM, 0);
+ if (sk < 0) {
+ perror("socket");
+ exit(1);
+ }
+ if (listen(sk, 1) < 0) {
+ perror("listen");
+ exit(1);
+ }
+ len = sizeof(addr);
+ if (getsockname(sk, &addr, &len) < 0) {
+ perror("getsockname");
+ exit(1);
+ }
+ pid = fork();
+ if (!pid) {
+ lowprio_cycle_soak_loop();
+ exit(0);
+ }
+ nice(-20);
+ child(&addr, len);
+ kill(pid, SIGHUP);
+ exit(0);
+}