I did some more optimisation.

Most performance hit in Dovecot do "for loop" in string operations.

In one case (in message-parser.c) "for loop" has another "for" inside with the same variable used as iterator. This case is very hard to optimise by compiler.

I do changes only in top functions listed in oprofile.
Maybe I do more in future.


Code was analysed and tested but it's hard to generate all cases.
It's working well in production server (I'm monitoring).


In istream-crlf.c I've changed one thing.
When destination buffer is full after '\r' addition (to dest), It didn't skip '\n' in source buffer. I think this was buggy in earlier code, and '\n' was skipped (this piece of code is used very rare).


Please check this out. This can help in huge e-mail systems :P


And another problem. Why You use safe_memset instead of memset?
Now this function have the largest impact in Dovecot performance.
Another on list is t_push.


Regards,
Len7hir
--- dovecot-2.0-pure/src/lib-mail/message-parser.c      2010-08-08 
10:05:30.000000000 +0200
+++ dovecot-2.0/src/lib-mail/message-parser.c   2010-08-26 10:01:57.000000000 
+0200
@@ -81,26 +81,40 @@
 {
        unsigned int missing_cr_count = 0;
        const unsigned char *data = block->data;
-       size_t i;
+       unsigned char *last, *curr;
 
        block->hdr = NULL;
 
-       for (i = 0; i < block->size; i++) {
-               if (data[i] <= '\n') {
-                       if (data[i] == '\n') {
-                               ctx->part->body_size.lines++;
-                               if ((i > 0 && data[i-1] != '\r') ||
-                                   (i == 0 && ctx->last_chr != '\r'))
-                                       missing_cr_count++;
-                       } else if (data[i] == '\0')
-                               ctx->part->flags |= MESSAGE_PART_FLAG_HAS_NULS;
-               }
+       /* check nuls */
+       if (memchr(data, 0, block->size) != NULL) {
+               ctx->part->flags |= MESSAGE_PART_FLAG_HAS_NULS;
+       }
+
+       /* count lines and missing cr (from begining + 1) */
+       last = (unsigned char *)data + 1;
+       while (TRUE) {
+               curr = memchr(last, '\n', block->size - (last - data));
+               if (curr == NULL)
+                       break;
+
+               ctx->part->body_size.lines++;
+
+               if (*(curr - 1) != '\r')
+                       missing_cr_count++;
+
+               last = curr + 1;
+       }
+
+       /* exceptional condition (test begining) */
+       if (*data == '\n' && ctx->last_chr != '\r') {
+               ctx->part->body_size.lines++;
+               missing_cr_count++;
        }
 
        ctx->part->body_size.physical_size += block->size;
        ctx->part->body_size.virtual_size += block->size + missing_cr_count;
 
-       ctx->last_chr = data[i-1];
+       ctx->last_chr = *(data + block->size - 1);
        ctx->skip += block->size;
 }
 
@@ -196,6 +210,7 @@
                   const unsigned char *data, size_t size, bool full,
                   struct message_boundary **boundary_r)
 {
+       unsigned char *ptr;
        size_t i;
 
        *boundary_r = NULL;
@@ -215,10 +230,9 @@
        }
 
        /* need to find the end of line */
