16

volatileコンパイラは、修飾された変数への読み取り/書き込みを削除したり、並べ替えたりすることはできません。

しかし、修飾されているかどうかに関係なく、他の変数が存在する場合はvolatileどうでしょうか?

シナリオ 1

volatile int a;
volatile int b;

a = 1;
b = 2;
a = 3;
b = 4;

コンパイラは、1 番目と 2 番目、または 3 番目と 4 番目の代入を並べ替えることができますか?

シナリオ 2

volatile int a;
int b, c;

b = 1;
a = 1;
c = b;
a = 3;

同じ質問ですが、コンパイラは最初と 2 番目、または 3 番目と 4 番目の代入を並べ替えることができますか?

4

4 に答える 4

13

C++ 標準は次のように述べています (1.9/6):

抽象マシンの観察可能な動作は、揮発性データへの読み取りと書き込み、およびライブラリ I/O 関数の呼び出しのシーケンスです。

シナリオ 1 では、提案する変更のいずれかによって、揮発性データへの書き込みシーケンスが変更されます。

シナリオ 2 では、提案する変更によってシーケンスが変更されることはありません。したがって、「as-if」ルール (1.9/1) の下で許可されます。

... 準拠する実装は、抽象マシンの観察可能な動作を (のみ) エミュレートするために必要です ...

これが発生したことを伝えるには、マシンコードを調べるか、デバッガーを使用するか、未定義または未指定の動作を引き起こし、その結果が実装でたまたまわかっている必要があります。たとえば、実装では、同時実行スレッドが同じメモリを持つビューについて保証する場合がありますが、それは C++ 標準の範囲外です。そのため、標準では特定のコード変換が許可される場合がありますが、特定の実装では、コードがマルチスレッド プログラムで実行されるかどうかがわからないという理由で、それが除外される可能性があります。

観察可能な動作を使用して並べ替えが行われたかどうかをテストする場合 (たとえば、上記のコードで変数の値を出力するなど)、もちろん標準では許可されません。

于 2010-03-29T00:51:14.160 に答える
4

シナリオ 1 の場合、コンパイラは、あなたが言及した並べ替えを実行するべきではありません。シナリオ 2 の場合、答えは以下によって異なります。

  • bおよび変数が現在の関数の外で見えるかどうかc(非ローカルであるか、アドレスが渡されているかのいずれかによって)
  • volatile誰と話しているか (明らかに、C/C++ における文字列の扱いについて意見の相違があるようです)
  • コンパイラの実装

したがって(私の最初の答えを和らげます)、シナリオ2の特定の動作に依存している場合、特定のプラットフォームでの動作がどのようなものによって決定される移植不可能なコードとして扱う必要があると思います.実装のドキュメントが示す可能性があります (ドキュメントがそれについて何も述べていない場合は、動作が保証されていないことになります。

C99 5.1.2.3/2 から「プログラムの実行」:

揮発性オブジェクトへのアクセス、オブジェクトの変更、ファイルの変更、またはこれらの操作のいずれかを行う関数の呼び出しはすべて、実行環境の状態の変化である副作用です。式の評価により、副作用が生じる場合があります。シーケンスポイントと呼ばれる実行シーケンスの特定のポイントでは、以前の評価のすべての副作用が完了し、後続の評価の副作用は発生していません。

...

(パラグラフ 5) 適合する実装に関する最小要件は次のとおりです。

  • シーケンス ポイントでは、以前のアクセスが完了し、後続のアクセスがまだ発生していないという意味で、揮発性オブジェクトは安定しています。

volatileHerb Sutter がC/C++ で必要なアクセスの動作について述べていることの一部を次に示します (「 volatilevs. volatilehttp://www.ddj.com/hpc-high-performance-computing/212701484から) :

近くの通常の読み取りと書き込みはどうですか?最適化できない読み取りと書き込みの周りでそれらを並べ替えることができますか? 現在、C/C++ コンパイラの実装はさまざまであり、すぐに収束する可能性は低いため、実用的な移植可能な答えはありません。たとえば、C++ 標準の 1 つの解釈では、通常の読み取りは C/C++ の揮発性の読み取りまたは書き込みを介してどちらの方向にも自由に移動できますが、通常の書き込みは C/C++ の揮発性の読み取りまたは書き込みを介してまったく移動できないとされています。 C/C++ volatile は、順序付けられたアトミックよりも制限が少なく、制限が厳しくなります。一部のコンパイラ ベンダーはその解釈をサポートしています。揮発性の読み取りまたは書き込みをまったく最適化しないものもあります。さらに、独自の優先セマンティクスを持っているものもあります。

そして、価値のあるものとして、Microsoft は C/C++volatileキーワードについて次のことを文書化しています (Microsoft-sepcific として):

  • 揮発性オブジェクトへの書き込み (揮発性書き込み) にはリリース セマンティクスがあります。命令シーケンスで揮発性オブジェクトへの書き込みの前に発生するグローバルまたは静的オブジェクトへの参照は、コンパイルされたバイナリでの揮発性書き込みの前に発生します。

  • 揮発性オブジェクトの読み取り (揮発性読み取り) には、取得セマンティクスがあります。命令シーケンスで揮発性メモリの読み取り後に発生するグローバルまたは静的オブジェクトへの参照は、コンパイルされたバイナリでの揮発性読み取りの後に発生します。

これにより、揮発性オブジェクトをマルチスレッド アプリケーションでのメモリのロックと解放に使用できます。

于 2010-03-29T00:44:09.030 に答える
2

揮発性はメモリ フェンスではありません。スニペット #2 の B と C への代入は、削除するか、いつでも実行できます。#2 の宣言が #1 の動作を引き起こすのはなぜですか?

于 2010-03-29T00:45:09.870 に答える
0

一部のコンパイラは、volatile 修飾されたオブジェクトへのアクセスをメモリ フェンスと見なします。他の人はしません。一部のプログラムはvolatile、フェンスとして機能することを要求するように作成されています。他の人はそうではありません。

フェンスを必要とするように作成され、フェンスを提供するプラットフォームで実行されるコードは、フェンスを必要としないように作成され、フェンスを提供しないプラットフォームで実行されるコードよりも適切に実行される可能性がありますが、フェンスが提供されていない場合、フェンスを必要とするコードは誤動作します。 . フェンスを必要としないコードは、フェンスを提供するプラットフォームでは、フェンスを必要とするコードよりも遅く実行されることが多く、フェンスを提供する実装は、そうでないコードよりもそのようなコードの実行が遅くなります。

適切なアプローチは、メモリ フェンスを意味するシステムではマクロを何も展開しないように定義すること、またはメモリ フェンスを意味semi_volatileしないシステムではマクロを展開しないように定義することです。他の変数に対して順序付けされたアクセスが必要な変数が として修飾され、そのマクロが正しく定義されている場合、メモリ フェンスの有無にかかわらずシステムで信頼性の高い操作が実現され、最も効率的な操作が実現されます。フェンスを備えたシステムで達成されます。コンパイラが必要に応じて機能する修飾子を実際に実装している場合、その修飾子を使用してさらに優れたコードを実現するマクロとして定義できます。volatilevolatilevolatilesemi-volatilesemivolatile

関連する概念は多くのプラットフォームに適用可能であり、フェンスが意味をなさないプラットフォームは単にそれらを無視できるため、これは標準が本当に取り組むべき領域です。

于 2016-07-08T00:22:19.443 に答える