Gccの__thread
はどのように実装されていますか? pthread_getspecific
とpthread_setspecific
の単なるラッパーですか?
TLSにposix APIを使用する私のプログラムでは、プログラムランタイムの30%がpthread_getspecific
に費やされていることに少しがっかりしています。リソースを必要とする各関数呼び出しのエントリで呼び出しました。コンパイラーは、最適化をインライン化した後、pthread_getspecific
を最適化しないようです。したがって、関数がインライン化された後、コードは基本的に正しいTLSポインターを繰り返し検索して、同じポインターを返します。
__thread
はこの状況で助けてくれますか? C11にはthread_local
があることは知っていますが、私が持っているgccはまだサポートしていません。 (しかし、私のgccはマクロではなく_Thread_local
をサポートしていることがわかりました。)
簡単にテストして確認できることを知っています。しかし、私は今どこかに行かなければなりません、そして、私はかなり大きな書き直しを試みる前に、機能についてよりよく知りたいです。
最近の [〜#〜] gcc [〜#〜] 、例: GCC 5 C11とそのthread_local
をサポートします(たとえば、gcc -std=c11
でコンパイルする場合)。 FUZxxl がコメントされているため、(C11 thread_local
の代わりに)古いGCCバージョンでサポートされている__thread
修飾子を使用できます。 スレッドローカルストレージ についてお読みください。
pthread_getspecific
は非常に遅い(POSIXライブラリにあるため、GCCでは提供されないが、たとえば GNU glibc または musl-libc )関数呼び出し。 thread_local
変数を使用すると、おそらくより高速になります。
実装例については、 MUSLのthread/pthread_getspecific.c
ファイル のソースコードをご覧ください。関連する質問の この回答 を読んでください。
_thread
とthread_local
は、(多くの場合)pthread_getspecific
の呼び出しに魔法のように変換されません。通常、これらには特定のアドレスモードやレジスタが含まれます(詳細は [〜#〜] abi [〜#〜] ;に関連する実装固有です。Linuxでは、x86-64にはより多くのレジスタとアドレスモード、TLSの実装はi386よりも高速です) コンパイラ 、 リンカー および ランタイムシステム 。反対に、pthread_getspecific
の一部の実装が(POSIXスレッドの実装で)内部thread_local
変数を使用していることがあります。
例として、次のコードをコンパイルします
#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
を使用すると、TLSを使用してget_data
に次のコードが提供されます。
.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
したがって、__thread
(またはC11のthread_local
)でTLSを使用する方が、pthread_getspecific
を使用する(呼び出しのオーバーヘッドを回避する)よりもおそらく高速です。
thread_local
は <threads.h>
で定義された便利なマクロ (C11標準ヘッダー)であることに注意してください。
gccの__thread
は、C11の_Thread_local
とまったく同じ意味を持ちます。実装の詳細はプラットフォーム間で異なるため、プログラミング対象のプラットフォームを教えてはいけません。たとえば、x86 Linuxでは、gccは%fs
を呼び出す代わりに、pthread_getspecific
セグメントプレフィックスを持つメモリ命令としてスレッドローカル変数へのアクセスをコンパイルする必要があります。