¿Cómo funciona gcc `__thread`?
multithreading thread-local-storage (2)
¿Cómo se implementa __thread
en gcc? ¿Es simplemente un envoltorio sobre pthread_getspecific
y pthread_setspecific
?
Con mi programa que utiliza la API de posix para TLS, estoy un poco decepcionado al ver que el 30% de mi tiempo de ejecución del programa se gasta en pthread_getspecific
. Lo llamé en la entrada de cada llamada de función que necesita el recurso. El compilador no parece optimizar pthread_getspecific
después de integrar la optimización. Por lo tanto, una vez que las funciones están en línea, el código busca el puntero TLS correcto una y otra vez para obtener el mismo puntero.
¿ __thread
ayudará __thread
en esta situación? Sé que hay thread_local
en C11, pero el gcc que tengo todavía no lo admite. (Pero ahora veo que mi gcc admite _Thread_local
no la macro).
Sé que puedo simplemente probarlo y ver. Pero ahora tengo que ir a otro lugar, y me gustaría saber más sobre una característica antes de intentar una reescritura bastante grande.
El __thread
de gcc tiene exactamente la misma semántica que _Thread_local
de C11. No nos dice para qué plataforma está programando, ya que los detalles de la implementación varían según las plataformas. Por ejemplo, en x86 Linux, gcc debería compilar el acceso a las variables locales de subprocesos como instrucciones de memoria con un prefijo de segmento %fs
lugar de invocar pthread_getspecific
.
GCC reciente, por ejemplo, GCC 5 es compatible con C11 y su thread_local
(si se compila con, por ejemplo, gcc -std=c11
). Como FUZxxl comentó, usted podría usar (en lugar de C11 thread_local
) el calificador __thread
compatible con versiones anteriores de GCC. Lea acerca de Thread Local Storage .
pthread_getspecific
es bastante lento (está en la biblioteca POSIX, por lo que no es provisto por GCC sino por ejemplo por GNU glibc o musl-libc ) ya que involucra una llamada a la función. El uso de variables thread_local
probablemente será más rápido.
Busque en el código fuente del archivo thread/pthread_getspecific.c
de MUSL para ver un ejemplo de implementación. Lea esta respuesta a una pregunta relacionada.
Y _thread
& thread_local
(a menudo) no se traducen mágicamente a llamadas a pthread_getspecific
. Por lo general, involucran algún modo de dirección y / o registro específico (los detalles son específicos de la implementación, relacionados con ABI ; en Linux, supongo que como x86-64 tiene más registros y modos de dirección, su implementación de TLS es más rápida que en i386), con la ayuda del compiler , el linker y el sistema de ejecución . Podría suceder, por el contrario, que algunas implementaciones de pthread_getspecific
están usando algunas variables internas thread_local
(en su implementación de subprocesos POSIX).
Como ejemplo, compilando el siguiente código
#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));
}
usando GCC 5.2 (en Debian / Sid) con gcc -m32 -S -O2 -fverbose-asm
da el siguiente código para get_data
usando TLS:
.type get_data, @function
get_data:
.LFB3:
.cfi_startproc
movl %gs:data@ntpoff, %eax # data,
ret
.cfi_endproc
y el siguiente código de get_by_key
con una llamada explícita a 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
Por lo tanto, el uso de TLS con __thread
(o thread_local
en C11) probablemente debería ser más rápido que usar pthread_getspecific
(evitando la sobrecarga de una llamada).
Observe que thread_local
es una macro de conveniencia definida en <threads.h>
(un encabezado estándar de C11).