summaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
authorJussi Kivilinna <jussi.kivilinna@iki.fi>2022-02-12 20:31:47 +0100
committerJussi Kivilinna <jussi.kivilinna@iki.fi>2022-03-08 19:03:03 +0100
commitb96eb6f08d1df86fc8578133e0a16566c988e37d (patch)
tree5dc62fd8811c3029acc6f2422eb220b55e658c09 /common
parentiobuf: add zerocopy optimization for iobuf_read (diff)
downloadgnupg2-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.c81
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;