common/iobuf: optimize iobuf_read_line
authorJussi Kivilinna <jussi.kivilinna@iki.fi>
Thu, 8 Nov 2018 19:31:12 +0000 (21:31 +0200)
committerJussi Kivilinna <jussi.kivilinna@iki.fi>
Thu, 8 Nov 2018 19:31:12 +0000 (21:31 +0200)
* common/iobuf.c (iobuf_read_line): Add fast path for finding '\n'
character in buffer.
--

This patch reduce per byte overhead in iobuf_read_line by avoiding
using iobuf_get when possible and use memchr to find '\n'. This
speeds armored decryption.

Benchmark results below, tested on Intel Core i7-4790K (turbo off).
Encrypted 2 GiB through pipe to ramfs file using AES128. Decrypt
ramfs file out through pipe to /dev/null.

before patch-set
----------------
               gpg process
armor:         user time    pipe transfer rate
 encrypt-aead:  13.8         140 MB/s
 decrypt-aead:  30.6         68 MB/s
 encrypt-cfb:   17.4         114 MB/s
 decrypt-cfb:   32.6         64 MB/s

after (decrypt+iobuf opt)
-------------------------
               gpg process
armor:         user time    pipe transfer rate
 decrypt-aead:  22.5         92 MB/s
 decrypt-cfb:   24.4         85 MB/s

Signed-off-by: Jussi Kivilinna <jussi.kivilinna@iki.fi>
common/iobuf.c

index 18a458e..5eeba8f 100644 (file)
@@ -2610,12 +2610,50 @@ iobuf_read_line (iobuf_t a, byte ** addr_of_buffer,
     }
 
   p = buffer;
-  while ((c = iobuf_get (a)) != -1)
+  while (1)
     {
-      *p++ = c;
-      nbytes++;
-      if (c == '\n')
-       break;
+      if (!a->nofast && a->d.start < a->d.len && nbytes < length - 1)
+       /* Fast path for finding '\n' by using standard C library's optimized
+          memchr.  */
+       {
+         unsigned size = a->d.len - a->d.start;
+         byte *newline_pos;
+
+         if (size > length - 1 - nbytes)
+           size = length - 1 - nbytes;
+
+         newline_pos = memchr (a->d.buf + a->d.start, '\n', size);
+         if (newline_pos)
+           {
+             /* Found newline, copy buffer and return. */
+             size = (newline_pos - (a->d.buf + a->d.start)) + 1;
+             memcpy (p, a->d.buf + a->d.start, size);
+             p += size;
+             nbytes += size;
+             a->d.start += size;
+             a->nbytes += size;
+             break;
+           }
+         else
+           {
+             /* No newline, copy buffer and continue. */
+             memcpy (p, a->d.buf + a->d.start, size);
+             p += size;
+             nbytes += size;
+             a->d.start += size;
+             a->nbytes += size;
+           }
+       }
+      else
+       {
+         c = iobuf_readbyte (a);
+         if (c == -1)
+           break;
+         *p++ = c;
+         nbytes++;
+         if (c == '\n')
+           break;
+       }
 
       if (nbytes == length - 1)
        /* We don't have enough space to add a \n and a \0.  Increase