S.J. S.J. - 3 months ago 11
C Question

thread-local storage overhead

Assume there is some not-reentrant function that uses global variables:


int i;
void foo(void){
/* modify i */
}


And then, I want to use this function in multithreaded code, so I can change code this way:


void foo(int i){
/* modify i */
}


or, by using gcc __thread specifier, more simplier:


__thread int i;
void foo(void){
/* modify i */
}


Advantages of the last is that I don't need to change another code which call foo().

My questions is, how much overhead of thread-local storage is? Is there some not obvious issues with TLS?

Is there some overhead if I will modify TLS`ed variable via separate pointer, like this:


__thread int i;
void foo(void){
int *p = &i;
/* modify i using p pointer */
}


Thanks.

Jon Jon
Answer

And then, I want to use this function in multithreaded code, so I can change code this way:

void foo(int i){
    /* modify i */
}

This will certainly not work, as you will only be modifying a copy of i. You 'd need to pass an int* or int& instead if you want the changes to stick.

Using TLS will certainly not cause any significant overhead (either in space or time) over any custom approach you might follow to implement the same functionality. Compilers in practice implement TLS by dynamically allocating a storage "slot" in a global data structure that holds your thread-local variable.

When you access a thread-local variable at runtime, there is an extra level of indirection: first the runtime has to access the appropriate thread-local variable table for the current thread, and then to fetch the value out of the table. This fetching is done using an index into the array (which is an O(1) operation).

If you intend to do this:

__thread int i;
void foo(void){
    int *p = &i;
    /* modify i using p pointer */
}

then there is no need to access i using a pointer. Think of i as a global variable that has a different value for each running thread. You wouldn't need to access a normal global through a pointer to make changes stick, so there's also no need to use a pointer with a thread-local variable.

Finally, thread-local storage is not really meant to store large numbers of variables per thread (there are compiler-dependent limits on the size of the TLS table) but this is something you can easily work around: put many variables into a struct and make a pointer to the struct thread-local.