From 94ba0d806bc2d879bb01c5a2c8ed3c0a01677847 Mon Sep 17 00:00:00 2001 From: yeoncheol-kim Date: Thu, 22 Feb 2024 12:05:26 +0900 Subject: [PATCH] INTERNAL: Reduce add_iov() work for TCP connections Back in 2006 memcached's frontend was optimized for primarily UDP traffic. The extra fiddling for every subsection of a response has persisted since then, where most users are TCP. Shortening this function has a large effect on performance (6%+) --- memcached.c | 69 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 26 deletions(-) diff --git a/memcached.c b/memcached.c index 300cc2e71..3304e2cc8 100644 --- a/memcached.c +++ b/memcached.c @@ -1206,6 +1206,9 @@ static int ensure_iov_space(conn *c) * connection. * * Returns 0 on success, -1 on out-of-memory. + * Note: This is a hot path for at least ASCII protocol. While there is + * redundant code in splitting TCP/UDP handling, any reduction in steps has a + * large impact for TCP connections. */ static int add_iov(conn *c, const void *buf, int len) @@ -1213,20 +1216,48 @@ static int add_iov(conn *c, const void *buf, int len) assert(c != NULL); struct msghdr *m; int leftover; - bool limit_to_mtu; - do { - m = &c->msglist[c->msgused - 1]; + if (IS_UDP(c->transport)) { + do { + m = &c->msglist[c->msgused - 1]; - /* - * Limit UDP packets, and the first payloads of TCP replies, to - * UDP_MAX_PAYLOAD_SIZE bytes. - */ - limit_to_mtu = IS_UDP(c->transport) || (1 == c->msgused); + /* + * Limit UDP packets to UDP_MAX_PAYLOAD_SIZE bytes. + */ + + /* We may need to start a new msghdr if this one is full. */ + if (m->msg_iovlen == IOV_MAX || + (c->msgbytes >= UDP_MAX_PAYLOAD_SIZE)) { + add_msghdr(c); + m = &c->msglist[c->msgused - 1]; + } - /* We may need to start a new msghdr if this one is full. */ - if (m->msg_iovlen == IOV_MAX || - (limit_to_mtu && c->msgbytes >= UDP_MAX_PAYLOAD_SIZE)) { + if (ensure_iov_space(c) != 0) + return -1; + + /* If the fragment is too big to fit in the datagram, split it up */ + if (len + c->msgbytes > UDP_MAX_PAYLOAD_SIZE) { + leftover = len + c->msgbytes - UDP_MAX_PAYLOAD_SIZE; + len -= leftover; + } else { + leftover = 0; + } + + m = &c->msglist[c->msgused - 1]; + m->msg_iov[m->msg_iovlen].iov_base = (void *)buf; + m->msg_iov[m->msg_iovlen].iov_len = len; + + c->msgbytes += len; + c->iovused++; + m->msg_iovlen++; + + buf = ((char *)buf) + len; + len = leftover; + } while (leftover > 0); + } else { + /* Optimized path for TCP connections */ + m = &c->msglist[c->msgused - 1]; + if (m->msg_iovlen == IOV_MAX) { add_msghdr(c); m = &c->msglist[c->msgused - 1]; } @@ -1234,26 +1265,12 @@ static int add_iov(conn *c, const void *buf, int len) if (ensure_iov_space(c) != 0) return -1; - /* If the fragment is too big to fit in the datagram, split it up */ - if (limit_to_mtu && len + c->msgbytes > UDP_MAX_PAYLOAD_SIZE) { - leftover = len + c->msgbytes - UDP_MAX_PAYLOAD_SIZE; - len -= leftover; - } else { - leftover = 0; - } - - m = &c->msglist[c->msgused - 1]; m->msg_iov[m->msg_iovlen].iov_base = (void *)buf; m->msg_iov[m->msg_iovlen].iov_len = len; - c->msgbytes += len; c->iovused++; m->msg_iovlen++; - - buf = ((char *)buf) + len; - len = leftover; - } while (leftover > 0); - + } return 0; }