未缓存 TLS 变量访问的结果
Posted
技术标签:
【中文标题】未缓存 TLS 变量访问的结果【英文标题】:Result of TLS variable access not cached 【发布时间】:2017-12-11 13:49:27 【问题描述】:编辑:看来这确实是一个编译器错误:https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82803
我正在编写一个用于写入日志的包装器,它使用 TLS 来存储 std::stringstream
缓冲区。此代码将由共享库使用。查看 godbolt.org 上的代码时,似乎 gcc 和 clang 都不会缓存 TLS 查找的结果(当我相信我已经以一种应该允许的方式设计了我的类时,循环重复调用了 '__tls_get_addr()'。
#include <sstream>
class LogStream
public:
LogStream()
: m_buffer(getBuffer())
LogStream(std::stringstream& buffer)
: m_buffer(buffer)
static std::stringstream& getBuffer()
thread_local std::stringstream buffer;
return buffer;
template <typename T>
inline LogStream& operator<<(const T& t)
m_buffer << t;
return *this;
private:
std::stringstream& m_buffer;
;
int main()
LogStream log;
for (int i = 0; i < 12345678; ++i)
log << i;
查看汇编代码输出 gcc 和 clang 生成非常相似的输出:
clang 5.0.0:
xor ebx, ebx
.LBB0_3: # =>This Inner Loop Header: Depth=1
data16
lea rdi, [rip + LogStream::getBuffer[abi:cxx11]()::buffer[abi:cxx11]@TLSGD]
data16
data16
rex64
call __tls_get_addr@PLT // Called on every loop iteration.
lea rdi, [rax + 16]
mov esi, ebx
call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)@PLT
inc ebx
cmp ebx, 12345678
jne .LBB0_3
gcc 7.2:
xor ebx, ebx
.L3:
lea rdi, guard variable for LogStream::getBuffer[abi:cxx11]()::buffer@tlsld[rip]
call __tls_get_addr@PLT // Called on every loop iteration.
mov esi, ebx
add ebx, 1
lea rdi, LogStream::getBuffer[abi:cxx11]()::buffer@dtpoff[rax+16]
call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)@PLT
cmp ebx, 12345678
jne .L3
如何让两个编译器相信不需要重复进行查找?
编译器选项:-std=c++11 -O3 -fPIC
Godbolt link
【问题讨论】:
强制 getbuffer 不内联或动态分配 LogStream 似乎对我有用......不知道为什么,对我来说内联逻辑似乎很糟糕...... 不指定-fPIC
也不会生成奇怪的代码。
@SebastianRedl 是的,独立应用的生成还可以
确实,见Bug 82803
【参考方案1】:
这看起来确实像是 Clang 和 GCC 中的优化错误。
这就是我认为会发生的事情。 (我可能完全关闭了。)编译器将所有内容完全内联到这段代码:
int main()
// pseudo-access
std::stringstream& m_buffer = LogStream::getBuffer::buffer;
for (int i = 0; i < 12345678; ++i)
m_buffer << i;
然后,没有意识到在-fPIC
下访问局部线程非常昂贵,它决定不需要对全局的临时引用并内联它:
int main()
for (int i = 0; i < 12345678; ++i)
// pseudo-access
LogStream::getBuffer::buffer << i;
无论实际发生什么,这显然是对您编写的代码的悲观化。您应该将此作为错误报告给 GCC 和 Clang。
GCC 错误跟踪器:https://gcc.gnu.org/bugzilla/ Clang bugtracker:https://bugs.llvm.org/
【讨论】:
看来gcc至少已经被报道过了:gcc.gnu.org/bugzilla/show_bug.cgi?id=82803以上是关于未缓存 TLS 变量访问的结果的主要内容,如果未能解决你的问题,请参考以下文章