函数局部string变量是否应优先使用thread_local而非自动存储类?
函数局部的std::string变量是否通常应采用thread_local而非自动存储类?
以下代码中,g1和g2是逻辑等价的函数:
#include <string> void f(std::string &); void g1() { std::string s; f(s); } void g2() { thread_local std::string s; s.clear(); f(s); }
对应的x86-64 clang 17.0.1生成的汇编代码如下(x86-64 gcc 13.2生成的汇编代码类似):
g1(): # @g1() push r14 push rbx sub rsp, 40 lea r14, [rsp + 24] mov qword ptr [rsp + 8], r14 mov qword ptr [rsp + 16], 0 mov byte ptr [rsp + 24], 0 lea rdi, [rsp + 8] call f(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&)@PLT mov rdi, qword ptr [rsp + 8] cmp rdi, r14 je .LBB0_3 call operator delete(void*)@PLT .LBB0_3: add rsp, 40 pop rbx pop r14 ret mov rbx, rax mov rdi, qword ptr [rsp + 8] cmp rdi, r14 je .LBB0_6 call operator delete(void*)@PLT .LBB0_6: mov rdi, rbx call _Unwind_Resume@PLT std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() [base object destructor]: # @std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() [base object destructor] mov rax, rdi mov rdi, qword ptr [rdi] add rax, 16 cmp rdi, rax jne operator delete(void*)@PLT # TAILCALL ret g2(): # @g2() push rax cmp byte ptr fs:[guard variable for g2()::s[abi:cxx11]@TPOFF], 0 je .LBB2_1 .LBB2_2: mov rax, qword ptr fs:[0] lea rdi, [rax + g2()::s[abi:cxx11]@TPOFF] mov qword ptr fs:[g2()::s[abi:cxx11]@TPOFF+8], 0 mov rax, qword ptr fs:[g2()::s[abi:cxx11]@TPOFF] mov byte ptr [rax], 0 # std::string::clear() pop rax jmp f(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&)@PLT # TAILCALL .LBB2_1: mov rax, qword ptr fs:[0] lea rcx, [rax + g2()::s[abi:cxx11]@TPOFF+16] mov qword ptr fs:[g2()::s[abi:cxx11]@TPOFF], rcx lea rsi, [rax + g2()::s[abi:cxx11]@TPOFF] mov qword ptr fs:[g2()::s[abi:cxx11]@TPOFF+8], 0 mov byte ptr fs:[g2()::s[abi:cxx11]@TPOFF+16], 0 lea rdi, [rip + std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() [base object destructor]] lea rdx, [rip + __dso_handle] call __cxa_thread_atexit@PLT mov byte ptr fs:[guard variable for g2()::s[abi:cxx11]@TPOFF], 1 jmp .LBB2_2 g2()::s[abi:cxx11]: .zero 32 guard variable for g2()::s[abi:cxx11]: .byte 0 # 0x0 DW.ref.__gxx_personality_v0: .quad __gxx_personality_v0
需要注意的是,调用std::string::clear()不会释放字符串已分配的存储空间(即字符串实例的capacity不会改变)。
利弊分析
- 优势:在单个线程的生命周期内,使用
thread_local的字符串变量可以减少动态内存分配与释放操作;同时能减少异常处理相关的目标代码。此外,堆碎片减少的收益,可能抵消存储无法复用的缺点。 - 缺点:该
thread_local存储属于线程专属,无法被其他用途复用。
内容的提问来源于stack exchange,提问作者WaltK
相关产品推荐
相关产品推荐

