Как работает 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,

Другие вопросы по тегам