diff options
author | Jussi Kivilinna <jussi.kivilinna@iki.fi> | 2022-02-12 20:31:47 +0100 |
---|---|---|
committer | Jussi Kivilinna <jussi.kivilinna@iki.fi> | 2022-03-08 19:03:03 +0100 |
commit | b96eb6f08d1df86fc8578133e0a16566c988e37d (patch) | |
tree | 5dc62fd8811c3029acc6f2422eb220b55e658c09 /common | |
parent | iobuf: add zerocopy optimization for iobuf_read (diff) | |
download | gnupg2-b96eb6f08d1df86fc8578133e0a16566c988e37d.tar.xz gnupg2-b96eb6f08d1df86fc8578133e0a16566c988e37d.zip |
iobuf: add zerocopy optimization for iobuf_write
* common/iobuf.c (filter_flush): Use 'iobuf->e_d' if configured.
(iobuf_write): Configure 'iobuf->e_d' for 'filter_flush' if
'iobuf->d.buf' is empty and external buffer is larger than threshold.
--
Zero-copy operation in iobuf_write() and filter_flush() allow bypassing
'iobuf->d.buf' for greater performance. This mainly helps OCB
performance where additional memory copies through iobuf stack
can take significant portion of program time.
GnuPG-bug-id: T5828
Signed-off-by: Jussi Kivilinna <jussi.kivilinna@iki.fi>
Diffstat (limited to 'common')
-rw-r--r-- | common/iobuf.c | 81 |
1 files changed, 75 insertions, 6 deletions
diff --git a/common/iobuf.c b/common/iobuf.c index 49e03da22..bde6a5ccc 100644 --- a/common/iobuf.c +++ b/common/iobuf.c @@ -2052,9 +2052,14 @@ underflow_target (iobuf_t a, int clear_pending_eof, size_t target) static int filter_flush (iobuf_t a) { + int external_used = 0; + byte *src_buf; + size_t src_len; size_t len; int rc; + a->e_d.used = 0; + if (a->use == IOBUF_OUTPUT_TEMP) { /* increase the temp buffer */ size_t newsize = a->d.size + iobuf_buffer_size; @@ -2071,9 +2076,31 @@ filter_flush (iobuf_t a) log_bug ("flush on non-output iobuf\n"); else if (!a->filter) log_bug ("filter_flush: no filter\n"); - len = a->d.len; - rc = a->filter (a->filter_ov, IOBUFCTRL_FLUSH, a->chain, a->d.buf, &len); - if (!rc && len != a->d.len) + + if (a->d.len == 0 && a->e_d.buf && a->e_d.len > 0) + { + src_buf = a->e_d.buf; + src_len = a->e_d.len; + external_used = 1; + } + else + { + src_buf = a->d.buf; + src_len = a->d.len; + external_used = 0; + } + + if (src_len == 0) + { + if (DBG_IOBUF) + log_debug ("filter_flush, nothing to flush%s\n", + external_used ? " (external buffer)" : ""); + return 0; + } + + len = src_len; + rc = a->filter (a->filter_ov, IOBUFCTRL_FLUSH, a->chain, src_buf, &len); + if (!rc && len != src_len) { log_info ("filter_flush did not write all!\n"); rc = GPG_ERR_INTERNAL; @@ -2081,6 +2108,8 @@ filter_flush (iobuf_t a) else if (rc) a->error = rc; a->d.len = 0; + if (external_used) + a->e_d.used = len; return rc; } @@ -2303,11 +2332,37 @@ iobuf_write (iobuf_t a, const void *buffer, unsigned int buflen) return -1; } + a->e_d.buf = NULL; + a->e_d.len = 0; + + /* Hint for how full to fill iobuf internal drain buffer. */ + a->e_d.preferred = (buflen >= IOBUF_ZEROCOPY_THRESHOLD_SIZE); + do { - if (buflen && a->d.len < a->d.size) + if (a->d.len == 0 && buflen >= IOBUF_ZEROCOPY_THRESHOLD_SIZE) + { + /* Setup external drain buffer for faster moving of data + * (avoid memcpy). */ + a->e_d.buf = (byte *)buf; + a->e_d.len = buflen / IOBUF_ZEROCOPY_THRESHOLD_SIZE + * IOBUF_ZEROCOPY_THRESHOLD_SIZE; + if (a->e_d.len == 0) + a->e_d.buf = NULL; + if (a->e_d.buf && DBG_IOBUF) + log_debug ("iobuf-%d.%d: writing from external buffer, %lu bytes\n", + a->no, a->subno, (ulong)a->e_d.len); + } + + if (a->e_d.buf == NULL && buflen && a->d.len < a->d.size) { - unsigned size = a->d.size - a->d.len; + unsigned size; + + if (a->e_d.preferred && a->d.len < IOBUF_ZEROCOPY_THRESHOLD_SIZE) + size = IOBUF_ZEROCOPY_THRESHOLD_SIZE - a->d.len; + else + size = a->d.size - a->d.len; + if (size > buflen) size = buflen; memcpy (a->d.buf + a->d.len, buf, size); @@ -2315,12 +2370,26 @@ iobuf_write (iobuf_t a, const void *buffer, unsigned int buflen) buf += size; a->d.len += size; } + if (buflen) { rc = filter_flush (a); if (rc) - return rc; + { + a->e_d.buf = NULL; + a->e_d.len = 0; + return rc; + } } + + if (a->e_d.buf && a->e_d.used > 0) + { + buf += a->e_d.used; + buflen -= a->e_d.used; + } + + a->e_d.buf = NULL; + a->e_d.len = 0; } while (buflen); return 0; |