最終更新日時(UTC):
が更新

履歴 編集

variable
<new>

std::hardware_destructive_interference_size(C++17)

namespace std {
  inline constexpr std::size_t hardware_destructive_interference_size = implementation-defined;
}

概要

2つのオブジェクトに並行アクセスする際に、パフォーマンス低下を避けられる最小アライメントサイズ。

struct keep_apart {
  std::atomic<int> cat;
  std::atomic<int> dog;
};

このような構造体がある場合、catdogが同じキャッシュラインに乗ることがある。スレッド1ではcat変数、スレッド2ではdog変数を操作するような状況で、それぞれが共通のキャッシュを無効化してしまうパフォーマンス劣化の問題が起こりえる。こういった状況を「false sharing」という。

hardware_destructive_interference_sizeは、false sharingを回避するための、変数ごとにキャッシュラインを分けられる最小アライメントサイズである。

struct keep_apart {
  alignas(std::hardware_destructive_interference_size) std::atomic<int> cat;
  alignas(std::hardware_destructive_interference_size) std::atomic<int> dog;
};

備考

  • この変数の値は、alignof(max_align_t)以上である

#include <iostream>
#include <new>
#include <thread>
#include <vector>

struct X {
  alignas(std::hardware_destructive_interference_size) int a;
  alignas(std::hardware_destructive_interference_size) int b;
};

struct IndivisualCacheInt {
  alignas(std::hardware_destructive_interference_size) int value;
};

int main()
{
  std::cout << "hardware_destructive_interference_size : "
            << std::hardware_destructive_interference_size
            << std::endl;

  // 構造体内のメンバ変数aとbを、それぞれ別なキャッシュラインに乗せる
  {
    X x;
    x.a = 0;
    x.b = 0;
    std::thread t1{[&x]{
      for (int i = 0; i < 100; ++i) {
        ++x.a;
      }
    }};

    std::thread t2{[&x]{
      for (int i = 0; i < 100; ++i) {
        ++x.b;
      }
    }};

    t1.join();
    t2.join();
  }

  // 連続したメモリの各要素を、個別のキャッシュに乗せる
  {
    std::vector<IndivisualCacheInt> v{10};
    std::vector<std::thread> threads;
    for (std::size_t i = 0; i < v.size(); ++i) {
      threads.push_back(std::thread{[&v, i]{
        for (int j = 0; j < 100; ++j) {
          ++v[i].value;
        }
      }});
    }

    for (std::thread& t : threads) {
      t.join();
    }
  }
}

出力例

(実装がないため、動作確認できていない)

バージョン

言語

  • C++17

処理系

参照