2015-08-27 23 views
15

Come è implementato __thread in gcc? È semplicemente un wrapper su pthread_getspecific e pthread_setspecific?Come funziona gcc `__thread`?

Con il mio programma che utilizza l'API posix per TLS, sono un po 'deluso ora vedendo che il 30% del mio runtime del programma viene speso su pthread_getspecific. L'ho chiamato alla voce di ogni chiamata di funzione che ha bisogno della risorsa. Il compiler non sembra ottimizzare pthread_getspecific dopo aver ottimizzato l'ottimizzazione. Quindi, dopo che le funzioni sono state allineate, il codice cerca fondamentalmente il puntatore TLS corretto ancora e ancora per ottenere lo stesso puntatore restituito.

__thread mi aiuti in questa situazione? So che c'è il thread_local in C11, ma il gcc che ho non lo supporta ancora. (Ma ora vedo che il mio gcc supporta _Thread_local ma non la macro.)

So che posso semplicemente testarlo e vedere. Ma adesso devo andare da qualche altra parte, e mi piacerebbe conoscere meglio una funzione prima di tentare una riscrittura piuttosto grande.

+1

'__thread' è implementato in modo diverso su piattaforme diverse, su alcuni (non ci hai detto per quale programma stai programmando), potrebbe essere implementato con' pthread_getspecific'. – fuz

+0

Per favore ci dia più informazioni! Mi piacerebbe davvero risolvere il tuo problema ma al momento non ne so abbastanza su quale piattaforma usi/come compili il tuo codice per essere in grado di darti una risposta su come rendere il thread storage locale più performante. – fuz

risposta

8

Recenti GCC, ad es. GCC 5 supportano C11 e il suo thread_local (se si compila con ad esempio gcc -std=c11). Come commentato FUZxxl, è possibile utilizzare (anziché C11 thread_local) il qualificatore __thread supportato dalle versioni GCC precedenti. Maggiori informazioni su Thread Local Storage.

pthread_getspecific è infatti abbastanza lento (che si trova nella libreria POSIX, quindi non viene fornito da GCC ma per esempio mediante GNU glibc o musl-libc) poiché comporta una chiamata di funzione. L'utilizzo delle variabili thread_local sarà probabilmente più veloce.

Cercare il codice sorgente di MUSL's thread/pthread_getspecific.c file per un esempio di implementazione. Leggi this answer per una domanda correlata.

E _thread & thread_local sono (spesso) non magicamente tradotto per le chiamate verso pthread_getspecific. Di solito coinvolgono alcune modalità di indirizzo e/o registro specifici (i dettagli sono specifici dell'implementazione, relativi allo ABI; su Linux, suppongo che dal momento che x86-64 abbia più registri delle modalità di indirizzo &, la sua implementazione di TLS sia più veloce che su i386), con l'aiuto di compiler, linker e runtime system. Potrebbe accadere, al contrario, che alcune implementazioni di pthread_getspecific utilizzino alcune variabili interne thread_local (nell'implementazione dei thread POSIX).

Come esempio, compilando il seguente codice

#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 (su Debian/Sid) con gcc -m32 -S -O2 -fverbose-asm pronunciato la seguente codice per get_data utilizzando TLS:

.type get_data, @function 
get_data: 
.LFB3: 
    .cfi_startproc 
    movl %gs:[email protected], %eax # data, 
    ret 
.cfi_endproc 

e il seguente codice di get_by_key con una chiamata esplicita 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 

Quindi utilizzare TLS con __thread (o thread_local in C11) dovrebbe probabilmente essere più veloce rispetto all'utilizzo di pthread_getspecific (evitando il sovraccarico di una chiamata).

Si noti che thread_local è un convenience macro defined in <threads.h> (un'intestazione standard C11).

+0

Pthread_setspecific fa più lavoro extra rispetto al TLS integrato? – xiver77

+0

'__thread' è un'estensione pre-C11 di gcc che ha la stessa semantica di' _Thread_local' di C11, in effetti garantisce un po 'più di '_Thread_local'. 'pthread_getspecific' non implica necessariamente una chiamata di funzione, può essere implementata come una macro. – fuz

+0

@FUZxxl: potrebbe essere implementato da una macro (ma suppongo che lo standard richieda di poterlo usare attraverso un puntatore a funzione), ma di solito non è implementato come macro –

3

gcc __thread ha esattamente la stessa semantica di C11 _Thread_local. Non ci dite quale piattaforma state programmando poiché i dettagli di implementazione variano tra piattaforme. Ad esempio, su x86 Linux, gcc dovrebbe compilare l'accesso al thread delle variabili locali come istruzioni di memoria con un prefisso di segmento %fs invece di richiamare pthread_getspecific.

+0

Sto usando una CPU Intel. Quindi vuoi dire gcc usa un registro speciale come il registro puntatore dello stack ma dedicato al TLS? Pthread_getspecific fa la stessa cosa? – xiver77

+0

@ xiver77 "Sto usando una CPU Intel" non è abbastanza informazioni. Per quale sistema operativo e architettura stai programmando? Intel rende le CPU con molte architetture diverse. Sulle piattaforme i386 l'ABI lo supporta, il registro del segmento '% fp' è impostato su un indirizzo di base diverso da zero che punta ai dati locali del thread. Non posso dirti se gcc può farlo sulla tua piattaforma dato che non mi dai abbastanza informazioni. Potresti anche darmi la versione di gcc, l'invocazione di gcc e l'output assembly (usa l'opzione '-S')? – fuz

+0

Ci scusiamo per una risposta tardiva. La mia piattaforma è Ubuntu 15.10 i386 gcc 4.9.2. Controllerò e vedrò anche l'output dell'assembly per '__thread' in questo momento. – xiver77