なぜ揮発性があるのですか?


222

volatileキーワードは何をしますか?C ++では、どのような問題を解決しますか?

私の場合、私は故意にそれを必要としたことはありません。


シングルトンパターンに関するvolatileについての興味深い議論を次に示します。aristeia.com
Papers

3
揮発性キーワードに大きく依存する可能性のある競合状態をコンパイラーが検出できるようにする興味深い手法があります。http://www.ddj.com/cpp/184403766でそれについて読むことができます
Neno Ganchev 2008

これは、いつvolatileでも効果的に使用できる例を示したすばらしいリソースであり、かなり平易な言葉でまとめられています。リンク:publications.gbdirect.co.uk/c_book/chapter8/...
Coderの最適化された

私はそれをロックフリーコード/ダブルチェックロックに使用します
ポール

私にとっては、キーワードvolatileよりも便利ですfriend
acegs 2018

回答:


268

volatile たとえば、完全に別のプロセス/デバイス/書き込み可能なものからメモリ内のスポットを読み取る場合に必要です。

以前はストレートCのマルチプロセッサシステムでデュアルポートRAMを使用していました。セマフォとしてハードウェア管理の16ビット値を使用して、相手がいつ終了したかを知りました。基本的にこれを行いました:

void waitForSemaphore()
{
   volatile uint16_t* semPtr = WELL_KNOWN_SEM_ADDR;/*well known address to my semaphore*/
   while ((*semPtr) != IS_OK_FOR_ME_TO_PROCEED);
}

なしvolatileでは、オプティマイザはループを役に立たないと見なし(男は決して値を設定しません!彼は気が狂って、そのコードを削除します!)、私のコードはセマフォを取得せずに続行し、後で問題を引き起こします。


この場合、uint16_t* volatile semPtr代わりにが書き込まれた場合はどうなりますか?これはポインタを(指された値ではなく)揮発性としてマークする必要があるため、ポインタ自体のチェックはsemPtr == SOME_ADDR最適化されない可能性があります。ただし、これはまた、不安定な先の尖った値を意味します。番号?
Zyl 2014

@Zylいいえ、ありません。実際には、あなたが提案することが起こりそうなことです。しかし、理論的には、値へのアクセスを最適化するコンパイラーで終わる可能性があります。これは、これらの値が変更されることはないと判断したためです。そして、ポインタではなく値に適用する揮発性を意味する場合、あなたはうんざりするでしょう。繰り返しになりますが、可能性は低いですが、正しく機能するように誤りを犯す方が、今日の偶然の動作を利用するよりも優れています。
iheanyi

1
@Doug T. Aより良い説明があり、この
machineaddict

3
@curiousguyそれは間違って決定しませんでした。それは与えられた情報に基づいて正しい控除を行いました。何かを揮発性としてマークしなかった場合、コンパイラーはそれを揮発性ではないと見なすことができます。これは、コンパイラがコードを最適化するときに行うことです。より多くの情報がある場合、つまり、そのデータが実際に揮発性である場合、その情報を提供するのはプログラマの責任です。バグのあるコンパイラによってあなたが主張しているのは、本当に悪いプログラミングです。
iheanyi

1
@curiousguyいいえ、揮発性キーワードが一度表示されたからといって、すべてが突然揮発性になるとは限りません。コンパイラーが正しいことを行い、プログラマーが誤って予期していることに反する結果を達成するシナリオを示しました。「最も厄介な解析」がコンパイラエラーの兆候ではないように、ここでもそうではありません。
iheanyi

82

volatileメモリマップされたハードウェアデバイスの読み取りまたは書き込みを行う必要がある組み込みシステムまたはデバイスドライバーを開発する場合に必要です。特定のデバイスレジスタの内容はいつでも変更される可能性があるvolatileため、このようなアクセスがコンパイラによって最適化されないようにするには、キーワードが必要です。


9
これは、組み込みシステムだけでなく、すべてのデバイスドライバー開発にも当てはまります。
MladenJanković

同じアドレスを2回読み取る8ビットISAバスで私がこれを必要とした唯一のとき-コンパイラーにバグがあり、それを無視しました(初期のZortech c ++)
Martin Beckett

揮発性が外部デバイスの制御に適切であることは非常にまれです。そのセマンティクスは、現代のMMIOにとっては間違っています。あまりに多くのオブジェクトを揮発性にする必要があり、最適化が損なわれます。しかし、最新のMMIOは、フラグが設定されるまで通常のメモリのように動作するため、揮発性は必要ありません。多くのドライバは揮発性を使用しません。
curiousguy

