gcc“线程”是如何工作的?
gcc中的线程是如何实现的?它只是gcc“线程”是如何工作的?,c,multithreading,gcc,thread-local-storage,C,Multithreading,Gcc,Thread Local Storage,gcc中的线程是如何实现的?它只是pthread\u getspecific和pthread\u setspecific的包装器吗 对于我使用POSIXAPI for TLS的程序,现在看到30%的程序运行时都花在pthread\u getspecific上,我有点失望。我在每个需要资源的函数调用的入口调用它。在内联优化之后,编译器似乎没有优化出pthread\u getspecific。因此,在函数内联之后,代码基本上是一次又一次地搜索正确的TLS指针,以返回相同的指针 在这种情况下,\u t
pthread\u getspecific
和pthread\u setspecific
的包装器吗
对于我使用POSIXAPI for TLS的程序,现在看到30%的程序运行时都花在pthread\u getspecific
上,我有点失望。我在每个需要资源的函数调用的入口调用它。在内联优化之后,编译器似乎没有优化出pthread\u getspecific
。因此,在函数内联之后,代码基本上是一次又一次地搜索正确的TLS指针,以返回相同的指针
在这种情况下,\u thread
能帮我吗?我知道C11中有thread\u local
,但是我的gcc还不支持它。(但现在我看到我的gcc确实支持\u Thread\u local
,只是不支持宏。)
我知道我可以简单地测试一下,然后看看。但是我现在必须去别的地方,我想在我尝试一个相当大的重写之前更好地了解一个特性。gcc的
\uuuThread
与C11的\uThread\uLocal
具有完全相同的语义。您没有告诉我们您为哪个平台编程,因为不同平台的实现细节不同。例如,在x86 Linux上,gcc应该使用%fs
段前缀将对线程局部变量的访问编译为内存指令,而不是调用pthread\u getspecific
,例如,确实支持C11及其thread\u local
(如果使用gcc-std=C11
进行编译)。如前所述,您可以使用(而不是C11thread\u local
)旧GCC版本支持的\u thread
限定符。了解
pthread_getspecific
确实非常慢(它在POSIX库中,因此不是由GCC提供的,而是由或提供的),因为它涉及函数调用。使用thread\u local
变量可能会更快
查看的源代码
以实现为例。阅读相关问题
和\u thread
和thread\u local
通常不会神奇地转换为调用pthread\u getspecific
。它们通常涉及一些特定的地址模式和/或寄存器(详细信息是特定于实现的,与;在Linux上,我想因为x86-64有更多的寄存器和地址模式,所以它的TLS实现速度比在i386上更快),并得到了、和的帮助。相反,pthread\u getspecific
的一些实现可能使用一些内部thread\u local
变量(在POSIX线程的实现中)
例如,编译以下代码
#include <pthread.h>
const extern pthread_key_t key;
__thread int data;
int
get_data (void) {
return data;
}
int
get_by_key (void) {
return *(int*) (pthread_getspecific (key));
}
下面的代码是get_by_key
,显式调用pthread\u getspecific
:
get_by_key:
.LFB4:
.cfi_startproc
subl $24, %esp #,
.cfi_def_cfa_offset 28
pushl key # key
.cfi_def_cfa_offset 32
call pthread_getspecific #
movl (%eax), %eax # MEM[(int *)_4], MEM[(int *)_4]
addl $28, %esp #,
.cfi_def_cfa_offset 4
ret
.cfi_endproc
因此,将TLS与\uuuuThread
(或C11中的thread\uLocal
)一起使用可能比使用pthread\uGetSpecific
(避免调用开销)更快
请注意,
thread\u local
是一个(C11标准头)。我使用的是英特尔cpu。你是说gcc使用一个特殊的寄存器,比如堆栈指针寄存器,但专用于TLS?pthread_getspecific也做同样的事情吗?@xiver77“我正在使用英特尔cpu”的信息不够。您为什么操作系统和体系结构编程?英特尔生产具有多种不同体系结构的CPU。在i386平台上,如果ABI支持此功能,则将%fp
段寄存器设置为指向线程本地数据的非零基址。我不能告诉你gcc是否可以在你的平台上这样做,因为你没有给我足够的信息。您还可以给我gcc的版本、gcc的调用和程序集输出(使用-S
开关)吗?很抱歉回复太晚。我的平台是Ubuntu15.10i386GCC4.9.2。我现在还将检查并查看\uuu thread
的程序集输出。@xiver77如何调用gcc?在i386 Linux上,gcc应该编译对\uuuuuThread
变量的访问,而不调用pthread\uGetSpecific
。代码的另一部分调用了pthread\u getspecific
,或者发生了一些奇怪的事情。@xiver77此程序集根本不调用pthread\u getspecific
。我猜这些调用来自其他地方。pthread_setspecific是否比内置TLS做了更多额外的工作?\u thread
是gcc的前C11扩展,其语义与C11的\u thread\u local
相同,事实上它保证比\u thread\u local
多一点pthread_getspecific
不一定涉及函数调用,它可以作为宏来实现。@fuzzxl:它可以通过宏来实现(但我想标准要求您可以通过函数指针来使用它),但它通常不作为宏来实现macro@BasileStarynkevitch标准说它可以是宏的,奇怪的是,glibc没有实现它。TLS在i386和amd64 Linux上都是通过段寄存器实现的(i386是%fs
,amd64是%gs
)。速度差异可以忽略不计。\u线程
在不同的平台上实现不同,在某些平台上(您没有告诉我们您是为哪个平台编程的),它可能使用pthread\u getspecific
实现。请给我们更多信息!我真的很想解决您的问题,但现在我对您使用的平台/如何编译代码的了解还不够,无法回答如何使线程本地存储性能更好。
get_by_key:
.LFB4:
.cfi_startproc
subl $24, %esp #,
.cfi_def_cfa_offset 28
pushl key # key
.cfi_def_cfa_offset 32
call pthread_getspecific #
movl (%eax), %eax # MEM[(int *)_4], MEM[(int *)_4]
addl $28, %esp #,
.cfi_def_cfa_offset 4
ret
.cfi_endproc