Why compiler is not able to optimize read from TLS?

Viewed 167

Consider follwing header file, "tls.h":

#include <stdint.h>

// calling this function is expensive
uint64_t foo(uint64_t x);

extern __thread uint64_t cache;

static inline uint64_t
get(uint64_t x)
{
    // if cache is not valid
    if (cache == UINT64_MAX)
        cache = foo(x);

    return cache + x;
}

and source file "tls.c":

#include "tls.h"

__thread uint64_t cache = {0};

uint64_t foo(uint64_t x)
{
    // imagine some calculations are performed here
    return 0;
}

Below is example usage of get() function in "main.c":

#include "tls.h"

uint64_t t = 0;

int main()
{
    uint64_t x = 0;

    for(uint64_t i = 0; i < 1024UL * 1024 * 1024; i++){
        t += get(i);
        x++;
    }
}

Presented files are compiled as following:

gcc -c -O3 tls.c
gcc -c -O3 main.c
gcc -O3 main.o tls.o

Examining the performance of loop in "main.c" revealed that compiler optimization is very poor. After disassembling the binary, it is clear that tls is being accessed in every iteration. Execution time on my machine is 1.7s.

However, if I remove check for cache validity in get() method so that it looks like this:

static inline uint64_t
get(uint64_t x)
{
    return cache + x;
}

the compiler is now able to create much faster code - it completely removes the loop and generates only only one "add" instruction. Execution time is ~0.02s.

Why the compiler is not able to optimize the first case? TLS variable cannot be changed by other threads so compiler should be able to optimize this, right?

Is there any other way I can optimize the get() function?

0 Answers
Related