GCC 4.8ドラフトの変更ログ から:
G ++は C++ 11
thread_local
キーワードを実装しました。これは、GNU__thread
キーワードとは異なり、主に動的な初期化と破棄のセマンティクスを許可します。残念ながら、このサポートには、関数ローカルでないthread_local
変数は動的な初期化を必要としない場合でも、静的な初期化セマンティクスでTLS変数に__thread
を使用し続けることができます。
この実行時ペナルティの本質と起源は何ですか?
明らかに、非関数ローカルthread_local
変数をサポートするには、すべてのスレッドメインへのエントリの前にスレッド初期化フェーズが必要です(グローバル変数の静的初期化フェーズがあるように)が、それらは何らかの実行を参照しています時間を超えたペナルティ?
大まかに言えば、gccのthread_localの新しい実装のアーキテクチャは何ですか?
(免責事項:GCCの内部についてはあまり知らないので、これは経験に基づいた推測でもあります。)
動的な_thread_local
_初期化がcommit 462819c に追加されました。変更の1つは次のとおりです。
* semantics.c (finish_id_expression): Replace use of thread_local
_variable with a call to its wrapper.
_
したがって、実行時のペナルティは、_thread_local
_変数のすべての参照が関数呼び出しになることです。簡単なテストケースで確認しましょう。
_// 3.cpp
extern thread_local int tls;
int main() {
tls += 37; // line 6
tls &= 11; // line 7
tls ^= 3; // line 8
return 0;
}
// 4.cpp
thread_local int tls = 42;
_
コンパイルされたとき*everytls
参照の使用は遅延初期化する__ZTW3tls
_への関数呼び出しになる変数を一度:
_00000000004005b0 <main>:
main():
4005b0: 55 Push rbp
4005b1: 48 89 e5 mov rbp,rsp
4005b4: e8 26 00 00 00 call 4005df <_ZTW3tls> // line 6
4005b9: 8b 10 mov edx,DWORD PTR [rax]
4005bb: 83 c2 25 add edx,0x25
4005be: 89 10 mov DWORD PTR [rax],edx
4005c0: e8 1a 00 00 00 call 4005df <_ZTW3tls> // line 7
4005c5: 8b 10 mov edx,DWORD PTR [rax]
4005c7: 83 e2 0b and edx,0xb
4005ca: 89 10 mov DWORD PTR [rax],edx
4005cc: e8 0e 00 00 00 call 4005df <_ZTW3tls> // line 8
4005d1: 8b 10 mov edx,DWORD PTR [rax]
4005d3: 83 f2 03 xor edx,0x3
4005d6: 89 10 mov DWORD PTR [rax],edx
4005d8: b8 00 00 00 00 mov eax,0x0 // line 9
4005dd: 5d pop rbp
4005de: c3 ret
00000000004005df <_ZTW3tls>:
_ZTW3tls():
4005df: 55 Push rbp
4005e0: 48 89 e5 mov rbp,rsp
4005e3: b8 00 00 00 00 mov eax,0x0
4005e8: 48 85 c0 test rax,rax
4005eb: 74 05 je 4005f2 <_ZTW3tls+0x13>
4005ed: e8 0e fa bf ff call 0 <tls> // initialize the TLS
4005f2: 64 48 8b 14 25 00 00 00 00 mov rdx,QWORD PTR fs:0x0
4005fb: 48 c7 c0 fc ff ff ff mov rax,0xfffffffffffffffc
400602: 48 01 d0 add rax,rdx
400605: 5d pop rbp
400606: c3 ret
_
これを___thread
_バージョンと比較してください。これには、この余分なラッパーはありません。
_00000000004005b0 <main>:
main():
4005b0: 55 Push rbp
4005b1: 48 89 e5 mov rbp,rsp
4005b4: 48 c7 c0 fc ff ff ff mov rax,0xfffffffffffffffc // line 6
4005bb: 64 8b 00 mov eax,DWORD PTR fs:[rax]
4005be: 8d 50 25 lea edx,[rax+0x25]
4005c1: 48 c7 c0 fc ff ff ff mov rax,0xfffffffffffffffc
4005c8: 64 89 10 mov DWORD PTR fs:[rax],edx
4005cb: 48 c7 c0 fc ff ff ff mov rax,0xfffffffffffffffc // line 7
4005d2: 64 8b 00 mov eax,DWORD PTR fs:[rax]
4005d5: 89 c2 mov edx,eax
4005d7: 83 e2 0b and edx,0xb
4005da: 48 c7 c0 fc ff ff ff mov rax,0xfffffffffffffffc
4005e1: 64 89 10 mov DWORD PTR fs:[rax],edx
4005e4: 48 c7 c0 fc ff ff ff mov rax,0xfffffffffffffffc // line 8
4005eb: 64 8b 00 mov eax,DWORD PTR fs:[rax]
4005ee: 89 c2 mov edx,eax
4005f0: 83 f2 03 xor edx,0x3
4005f3: 48 c7 c0 fc ff ff ff mov rax,0xfffffffffffffffc
4005fa: 64 89 10 mov DWORD PTR fs:[rax],edx
4005fd: b8 00 00 00 00 mov eax,0x0 // line 9
400602: 5d pop rbp
400603: c3 ret
_
ただし、_thread_local
_のすべてのユースケースでこのラッパーは必要ありません。これは _decl2.c
_ から明らかにできます。ラッパーは次の場合にのみ生成されます。
それは、notfunction-localであり、
extern
(上記の例)、または__thread
_変数には使用できません)、または__thread
_変数でも許可されません)。他のすべてのユースケースでは、___thread
_と同じように動作します。つまり、いくつかの_extern __thread
_変数がない限り、パフォーマンスを損なうことなく、すべての___thread
_を_thread_local
_に置き換えることができます。
*:インライン化により関数の境界が見えにくくなるため、-O0でコンパイルしました。 -O3に設定しても、これらの初期化チェックは残ります。
変数が現在のTUで定義されている場合、インライナーがオーバーヘッドを処理します。これは、thread_localのほとんどの用途に当てはまると思われます。
外部変数の場合、プログラマーが非定義のTUで変数を使用しないことで、変数が静的に初期化されるか、または定義中の変数TUは別のTUで使用する前に実行されます)、-fno-extern-tls-initオプションでこのオーバーヘッドを回避できます。
C++ 11 thread_localには、__ thread指定子と同じランタイム効果があります(___thread
_はC標準の一部ではありません; _thread_local
_はC++標準の一部です)
tLS変数(___thread
_指定子で宣言された)が宣言されている場所に依存します。
-fPIC
_コンパイラオプションでコンパイルされた)共有ライブラリコード内で宣言され、_-ftls-model=initial-exec
_コンパイラオプションが指定されている場合、アクセスは高速です。ただし、次の制限が適用されます。共有ライブラリはdlopen/dlsymを介してロードできません(動的ロード)。ライブラリを使用する唯一の方法は、コンパイル中にリンクすることです(リンカーオプション_-l<libraryname>
_)-fPIC
_コンパイラオプションセット)内で宣言されている場合、一般的な動的TLSモデルが想定されるため、アクセスが非常に遅くなります。ここで、TLS変数への各アクセスは_tls_get_addr()
;これは、共有ライブラリの使用方法に制限がないため、デフォルトのケースです。ソース:Ulrich DrepperによるスレッドローカルストレージのELF処理 https://www.akkadia.org/drepper/tls.pdf このテキストには、サポートされているターゲットプラットフォーム用に生成されたコードもリストされています。