C++1zから、並行プログラミングで問題になりうる、キャッシュの無効化問題を制御できるようになります。
false sharingの制御
struct keep_apart { atomic<int> cat; atomic<int> dog; };
このような構造体がある場合、cat
とdog
が同じキャッシュラインに乗ることがあります。スレッド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 = 実装定義; }
備考
私は並行プログラミングの専門家ではないこともあって、使用している用語や説明に間違いがあるかもしれません。そういったことがあったらご指摘いただけると助かります。
参照
- N4523
constexpr std::thread::hardware_{true,false}_sharing_size
- P0154R0
constexpr std::hardware_{constructive,destructive}_interference_size
- P0154R1
constexpr std::hardware_{constructive,destructive}_interference_size
- 今さら聞けないマルチプロセッサの基礎教えます ――キャッシュの共有,割り込みの共有,OSによる制御 - ページ11 キャッシュの利用にも注意が必要
- false sharingの整理 - yoskhdia’s diary
お断り
この記事の内容は、C++1zが正式リリースされる際には変更される可能性があります。正式リリース後には、C++日本語リファレンスサイトcpprefjpの以下の階層の下に解説ページを用意する予定です。