71

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

G++ はC++11 thread_localキーワードを実装するようになりました。__threadこれは、動的な初期化と破棄のセマンティクスを許可するという点で、主にGNU キーワードとは異なります。thread_local残念ながら、このサポートでは、動的な初期化が必要ない場合でも、非関数ローカル変数への参照に対して実行時のペナルティが 必要になるため、ユーザーは__thread静的な初期化セマンティクスを使用して TLS 変数を引き続き使用することを希望する場合があります。

このランタイム ペナルティの正確な性質と原因は何ですか?

明らかに、非関数ローカルthread_local変数をサポートするには、すべてのスレッド メインへのエントリの前にスレッド初期化フェーズが必要です (グローバル変数の静的初期化フェーズがあるのと同じように)。 ?

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

4

3 に答える 3

50

(免責事項: GCC の内部についてはよく知らないので、これも経験に基づいた推測です。)

動的thread_local初期化はコミット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;

コンパイル*すると、参照のすべての使用がtlsへの関数呼び出しになり_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ます。ラッパーは、次の場合にのみ生成されます。

  • 関数ローカルではなく

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

他のすべての使用例では、 と同じように動作し__threadます。つまり、いくつかの変数がない限り、パフォーマンスを損なうことなくすべてをextern __thread置き換えることができます。__threadthread_local


*: インライン化すると関数の境界が見えにくくなるため、-O0 を付けてコンパイルしました。-O3 まで上げても、これらの初期化チェックは残っています。

于 2012-10-29T14:44:03.083 に答える
9

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

extern 変数の場合、プログラマーが、非定義 TU での変数の使用が動的初期化をトリガーする必要がないことを確認できる場合 (変数が静的に初期化されるか、定義 TU での変数の使用が前に実行されるため) -fno-extern-tls-init オプションを使用すると、このオーバーヘッドを回避できます。

于 2013-02-16T14:43:51.133 に答える