未缓存 TLS 变量访问的结果
Result of TLS variable access not cached
编辑:看来这确实是一个编译器错误:https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82803
我正在编写一个用于写入日志的包装器,它使用 TLS 来存储 std::stringstream
缓冲区。此代码将由共享库使用。当查看 godbolt.org 上的代码时,似乎 gcc 和 clang 都不会缓存 TLS 查找的结果(当我相信我已经设计了 [=35 时,循环重复调用'__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
这看起来确实像是 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 错误追踪器:https://bugs.llvm.org/
编辑:看来这确实是一个编译器错误:https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82803
我正在编写一个用于写入日志的包装器,它使用 TLS 来存储 std::stringstream
缓冲区。此代码将由共享库使用。当查看 godbolt.org 上的代码时,似乎 gcc 和 clang 都不会缓存 TLS 查找的结果(当我相信我已经设计了 [=35 时,循环重复调用'__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
这看起来确实像是 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 错误追踪器:https://bugs.llvm.org/