既然C ++正在添加thread_local
存储作为语言功能,我想知道一些事情:
thead_local
的成本可能是多少?
thread_local
的内容都必须为每个创建的线程提供特定于线程的存储空间。答案 0 :(得分:11)
存储空间:变量的大小*线程数,或者可能(sizeof(var)+ sizeof(var *))*线程数。
实现线程局部存储有两种基本方法:
使用某种系统调用来获取有关当前内核线程的信息。 Sloooow。
使用一些指针,可能在处理器寄存器中,在内核的每个线程上下文切换时正确设置 - 与所有其他寄存器同时。便宜。
在intel平台上,变体2通常通过一些段寄存器(FS或GS,我不记得)来实现。 GCC和MSVC都支持这一点。因此,访问时间与全局变量一样快。
这也是可能的,但我在实践中还没有看到它,因为这可以通过现有的库函数来实现,例如pthread_getspecific
。然后性能将像1.或2.,加上库调用开销。请记住,变量2 +库调用开销仍然比内核调用快得多。
答案 1 :(得分:9)
可以在此处找到Uli Drepper(glibc的维护者)在Linux上的工作原理说明:www.akkadia.org/drepper/tls.pdf
处理动态加载模块等的要求使得整个机制有点复杂,这或许可以部分解释为什么文档权重为79页(!)。
内存使用方面,每个每个线程的变量显然需要它自己的每线程内存(尽管在某些情况下这可以懒得完成,只有在首次访问变量时才分配空间) ,然后是偏移表等所需的一些额外数据结构。
在性能方面,访问TLS变量的额外成本主要围绕检索变量的地址。在x86 Linux上,GS寄存器用作在x86-64 FS上获取线程ID的开始。通常有一些指针解引用,以及一个动态加载代码的函数调用(__tls_get_addr)。还有创建新线程的成本较慢,因为实现需要分配空间并可能初始化所有TLS变量(如果不是懒惰的话)。
TLS非常适合轻松地使一些旧的线程不安全的代码模式线程安全(想想错误),但对于从一开始就为多线程世界设计的新代码,它很少需要。