GCC 4.8 draft changelog人中:

G++现在实现了C++11thread_local关键字;这与

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

显然,为了支持非函数局部thread_local变量,在进入每个线程主线程之前,需要有一个线程初始化阶段(就像全局变量有一个静态初始化阶段),但它们指的是运行时惩罚吗?

粗略地说,gcc新的thread_local实现的架构是什么?

推荐答案

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

在提交462819c中添加了动态thread_local初始化.其中一个变化是:

* 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;

编译*时,我们看到every使用tls引用变成了对_ZTW3tls的函数调用,_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页上看出来.

  • 它是not个本地函数,

    1. 它是extern(如上图所示),或
    2. 该类型有一个非平凡的析构函数(不允许有__thread个变量),或者
    3. 类型变量由非常量表达式初始化(__thread个变量也不允许使用非常量表达式).

在所有其他用例中,它的行为与__thread相同.这意味着,除非你有extern __thread个变量,否则你可以用thread_local替换所有的__thread个变量,而不会损失任何性能.


*:我使用-O0编译,因为内联将使函数边界不那么可见.即使我们达到-O3,这些初始化判断仍然存在.

Linux相关问答推荐

如何根据具体情况打印两行输出?

在Linux中随机化txt文件但保证不重复行

如何正确Forking 并完成进程以避免 EAGAIN 错误

Linux:用户态线程在执行系统调用时是否有更高的优先级?

在 cURL 中使用的确切位置将字节分成一些范围部分

如何在vim中使用正则表达式来切换文件中所有字符的大小写

如何用 Sed 替换 Match 后的 2 个连续行

内核是如何工作的?

使用 gdb 将地址转换为行

使用 awk 或 sed 删除特定字符

GLIBCXX 版本

如何在非阻塞套接字上处理 OpenSSL SSL_ERROR_WANT_READ / WANT_WRITE

kdevtmpfsi 使用整个 CPU

如何从命令行打开 Ubuntu Linux 上的 AVD 管理器?

在 mac 上通过 ssh 连接到 amazon aws linux 服务器

判断 VT-x 是否已激活而无需在 Linux 中重新启动?

以原子方式移动目录

CLOCK_MONOTONIC 和 CLOCK_MONOTONIC_RAW 有什么区别?

我可以打开一个套接字并将其传递给 Linux 中的另一个进程吗

更改 /etc/profile 后,我需要做什么来重置我的 shell?