From: Rusty Russell Date: Mon, 14 Oct 2013 10:04:35 +0000 (+1030) Subject: ccan/io: benchmarks. X-Git-Url: http://git.ozlabs.org/?p=ccan;a=commitdiff_plain;h=8d97cddd18f504409ddb66dfe396798c99c2dfbd ccan/io: benchmarks. More stress test than benchmarks, but provides a beginning. Signed-off-by: Rusty Russell --- diff --git a/ccan/io/benchmarks/Makefile b/ccan/io/benchmarks/Makefile new file mode 100644 index 00000000..21f3441d --- /dev/null +++ b/ccan/io/benchmarks/Makefile @@ -0,0 +1,25 @@ +ALL:=run-loop run-different-speed run-length-prefix +CCANDIR:=../../.. +CFLAGS:=-Wall -I$(CCANDIR) -O3 -flto +LDFLAGS:=-O3 -flto +LDLIBS:=-lrt + +OBJS:=time.o poll.o io.o err.o + +default: $(ALL) + +run-loop: run-loop.o $(OBJS) +run-different-speed: run-different-speed.o $(OBJS) +run-length-prefix: run-length-prefix.o $(OBJS) + +time.o: $(CCANDIR)/ccan/time/time.c + $(CC) $(CFLAGS) -c -o $@ $< +poll.o: $(CCANDIR)/ccan/io/poll.c + $(CC) $(CFLAGS) -c -o $@ $< +io.o: $(CCANDIR)/ccan/io/io.c + $(CC) $(CFLAGS) -c -o $@ $< +err.o: $(CCANDIR)/ccan/err/err.c + $(CC) $(CFLAGS) -c -o $@ $< + +clean: + $(RM) -f *.o $(ALL) diff --git a/ccan/io/benchmarks/run-different-speed.c b/ccan/io/benchmarks/run-different-speed.c new file mode 100644 index 00000000..e21b1dd9 --- /dev/null +++ b/ccan/io/benchmarks/run-different-speed.c @@ -0,0 +1,179 @@ +/* Simulate a server with connections of different speeds. We count + * how many connections complete in 10 seconds. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define REQUEST_SIZE 1024 +#define REPLY_SIZE 10240 +#define NUM_CONNS 500 /* per child */ +#define NUM_CHILDREN 2 + +static unsigned int completed; + +struct client { + char request_buffer[REQUEST_SIZE]; + char reply_buffer[REPLY_SIZE]; +}; + +static struct io_op *write_reply(struct io_conn *conn, struct client *client); +static struct io_op *read_request(struct io_conn *conn, struct client *client) +{ + return io_read(client->request_buffer, REQUEST_SIZE, + io_next(conn, write_reply, client)); +} + +/* once we're done, loop again. */ +static struct io_op *write_complete(struct io_conn *conn, struct client *client) +{ + completed++; + return read_request(conn, client); +} + +static struct io_op *write_reply(struct io_conn *conn, struct client *client) +{ + return io_write(client->reply_buffer, REPLY_SIZE, + io_next(conn, write_complete, client)); +} + +/* This runs in the child. */ +static void create_clients(struct sockaddr_un *addr, int waitfd) +{ + struct client data; + int i, sock[NUM_CONNS], speed[NUM_CONNS], done[NUM_CONNS], count = 0; + + for (i = 0; i < NUM_CONNS; i++) { + /* Set speed. */ + speed[i] = (1 << (random() % 10)); + sock[i] = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock[i] < 0) + err(1, "creating socket"); + if (connect(sock[i], (void *)addr, sizeof(*addr)) != 0) + err(1, "connecting socket"); + /* Make nonblocking. */ + fcntl(sock[i], F_SETFD, fcntl(sock[i], F_GETFD)|O_NONBLOCK); + done[i] = 0; + } + + read(waitfd, &i, 1); + + for (;;) { + for (i = 0; i < NUM_CONNS; i++) { + int ret, bytes = speed[i]; + if (done[i] < REQUEST_SIZE) { + if (REQUEST_SIZE - done[i] < bytes) + bytes = REQUEST_SIZE - done[i]; + ret = write(sock[i], data.request_buffer, + bytes); + if (ret > 0) + done[i] += ret; + else if (ret < 0 && errno != EAGAIN) + goto fail; + } else { + if (REQUEST_SIZE + REPLY_SIZE - done[i] < bytes) + bytes = REQUEST_SIZE + REPLY_SIZE + - done[i]; + ret = read(sock[i], data.reply_buffer, + bytes); + if (ret > 0) { + done[i] += ret; + if (done[i] == REQUEST_SIZE + REPLY_SIZE) { + count++; + done[i] = 0; + } + } else if (ret < 0 && errno != EAGAIN) + goto fail; + } + } + } +fail: + printf("Child did %u\n", count); + exit(0); +} + +static int timeout[2]; +static void sigalarm(int sig) +{ + write(timeout[1], "1", 1); +} + +static struct io_op *do_timeout(struct io_conn *conn, char *buf) +{ + return io_break(conn, NULL); +} + +static struct io_op *do_timeout_read(struct io_conn *conn, char *buf) +{ + return io_read(buf, 1, io_next(conn, do_timeout, buf)); +} + +int main(int argc, char *argv[]) +{ + struct client client; + unsigned int i, j; + struct sockaddr_un addr; + struct timespec start, end; + int fd, wake[2]; + char buf; + + addr.sun_family = AF_UNIX; + sprintf(addr.sun_path, "/tmp/run-different-speed.sock.%u", getpid()); + + if (pipe(wake) != 0 || pipe(timeout) != 0) + err(1, "Creating pipes"); + + fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd < 0) + err(1, "Creating socket"); + + if (bind(fd, (void *)&addr, sizeof(addr)) != 0) + err(1, "Binding to %s", addr.sun_path); + + if (listen(fd, NUM_CONNS) != 0) + err(1, "Listening on %s", addr.sun_path); + + for (i = 0; i < NUM_CHILDREN; i++) { + switch (fork()) { + case -1: + err(1, "forking"); + case 0: + close(wake[1]); + create_clients(&addr, wake[0]); + break; + } + for (j = 0; j < NUM_CONNS; j++) { + int ret = accept(fd, NULL, 0); + if (ret < 0) + err(1, "Accepting fd"); + /* For efficiency, we share client structure */ + io_new_conn(ret, read_request, NULL, &client); + } + } + + io_new_conn(timeout[0], do_timeout_read, NULL, &buf); + + close(wake[0]); + for (i = 0; i < NUM_CHILDREN; i++) + write(wake[1], "1", 1); + + signal(SIGALRM, sigalarm); + alarm(10); + start = time_now(); + io_loop(); + end = time_now(); + close(fd); + + printf("%u connections complete (%u ns per conn)\n", + completed, + (int)time_to_nsec(time_divide(time_sub(end, start), completed))); + return 0; +} diff --git a/ccan/io/benchmarks/run-length-prefix.c b/ccan/io/benchmarks/run-length-prefix.c new file mode 100644 index 00000000..7d033634 --- /dev/null +++ b/ccan/io/benchmarks/run-length-prefix.c @@ -0,0 +1,187 @@ +/* Simulate a server with connections of different speeds. We count + * how many connections complete in 10 seconds. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define REQUEST_MAX 131072 +#define NUM_CONNS 500 /* per child */ +#define NUM_CHILDREN 2 + +static unsigned int completed; + +struct client { + unsigned int len; + char *request_buffer; +}; + +static struct io_op *write_reply(struct io_conn *conn, struct client *client); +static struct io_op *read_body(struct io_conn *conn, struct client *client) +{ + assert(client->len <= REQUEST_MAX); + return io_read(client->request_buffer, client->len, + io_next(conn, write_reply, client)); +} + +static struct io_op *read_header(struct io_conn *conn, struct client *client) +{ + return io_read(&client->len, sizeof(client->len), + io_next(conn, read_body, client)); +} + +/* once we're done, loop again. */ +static struct io_op *write_complete(struct io_conn *conn, struct client *client) +{ + completed++; + return read_header(conn, client); +} + +static struct io_op *write_reply(struct io_conn *conn, struct client *client) +{ + return io_write(&client->len, sizeof(client->len), + io_next(conn, write_complete, client)); +} + +/* This runs in the child. */ +static void create_clients(struct sockaddr_un *addr, int waitfd) +{ + struct client data; + int i, sock[NUM_CONNS], len[NUM_CONNS], done[NUM_CONNS], + result[NUM_CONNS], count = 0; + + for (i = 0; i < NUM_CONNS; i++) { + len[i] = (random() % REQUEST_MAX) + 1; + sock[i] = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock[i] < 0) + err(1, "creating socket"); + if (connect(sock[i], (void *)addr, sizeof(*addr)) != 0) + err(1, "connecting socket"); + /* Make nonblocking. */ + fcntl(sock[i], F_SETFD, fcntl(sock[i], F_GETFD)|O_NONBLOCK); + done[i] = 0; + } + + read(waitfd, &i, 1); + + for (;;) { + for (i = 0; i < NUM_CONNS; i++) { + int ret, totlen = len[i] + sizeof(len[i]); + if (done[i] < sizeof(len[i]) + len[i]) { + data.len = len[i]; + ret = write(sock[i], (void *)&data + done[i], + totlen - done[i]); + if (ret > 0) + done[i] += ret; + else if (ret < 0 && errno != EAGAIN) + goto fail; + } else { + int off = done[i] - totlen; + ret = read(sock[i], (void *)&result[i] + off, + sizeof(result[i]) - off); + if (ret > 0) { + done[i] += ret; + if (done[i] == totlen + + sizeof(result[i])) { + assert(result[i] == len[i]); + count++; + done[i] = 0; + } + } else if (ret < 0 && errno != EAGAIN) + goto fail; + } + } + } +fail: + printf("Child did %u\n", count); + exit(0); +} + +static int timeout[2]; +static void sigalarm(int sig) +{ + write(timeout[1], "1", 1); +} + +static struct io_op *do_timeout(struct io_conn *conn, char *buf) +{ + return io_break(conn, NULL); +} + +static struct io_op *do_timeout_read(struct io_conn *conn, char *buf) +{ + return io_read(buf, 1, io_next(conn, do_timeout, buf)); +} + +int main(int argc, char *argv[]) +{ + unsigned int i, j; + struct sockaddr_un addr; + struct timespec start, end; + char buffer[REQUEST_MAX]; + int fd, wake[2]; + char buf; + + addr.sun_family = AF_UNIX; + sprintf(addr.sun_path, "/tmp/run-different-speed.sock.%u", getpid()); + + if (pipe(wake) != 0 || pipe(timeout) != 0) + err(1, "Creating pipes"); + + fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd < 0) + err(1, "Creating socket"); + + if (bind(fd, (void *)&addr, sizeof(addr)) != 0) + err(1, "Binding to %s", addr.sun_path); + + if (listen(fd, NUM_CONNS) != 0) + err(1, "Listening on %s", addr.sun_path); + + for (i = 0; i < NUM_CHILDREN; i++) { + switch (fork()) { + case -1: + err(1, "forking"); + case 0: + close(wake[1]); + create_clients(&addr, wake[0]); + break; + } + for (j = 0; j < NUM_CONNS; j++) { + struct client *client = malloc(sizeof(*client)); + int ret = accept(fd, NULL, 0); + if (ret < 0) + err(1, "Accepting fd"); + /* For efficiency, we share buffer */ + client->request_buffer = buffer; + io_new_conn(ret, read_header, NULL, client); + } + } + + io_new_conn(timeout[0], do_timeout_read, NULL, &buf); + + close(wake[0]); + for (i = 0; i < NUM_CHILDREN; i++) + write(wake[1], "1", 1); + + signal(SIGALRM, sigalarm); + alarm(10); + start = time_now(); + io_loop(); + end = time_now(); + close(fd); + + printf("%u connections complete (%u ns per conn)\n", + completed, + (int)time_to_nsec(time_divide(time_sub(end, start), completed))); + return 0; +} diff --git a/ccan/io/benchmarks/run-loop.c b/ccan/io/benchmarks/run-loop.c new file mode 100644 index 00000000..9af564c7 --- /dev/null +++ b/ccan/io/benchmarks/run-loop.c @@ -0,0 +1,130 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define NUM 500 +#define NUM_ITERS 10000 + +struct buffer { + int iters; + struct io_conn *reader, *writer; + char buf[32]; +}; + +static struct io_op *poke_writer(struct io_conn *conn, struct buffer *buf); +static struct io_op *poke_reader(struct io_conn *conn, struct buffer *buf); + +static struct io_op *do_read(struct io_conn *conn, struct buffer *buf) +{ + assert(conn == buf->reader); + + return io_read(&buf->buf, sizeof(buf->buf), + io_next(conn, poke_writer, buf)); +} + +static struct io_op *do_write(struct io_conn *conn, struct buffer *buf) +{ + assert(conn == buf->writer); + + return io_write(&buf->buf, sizeof(buf->buf), + io_next(conn, poke_reader, buf)); +} + +static struct io_op *poke_writer(struct io_conn *conn, struct buffer *buf) +{ + assert(conn == buf->reader); + + if (buf->iters == NUM_ITERS) + return io_close(conn, NULL); + + /* You write. */ + io_wake(buf->writer, do_write, buf); + + /* I'll wait until you wake me. */ + return io_idle(conn); +} + +static struct io_op *poke_reader(struct io_conn *conn, struct buffer *buf) +{ + assert(conn == buf->writer); + /* You read. */ + io_wake(buf->reader, do_read, buf); + + if (++buf->iters == NUM_ITERS) + return io_close(conn, NULL); + + /* I'll wait until you tell me to write. */ + return io_idle(conn); +} + +static struct io_op *reader(struct io_conn *conn, struct buffer *buf) +{ + assert(conn == buf->reader); + + /* Wait for writer to tell us to read. */ + return io_idle(conn); +} + +int main(void) +{ + unsigned int i; + int fds[2], last_read, last_write; + struct timespec start, end; + struct buffer buf[NUM]; + + if (pipe(fds) != 0) + err(1, "pipe"); + last_read = fds[0]; + last_write = fds[1]; + + for (i = 1; i < NUM; i++) { + buf[i].iters = 0; + if (pipe(fds) < 0) + err(1, "pipe"); + memset(buf[i].buf, i, sizeof(buf[i].buf)); + sprintf(buf[i].buf, "%i-%i", i, i); + + buf[i].reader = io_new_conn(last_read, reader, NULL, &buf[i]); + if (!buf[i].reader) + err(1, "Creating reader %i", i); + buf[i].writer = io_new_conn(fds[1], do_write, NULL, &buf[i]); + if (!buf[i].writer) + err(1, "Creating writer %i", i); + last_read = fds[0]; + } + + /* Last one completes the cirle. */ + i = 0; + buf[i].iters = 0; + sprintf(buf[i].buf, "%i-%i", i, i); + buf[i].reader = io_new_conn(last_read, reader, NULL, &buf[i]); + if (!buf[i].reader) + err(1, "Creating reader %i", i); + buf[i].writer = io_new_conn(last_write, do_write, NULL, &buf[i]); + if (!buf[i].writer) + err(1, "Creating writer %i", i); + + /* They should eventually exit */ + start = time_now(); + if (io_loop() != NULL) + errx(1, "io_loop?"); + end = time_now(); + + for (i = 0; i < NUM; i++) { + char b[sizeof(buf[0].buf)]; + memset(b, i, sizeof(b)); + sprintf(b, "%i-%i", i, i); + if (memcmp(b, buf[(i + NUM_ITERS) % NUM].buf, sizeof(b)) != 0) + errx(1, "Buffer for %i was '%s' not '%s'", + i, buf[(i + NUM_ITERS) % NUM].buf, b); + } + + printf("run-many: %u %u iterations: %llu usec\n", + NUM, NUM_ITERS, (long long)time_to_usec(time_sub(end, start))); + return 0; +}