> New Revision: 997203
> 
> URL: http://svn.apache.org/viewvc?rev=997203&view=rev
> Log:
> Merge r985037, r985046, r995507 and r995603 from the performance branch.
> 
> These changes introduce the svn_stringbuf_appendbyte() function, which has
> significantly less overhead than svn_stringbuf_appendbytes(), and can be
> used in a number of places within our codebase.

Hi Stefan2.

I have been wondering about the actual benefit of such tightly
hand-optimized code.  Today I got around to giving it a quick spin.

In my first test, it made no difference whatsoever, because the
optimized code is never executed.  The recent merge r1002898 of r1001413
from the performance branch introduced a bug:

-  if (str->blocksize > old_len + 1)
+  if (str->blocksize < old_len + 1)

which totally disables the optimized code path.

Fixed in r1005437.

That solved, a loop doing a million simple appendbyte calls runs more
than twice as fast as calling appendbytes(..., 1).  That's fantastic.

But what about that hand-optimization?  I wrote a simple version of the
function, and called it 'appendbyte0':

svn_stringbuf_appendbyte0(svn_stringbuf_t *str, char byte)
{
  if (str->blocksize > str->len + 1)
    {
      str->data[str->len++] = byte;
      str->data[str->len] = '\0';
    }
  else
    svn_stringbuf_appendbytes(str, &byte, 1);
}

Here are the results (see full patch attached):

Times:  appendbytes appendbyte0 appendbyte  (in ms)
run:          89          31          34
run:          88          30          35
run:          88          31          34
run:          88          30          34
run:          88          31          34
min:          88          30          34

This tells me that the hand-optimization is actually harmful and the
compiler does a 10% better job by itself.

Have I made a mistake?

What are the results for your system?

(I'm using GCC 4.4.1 on an Intel Centrino laptop CPU.)

- Julian

A comparative performance test of svn_stringbuf_appendbyte().

* subversion/libsvn_subr/svn_string.c
  (svn_stringbuf_appendbyte0): New function, for comparative testing.

* subversion/tests/libsvn_subr/string-test.c
  (test24): New test, measuring and printing a speed comparison.
  (test_funcs): Add it.

--This line, and those below, will be ignored--

Index: subversion/libsvn_subr/svn_string.c
===================================================================
--- subversion/libsvn_subr/svn_string.c	(revision 1005437)
+++ subversion/libsvn_subr/svn_string.c	(working copy)
@@ -392,6 +392,29 @@ svn_stringbuf_ensure(svn_stringbuf_t *st
 }
 
 
+/* Unoptimized version of svn_stringbuf_appendbyte(), for comparison. */
+void
+svn_stringbuf_appendbyte0(svn_stringbuf_t *str, char byte)
+{
+  /* In most cases, there will be pre-allocated memory left
+   * to just write the new byte at the end of the used section
+   * and terminate the string properly.
+   */
+  if (str->blocksize > str->len + 1)
+    {
+      str->data[str->len++] = byte;
+      str->data[str->len] = '\0';
+    }
+  else
+    {
+      /* we need to re-allocate the string buffer
+       * -> let the more generic implementation take care of that part
+       */
+      svn_stringbuf_appendbytes(str, &byte, 1);
+    }
+}
+
+
 /* WARNING - Optimized code ahead! 
  * This function has been hand-tuned for performance. Please read 
  * the comments below before modifying the code.
Index: subversion/tests/libsvn_subr/string-test.c
===================================================================
--- subversion/tests/libsvn_subr/string-test.c	(revision 1003481)
+++ subversion/tests/libsvn_subr/string-test.c	(working copy)
@@ -511,6 +511,50 @@ test23(apr_pool_t *pool)
   return test_stringbuf_unequal("abc", "abb", pool);
 }
 
+void
+svn_stringbuf_appendbyte0(svn_stringbuf_t *str, char byte);
+
+static svn_error_t *
+test24(apr_pool_t *pool)
+{
+  apr_time_t start;
+  int t1, t2, t3;
+  int min_t1 = 999999, min_t2 = 999999, min_t3 = 999999;
+  char byte = 'X';
+  int N = 2000000, TESTS = 5;
+  int i, j;
+
+  printf("Times:  appendbytes appendbyte0 appendbyte  (in ms)\n");
+  for (j = 0; j < TESTS; j++)
+    {
+      a = svn_stringbuf_create("", pool);
+      start = apr_time_now();
+      for (i = 0; i < N; i++)
+        svn_stringbuf_appendbytes(a, &byte, 1);
+      t1 = (int)(apr_time_now() - start) / 1000;
+      if (t1 < min_t1) min_t1 = t1;
+
+      a = svn_stringbuf_create("", pool);
+      start = apr_time_now();
+      for (i = 0; i < N; i++)
+        svn_stringbuf_appendbyte0(a, byte);
+      t2 = (int)(apr_time_now() - start) / 1000;
+      if (t2 < min_t2) min_t2 = t2;
+
+      a = svn_stringbuf_create("", pool);
+      start = apr_time_now();
+      for (i = 0; i < N; i++)
+        svn_stringbuf_appendbyte(a, byte);
+      t3 = (int)(apr_time_now() - start) / 1000;
+      if (t3 < min_t3) min_t3 = t3;
+
+      printf("run:  %10d  %10d  %10d\n", t1, t2, t3);
+    }
+  printf("min:  %10d  %10d  %10d\n", min_t1, min_t2, min_t3);
+
+  return SVN_NO_ERROR;
+}
+
 /*
    ====================================================================
    If you add a new test to this file, update this array.
@@ -568,5 +612,7 @@ struct svn_test_descriptor_t test_funcs[
                    "compare stringbufs; different lengths"),
     SVN_TEST_PASS2(test23,
                    "compare stringbufs; same length, different content"),
+    SVN_TEST_PASS2(test24,
+                   "benchmark svn_stringbuf_appendbyte"),
     SVN_TEST_NULL
   };

Reply via email to