1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-05-28 21:50:02 +02:00

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>
This commit is contained in:
Jussi Kivilinna 2022-02-12 21:31:47 +02:00
parent 15df88d135
commit b96eb6f08d

View File

@ -2052,9 +2052,14 @@ underflow_target (iobuf_t a, int clear_pending_eof, size_t target)
static int static int
filter_flush (iobuf_t a) filter_flush (iobuf_t a)
{ {
int external_used = 0;
byte *src_buf;
size_t src_len;
size_t len; size_t len;
int rc; int rc;
a->e_d.used = 0;
if (a->use == IOBUF_OUTPUT_TEMP) if (a->use == IOBUF_OUTPUT_TEMP)
{ /* increase the temp buffer */ { /* increase the temp buffer */
size_t newsize = a->d.size + iobuf_buffer_size; 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"); log_bug ("flush on non-output iobuf\n");
else if (!a->filter) else if (!a->filter)
log_bug ("filter_flush: no filter\n"); 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 (a->d.len == 0 && a->e_d.buf && a->e_d.len > 0)
if (!rc && len != a->d.len) {
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"); log_info ("filter_flush did not write all!\n");
rc = GPG_ERR_INTERNAL; rc = GPG_ERR_INTERNAL;
@ -2081,6 +2108,8 @@ filter_flush (iobuf_t a)
else if (rc) else if (rc)
a->error = rc; a->error = rc;
a->d.len = 0; a->d.len = 0;
if (external_used)
a->e_d.used = len;
return rc; return rc;
} }
@ -2303,11 +2332,37 @@ iobuf_write (iobuf_t a, const void *buffer, unsigned int buflen)
return -1; 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 do
{ {
if (buflen && a->d.len < a->d.size) if (a->d.len == 0 && buflen >= IOBUF_ZEROCOPY_THRESHOLD_SIZE)
{ {
unsigned size = a->d.size - a->d.len; /* 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;
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) if (size > buflen)
size = buflen; size = buflen;
memcpy (a->d.buf + a->d.len, buf, size); 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; buf += size;
a->d.len += size; a->d.len += size;
} }
if (buflen) if (buflen)
{ {
rc = filter_flush (a); rc = filter_flush (a);
if (rc) 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); while (buflen);
return 0; return 0;