[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

bench0.png

O1

bench1.png

O2

bench2.png

O3

bench.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值