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等新標準特性。測試

相關文章
相關標籤/搜索