Как работает gcc `__thread`?
Как __thread
в gcc реализовано? Это просто обертка pthread_getspecific
а также pthread_setspecific
?
С моей программой, использующей API posix для TLS, я немного разочарован тем, что 30% времени выполнения моей программы тратится на pthread_getspecific
, Я вызывал его на входе каждого вызова функции, которому нужен ресурс. Компилятор не оптимизируется pthread_getspecific
после встраивания оптимизации. Таким образом, после того, как функции встроены, код снова и снова ищет правильный указатель TLS, чтобы вернуть тот же самый указатель.
Будет __thread
помочь мне в этой ситуации? Я знаю что есть thread_local
в C11, но gcc, который я имею, еще не поддерживает это. (Но теперь я вижу, что мой GCC поддерживает _Thread_local
просто не макрос.)
Я знаю, что могу просто проверить это и посмотреть. Но я должен сейчас пойти куда-нибудь еще, и я хотел бы узнать больше о функции, прежде чем пытаться переписать довольно много.
2 ответа
Последние GCC, например, GCC 5 действительно поддерживают C11 и его thread_local
(если компилируется, например, с gcc -std=c11
). Как прокомментировал fuz, вы можете использовать (вместо C11 thread_local
) __thread
Спецификатор поддерживается старыми версиями GCC. Читайте о теме локального хранилища.
pthread_getspecific
действительно довольно медленный (он есть в библиотеке POSIX, поэтому не предоставляется GCC, а, например, GNU glibc или http://musl-libc.org/), поскольку он включает вызов функции. С помощью thread_local
переменные очень вероятно будут быстрее.
Посмотрите на исходный код MUSL thread/pthread_getspecific.c
файл для примера реализации. Прочитайте этот ответ на связанный вопрос.
А также _thread
& thread_local
(часто) магически не переводятся на призывы к pthread_getspecific
, Обычно они включают в себя определенный режим адреса и / или регистр (подробности зависят от реализации, связанной с ABI; в Linux я предполагаю, что, поскольку в x86-64 больше регистров и режимов адреса, его реализация TLS быстрее, чем в i386), с помощью компилятора, компоновщика и системы времени выполнения. Может случиться, что некоторые реализации pthread_getspecific
используют некоторые внутренние thread_local
переменные (в вашей реализации потоков POSIX).
Как пример, компиляция следующего кода
#include <pthread.h>
const extern pthread_key_t key;
__thread int data;
int
get_data (void) {
return data;
}
int
get_by_key (void) {
return *(int*) (pthread_getspecific (key));
}
используя GCC 5.2 (в Debian/Sid) с gcc -m32 -S -O2 -fverbose-asm
дает следующий код для get_data
используя TLS:
.type get_data, @function
get_data:
.LFB3:
.cfi_startproc
movl %gs:data@ntpoff, %eax # data,
ret
.cfi_endproc
и следующий код get_by_key
с явным призывом к pthread_getspecific
:
get_by_key:
.LFB4:
.cfi_startproc
subl $24, %esp #,
.cfi_def_cfa_offset 28
pushl key # key
.cfi_def_cfa_offset 32
call pthread_getspecific #
movl (%eax), %eax # MEM[(int *)_4], MEM[(int *)_4]
addl $28, %esp #,
.cfi_def_cfa_offset 4
ret
.cfi_endproc
Следовательно, используя TLS с __thread
(или же thread_local
в C11) должно быть быстрее, чем при использовании pthread_getspecific
(избегая накладных расходов на вызов).
Заметить, что thread_local
это удобный макрос, определенный в <threads.h>
(стандартный заголовок C11).
ССЗ __thread
имеет точно такую же семантику, как C11 _Thread_local
, Вы не говорите нам, для какой платформы вы программируете, так как детали реализации зависят от платформы. Например, в Linux x86 gcc должен скомпилировать доступ к локальным переменным потока в виде инструкций памяти с %fs
префикс сегмента вместо вызова pthread_getspecific
,