> 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 };