web-dev-qa-db-fra.com

Comment fonctionne le gcc `__thread`?

Comment est __thread dans gcc implémenté? Est-ce simplement un wrapper sur pthread_getspecific et pthread_setspecific?

Avec mon programme qui utilise l'API posix pour TLS, je suis un peu déçu de voir maintenant que 30% du temps d'exécution de mon programme est consacré à pthread_getspecific. Je l'ai appelé à l'entrée de chaque appel de fonction qui a besoin de la ressource. Le compilateur ne semble pas optimiser pthread_getspecific après optimisation en ligne. Ainsi, une fois les fonctions intégrées, le code recherche le pointeur TLS correct encore et encore pour obtenir le même pointeur.

Volonté __thread m'aider dans cette situation? Je sais qu'il y a thread_local en C11, mais le gcc que j'ai ne le supporte pas encore. (Mais maintenant, je vois que mon gcc prend en charge _Thread_local mais pas la macro.)

Je sais que je peux simplement le tester et voir. Mais je dois aller ailleurs maintenant et j'aimerais en savoir plus sur une fonctionnalité avant de tenter une réécriture assez importante.

21
xiver77

Récent GCC , par exemple GCC 5 supporte C11 et son thread_local (Si vous compilez avec par exemple gcc -std=c11). Comme FUZxxl commenté, vous pouvez utiliser (au lieu de C11 thread_local) Le qualificatif __thread Pris en charge par les anciennes versions de GCC. Lisez à propos de Thread Local Storage .

pthread_getspecific Est en effet assez lent (il est dans la bibliothèque POSIX, donc n'est pas fourni par GCC mais par exemple par GNU glibc ou musl-libc ) car il implique un appel de fonction. L'utilisation des variables thread_local Sera très probablement plus rapide.

Regardez dans le code source de fichier thread/pthread_getspecific.c De MUSL pour un exemple d'implémentation. Lisez cette réponse à une question connexe.

Et _thread & thread_local Ne sont (souvent) pas traduits comme par magie en appels à pthread_getspecific. Ils impliquent généralement un mode d'adresse et/ou un registre spécifiques (les détails sont spécifiques à l'implémentation, liés au ABI ; sous Linux, je suppose que depuis que x86-64 a plus de registres et de modes d'adresse, son implémentation de TLS est plus rapide que sur i386), avec l'aide de compilateur , linker et système d'exécution . Il peut arriver au contraire que certaines implémentations de pthread_getspecific Utilisent des variables internes thread_local (Dans votre implémentation de threads POSIX).

Par exemple, la compilation du code suivant

#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));
}

utiliser GCC 5.2 (sur Debian/Sid) avec gcc -m32 -S -O2 -fverbose-asm donne le code suivant pour get_data en utilisant TLS:

  .type get_data, @function
get_data:
.LFB3:
  .cfi_startproc
  movl  %gs:data@ntpoff, %eax   # data,
  ret
.cfi_endproc

et le code suivant de get_by_key avec un appel explicite à 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

Par conséquent, l'utilisation de TLS avec __thread (Ou thread_local En C11) devrait probablement être plus rapide que l'utilisation de pthread_getspecific (En évitant la surcharge d'un appel).

Notez que thread_local Est un macro de commodité définie dans <threads.h> (un en-tête standard C11).

13

gcc's __thread a exactement la même sémantique que C11 _Thread_local. Vous ne nous dites pas pour quelle plate-forme vous programmez car les détails d'implémentation varient selon les plates-formes. Par exemple, sous x86 Linux, gcc doit compiler l'accès aux variables locales de thread en tant qu'instructions de mémoire avec un %fs préfixe de segment au lieu d'appeler pthread_getspecific.

4
fuz