69

一部のプロセッサーには、64ビットを超える精度の浮動小数点レジスターがあります(例えば、SSEなしの32ビットx86、Peterのコメントを参照)。こうすることで、倍精度の数値に対して複数の演算を実行した場合、各中間結果を64ビットに切り捨てた場合よりも、実際には高精度の答えが得られます。

これは通常素晴らしいことですが、コンパイラーがレジスターを割り当て、最適化を行った方法に応じて、まったく同じ入力に対するまったく同じ操作で異なる結果が得られることを意味します。一貫性が必要な場合は、volatileキーワードを使用して、各操作を強制的にメモリに戻すことができます。

また、Kahan加算など、代数的には意味を持たないが浮動小数点エラーを削減する一部のアルゴリズムにも役立ちます。代数的にそれはnopなので、いくつかの中間変数が揮発性でない限り、それはしばしば誤って最適化されます。


5
数値導関数を計算する場合にも役立ちます。x+ h-x == hを確認するには、適切なデルタを計算できるように、hh = x + h-xを揮発性として定義します。
Alexandre C.

5
+1、確かに私の経験では、浮動小数点演算がデバッグとリリースで異なる結果を生成する場合があったため、ある構成用に記述された単体テストが別の構成で失敗しました。1つの浮動小数点変数をのvolatile double代わりに宣言することで解決しました。これによりdouble、FPU精度から64ビット(RAM)精度に切り捨てられてから、さらに計算が続行されます。浮動小数点誤差がさらに誇張されたため、結果は大幅に異なりました。
Serge Rogatch、2015

「モダン」の定義は少しずれています。SSE / SSE2を回避する32ビットx86コードのみがこの影響を受け、10年前でも「モダン」ではありませんでした。MIPS / ARM / POWERはすべて64ビットのハードウェアレジスタを備えており、SSE2を搭載したx86も備えています。C ++の実装x86-64は常にSSE2を使用し、コンパイラーはg++ -mfpmath=sseそれを32ビットx86にも使用するようなオプションがあります。を使用gcc -ffloat-storeして、x87 を使用している場合でも四捨五入を強制できます。または、x87の精度を53ビットの仮数に設定できます:randomascii.wordpress.com/2012/03/21/…
Peter Cordes

しかし、それでも良い答えです。廃止されたx87 code-genの場合、使用volatileして、すべての場所の利点を失うことなく、いくつかの特定の場所で丸めを強制できます。
Peter Cordes

1
または、不正確と不整合を混同しますか?
チップスター

49

「約束のように揮発性」ダン・サックスの記事:

(...)揮発性オブジェクトとは、値が自然に変化する可能性があるオブジェクトです。つまり、オブジェクトを揮発性であると宣言すると、プログラム内のステートメントがオブジェクトを変更していないように見えても、オブジェクトが状態を変更する可能性があることをコンパイラーに伝えています。」

volatileキーワードに関する彼の3つの記事へのリンクは次のとおりです。


23

ロックフリーのデータ構造を実装する場合は、volatileを使用する必要があります。それ以外の場合、コンパイラは変数へのアクセスを自由に最適化できます。これにより、セマンティクスが変更されます。

言い換えると、volatileは、この変数へのアクセスが物理メモリの読み取り/書き込み操作に対応している必要があることをコンパイラに通知します。

たとえば、これはWin32 APIでInterlockedIncrementを宣言する方法です。

LONG __cdecl InterlockedIncrement(
  __inout  LONG volatile *Addend
);

InterlockedIncrementを使用できるようにするために、変数をvolatileと宣言する必要はまったくありません。
curiousguy

この答えはC ++ 11で廃止std::atomic<LONG>されたため、純粋なロード/純粋なストアを最適化したり、並べ替えたりするなどの問題を発生させることなく、ロックレスコードをより安全に記述できます。
Peter Cordes

10

1990年代初頭に私が使用していた大きなアプリケーションには、setjmpとlongjmpを使用したCベースの例外処理が含まれていました。"catch"句として機能するコードのブロックに値を保存する必要がある変数では、volatileキーワードが必要でした。これらの変数がレジスターに格納され、longjmpによって消去されないようにするためです。


10

標準Cでは、使用する場所の1つはvolatileシグナルハンドラーです。実際、標準Cでは、シグナルハンドラーで安全に実行できることは、volatile sig_atomic_t変数を変更するか、すばやく終了することです。実際、私の知る限り、これは、標準Cで唯一の場所であり、volatile未定義の動作を回避するために使用する必要があります。

ISO / IEC 9899:2011§7.14.1.1 signal機能

¶5 abortまたはraise関数の呼び出しの結果として以外にシグナルが発生した場合、値を割り当てる以外の方法で、ロックされていないアトミックオブジェクトではない静的またはスレッドストレージ期間のオブジェクトをシグナルハンドラが参照する場合の動作は未定義です。として宣言されたオブジェクトに対してvolatile sig_atomic_t、またはシグナルハンドラが、abort関数、_Exit関数、 quick_exit関数、またはsignal関数の呼び出しを引き起こしたシグナルに対応するシグナル番号に等しい最初の引数を持つ関数以外の標準ライブラリ内の関数を呼び出すハンドラ。さらに、そのようなsignal関数の呼び出しがSIG_ERRを返す場合、の値errnoは不確定です。252)

252)非同期シグナルハンドラーによってシグナルが生成された場合の動作は未定義です。

つまり、標準Cでは次のように記述できます。

static volatile sig_atomic_t sig_num = 0;

static void sig_handler(int signum)
{
    signal(signum, sig_handler);
    sig_num = signum;
}

他にはあまりありません。

POSIXは、シグナルハンドラーで実行できることについてはるかに寛容ですが、それでも制限があります(そして、制限の1つは、標準I / Oライブラリ(printf()他)を安全に使用できないことです)。


7

組み込み向けに開発していて、割り込みハンドラーで変更できる変数をチェックするループがあります。「揮発性」がないと、ループは何もしない状態になります。コンパイラーが認識できる限り、変数は変更されないため、チェックは最適化されます。

同じことが、より伝統的な環境の別のスレッドで変更される可能性のある変数にも当てはまりますが、同期呼び出しが頻繁に行われるため、コンパイラーは最適化にそれほど自由ではありません。


7

コードをステップ実行するときに表示できるようにしたい変数をコンパイラーが最適化することを主張する場合、デバッグビルドでそれを使用しました。


7

意図したとおりに使用することに加えて、揮発性は(テンプレート)メタプログラミングで使用されます。揮発性属性(constなど)が過負荷の解決に関与するため、偶発的な過負荷を防止するために使用できます。

template <typename T> 
class Foo {
  std::enable_if_t<sizeof(T)==4, void> f(T& t) 
  { std::cout << 1 << t; }
  void f(T volatile& t) 
  { std::cout << 2 << const_cast<T&>(t); }

  void bar() { T t; f(t); }
};

これは合法です。両方のオーバーロードは呼び出し可能であり、ほとんど同じです。とにかくvolatileバーが非揮発性を渡さないことがわかっているので、オーバーロードのキャストは合法Tです。volatile非揮発性の場合はそのオーバーロードの解決に選ばれたことがない、けれどもバージョンは、厳密に悪化していますf可能です。

コードが実際にvolatileメモリアクセスに依存することはありません。


例を挙げてこれについて詳しく説明してもらえますか?それは本当に私がよく理解するのに役立ちます。ありがとう!
batbrat 2018

volatileオーバーロードのキャスト」キャストは明示的な変換です。これは構文構造です。多くの人々がその混乱を引き起こしています(標準の作者でさえ)。
curiousguy

6
  1. スピンロックといくつか(すべて?)のロックフリーデータ構造を実装するために使用する必要があります。
  2. アトミック操作/命令で使用する
  3. コンパイラーのバグ(最適化中に誤って生成されたコード)を克服するために一度助けてくれました

5
ライブラリ、コンパイラ組み込み関数、またはインラインアセンブリコードを使用する方がよいでしょう。揮発性は信頼できません。
Zan Lynxは

1
1と2はどちらもアトミック操作を使用しますが、volatileはアトミックセマンティクスを提供せず、atomicのプラットフォーム固有の実装はvolatileを使用する必要性に取って代わるため、1と2については、これらにvolatileは必要ありません。

揮発性の原子的セマンティクスを提供することについて何か言う人はいますか?私は、揮発性のWITHアトミック操作を使用する必要があると言いましたが、それがwin32 APIのインターロックされた操作の宣言を真に見ていないと思われる場合(この男も彼の回答でこれを説明しました)
MladenJanković11年

4

volatileキーワードは、コンパイラによって決定することができない方法で変更することができ、オブジェクト上の任意の最適化を適用するから、コンパイラを防ぐためのものです。

として宣言されたオブジェクトはvolatile、現在のコードのスコープ外のコードでいつでも値を変更できるため、最適化から除外されます。システムvolatileは、以前の命令が同じオブジェクトからの値を要求した場合でも、要求された時点でその値を一時レジスターに保持するのではなく、常にメモリー位置からオブジェクトの現在の値を読み取ります。

以下のケースを検討してください

1)スコープ外の割り込みサービスルーチンによって変更されたグローバル変数。

2)マルチスレッドアプリケーション内のグローバル変数。

volatile修飾子を使用しない場合、次の問題が発生する可能性があります

1)最適化をオンにすると、コードが期待どおりに機能しない場合があります。

2)割り込みを有効にして使用すると、コードが期待どおりに動作しない場合があります。

揮発性:プログラマーの親友

https://en.wikipedia.org/wiki/Volatile_(computer_programming)


投稿したリンクは非常に古く、現在のベストプラクティスを反映していません。
Tim Seguine、2016年

2

揮発性キーワードは、変数へのアクセスを最適化しないようにコンパイラーに指示するために使用されるという事実(スレッドまたは割り込みルーチンによって変更される可能性がある)に加えて、いくつかのコンパイラーのバグを削除するために使用できます- はい、できますなる ---。

たとえば、組み込みプラットフォームで作業していたのは、コンパイラーが変数の値に関して誤った仮定を行っていたためです。コードが最適化されていない場合、プログラムは正常に実行されます。最適化(これは重要なルーチンだったために本当に必要でした)を使用すると、コードは正しく機能しません。(あまり正確ではありませんが)唯一の解決策は、「障害のある」変数を揮発性として宣言することでした。


3
コンパイラが揮発性へのアクセスを最適化しないという考えは誤りです。標準は最適化について何も知りません。コンパイラーは、標準で規定されている内容を尊重する必要がありますが、通常の動作を妨げない最適化を自由に実行できます。
ターミナル

3
私の経験から、gccアームのすべての最適化「バグ」の99.9%は、プログラマー側のエラーです。これがこの回答に当てはまるかどうかはわかりません。一般的なトピックについての
発言

@Terminus「コンパイラが揮発性へのアクセスを最適化しないという考えは間違った仮定です」ソース?
curiousguy

2

あなたのプログラムはvolatileキーワードがなくても動作するようです?おそらくこれが理由です:

前述のように、volatileキーワードは次のような場合に役立ちます

volatile int* p = ...;  // point to some memory
while( *p!=0 ) {}  // loop until the memory becomes zero

ただし、外部関数または非インライン関数が呼び出されると、ほとんど効果がないようです。例えば:

while( *p!=0 ) { g(); }

次に、volatileほとんど同じ結果の有無にかかわらず、結果が生成されます。

g()を完全にインライン化できる限り、コンパイラーは進行中のすべてを確認できるため、最適化できます。しかし、プログラムがコンパイラーが何が行われているのかを認識できない場所を呼び出す場合、コンパイラーが想定をこれ以上行うことは安全ではありません。したがって、コンパイラは常にメモリから直接読み取るコードを生成します。

ただし、(明示的な変更またはコンパイラー/リンカーの巧妙さが原因で)関数g()がインラインになると、その日の注意が必要volatileです。キーワードを忘れた場合、コードが壊れる可能性があります。

したがってvolatile、プログラムが機能していないように見えても、キーワードを追加することをお勧めします。これにより、将来の変更に関して意図がより明確で堅牢になります。


関数は、アウトライン関数への参照(リンク時に解決)を生成しながらコードをインライン化できることに注意してください。これは、部分的にインライン化された再帰関数の場合です。関数は、コンパイラによってセマンティックを「インライン化」することもできます。つまり、コンパイラは副作用と結果が、ソースコードに従って可能な副作用と結果の範囲内であると想定し、まだインライン化しません。これは、エンティティのすべての定義が実質的に等価でなければならないことを示す「有効な1つの定義ルール」に基づいています(完全に同一ではない場合)。
curiousguy

使用することにより可能です(グローバルな最適化をしても、リンク時に)その本体は、コンパイラによって表示されている機能で(そのセマンティックのか「インライン化」)呼び出しのインライン化移植性の回避volatile修飾された関数ポインタを:void (* volatile fun_ptr)() = fun; fun_ptr();
curiousguy

2

Cの初期の頃は、コンパイラーはlvalueの読み取りと書き込みを行うすべてのアクションをメモリー操作として解釈し、コードに表示された読み取りと書き込みと同じ順序で実行されていました。多くの場合、コンパイラーに操作の順序を変更して統合する一定の自由度が与えられれば、効率は大幅に向上しますが、これには問題がありました。それらを指定する必要があったためであっても操作は、多くの場合、単に特定の順序で指定されたいくつかのために、したがって、プログラマは必ずしもそうではありませんでした多くの等しく良い選択肢のいずれかを、選びました。特定のシーケンスで特定の操作を実行することが重要な場合があります。

シーケンス処理のどの詳細が重要であるかは、ターゲットプラットフォームとアプリケーション分野によって異なります。標準は、特に詳細な制御を提供するのではなく、単純なモデルを選択しました。修飾されていない左辺値を使用して一連のアクセスが行われたvolatile場合、コンパイラーは、適切と思われるようにそれらを並べ替えて統合します。volatile-qualified lvalueを使用してアクションを実行する場合、品質の実装では、非標準の構文を使用する必要なく、目的のプラットフォームとアプリケーションフィールドをターゲットとするコードで必要となる可能性がある追加の順序保証を提供する必要があります。

残念ながら、多くのコンパイラーは、プログラマーが必要とする保証を特定するのではなく、標準によって義務付けられた最低限の保証を提供することを選択しました。これはvolatile、本来あるべきよりもずっと役に立たなくなります。たとえば、gccまたはclangでは、基本的な「ハンドオフミューテックス」を実装する必要があるプログラマー(ミューテックスを取得して解放したタスクは、他のタスクが完了するまで再度実行しない)は、必ず実行する必要があります。 4つのことの:

  1. mutexの獲得と解放を、コンパイラーがインライン化できず、プログラム全体の最適化を適用できない関数に置きます。

  2. mutexによって保護されているすべてのオブジェクトをvolatile--mutexを取得してから解放する前にすべてのアクセスが発生する場合は必要のないものとして修飾します。

  3. 修飾されていないすべてのオブジェクトがあるかのようにコードを生成するコンパイラを強制するために最適化レベル0を使用しregisterますvolatile

  4. gcc固有のディレクティブを使用します。

対照的に、iccなどのシステムプログラミングにより適した高品質のコンパイラを使用する場合は、別のオプションがあります。

  1. volatile取得または解放が必要なすべての場所で-qualified書き込みが実行されることを確認してください。

基本的な「ハンドオフミューテックス」を取得するには、volatile読み取りが必要です(準備ができているかどうかを確認するため)。volatile書き込みも必要ありません(反対側は、返されるまで再取得を試みません)。意味のないvolatile書き込みを実行することは、gccまたはclangで使用可能なオプションよりも優れています。


1

覚えておきたい使用法の1つは、シグナルハンドラー関数で、グローバル変数にアクセス/変更する場合(たとえば、exit = trueとマークする)、その変数を 'volatile'として宣言する必要があることです。


1

すべての答えは素晴らしいです。しかしその上で、私は例を共有したいと思います。

以下は、小さなcppプログラムです。

#include <iostream>

int x;

int main(){
    char buf[50];
    x = 8;

    if(x == 8)
        printf("x is 8\n");
    else
        sprintf(buf, "x is not 8\n");

    x=1000;
    while(x > 5)
        x--;
    return 0;
}

ここで、上記のコードのアセンブリを生成します(ここでは、アセンブリの該当部分のみを貼り付けます)。

アセンブリを生成するコマンド:

g++ -S -O3 -c -fverbose-asm -Wa,-adhln assembly.cpp

そしてアセンブリ:

main:
.LFB1594:
    subq    $40, %rsp    #,
    .seh_stackalloc 40
    .seh_endprologue
 # assembly.cpp:5: int main(){
    call    __main   #
 # assembly.cpp:10:         printf("x is 8\n");
    leaq    .LC0(%rip), %rcx     #,
 # assembly.cpp:7:     x = 8;
    movl    $8, x(%rip)  #, x
 # assembly.cpp:10:         printf("x is 8\n");
    call    _ZL6printfPKcz.constprop.0   #
 # assembly.cpp:18: }
    xorl    %eax, %eax   #
    movl    $5, x(%rip)  #, x
    addq    $40, %rsp    #,
    ret 
    .seh_endproc
    .p2align 4,,15
    .def    _GLOBAL__sub_I_x;   .scl    3;  .type   32; .endef
    .seh_proc   _GLOBAL__sub_I_x

sprintfコンパイラーxはプログラムの外部で変更されないと想定しているため、アセンブリーでコードが生成されなかったことがアセンブリーでわかります。whileループについても同様です。whileループは最適化により完全に削除されました。コンパイラーがループを役に立たないコードと見なし、直接割り当てられた5ためですx(を参照movl $5, x(%rip))。

問題は、外部プロセス/ハードウェアがとのx間のどこかの値を変更する場合に発生します。ブロックが機能することを期待しますが、残念ながらコンパイラはその部分を削除しました。x = 8;if(x == 8)else

これを解決するために、で、生成されたアセンブリコードにassembly.cpp変更int x;してvolatile int x;、すぐに見てみましょう。

main:
.LFB1594:
    subq    $104, %rsp   #,
    .seh_stackalloc 104
    .seh_endprologue
 # assembly.cpp:5: int main(){
    call    __main   #
 # assembly.cpp:7:     x = 8;
    movl    $8, x(%rip)  #, x
 # assembly.cpp:9:     if(x == 8)
    movl    x(%rip), %eax    # x, x.1_1
 # assembly.cpp:9:     if(x == 8)
    cmpl    $8, %eax     #, x.1_1
    je  .L11     #,
 # assembly.cpp:12:         sprintf(buf, "x is not 8\n");
    leaq    32(%rsp), %rcx   #, tmp93
    leaq    .LC0(%rip), %rdx     #,
    call    _ZL7sprintfPcPKcz.constprop.0    #
.L7:
 # assembly.cpp:14:     x=1000;
    movl    $1000, x(%rip)   #, x
 # assembly.cpp:15:     while(x > 5)
    movl    x(%rip), %eax    # x, x.3_15
    cmpl    $5, %eax     #, x.3_15
    jle .L8  #,
    .p2align 4,,10
.L9:
 # assembly.cpp:16:         x--;
    movl    x(%rip), %eax    # x, x.4_3
    subl    $1, %eax     #, _4
    movl    %eax, x(%rip)    # _4, x
 # assembly.cpp:15:     while(x > 5)
    movl    x(%rip), %eax    # x, x.3_2
    cmpl    $5, %eax     #, x.3_2
    jg  .L9  #,
.L8:
 # assembly.cpp:18: }
    xorl    %eax, %eax   #
    addq    $104, %rsp   #,
    ret 
.L11:
 # assembly.cpp:10:         printf("x is 8\n");
    leaq    .LC1(%rip), %rcx     #,
    call    _ZL6printfPKcz.constprop.1   #
    jmp .L7  #
    .seh_endproc
    .p2align 4,,15
    .def    _GLOBAL__sub_I_x;   .scl    3;  .type   32; .endef
    .seh_proc   _GLOBAL__sub_I_x

ここではsprintfprintfwhileループのアセンブリコードが生成されたことがわかります。利点は、x変数が外部プログラムまたはハードウェアによって変更された場合sprintf、コードの一部が実行されることです。そして、同様にwhileループは今忙しい待機のために使用することができます。


0

他の回答では、次の目的でいくつかの最適化を回避することについてすでに言及しています:

  • メモリマップレジスタ(または "MMIO")を使用する
  • デバイスドライバーを書く
  • プログラムのデバッグを容易にする
  • 浮動小数点計算をより確定的にする

揮発性は、値が外部から来て予測不可能であり、既知の値に基づくコンパイラの最適化を回避する必要がある場合、および結果が実際には使用されていないが計算する必要がある場合、または使用されている場合に不可欠です。ベンチマークのために数回計算し、正確なポイントで開始および終了する計算が必要です。

揮発性読み取りは入力操作(のようなscanfまたはその使用cin)に似ています。値はプログラムの外部から取得されているように見えるため、値に依存する計算は、その後に開始する必要があります

揮発性書き込みは、出力操作(のようなprintfまたはその使用cout)に似ています。値はプログラムの外部で通信されるように見えるため、値が計算に依存する場合は、の前に終了する必要があります

したがって、1組の揮発性読み取り/書き込みを使用してベンチマークを制御し、時間測定を意味のあるものにすることができます

volatileがなければ、時間測定などの関数を使用した計算の並べ替えを妨げるものがないため、以前にコンパイラーによって計算を開始できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.