C++1z false sharingとtrue sharingの制御

C++1zから、並行プログラミングで問題になりうる、キャッシュの無効化問題を制御できるようになります。

false sharingの制御

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

このような構造体がある場合、catdogが同じキャッシュラインに乗ることがあります。スレッド1ではcat変数、スレッド2ではdog変数を操作するような状況で、それぞれが共通のキャッシュを無効化してしまうパフォーマンス劣化の問題が起こりえます。こういった状況をfalse sharingと言いますが、これを回避するために、<new>ヘッダにstd::hardware_destructive_interference_sizeという定数が定義されます。(「破壊的な干渉サイズ」といったところでしょうか)

この定数を構造体メンバのアライメントとして指定することによって、それぞれのメンバが独立したキャッシュラインに乗り、false sharingが回避できるようになります。

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

true sharingの制御

true sharingという用語は一般的ではないようですが、意図的に複数のメンバを同一のキャッシュラインに乗せることがそう呼ばれます。

そのようなことをするために、同ヘッダにstd::hardware_constructive_interference_sizeという定数が定義されます。構造体全体に対してこの定数をアライメント指定することによって、構造体全体が同一キャッシュラインに乗ることが期待できるようになります。

struct alignas(hardware_constructive_interference_size) together {
    atomic<int> dog;
    int puppy;
};

宣言

// <new>
namespace std {
  static constexpr size_t hardware_destructive_interference_size = 実装定義;
  static constexpr size_t hardware_constructive_interference_size = 実装定義;
}

備考

私は並行プログラミングの専門家ではないこともあって、使用している用語や説明に間違いがあるかもしれません。そういったことがあったらご指摘いただけると助かります。

参照

お断り

この記事の内容は、C++1zが正式リリースされる際には変更される可能性があります。正式リリース後には、C++日本語リファレンスサイトcpprefjpの以下の階層の下に解説ページを用意する予定です。