継続的な最適化:CppMem による非同期アクセス

前回の投稿で私の課題について説明しました。進行中の最適化のプロセスから始めましょう。確かに、CppMem で推論を検証します。 Meeting C++ 2014 でのプレゼンテーションで大きな間違いを犯したことがあります。

君に言っておく。それが私たちの出発点です。

プログラム

05
14

完全に非同期

プログラムには 2 つのデータ競合があるため、動作が未定義です。変数 x または変数 y へのアクセスは保護されます。プログラムには未定義の動作があるため、それぞれの結果が可能です。 C++ の専門用語では、巡航ミサイルが発射されるか、PC が発火する可能性があることを意味します。私にはそんなことはありませんでしたが...

そのため、x と y の値について明言することはできません。

悪くない

既知のアーキテクチャは、int 変数のアクセスがアトミックであることを保証します。ただし、int 変数は自然にアラインされている必要があります。自然に整列されたということは、32 ビット アーキテクチャでは int 変数が 4 で割り切れるアドレスを持たなければならないことを意味します。 C++11 では、データ型の配置を調整できます。

もう一度。 int 変数をアトミックと見なすべきだとは言いません。この場合のコンパイラは、C++11 標準以上のものを保証するとだけ言っておきます。ただし、この規則を使用すると、プログラムは C++ 標準に準拠しなくなります。

これが私の推論でした。ここで、プログラムの未定義の動作について CppMem が何を言うかを見てみましょう。

CppMem

22
36

プログラムは最小限に抑えられます。中括弧 (4 行目と 12 行目) とパイプ記号 (8 行目) を使用して、ねじを簡単に定義できます。 4 行目と 7 行目、または 8 行目と 11 行目の追加の中括弧は、スレッドの作業パッケージを定義します。変数 x と y の出力には興味がないので、9 行目と 10 行目だけを読みます。

それが CppMem の理論でした。分析に移りましょう。

ダイ分析

プログラムを実行すると、CppMem は赤い文字 (1) で文句を言います )、スレッドの 4 つの可能なインターリーブすべてが競合フリーではないこと。最初の実行のみが一貫しています。これで、CppMem を使用して 4 つの実行 (2) を切り替えることができます ) 注釈付きグラフを分析します (3 ).

グラフから CppMem を最大限に活用します。そこで、4 つのグラフについて詳しく説明します。

最初の実行

段落からどのような情報を引き出すことができますか (3 )?

グラフのノードはプログラムの式を表し、エッジは式間の関係を表します。説明では、(a) から (f) までの名前を参照します。では、この具体的な実行の注釈から何を導き出すことができるでしょうか?

  • a:Wna x=0: x の非アトミック書き込みである最初の式 (a) です。
  • sb (前に配列): 最初の式 (a) の書き込みは、2 番目の式 (b) の書き込みの前に順序付けられます。これらの関係は、式 (c) と (d)、または (e) と (f) の間にも成り立ちます。
  • rf (読み取り元) :式 (e) は、式 (b) から y の値を読み取ります。したがって、(f) は (a) から読み取ります。
  • sw s(同期) :式 (a) は (f) と同期します。式 (f) は別のスレッドで実行されるため、この関係が成り立ちます。スレッドの作成は同期ポイントです。スレッドの作成前に発生するすべてのことは、スレッドに表示されます。対称性の理由から、(b) と (e) の間で同じことが成り立ちます。
  • dr (データ競合 ):これは、変数 x と y の読み取りと書き込みの間のデータ競合です。したがって、プログラムの動作は未定義です。

実行が一貫しているのはなぜですか?

値 x と y は、メイン スレッド (a) と (b) で x と y の値から読み取られるため、実行は一貫しています。式 (c) および (d) の別のスレッドから x および y から値が読み取られる場合、(e) および (f) の x および y の値が部分的にしか読み取られないという効果が生じる可能性があります。 .これは一貫していません。あるいは違う言い方をする。具体的な実行では、x と y は値 0 を取得します。これは、式 (e) と (f) に加えてわかります。

この保証は、私が今参照している次の 3 回の実行には適用されません。

2 回目の実行

式 (e) は、この一貫性のない実行で、式 (d) から y の値を読み取ります。 (d) の書き込みは、(e) の読み取りと並行して行われます。

3 回目の実行

これは、2 回目の実行と対称的です。式 (f) は式 (c) から読み取ります。

4 回目の実行

今、すべてがうまくいかない。式 (e) および (f) は、式 (d) および (c) から読み取ります。

短い結論

CppMem のデフォルト構成を使用し、グラフのみを使用しましたが、多くの貴重な情報と洞察を得ることができました。特に、CppMem はその場ですぐに使用できます。

<オール>
  • x と y の 4 つの組み合わせすべてが可能です:(0,0)、(11,0)、(0,2000)、(11,2000) .
  • プログラムにデータ競合があるため、動作が未定義です。
  • 4 つの実行のうち、一貫しているのは 1 つだけです。
  • 次は?

    マルチスレッド プログラムを同期する最も明白な方法は何ですか?もちろん、ミューテックスを使用します。これは次の投稿のトピックです。