From b04fc33ef748293498023f3cf2dc987e8c1ffdd4 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Mon, 25 Feb 2013 02:25:49 +0100 Subject: [PATCH] linux: use eventfds for async handles Use eventfds to drive async handles, fall back to regular pipes on older kernels (pre-2.6.22). Gives a nice boost on the async handle benchmarks. Before: 12,516,113 async events in 5.0 seconds (2,503,222/s, 1,048,576 unique async1: 11.95 sec (83,701/sec) async2: 11.65 sec (85,862/sec) async4: 5.20 sec (192,154/sec) async8: 9.97 sec (100,315/sec) async_pummel_1: 1,000,000 callbacks in 2.56 seconds (389,919/sec) async_pummel_2: 1,000,000 callbacks in 2.65 seconds (377,205/sec) async_pummel_4: 1,000,000 callbacks in 2.18 seconds (457,704/sec) async_pummel_8: 1,000,000 callbacks in 4.19 seconds (238,632/sec) After: 16,168,081 async events in 5.0 seconds (3,233,616/s, 1,048,576 unique async1: 11.08 sec (90,213/sec) async2: 10.17 sec (98,297/sec) async4: 4.81 sec (207,789/sec) async8: 8.98 sec (111,419/sec) async_pummel_1: 1,000,000 callbacks in 1.16 seconds (863,296/sec) async_pummel_2: 1,000,000 callbacks in 1.45 seconds (691,459/sec) async_pummel_4: 1,000,000 callbacks in 0.66 seconds (1,514,770/sec) async_pummel_8: 1,000,000 callbacks in 1.42 seconds (704,549/sec) That's a speedup from anywhere between 10% to 330%. --- src/unix/async.c | 94 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 88 insertions(+), 6 deletions(-) diff --git a/src/unix/async.c b/src/unix/async.c index 36d94402..43f54ed4 100644 --- a/src/unix/async.c +++ b/src/unix/async.c @@ -28,12 +28,14 @@ #include #include #include +#include #include static void uv__async_event(uv_loop_t* loop, struct uv__async* w, unsigned int nevents); static int uv__async_make_pending(int* pending); +static int uv__async_eventfd(void); int uv_async_init(uv_loop_t* loop, uv_async_t* handle, uv_async_cb async_cb) { @@ -140,18 +142,45 @@ static void uv__async_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) { } wa = container_of(w, struct uv__async, io_watcher); + +#if defined(__linux__) + if (wa->wfd == -1) { + uint64_t val; + assert(n == sizeof(val)); + memcpy(&val, buf, sizeof(val)); /* Avoid alignment issues. */ + wa->cb(loop, wa, val); + return; + } +#endif + wa->cb(loop, wa, n); } void uv__async_send(struct uv__async* wa) { + const void* buf; + ssize_t len; + int fd; int r; + buf = ""; + len = 1; + fd = wa->wfd; + +#if defined(__linux__) + if (fd == -1) { + static const uint64_t val = 1; + buf = &val; + len = sizeof(val); + fd = wa->io_watcher.fd; /* eventfd */ + } +#endif + do - r = write(wa->wfd, "", 1); + r = write(fd, buf, len); while (r == -1 && errno == EINTR); - if (r == 1) + if (r == len) return; if (r == -1) @@ -170,11 +199,19 @@ void uv__async_init(struct uv__async* wa) { int uv__async_start(uv_loop_t* loop, struct uv__async* wa, uv__async_cb cb) { int pipefd[2]; + int fd; if (wa->io_watcher.fd != -1) return 0; - if (uv__make_pipe(pipefd, UV__F_NONBLOCK)) + fd = uv__async_eventfd(); + if (fd >= 0) { + pipefd[0] = fd; + pipefd[1] = -1; + } + else if (fd != -ENOSYS) + return -1; + else if (uv__make_pipe(pipefd, UV__F_NONBLOCK)) return -1; uv__io_init(&wa->io_watcher, uv__async_io, pipefd[0]); @@ -192,8 +229,53 @@ void uv__async_stop(uv_loop_t* loop, struct uv__async* wa) { uv__io_stop(loop, &wa->io_watcher, UV__POLLIN); close(wa->io_watcher.fd); - close(wa->wfd); - wa->io_watcher.fd = -1; - wa->wfd = -1; + + if (wa->wfd != -1) { + close(wa->wfd); + wa->wfd = -1; + } +} + + +static int uv__async_eventfd() { +#if defined(__linux__) + static int no_eventfd2; + static int no_eventfd; + int fd; + + if (no_eventfd2) + goto skip_eventfd2; + + fd = uv__eventfd2(0, UV__EFD_CLOEXEC | UV__EFD_NONBLOCK); + if (fd != -1) + return fd; + + if (errno != ENOSYS) + return -errno; + + no_eventfd2 = 1; + +skip_eventfd2: + + if (no_eventfd) + goto skip_eventfd; + + fd = uv__eventfd(0); + if (fd != -1) { + uv__cloexec(fd, 1); + uv__nonblock(fd, 1); + return fd; + } + + if (errno != ENOSYS) + return -errno; + + no_eventfd = 1; + +skip_eventfd: + +#endif + + return -ENOSYS; }