From c4901a1e03a7730e4471fd1143f1caf79695493d Mon Sep 17 00:00:00 2001 From: Andres Freund Date: Fri, 18 Mar 2016 11:43:59 -0700 Subject: [PATCH] Only clear latch self-pipe/event if there is a pending notification. This avoids a good number of, individually quite fast, system calls in scenarios with many quick queries. Besides the aesthetic benefit of seing fewer superflous system calls with strace, it also improves performance by ~2% measured by pgbench -M prepared -c 96 -j 8 -S (scale 100). Without having benchmarked it, this patch also adjust the windows code, as that makes it easier to unify the unix/windows codepaths in a later patch. There's little reason to diverge in behaviour between the platforms. Discussion: CA+TgmoYc1Zm+Szoc_Qbzi92z2c1vRHZmjhfPn5uC=w8bXv6Avg@mail.gmail.com Reviewed-By: Robert Haas --- src/backend/port/unix_latch.c | 81 +++++++++++++++++++++++----------- src/backend/port/win32_latch.c | 19 ++++---- 2 files changed, 65 insertions(+), 35 deletions(-) diff --git a/src/backend/port/unix_latch.c b/src/backend/port/unix_latch.c index e7be7ec117..104401d0fe 100644 --- a/src/backend/port/unix_latch.c +++ b/src/backend/port/unix_latch.c @@ -283,27 +283,31 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock, do { /* - * Clear the pipe, then check if the latch is set already. If someone - * sets the latch between this and the poll()/select() below, the - * setter will write a byte to the pipe (or signal us and the signal - * handler will do that), and the poll()/select() will return - * immediately. + * Check if the latch is set already. If so, leave loop immediately, + * avoid blocking again. We don't attempt to report any other events + * that might also be satisfied. + * + * If someone sets the latch between this and the poll()/select() + * below, the setter will write a byte to the pipe (or signal us and + * the signal handler will do that), and the poll()/select() will + * return immediately. + * + * If there's a pending byte in the self pipe, we'll notice whenever + * blocking. Only clearing the pipe in that case avoids having to + * drain it every time WaitLatchOrSocket() is used. Should the + * pipe-buffer fill up we're still ok, because the pipe is in + * nonblocking mode. It's unlikely for that to happen, because the + * self pipe isn't filled unless we're blocking (waiting = true), or + * from inside a signal handler in latch_sigusr1_handler(). * * Note: we assume that the kernel calls involved in drainSelfPipe() * and SetLatch() will provide adequate synchronization on machines * with weak memory ordering, so that we cannot miss seeing is_set if * the signal byte is already in the pipe when we drain it. */ - drainSelfPipe(); - if ((wakeEvents & WL_LATCH_SET) && latch->is_set) { result |= WL_LATCH_SET; - - /* - * Leave loop immediately, avoid blocking again. We don't attempt - * to report any other events that might also be satisfied. - */ break; } @@ -313,24 +317,26 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock, */ #if defined(LATCH_USE_POLL) nfds = 0; + + /* selfpipe is always in pfds[0] */ + pfds[0].fd = selfpipe_readfd; + pfds[0].events = POLLIN; + pfds[0].revents = 0; + nfds++; + if (wakeEvents & (WL_SOCKET_READABLE | WL_SOCKET_WRITEABLE)) { - /* socket, if used, is always in pfds[0] */ - pfds[0].fd = sock; - pfds[0].events = 0; + /* socket, if used, is always in pfds[1] */ + pfds[1].fd = sock; + pfds[1].events = 0; if (wakeEvents & WL_SOCKET_READABLE) - pfds[0].events |= POLLIN; + pfds[1].events |= POLLIN; if (wakeEvents & WL_SOCKET_WRITEABLE) - pfds[0].events |= POLLOUT; - pfds[0].revents = 0; + pfds[1].events |= POLLOUT; + pfds[1].revents = 0; nfds++; } - pfds[nfds].fd = selfpipe_readfd; - pfds[nfds].events = POLLIN; - pfds[nfds].revents = 0; - nfds++; - if (wakeEvents & WL_POSTMASTER_DEATH) { /* postmaster fd, if used, is always in pfds[nfds - 1] */ @@ -364,19 +370,27 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock, else { /* at least one event occurred, so check revents values */ + + if (pfds[0].revents & POLLIN) + { + /* There's data in the self-pipe, clear it. */ + drainSelfPipe(); + } + if ((wakeEvents & WL_SOCKET_READABLE) && - (pfds[0].revents & POLLIN)) + (pfds[1].revents & POLLIN)) { /* data available in socket, or EOF/error condition */ result |= WL_SOCKET_READABLE; } if ((wakeEvents & WL_SOCKET_WRITEABLE) && - (pfds[0].revents & POLLOUT)) + (pfds[1].revents & POLLOUT)) { /* socket is writable */ result |= WL_SOCKET_WRITEABLE; } - if (pfds[0].revents & (POLLHUP | POLLERR | POLLNVAL)) + if ((wakeEvents & WL_SOCKET_WRITEABLE) && + (pfds[1].revents & (POLLHUP | POLLERR | POLLNVAL))) { /* EOF/error condition */ if (wakeEvents & WL_SOCKET_READABLE) @@ -468,6 +482,11 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock, else { /* at least one event occurred, so check masks */ + if (FD_ISSET(selfpipe_readfd, &input_mask)) + { + /* There's data in the self-pipe, clear it. */ + drainSelfPipe(); + } if ((wakeEvents & WL_SOCKET_READABLE) && FD_ISSET(sock, &input_mask)) { /* data available in socket, or EOF */ @@ -498,6 +517,16 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock, } #endif /* LATCH_USE_SELECT */ + /* + * Check again whether latch is set, the arrival of a signal/self-byte + * might be what stopped our sleep. It's not required for correctness + * to signal the latch as being set (we'd just loop if there's no + * other event), but it seems good to report an arrived latch asap. + * This way we also don't have to compute the current timestamp again. + */ + if ((wakeEvents & WL_LATCH_SET) && latch->is_set) + result |= WL_LATCH_SET; + /* If we're not done, update cur_timeout for next iteration */ if (result == 0 && (wakeEvents & WL_TIMEOUT)) { diff --git a/src/backend/port/win32_latch.c b/src/backend/port/win32_latch.c index b1b071339e..bbf1b24bdf 100644 --- a/src/backend/port/win32_latch.c +++ b/src/backend/port/win32_latch.c @@ -181,14 +181,11 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock, do { /* - * Reset the event, and check if the latch is set already. If someone - * sets the latch between this and the WaitForMultipleObjects() call - * below, the setter will set the event and WaitForMultipleObjects() - * will return immediately. + * The comment in unix_latch.c's equivalent to this applies here as + * well. At least after mentally replacing self-pipe with windows + * event. There's no danger of overflowing, as "Setting an event that + * is already set has no effect.". */ - if (!ResetEvent(latchevent)) - elog(ERROR, "ResetEvent failed: error code %lu", GetLastError()); - if ((wakeEvents & WL_LATCH_SET) && latch->is_set) { result |= WL_LATCH_SET; @@ -217,9 +214,13 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock, else if (rc == WAIT_OBJECT_0 + 1) { /* - * Latch is set. We'll handle that on next iteration of loop, but - * let's not waste the cycles to update cur_timeout below. + * Reset the event. We'll re-check the, potentially, set latch on + * next iteration of loop, but let's not waste the cycles to + * update cur_timeout below. */ + if (!ResetEvent(latchevent)) + elog(ERROR, "ResetEvent failed: error code %lu", GetLastError()); + continue; } else if ((wakeEvents & (WL_SOCKET_READABLE | WL_SOCKET_WRITEABLE)) && -- 2.40.0