[interfacekit] Re: [Fwd: Message submitted to 'interfacekit']

> Ingo Weinhold wrote:
> >>If we are -really- concerned about performance, we can inline the
> >>'stubby' functions in the header.
> > 
> > We should be very careful with inlining functions. Unless the 
> > functions's tasks are trival (as BString::Length()/String() are for 
> > example), the function call overhead to function execution ratio 
> > should 
> > be insignificantly small. And inlining may introduce serious binary 
> > compatibility traps.
> 
> Excellent point.  I think we should *not* inline anything that isn't 
> already inlined.  Again, if later testing shows a performance issue, 
> we'll revisit the issue.  In the meantime, the conservative approach 
> is 
> best.

I don't know what you consider a performance issue (and actually not 
even what the conservative approach -- literally it would be to leave 
the implementation as it is). In the case of basic and often used 
classes like BString or BList I would label any implementation that is 
measurably slower unacceptable. Especially in this very case of 
BString, where an improvement is known, obvious and implemented.

That an additional strlen() will make BString::Prepend(const BString &) 
slower becomes clear when having a look at the involved operations. 
strlen() is a slow O(n) operation (n being the length of the prepended 
string), whereas memcpy() is a fast one. With increasing n the impact 
of strlen() grows relatively to the more or less O(1) realloc() and 
O(m) memmove() (m being the length of `this' string). I couldn't resist 
writing a little test simulating the situation. Although the test 
string is malloc()ed and free()d in the test loop, the result is 
impressively unambigious. Even with a prepended string as short as 10 
characters the `optimized' functions is more than 15% faster. For a 
length of 1000 it is more than twice as fast (with optimization < -O2 
even more). The impact of realloc() is of course significantly greater, 
when it is unable to grow the memory region -- sometimes a test run 
suffers from that. However, for very long strings (100000 chars and 
more) I get +50% again.

To conclude: I think, it is a total waste of time to reimplement the 
concerned methods to make them slower. But that's just my humble 
opinion.

CU, Ingo


// string-test.cpp

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <OS.h>

// prepend with strlen()
static
void
prepend1(char *str, char *prependString, int size1, int /*size2*/)
{
        int size2 = strlen(prependString) + 1;
        str = (char*)realloc(str, size1 + size2);
        memmove(str + size2, str, size1);
        memcpy(str, prependString, size2);
}

// prepend without strlen()
static
void
prepend2(char *str, char *prependString, int size1, int size2)
{
        str = (char*)realloc(str, size1 + size2);
        memmove(str + size2, str, size1);
        memcpy(str, prependString, size2);
}

typedef void prependFunction(char *, char*, int, int);

static
bigtime_t
run_test(prependFunction *prepend, int iterations, int size1, int 
size2)
{
        char *prependString = (char*)malloc(size2);
        memset(prependString, 'a', size2 - 1);
        prependString[size2 - 1] = '\0';
        bigtime_t startTime = system_time();
        for (int i = 0; i < iterations; i++) {
                char *str = (char*)malloc(size1);
                (*prepend)(str, prependString, size1, size2);
                free(str);
        }
        bigtime_t resultTime = system_time() - startTime;
        free(prependString);
        return resultTime;
}

int
main()
{
        enum { ITERATIONS = 100000 };
        int tests[] = { 1, 10, 100, 1000 };
        int testCount = sizeof(tests) / sizeof(int);
        for (int i = 0; i < testCount; i++) {
                int prependSize = tests[i];
                printf("\nTEST: size: %d\n", prependSize);
                bigtime_t testTime1 = run_test(prepend1, ITERATIONS, 10, 
prependSize);
                bigtime_t testTime2 = run_test(prepend2, ITERATIONS, 10, 
prependSize);
                printf("  prepend with strlen():    %lld us\n", testTime1);
                printf("  prepend without strlen(): %lld us\n", testTime2);
                double speedup = double(testTime1 - testTime2) / testTime2;
                double savedTime = double(testTime1 - testTime2) / testTime1;
                printf("  speedup:                  %.2f %%\n", speedup * 100);
                printf("  saved time:               %.2f %%\n", savedTime * 
100);
        }
        return 0;
}




Other related posts: