1. ホーム
  2. c++

[解決済み] C++のvolatileキーワードはメモリフェンスを導入するか?

2023-05-01 01:04:06

質問

私は、以下のことを理解しています。 volatile は値が変更される可能性があることを通知しますが、この機能を実現するために、コンパイラはメモリフェンスを導入する必要があるのでしょうか?

私の理解では、volatileオブジェクトに対する操作の順序を変更することはできず、保存する必要があります。これは、何らかのメモリ フェンスが必要であり、これを回避する方法は実際にはないことを示唆しているように思われます。私はこれを言って正しいのでしょうか?


に興味深い議論があります。 この関連する質問

Jonathan Wakely さんが書いています。 :

<ブロッククオート

... 別々の揮発性変数へのアクセスは、それらが別々の完全な式で発生する限り、コンパイラによって並べ替えられることはない。 コンパイラによって並べ替えられることはありません。 スレッドセーフのためにvolatileが役に立たないというのは正しいが、彼の言うような理由ではない しかし、彼の言うような理由ではありません。コンパイラがvolatileオブジェクトへのアクセスを並べ替える可能性があるからではなく へのアクセスを並べ替える可能性があるからではなく、CPUが並べ替える可能性があるからです。アトミック アトミック操作とメモリバリアによって、コンパイラとCPUは 並べ替えを防ぐ

に対して デビッド・シュワルツ 返信 コメント中 :

... C++標準の観点からすると、違いはありません。 コンパイラが何かをすることと、コンパイラが命令を出してハードウェアに何かをさせることの間に ハードウェアに何かをさせる命令であることに違いはありません。もしCPUが CPUが揮発性物質にアクセスする順番を変えても、標準はその順番を守ることを要求しない。 その順番を維持する必要はありません。...

... C++の標準では、何が順序を変更するのかについて区別していません。 を区別していません。そして、CPU は観察可能な影響を与えずに並べ替えられるから大丈夫、とは言えません。 C++標準では、順序は観測可能であると定義されています。 C++の標準では、順序は観察可能であると定義されています。コンパイラは、あるプラットフォームでC++規格に準拠している。 コンパイラは、あるプラットフォームでC++規格に準拠しています。 標準に準拠しています。もし、標準が揮発性物質にアクセスする際に順序を変えないことを要求しているならば 標準が揮発性物質へのアクセスが順番を変えないことを要求しているならば、順番を変えるプラットフォームは標準に準拠していない。...

私が言いたいのは、もし C++ 標準がコンパイラが異なる揮発性物質にアクセスすることを禁止しているならば という理論に基づいて、異なる揮発性物質へのアクセスの順序を変更することを禁止しているということです。 そのようなアクセスの順序は、プログラムの観察可能な動作の一部であるという理論に基づいています。 CPUがそうすることを禁止するコードをコンパイラが出力することも要求しています。 を禁止するコードを出すことも要求している。この規格では、コンパイラが何をするか、何をしないかを区別しない。 この規格では、コンパイラーが行うことと、コンパイラーの生成コードが CPU に行わせることを区別していません。

このことは、2 つの疑問を生みます。どちらかが正しいのでしょうか?実際の実装はどうなっているのか?

どのように解決するのか?

を説明するよりも、むしろ volatile が何をするものかを説明するよりも、どのような場合に volatile .

  • シグナルハンドラ内の場合。に書き込むため volatile 変数への書き込みは、標準ではシグナル ハンドラ内から実行できるほぼ唯一のことだからです。C++11 以降では std::atomic を使用できますが、アトミックがロックフリーである場合のみです。
  • を扱う場合 setjmp インテルによると .
  • ハードウェアを直接扱う場合で、コンパイラが読み取りまたは書き込みを最適化しないようにしたい場合。

例えば

volatile int *foo = some_memory_mapped_device;
while (*foo)
    ; // wait until *foo turns false

がなければ volatile 指定がない場合、コンパイラはループを完全に最適化することができます。そのため volatile 指定子はコンパイラに、その後の2回の読み出しが同じ値を返すと仮定してはいけないことを伝えます。

注意点として volatile はスレッドとは関係ないことに注意してください。上の例は、もし別のスレッドが *foo に書き込んでいる別のスレッドがあったとしても、acquire 操作を伴わないため、 上記の例は動作しません。

他のすべてのケースでは volatile の使用は、C++11 以前のコンパイラおよびコンパイラ拡張(msvc の /volatile:ms スイッチなど、X86/I64 でデフォルトで有効になっているもの) を扱う場合を除き、もうコード レビューをパスしないようにすべきです。