MPMCQueue源码分析(上)

github地址:https://github.com/rigtorp/MP...html

  • 关于__cpp_lib_hardware_interference_size

这个功能测试宏表示了c++17新引入的feature : https://zh.cppreference.com/w...
相关:https://www.it1352.com/178422...
其含义是两个对象间避免假数据共享(false sharing,也被称为伪共享)的最小偏移量。
关于false sharing : https://www.cnblogs.com/cyfon...c++

#ifdef __cpp_lib_hardware_interference_size
static constexpr size_t hardwareInterferenceSize = std::hardware_destructive_interference_size;
#else
static constexpr size_t hardwareInterferenceSize = 64;
#endif

上面这段代码即获取了当前硬件条件下的避免伪共享的最小偏移量,若是编译器尚未实现这个feature,则自定义为64字节。(主流的硬件架构cache line 大小为64字节 : https://blog.csdn.net/midion9...git

为了不伪共享的发生,让不一样对象处于不一样的缓存行便可,也就是设置偏移量为缓存行大小。github

  • 关于__cpp_aligned_new

c++17引入的新feature,具体信息见: https://www.jianshu.com/p/7ce...
其做用是提供内存对齐版本的new运算符。缓存

#if defined(__cpp_aligned_new)
template <typename T> using AlignedAllocator = std::allocator<T>;
#else
template <typename T> struct AlignedAllocator {
  using value_type = T;
  T *allocate(std::size_t n) {
    if (n > std::numeric_limits<std::size_t>::max() / sizeof(T)) {
      throw std::bad_array_new_length();
    }
#ifdef _WIN32
    auto *p = static_cast<T*>(_aligned_malloc(sizeof(T) * n, alignof(T)));
    if (p == nullptr) {
      throw std::bad_alloc();
    }
#else
    T *p;
    if (posix_memalign(reinterpret_cast<void **>(&p), alignof(T), sizeof(T) * n) != 0) {
      throw std::bad_alloc();
    }
#endif
    return p;
  }

  void deallocate(T *p, std::size_t) {
#ifdef WIN32
    _aligned_free(p);
#else
    free(p);
#endif
  }
};
#endif

若是编译器实现了aligned new的feature,则直接实现标准库版本的内存分配器,不然
自定义一个,根据平台不一样使用_aligned_malloc/posix_memalign实现。架构

  • 关于Slot类
template <typename T> struct Slot {
~Slot() noexcept {
  if (turn & 1) {
    destroy();
  }
}

template <typename... Args> void construct(Args &&... args) noexcept {
static_assert(std::is_nothrow_constructible<T, Args &&...>::value, "T must be nothrow constructible with Args&&...");
  new (&storage) T(std::forward<Args>(args)...);
}

void destroy() noexcept {
static_assert(std::is_nothrow_destructible<T>::value, "T must be nothrow destructible");
  reinterpret_cast<T*>(&storage)->~T();
}

T &&move() noexcept { return reinterpret_cast<T &&>(storage); }

// Align to avoid false sharing between adjacent slots
alignas(hardwareInterferenceSize) std::atomic<size_t> turn = {0};
typename std::aligned_storage<sizeof(T), alignof(T)>::type storage;
};

上面是Slot类的实现,重点观察如下几个地方:函数

  1. 数据成员turn的修饰符含义
  2. 数据成员storage的做用
  3. 析构函数
  4. construct函数中的placement new操做

使用std::aligned_storage定义了一个大小最多为sizeof(T), 对其要求为alignof(T)的对象storage。并在construct函数中经过placement new操做在其上构造T类型对象。
这一系列操做的目的就是提供给用户自定义的按照任何对齐要求的内存申请与构造的抽象能力,对于这套组合拳c++标准中有清晰的标注:
As with any other uninitialized storage, the objects are created using placement new and destroyed with explicit destructor calls.
参考文章:https://blog.csdn.net/ywcpig/...
https://en.cppreference.com/w...
使用alignas修饰符将数据成员turn的对齐要求修改成hardwardInterferenceSize,即文章第一部分定义的常量,因为结构体的大小必须是其数据成员最大对齐数的整数倍,所以不一样Slot对象的storage不可能存在于同一个cache line中,避免了false sharing。
析构函数中根据turn是否为0选择性调用destroy函数析构对象,能够看到turn的另外一个做用是标志此Slot中是否还存在T类对象。
tips:
对c++语法不够熟练的同窗能够再看下这段代码,涉及到大括号初始化,类内初始化,xvalue的产生,可变参数的完美转发,noexcept等新标准特性。测试

相关文章
相关标签/搜索