Why are the standard library functions so fast?

120 Views Asked by At

I wrote a simple code to test mystrlen by comparing it to the standard library strlen on a 2089360 byte string. I used two versions of mystrlen and both are very far from the standard strlen runtime, about 93x slower on single execution. what makes the standard strlen so fast?

I compiled all test with

gcc file.c -Ofast

here is my main:

#include <sys/time.h>
#include <string.h>
#include <stdio.h>

int main ()
{
    double time = 0;
    struct timeval start, end;
    const char *s = myBigString;

    for (int j = 0; j < 50; ++j)
    {
        gettimeofday(&start, NULL);

        // int k = mystrlen2(s1);
        int k = strlen(s1);

        gettimeofday(&end, NULL);
        time += end.tv_sec + end.tv_usec / 1e6 - start.tv_sec - start.tv_usec / 1e6; // in seconds
        printf("%d\n", k);
    }

    printf("average time program took %f seconds to execute 50 times\n", time / 50);
    return 0;

}

mystrlen1 code:

size_t mystrlen1(const char *s)
{
    size_t i = 0;
    while (s[i])
        i++;
    return  i;
}

mystrlen2 code:

size_t mystrlen2(const char *s)
{
    size_t i = 0;
    while (s[i])
        i += 2;
    if (!s[i - 1])
        i--;
    return  i;
}

as expected mystrlen2 is better than mystrlen1:

$ ./mystrlen1
average time program took 0.001149 seconds to execute 50 times

$ ./mystrlen2
average time program took 0.000446 seconds to execute 50 times

but now see the output of library strlen:

$ ./libstrlen
average time program took 0.000000 seconds to execute 50 times

what happened here? how it's possible it run in 0 sec?

EDIT:

after couple of tests I found out the problem is the printf instruction that take "lot of" time. If I remove that the runtime will be the same for each function.

time += ((end.tv_sec - start.tv_sec) * 1000000) + (end.tv_usec - start.tv_usec); // in micro seconds

the output is:

$ ./mystrlen1
average time program took 0.020000 microseconds to execute 50 times
$ ./mystrlen2
average time program took 0.020000 microseconds to execute 50 times
$ ./libstrlen
average time program took 0.020000 microseconds to execute 50 times
1

There are 1 best solutions below

0
ikegami On

Check the assembly generated by your program. I think you'll find that your compiler replaced

const char *s = "...";
int k = strlen(s1);

with

int k = 4;  // Or whatever the length is.

You can see this in effect here, where

size_t l0 = strlen( s );

results in

        mov     esi, 4

In fact, we see that the compiler in question realizes that mystring1 is equivalent to strlen, so

size_t l1 = mystrlen1( s );

results in

        mov     edx, 4

It's not worth discussing mystrlen2 since it's incorrect and results in undefined behaviour when provided an odd-length string.


Use a value generated at run time to see how your functions actually perform.

Still, it's entirely possible that you still won't actually be benchmarking your own code if you do this. Again, this other demo using a variable string still shows the compiler in question realizes that mystring1 is equivalent to strlen, and just replaces calls to mystrlen1 with a call to strlen.