diff --git a/Makefile b/Makefile index 0e6ff8ceca944f13dd2f072bcde102bfd3c9a813..139ebf00e484c843e211e95e898ee29cffc8b82b 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ BENCH_MAIN := bench.c -SYSCALLS := blocking io-uring io-uring-sqpoll epoll aio-sig aio-thrd +SYSCALLS := blocking io-uring io-uring-sqpoll io-uring-no-syscall epoll aio-sig aio-thrd OBJ := $(addprefix bench-,$(SYSCALLS)) @@ -19,7 +19,7 @@ docker-eval: all: $(OBJ) define generateTargets -bench-$(1): $(1).c bench.c | Makefile +bench-$(1): $(1).c bench.c stopwatch.c | Makefile $(CC) $(CFLAGS) -o $$@ $$^ $(LDFLAGS) endef diff --git a/aio-sig.c b/aio-sig.c index fda8c7365a0fc404f9da513027b1054d9ceb83a8..408aa6f372b9bdcd360d9b2cb909feae7cc79cdf 100644 --- a/aio-sig.c +++ b/aio-sig.c @@ -1,29 +1,25 @@ #include <aio.h> #include <err.h> -#include <semaphore.h> #include <signal.h> +#include <stdatomic.h> #include <stdint.h> #include <stdlib.h> -#include "rdtsc.h" +#include "stopwatch.h" -int64_t clock_before, clock_after; struct aiocb aiocb; -sem_t sem; +atomic_int done; void callback(int sig, siginfo_t* info, void* context) { - clock_after = rdtsc_s(); + stop_watch(); if (info->si_value.sival_int != 42) errx(EXIT_FAILURE, "got unexpected sigval value"); - sem_post(&sem); + atomic_store(&done, 1); } void init(int fd) { - if (sem_init(&sem, 0, 0) == -1) - err(EXIT_FAILURE, "sem_init failed"); - aiocb.aio_fildes = fd; aiocb.aio_sigevent.sigev_notify = SIGEV_SIGNAL; aiocb.aio_sigevent.sigev_signo = SIGUSR1; @@ -37,16 +33,17 @@ void init(int fd) { err(EXIT_FAILURE, "sigaction failed"); } -int64_t do_write(int fd, const void* buf, size_t count) { +void do_write(int fd, const void* buf, size_t count) { aiocb.aio_buf = (void*)buf; aiocb.aio_nbytes = count; - clock_before = rdtsc_s(); + start_watch(); int res = aio_write(&aiocb); - sem_wait(&sem); if (res == -1) err(EXIT_FAILURE, "aio_write failed"); - return clock_after - clock_before; + while(!atomic_load(&done)) {} + atomic_store(&done, 1); + } diff --git a/aio-thrd.c b/aio-thrd.c index 422917f35b116cd560ce3e7cf38c54102cc2c4d3..7e03762494143bb9db4027bbcc301249e5974c22 100644 --- a/aio-thrd.c +++ b/aio-thrd.c @@ -1,46 +1,41 @@ #include <aio.h> #include <err.h> -#include <semaphore.h> #include <signal.h> +#include <stdatomic.h> #include <stdint.h> #include <stdlib.h> -#include "rdtsc.h" +#include "stopwatch.h" -int64_t clock_before, clock_after; -struct aiocb aiocb; - -sem_t sem; +atomic_int done; +struct aiocb aiocb; void callback(union sigval sigval) { - clock_after = rdtsc_s(); + stop_watch(); if (sigval.sival_int != 42) errx(EXIT_FAILURE, "got unexpected sigval value"); - sem_post(&sem); + atomic_store(&done, 1); } void init(int fd) { - if (sem_init(&sem, 0, 0) == -1) - err(EXIT_FAILURE, "sem_init failed"); - aiocb.aio_fildes = fd; aiocb.aio_sigevent.sigev_notify = SIGEV_THREAD; aiocb.aio_sigevent.sigev_notify_function = callback; aiocb.aio_sigevent.sigev_value.sival_int = 42; } -int64_t do_write(int fd, void* buf, size_t count) { +void do_write(int fd, void* buf, size_t count) { aiocb.aio_buf = buf; aiocb.aio_nbytes = count; - clock_before = rdtsc_s(); + start_watch(); int res = aio_write(&aiocb); - sem_wait(&sem); + + while(!atomic_load(&done)) {} + atomic_store(&done, 0); if (res == -1) err(EXIT_FAILURE, "aio_write failed"); - - return clock_after - clock_before; } diff --git a/bench.c b/bench.c index 861ea702cf28a4ecefbdcf77a78a060dce92e79f..38c2828341a9a11527f080436d2841a3a1b78979 100644 --- a/bench.c +++ b/bench.c @@ -5,22 +5,16 @@ #include <stdlib.h> #include <sys/eventfd.h> +#include "stopwatch.h" + void init(int fd); -int64_t do_write(int fd, const void* buf, size_t count); +void do_write(int fd, const void* buf, size_t count); const size_t warmup = 10000; const size_t iterations = 1000000; #define BUFSIZE 64 -/* static int open_dev_zero() { */ - /* int fd = open("/dev/zero", O_RDONLY); */ - /* if (fd == -1) { */ - /* err(EXIT_FAILURE, "opening /dev/zero failed"); */ - /* } */ - /* return fd; */ -/* } */ - static int create_eventfd() { int fd = eventfd(0, 0); if (fd == -1) { @@ -30,25 +24,21 @@ static int create_eventfd() { } int main() { - double avg_cycles = 0; + int64_t avg_nanos = 0; + uint64_t write_buf = 1; - /* int fd = open_dev_zero(); */ int fd = create_eventfd(); init(fd); - /* char read_buf[BUFSIZE]; */ - uint64_t write_buf = 1; - for (size_t i = 0; i < warmup; ++i) do_write(fd, &write_buf, sizeof(write_buf)); - for (size_t i = 1; i <= iterations; ++i) { - ssize_t cycles = do_write(fd, &write_buf, sizeof(write_buf)); - avg_cycles += ((double)cycles - avg_cycles) / i; + for (int64_t i = 1; i <= iterations; ++i) { + do_write(fd, &write_buf, sizeof(write_buf)); + avg_nanos += (clock_diff_nanos() - avg_nanos) / i; } - printf("avg_cycles: %lf\n", avg_cycles); - + printf("%ld ns\n", avg_nanos); return 0; } diff --git a/blocking.c b/blocking.c index 9e057146d9850c74d25c7ae7d9bff98e791aca93..513e62199fc5a44d099d6e0ede0601e5e02b2ae5 100644 --- a/blocking.c +++ b/blocking.c @@ -2,19 +2,15 @@ #include <stdlib.h> #include <unistd.h> -#include "rdtsc.h" +#include "stopwatch.h" void init(__attribute__((unused)) int fd) {} -int64_t do_write(int fd, const void* buf, size_t count) { - int64_t clock_before, clock_after; - - clock_before = rdtsc_s(); +void do_write(int fd, const void* buf, size_t count) { + start_watch(); ssize_t res = write(fd, buf, count); - clock_after = rdtsc_s(); + stop_watch(); if (res == -1) err(EXIT_FAILURE, "write failed"); - - return clock_after - clock_before; } diff --git a/common.h b/common.h deleted file mode 100644 index 909aa54da958bc60b92ee9facccad859ff77ca9c..0000000000000000000000000000000000000000 --- a/common.h +++ /dev/null @@ -1,22 +0,0 @@ -#include <err.h> -#include <fcntl.h> -#include <stdint.h> -#include <stdlib.h> - -#define BUFSIZE 64 - -int64_t do_write(int fd, const void* buf, size_t count); - -__inline__ int64_t rdtsc_s(void) { - unsigned a, d; - asm volatile("cpuid" ::: "%rax", "%rbx", "%rcx", "%rdx"); - asm volatile("rdtsc" : "=a" (a), "=d" (d)); - return ((unsigned long)a) | (((unsigned long)d) << 32); -} - -__inline__ int64_t rdtsc_e(void) { - unsigned a, d; - asm volatile("rdtscp" : "=a" (a), "=d" (d)); - asm volatile("cpuid" ::: "%rax", "%rbx", "%rcx", "%rdx"); - return ((unsigned long)a) | (((unsigned long)d) << 32); -} diff --git a/epoll.c b/epoll.c index d898734ee9a9203afe743b135d605e6202f98d12..a4ef79a398d03ec8fda17534a611abd58ea8e5f2 100644 --- a/epoll.c +++ b/epoll.c @@ -3,7 +3,7 @@ #include <sys/epoll.h> #include <unistd.h> -#include "rdtsc.h" +#include "stopwatch.h" struct epoll_event ev; int nfds, epollfd; @@ -21,13 +21,11 @@ void init(int fd) { } -int64_t do_write(int fd, const void* buf, size_t count) { - int64_t clock_before, clock_after; - - clock_before = rdtsc_s(); +void do_write(int fd, const void* buf, size_t count) { + start_watch(); nfds = epoll_wait(epollfd, &ev, 1, -1); size_t res = write(fd, buf, count); - clock_after = rdtsc_s(); + stop_watch(); if (nfds == -1) err(EXIT_FAILURE, "epoll_wait failed"); @@ -37,6 +35,4 @@ int64_t do_write(int fd, const void* buf, size_t count) { if (res == -1) err(EXIT_FAILURE, "write failed"); - - return clock_after - clock_before; } diff --git a/io-uring-no-syscall.c b/io-uring-no-syscall.c new file mode 100644 index 0000000000000000000000000000000000000000..aa0cef02e0b1023a613bd64d71b68b537993d02d --- /dev/null +++ b/io-uring-no-syscall.c @@ -0,0 +1,42 @@ +#include <err.h> +#include <errno.h> +#include <liburing.h> +#include <stdlib.h> + +#define unlikely(x) __builtin_expect(!!(x), 0) + +#include "stopwatch.h" + +struct io_uring ring; + +void init(__attribute__((unused)) int fd) { + int res = io_uring_queue_init(16, &ring, IORING_SETUP_SQPOLL); + if (res < 0) { + errno = res; + err(EXIT_FAILURE, "io_uring_setup failed"); + } +} + +void do_write(int fd, const void* buf, size_t count) { + start_watch(); + struct io_uring_sqe *sqe = io_uring_get_sqe(&ring); + while (unlikely(!sqe)) { + sqe = io_uring_get_sqe(&ring); + } + io_uring_prep_write(sqe, fd, buf, count, 0); + + int res = io_uring_submit(&ring); + + struct io_uring_cqe* cqe; + while (io_uring_peek_cqe(&ring, &cqe) == -EAGAIN) {} + + stop_watch(); + + if (res < 0) { + err(EXIT_FAILURE, "io_submit failed"); + } + + if (cqe->res < 0) { + err(EXIT_FAILURE, "write request failed"); + } +} diff --git a/io-uring-sqpoll.c b/io-uring-sqpoll.c index e46e51706e79a30323c56289d7a0e983e7a6f7d7..7ea1e118bf24a4b5735813965c7d227accd8601d 100644 --- a/io-uring-sqpoll.c +++ b/io-uring-sqpoll.c @@ -5,7 +5,7 @@ #define unlikely(x) __builtin_expect(!!(x), 0) -#include "rdtsc.h" +#include "stopwatch.h" struct io_uring ring; @@ -17,9 +17,8 @@ void init(__attribute__((unused)) int fd) { } } -int64_t do_write(int fd, const void* buf, size_t count) { - int64_t clock_before, clock_after; - clock_before = rdtsc_s(); +void do_write(int fd, const void* buf, size_t count) { + start_watch(); struct io_uring_sqe *sqe = io_uring_get_sqe(&ring); while (unlikely(!sqe)) { @@ -29,7 +28,7 @@ int64_t do_write(int fd, const void* buf, size_t count) { int res = io_uring_submit_and_wait(&ring, 1); - clock_after = rdtsc_e(); + stop_watch(); if (res < 0) { err(EXIT_FAILURE, "io_uring_submit_and_wait failed"); @@ -44,6 +43,4 @@ int64_t do_write(int fd, const void* buf, size_t count) { if (cqe->res < 0) { err(EXIT_FAILURE, "write request failed"); } - - return clock_after - clock_before; } diff --git a/io-uring.c b/io-uring.c index 53f16f1d745450432f8fdc20a1efcb9c35c50c24..51afb7df194828fba770860b2a3b2280d1b6c8b8 100644 --- a/io-uring.c +++ b/io-uring.c @@ -3,7 +3,7 @@ #include <liburing.h> #include <stdlib.h> -#include "rdtsc.h" +#include "stopwatch.h" struct io_uring ring; @@ -15,30 +15,24 @@ void init(__attribute__((unused)) int fd) { } } -int64_t do_write(int fd, const void* buf, size_t count) { - int64_t clock_before, clock_after; - clock_before = rdtsc_s(); +void do_write(int fd, const void* buf, size_t count) { + start_watch(); struct io_uring_sqe *sqe = io_uring_get_sqe(&ring); io_uring_prep_write(sqe, fd, buf, count, 0); int res = io_uring_submit_and_wait(&ring, 1); - clock_after = rdtsc_e(); + stop_watch(); - if (res < 0) { + if (res < 0) err(EXIT_FAILURE, "io_uring_submit_and_wait failed"); - } struct io_uring_cqe* cqe; res = io_uring_peek_cqe(&ring, &cqe); - if (res < 0) { + if (res < 0) err(EXIT_FAILURE, "io_uring_peek_cqe failed"); - } - if (cqe->res < 0) { + if (cqe->res < 0) err(EXIT_FAILURE, "write request failed"); - } - - return clock_after - clock_before; } diff --git a/rdtsc.h b/rdtsc.h deleted file mode 100644 index ac687bb6762c64e94bffb91b65cf0f7857987672..0000000000000000000000000000000000000000 --- a/rdtsc.h +++ /dev/null @@ -1,15 +0,0 @@ -#include <stdint.h> - -static __inline__ int64_t rdtsc_s(void) { - unsigned a, d; - asm volatile("cpuid" ::: "%rax", "%rbx", "%rcx", "%rdx"); - asm volatile("rdtsc" : "=a" (a), "=d" (d)); - return ((unsigned long)a) | (((unsigned long)d) << 32); -} - -static __inline__ int64_t rdtsc_e(void) { - unsigned a, d; - asm volatile("rdtscp" : "=a" (a), "=d" (d)); - asm volatile("cpuid" ::: "%rax", "%rbx", "%rcx", "%rdx"); - return ((unsigned long)a) | (((unsigned long)d) << 32); -} diff --git a/stopwatch.c b/stopwatch.c new file mode 100644 index 0000000000000000000000000000000000000000..f781b7b8340a77c5b97ab36f59d53641e164a904 --- /dev/null +++ b/stopwatch.c @@ -0,0 +1,18 @@ +#include "stopwatch.h" + +struct timespec start, stop; + +int64_t sec_to_nanos(int64_t sec) { + return sec * 1000 * 1000 * 1000; +} + +double nanos_to_millis(int64_t nanos) { + return ((double)nanos) / (1000 * 1000); +} + +int64_t clock_diff_nanos() { + int64_t nanos = sec_to_nanos(stop.tv_sec - start.tv_sec); + nanos += (stop.tv_nsec - start.tv_nsec); + return nanos; +} + diff --git a/stopwatch.h b/stopwatch.h new file mode 100644 index 0000000000000000000000000000000000000000..0743b112bc3864a44f50e658fdb137d2fced0bad --- /dev/null +++ b/stopwatch.h @@ -0,0 +1,18 @@ +#pragma once + +#include <stdint.h> +#include <time.h> + +extern struct timespec start, stop; + +static inline void start_watch() { + clock_gettime(CLOCK_MONOTONIC, &start); +} + +static inline void stop_watch() { + clock_gettime(CLOCK_MONOTONIC, &stop); +} + +int64_t clock_diff_nanos(); +double nanos_to_millis(int64_t nanos); +int64_t sec_to_nanos(int64_t sec);