2

I ran a following test.

charspeed.c

#include <stdio.h>
#include <time.h>

#define CHAR_COUNT 26
#define CHAR_LIST "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
static const char *CHAR_ARRAY = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

#define RUN_COUNT 1000000000

#define GET_CLOCK (float)clock() / CLOCKS_PER_SEC

int main()
{
    long long int sum = 0;
    float start, end;

    start = GET_CLOCK;
    for (size_t i = 0; i < RUN_COUNT; i++)
    {
        char test = CHAR_LIST[i % CHAR_COUNT];
        sum += test; // Force the loop to run!
    }
    end = GET_CLOCK;
    printf("#define Time: %f\n", end - start);

    start = GET_CLOCK;
    for (size_t i = 0; i < RUN_COUNT; i++)
    {
        char test = CHAR_ARRAY[i % CHAR_COUNT];
        sum += test; // Must be the same as fist loop!
    }
    end = GET_CLOCK;
    printf("static const *CHAR_ARRAY Time: %f\n", end - start);
    printf("sum = %lld\n", sum); // Must access "sum" after loops!
    return 0;
}

Its outputs

#define Time: 1.741000
static const *CHAR_ARRAY Time: 1.868000

Why the string literal using #define directive faster than a pre-initialised static char array? Where exactly the string literal is stored and why it is faster to access them within a block scop?

The compiler option used is gcc -o charspeed charspeed.c

Cerlancism
  • 2,803
  • 5
  • 14
  • 20

3 Answers3

3

Just an additional answer to complete the others.

A string literal is not a const char *. The reason is that a const char * can be reassigned. In your case that means that you could do CHAR_ARRAY = "foo";.

Practically it means that the compiler cannot optimize as much the code dealing with a const char * than the code dealing with a string literal.

To solve this you could use one of the following:

const char *const CHAR_ARRAY = "...";
const char CHAR_ARRAY[] = "...";

This should guarantee the same performance as a string literal.

However, in you case, since you declared CHAR_ARRAY as static, that means that the compiler can see, only looking at the current source file, if CHAR_ARRAY can be reassigned to.

In practice that means that, assuming optimization are enabled:

  • for a string literal, a const char *const or a const char[], the same assembly code (A) will be generated.
  • for a non static const char * a different assembly code (B) will be generated, possibly slower since their is an added indirection and the result cannot be known at compile time.
  • for a static const char *, it will use either the assembly code (A) or (B) depending if the compiler can prove or not that the pointer might be reassigned. For instance if you add a function void f() { CHAR_ARRAY = "foo"; } at any place in your code you prohibit the compiler to use the assembly (A) and (B) will be used.
Benjamin T
  • 8,120
  • 20
  • 37
2

Note: Edited to 'synchronize' with OP's changes:

Maybe the problem is that you aren't giving a good enough test. A decent compiler will run both loops in zero time, because nothing of consequence happens inside them. I tried on MSVC, and your code gave 0 for both times.

However, increasing the loop counts ten-fold and putting in something that can't be optimized away, I get pretty much equal times for both:

#include <stdio.h>
#include <time.h>

#define CHAR_COUNT 26
#define CHAR_LIST "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
static const char* CHAR_ARRAY = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

#define RUN_COUNT 1000000000 // Increased by factor of 10!
#define GET_CLOCK (double)clock() / CLOCKS_PER_SEC

int main()
{
    long long int sum = 0;
    double start, end;

    start = GET_CLOCK;
    for (size_t i = 0; i < RUN_COUNT; i++) {
        char test = CHAR_LIST[i % CHAR_COUNT];
        sum += test; // Force the loop to run!
    }
    end = GET_CLOCK;
    printf("#define Time: %lf\n", end - start);

    start = GET_CLOCK;
    for (size_t i = 0; i < RUN_COUNT; i++) {
        char test = CHAR_ARRAY[i % CHAR_COUNT];
        sum += test; // Must be the same as fist loop!
    }
    end = GET_CLOCK;
    printf("static const *CHAR_ARRAY Time: %lf\n", end - start);
    printf("sum = %lld\n", sum); // Must access "sum" after loops!
    return 0;
}

Try doing something like this on your compiler/machine, to see if it makes a difference.

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
  • I tried your modification and edited my post using this, the string literal is still always faster. – Cerlancism Sep 22 '19 at 17:27
  • ok now it makes the speed even, so the conclusion is that both approach for handling strings should not affect performance? – Cerlancism Sep 22 '19 at 17:47
  • 1
    They ***shouldn't*** if you have compiler optimization turned on! Without it, that's a separate question. – Adrian Mole Sep 22 '19 at 17:49
0

First, I am almost certain you did not compile your code with full optimisation turned on.
If you did so, both loops would have totally disappeared (no side effect).

Considering that optimisation is turned off, and that the generated code matches exactly every step of your source code, in your first loop it's like if you did

int test=5;

while in the second it's more like

int something=5;
...
int *addr=&something;
int test=*addr;

In the first case you directly access the interesting value but in the second you reach the interesting value through an indirection.

But I insist, it should not be relevant in an optimised code.

prog-fh
  • 13,492
  • 1
  • 15
  • 30