-       for (i = 2; i < size; i++) {
-               if (data[i] == '\n')
-                       break;
-       }
+       ptr = (unsigned char *)memchr(data + 2, '\n', size - 2);
+       i = (ptr != NULL) ? ptr - data : size;
+
        if (i == size && i < BOUNDARY_END_MAX_LEN &&
            !ctx->input->eof && !full) {
                /* no LF found */
@@ -251,6 +265,7 @@
 static int parse_next_body_skip_boundary_line(struct message_parser_ctx *ctx,
                                              struct message_block *block_r)
 {
+       unsigned char *ptr;
        size_t i;
        int ret;
        bool full;
@@ -258,10 +273,8 @@
        if ((ret = message_parser_read_more(ctx, block_r, &full)) <= 0)
                return ret;
 
-       for (i = 0; i < block_r->size; i++) {
-               if (block_r->data[i] == '\n')
-                       break;
-       }
+       ptr = (unsigned char *)memchr(block_r->data, '\n', block_r->size);
+       i = (ptr != NULL) ? ptr - block_r->data : block_r->size;
 
        if (i == block_r->size) {
                parse_body_add_block(ctx, block_r);
@@ -323,6 +336,7 @@
 {
        struct message_boundary *boundary = NULL;
        const unsigned char *data;
+       unsigned char *last, *curr;
        size_t i, boundary_start;
        int ret;
        bool full;
@@ -343,20 +357,26 @@
        }
 
        i_assert(block_r->size > 0);
-       for (i = boundary_start = 0; i < block_r->size; i++) {
+       boundary_start = 0;
+       last = data;
+       while (TRUE) {
                /* skip to beginning of the next line. the first line was
                   handled already. */
                size_t next_line_idx = block_r->size;
 
-               for (; i < block_r->size; i++) {
-                       if (data[i] == '\n') {
-                               boundary_start = i;
-                               if (i > 0 && data[i-1] == '\r')
-                                       boundary_start--;
-                               next_line_idx = i + 1;
-                               break;
-                       }
-               }
+               curr = (unsigned char *)memchr(last, '\n',
+                               block_r->size - (last - data));
+               if (curr == NULL)
+                       break;
+
+               last = curr + 1;
+               i = (curr - data);
+
+               boundary_start = i;
+               if (curr > data && *(curr - 1) == '\r')
+                       boundary_start--;
+               next_line_idx = i + 1;
+
                if (boundary_start != 0) {
                        /* we can skip the first lines. input buffer can't be
                           full anymore. */
@@ -378,7 +398,7 @@
                }
        }
 
-       if (i >= block_r->size) {
+       if (curr == NULL) {
                /* the boundary wasn't found from this data block,
                   we'll need more data. */
                ret = 0;
--- dovecot-2.0-pure/src/lib/istream.c  2010-08-16 09:24:27.000000000 +0200
+++ dovecot-2.0/src/lib/istream.c       2010-09-01 12:31:35.000000000 +0200
@@ -337,7 +337,7 @@
 {
        struct istream_private *_stream = stream->real_stream;
        char *ret_buf;
-        size_t i;
+       unsigned char *data, *found;
 
        if (_stream->skip >= _stream->pos) {
                stream->stream_errno = 0;
@@ -352,13 +352,15 @@
 
        /* @UNSAFE */
        ret_buf = NULL;
-       for (i = _stream->skip; i < _stream->pos; i++) {
-               if (_stream->buffer[i] == 10) {
-                       /* got it */
-                       ret_buf = i_stream_next_line_finish(_stream, i);
-                        break;
-               }
+
+       data = _stream->buffer + _stream->skip;
+       found = memchr(data, '\n', _stream->pos - _stream->skip);
+
+       if (found != NULL) {
+               /* got it */
+               ret_buf = i_stream_next_line_finish(_stream, found - 
_stream->buffer);
        }
+
        if (ret_buf == NULL)
                return i_stream_last_line(_stream);
         return ret_buf;
--- dovecot-2.0-pure/src/lib/istream-crlf.c     2010-08-08 10:05:33.000000000 
+0200
+++ dovecot-2.0/src/lib/istream-crlf.c  2010-08-30 15:22:20.000000000 +0200
@@ -39,7 +39,8 @@
 {
        struct crlf_istream *cstream = (struct crlf_istream *)stream;
        const unsigned char *data;
-       size_t i, dest, size;
+       unsigned char *s_ptr, *s_end, *d_ptr, *d_end, *ptr;
+       size_t size, cpy_len;
        ssize_t ret;

        ret = i_stream_crlf_read_common(cstream);
@@ -49,27 +50,61 @@
        data = i_stream_get_data(stream->parent, &size);

        /* @UNSAFE: add missing CRs */
-       dest = stream->pos;
-       for (i = 0; i < size && dest < stream->buffer_size; i++) {
-               if (data[i] == '\n') {
-                       if (i == 0) {
-                               if (!cstream->last_cr)
-                                       stream->w_buffer[dest++] = '\r';
-                       } else {
-                               if (data[i-1] != '\r')
-                                       stream->w_buffer[dest++] = '\r';
-                       }
-                       if (dest == stream->buffer_size)
-                               break;
+       d_ptr = stream->w_buffer + stream->pos;
+       d_end = stream->w_buffer + stream->buffer_size;
+       s_ptr = (unsigned char *)data;
+       s_end = (unsigned char *)data + size;
+
+       if (s_ptr < s_end && *s_ptr == '\n' && !cstream->last_cr && d_ptr < 
d_end) {
+               *(d_ptr++) = '\r';
+
+               if (d_ptr < d_end) {
+                       *(d_ptr++) = '\n';
+                       s_ptr++;
                }
-               stream->w_buffer[dest++] = data[i];
        }
-       cstream->last_cr = stream->w_buffer[dest-1] == '\r';
-       i_stream_skip(stream->parent, i);

-       ret = dest - stream->pos;
+       while (d_ptr < d_end) {
+               ptr = memchr(s_ptr, '\n', s_end - s_ptr);
+               if (ptr == NULL)
+                       ptr = s_end;
+
+               cpy_len = ptr - s_ptr;
+               if (d_ptr + cpy_len > d_end)
+                       cpy_len = d_end - d_ptr;
+
+               if (cpy_len > 0) {
+                       memcpy(d_ptr, s_ptr, cpy_len);
+
+                       d_ptr += cpy_len;
+                       s_ptr += cpy_len;
+               }
+
+               i_assert(d_ptr <= d_end && s_ptr <= s_end);
+               if (d_ptr == d_end || s_ptr == s_end)
+                       break;
+
+               if (*(s_ptr - 1) != '\r')
+                       *(d_ptr++) = '\r';
+
+               if (d_ptr < d_end) {
+                       *(d_ptr++) = '\n';
+                       s_ptr++;
+               }
+
+               i_assert(s_ptr == ptr + 1);
+       }
+
+       cstream->last_cr = *(d_ptr - 1) == '\r';
+       i_stream_skip(stream->parent, s_ptr - data);
+
+       ret = (d_ptr - stream->w_buffer) - stream->pos;
        i_assert(ret > 0);
-       stream->pos = dest;
+       stream->pos = d_ptr - stream->w_buffer;
        return ret;
 }

Reply via email to