web-dev-qa-db-ja.com

GCC 4.8のC ++ 11 thread_local変数のパフォーマンスペナルティは何ですか?

GCC 4.8ドラフトの変更ログ から:

G ++は C++ 11thread_localキーワードを実装しました。これは、GNU __threadキーワードとは異なり、主に動的な初期化と破棄のセマンティクスを許可します。残念ながら、このサポートには、関数ローカルでないthread_local変数は動的な初期化を必要としない場合でも、静的な初期化セマンティクスでTLS変数に__threadを使用し続けることができます。

この実行時ペナルティの本質と起源は何ですか?

明らかに、非関数ローカルthread_local変数をサポートするには、すべてのスレッドメインへのエントリの前にスレッド初期化フェーズが必要です(グローバル変数の静的初期化フェーズがあるように)が、それらは何らかの実行を参照しています時間を超えたペナルティ?

大まかに言えば、gccのthread_localの新しい実装のアーキテクチャは何ですか?

63
Andrew Tomazos

(免責事項: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であり、

    1. extern(上記の例)、または
    2. 型に非自明なデストラクタがあります(___thread_変数には使用できません)、または
    3. 型変数は非定数式によって初期化されます(これは___thread_変数でも許可されません)。

他のすべてのユースケースでは、___thread_と同じように動作します。つまり、いくつかの_extern __thread_変数がない限り、パフォーマンスを損なうことなく、すべての___thread_を_thread_local_に置き換えることができます。


*:インライン化により関数の境界が見えにくくなるため、-O0でコンパイルしました。 -O3に設定しても、これらの初期化チェックは残ります。

44
kennytm

変数が現在のTUで定義されている場合、インライナーがオーバーヘッドを処理します。これは、thread_localのほとんどの用途に当てはまると思われます。

外部変数の場合、プログラマーが非定義のTUで変数を使用しないことで、変数が静的に初期化されるか、または定義中の変数TUは別のTUで使用する前に実行されます)、-fno-extern-tls-initオプションでこのオーバーヘッドを回避できます。

8
Jason Merrill

C++ 11 thread_localには、__ thread指定子と同じランタイム効果があります(___thread_はC標準の一部ではありません; _thread_local_はC++標準の一部です)

tLS変数(___thread_指定子で宣言された)が宣言されている場所に依存します。

  • tLS変数が実行可能ファイルで宣言されている場合、アクセスは高速です
  • tLS変数が(_-fPIC_コンパイラオプションでコンパイルされた)共有ライブラリコード内で宣言され、_-ftls-model=initial-exec_コンパイラオプションが指定されている場合、アクセスは高速です。ただし、次の制限が適用されます。共有ライブラリはdlopen/dlsymを介してロードできません(動的ロード)。ライブラリを使用する唯一の方法は、コンパイル中にリンクすることです(リンカーオプション_-l<libraryname>_)
  • tLS変数が共有ライブラリ(_-fPIC_コンパイラオプションセット)内で宣言されている場合、一般的な動的TLSモデルが想定されるため、アクセスが非常に遅くなります。ここで、TLS変数への各アクセスは_tls_get_addr();これは、共有ライブラリの使用方法に制限がないため、デフォルトのケースです。

ソース:Ulrich DrepperによるスレッドローカルストレージのELF処理 https://www.akkadia.org/drepper/tls.pdf このテキストには、サポートされているターゲットプラットフォーム用に生成されたコードもリストされています。

7
MichaelMoser