C++11 中线程安全的局部静态变量初始化的成本?

Sun*_*nda 4 c++ thread-safety static-initialization c++11

我们知道局部静态变量初始化在 C++11 中是线程安全的,现代编译器完全支持这一点。(在 C++11 中局部静态变量初始化线程安全吗?

使其线程安全的成本是多少?我知道这很可能依赖于编译器实现。

上下文:我有一个多线程应用程序(10 个线程)通过以下函数以非常高的速率访问单例对象池实例,我担心它的性能影响。

template <class T>
ObjectPool<T>* ObjectPool<T>::GetInst()
{
    static ObjectPool<T> instance;
    return &instance;
}
Run Code Online (Sandbox Code Playgroud)

nwp*_*nwp 5

查看生成的汇编代码会有所帮助。

来源

#include <vector>

std::vector<int> &get(){
  static std::vector<int> v;
  return v;
}
int main(){
  return get().size();
}
Run Code Online (Sandbox Code Playgroud)

汇编器

std::vector<int, std::allocator<int> >::~vector():
        movq    (%rdi), %rdi
        testq   %rdi, %rdi
        je      .L1
        jmp     operator delete(void*)
.L1:
        rep ret
get():
        movzbl  guard variable for get()::v(%rip), %eax
        testb   %al, %al
        je      .L15
        movl    get()::v, %eax
        ret
.L15:
        subq    $8, %rsp
        movl    guard variable for get()::v, %edi
        call    __cxa_guard_acquire
        testl   %eax, %eax
        je      .L6
        movl    guard variable for get()::v, %edi
        movq    $0, get()::v(%rip)
        movq    $0, get()::v+8(%rip)
        movq    $0, get()::v+16(%rip)
        call    __cxa_guard_release
        movl    $__dso_handle, %edx
        movl    get()::v, %esi
        movl    std::vector<int, std::allocator<int> >::~vector(), %edi
        call    __cxa_atexit
.L6:
        movl    get()::v, %eax
        addq    $8, %rsp
        ret
main:
        subq    $8, %rsp
        call    get()
        movq    8(%rax), %rdx
        subq    (%rax), %rdx
        addq    $8, %rsp
        movq    %rdx, %rax
        sarq    $2, %rax
        ret
Run Code Online (Sandbox Code Playgroud)

相比

来源

#include <vector>

static std::vector<int> v;
std::vector<int> &get(){
  return v;
}
int main(){
  return get().size();
}
Run Code Online (Sandbox Code Playgroud)

汇编器

std::vector<int, std::allocator<int> >::~vector():
        movq    (%rdi), %rdi
        testq   %rdi, %rdi
        je      .L1
        jmp     operator delete(void*)
.L1:
        rep ret
get():
        movl    v, %eax
        ret
main:
        movq    v+8(%rip), %rax
        subq    v(%rip), %rax
        sarq    $2, %rax
        ret
        movl    $__dso_handle, %edx
        movl    v, %esi
        movl    std::vector<int, std::allocator<int> >::~vector(), %edi
        movq    $0, v(%rip)
        movq    $0, v+8(%rip)
        movq    $0, v+16(%rip)
        jmp     __cxa_atexit
Run Code Online (Sandbox Code Playgroud)

我对汇编程序不是很好,但我可以看到在第一个版本v中它周围有一个锁并且get没有内联,而在第二个版本get中基本上已经消失了。
你可以各种编译器和优化标志,但似乎没有编译器能够内联或优化了锁,即使该方案显然是单线程的。
您可以添加staticto getwhich 使 gcc 内联,get同时保留锁定。

要了解这些锁和额外指令对您的编译器、标志、平台和周围代码的成本,您需要进行适当的基准测试。
我希望锁有一些开销并且比内联代码慢得多,当您实际使用向量时,这变得无关紧要,但是如果不进行测量,您永远无法确定。