[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的主要内容,如果未能解决你的问题,请参考以下文章

834. Sum of Distances in Tree —— weekly contest 84

128th LeetCode Weekly Contest Complement of Base 10 Integer

123th LeetCode Weekly Contest Add to Array-Form of Integer

Cost of path的计算

The Cost of JavaScript --------引用

php 使Cost of Goods插件与WC REST API兼容