gpt4 book ai didi

c++ - GCC 4.8 中 C++11 thread_local 变量的性能损失是多少?

转载 作者:塔克拉玛干 更新时间:2023-11-03 07:57:48 26 4
gpt4 key购买 nike

来自GCC 4.8 draft changelog :

G++ now implements the C++11 thread_local keyword; this differs from the GNU __thread keyword primarily in that it allows dynamic initialization and destruction semantics. Unfortunately, this support requires a run-time penalty for references to non-function-local thread_local variables even if they don't need dynamic initialization, so users may want to continue to use __thread for TLS variables with static initialization semantics.

这种运行时惩罚的性质和起源究竟是什么?

显然要支持非函数局部thread_local变量,在进入每个线程main之前需要一个线程初始化阶段(就像全局变量有一个静态初始化阶段一样),但是他们是否指的是除此之外的一些运行时惩罚?

gcc新实现thread_local的架构大概是怎样的?

最佳答案

(免责声明:我对 GCC 的内部了解不多,所以这也是一个有根据的猜测。)

动态 thread_local 初始化被添加到提交 462819c 中.其中一项更改是:

* 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 变量,否则您可以将所有 __thread 替换为 thread_local 而不会损失任何性能。


*:我使用 -O0 进行编译,因为内联器会使函数边界不那么明显。即使我们转向 -O3,这些初始化检查仍然存在。

关于c++ - GCC 4.8 中 C++11 thread_local 变量的性能损失是多少?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15587393/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com