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);