[C++ Weekly] EP2 Cost of Using Statics

Posted 鱼竿钓鱼干

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[C++ Weekly] EP2 Cost of Using Statics相关的知识,希望对你有一定的参考价值。

[C++ Weekly] EP2 Cost of Using Statics

C++ 11 为了确保 static 初始化线程安全,存在一定开销

#include <string>
#include <algorithm>

struct C 
    static const std::string &magic_static()
    
        static const std::string s = "bob";
        return s;
    
    const std::string &s = magic_static();

    const std::string &magic_static_ref()
    
        return s;
    
;

auto main() -> int

/*  
    C::magic_static().size();
    C::magic_static().size();
    C::magic_static().size();
    return C::magic_static().size();
*/
    C c;
    c.magic_static_ref().size();
    c.magic_static_ref().size();
    c.magic_static_ref().size();
    return c.magic_static_ref().size();

  • 我们创建了一个名为“C”的简单结构,在这个结构中,我们有一个使用“首次使用时构造”习语创建的静态,所以这个静态并不真正存在。 直到第一次调用这个 ‘magic_static’ 函数时才构造字符串’S’。
  • C++ 11 保证了静态变量的初始化以线程安全的方式进行。
  • magic_static_ref 不是static ,所以返回的是成员变量s的引用而不是对静态变量的引用

编译器视角

C++ Insightsstruct C

struct C

  static inline const std::basic_string<char, std::char_traits<char>, std::allocator<char> > & magic_static()
  
    static uint64_t __sGuard;
    alignas(const std::basic_string<char, std::char_traits<char>, std::allocator<char> >) static char __s[sizeof(const std::basic_string<char, std::char_traits<char>, std::allocator<char> >)];
    
    if( ! __sGuard )
    
      if( __cxa_guard_acquire(&__sGuard) )
      
        try 
        
          new (&__s) std::basic_string<char, std::char_traits<char>, std::allocator<char> >("bob", std::allocator<char>());
          __sGuard = true;
        
        catch(...)
        
          __cxa_guard_abort(&__sGuard);
          throw;
        
        
        __cxa_guard_release(&__sGuard);
      
    
    return *reinterpret_cast<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >*>(__s);
  
  
  const std::basic_string<char, std::char_traits<char>, std::allocator<char> > & s = magic_static();
  inline const std::basic_string<char, std::char_traits<char>, std::allocator<char> > & magic_static_ref()
  
    return this->s;
  
  
;

可以看到为了保证 static 初始化线程安全有一个类似双检查锁的动作,这会带来一定开销

汇编层面

magic_static

C::magic_static[abi:cxx11]():           # @C::magic_static[abi:cxx11]()
        push    rbp
        mov     rbp, rsp
        sub     rsp, 32
        cmp     byte ptr [rip + guard variable for C::magic_static[abi:cxx11]()::s[abi:cxx11]], 0
        jne     .LBB1_4
        lea     rdi, [rip + guard variable for C::magic_static[abi:cxx11]()::s[abi:cxx11]]
        call    __cxa_guard_acquire@PLT
        cmp     eax, 0
        je      .LBB1_4
        lea     rdi, [rbp - 8]
        mov     qword ptr [rbp - 32], rdi       # 8-byte Spill
        call    std::allocator<char>::allocator()@PLT
        mov     rdx, qword ptr [rbp - 32]       # 8-byte Reload
        lea     rdi, [rip + C::magic_static[abi:cxx11]()::s[abi:cxx11]]
        lea     rsi, [rip + .L.str]
        call    std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string<std::allocator<char> >(char const*, std::allocator<char> const&)
        jmp     .LBB1_3

magic_static_ref

C::magic_static_ref[abi:cxx11]():       # @C::magic_static_ref[abi:cxx11]()
        push    rbp
        mov     rbp, rsp
        mov     qword ptr [rbp - 8], rdi
        mov     rax, qword ptr [rbp - 8]
        mov     rax, qword ptr [rax]
        pop     rbp
        ret

Quick C++ Benchmarks

https://www.quick-bench.com/

#include <string>
#include <algorithm>
#include <benchmark/benchmark.h>

struct C 
    static const std::string &magic_static()
    
        static const std::string s = "bob";
        return s;
    
    const std::string &s = magic_static();

    const std::string &magic_static_ref()
    
        return s;
    
;

static void BM_MAGIC_STATIC_REF(benchmark::State& state) 
  C c;
  for (auto _ : state) 
    for(int i = 0; i < 1'000'000'000; ++i) 
      c.magic_static_ref().size();
    

// Register the function as a benchmark
BENCHMARK(BM_MAGIC_STATIC_REF);

// Define another benchmark
static void BM_MAGIC_STATIC(benchmark::State& state) 
  for (auto _ : state)
    for(int i = 0; i < 1'000'000'000; ++i) 
      C::magic_static().size();
    

// Register the function as a benchmark
BENCHMARK(BM_MAGIC_STATIC);

  • O0magic_static_ref 慢于 magic_static
  • O1 ~ O3magic_static_ref 直接被编译器优化掉了

O0

O1

O2

O3

以上是关于[C++ Weekly] EP2 Cost of Using Statics的主要内容,如果未能解决你的问题,请参考以下文章