キャッシュはキャッシュハードウェアによってプロセッサに対して透過的に制御されるため、Cプログラムで揮発性変数を使用する場合、指定された実際のメモリアドレスから毎回プログラムがデータを読み取ることが保証されますが、キャッシュは保証されません。
私の理解は、
揮発性キーワードは、変数参照を最適化してはならず、コードでプログラムされているように読み取る必要があることをコンパイラーに指示します。
キャッシュはキャッシュハードウェアによって透過的に制御されるため、プロセッサがアドレスを発行するとき、データがキャッシュからのものかメモリからのものかはわかりません。
それで、必要なたびにメモリアドレスを読み取る必要があるという要件がある場合、それがキャッシュからではなく必要なアドレスから参照されていることをどのように確認できますか?
どういうわけか、これら2つの概念はうまく適合していません。その方法を明確にしてください。
(キャッシュにライトバックポリシーがあると想像してください(問題の分析に必要な場合))
マイクロカーネルに感謝します:)
ファームウェア開発者はこちら。これは組み込みプログラミングの標準的な問題であり、多くの(非常に経験豊富な)開発者をつまずかせる問題です。
私の仮定は、ハードウェアレジスタにアクセスしようとしていることであり、そのレジスタ値は時間の経過とともに変化する可能性があります(割り込みステータス、タイマー、GPIO表示など)。
volatile
キーワードはソリューションの一部にすぎず、多くの場合、必要ない場合があります。これにより、変数は使用されるたびにmemoryから再読み取りされます(コンパイラーによって最適化されたり、複数の用途にわたってプロセッサーレジスターに格納されたりするのではなく)"memory"読み取られているのは実際のハードウェアレジスタであるのに対して、キャッシュされた場所はコードでは認識されず、volatile
キーワードの影響を受けません。関数がレジスタを1回だけ読み取る場合は、おそらくvolatile
を省略できますが、一般的なルールとして、ほとんどのハードウェアレジスタをvolatile
として定義することをお勧めします。
より大きな問題は、キャッシングとキャッシュの一貫性です。ここで最も簡単な方法は、レジスタがキャッシュされていないアドレス空間にあることを確認することです。つまり、レジスタにアクセスするたびに、キャッシュメモリではなく実際のハードウェアレジスタを読み書きできることが保証されます。より複雑ですがパフォーマンスが向上する可能性のあるアプローチは、キャッシュされたアドレス空間を使用し、このような特定の状況に対してコードで手動でキャッシュ更新を強制することです。どちらのアプローチでも、これを実現する方法はアーキテクチャに依存し、質問の範囲を超えています。 MTRR(x86の場合)、MMU、ページテーブルの変更などが含まれる可能性があります。
お役に立てば幸いです。何かを見逃した場合はお知らせください。回答を広げます。
あなたの質問からあなたの側には誤解があります。Volatile
キーワードは、あなたが説明するようにキャッシュとは関係ありません。
キーワードvolatile
が変数に指定されている場合、この変数はプログラムの他の部分から予期せず変更される可能性があるため、特定の最適化を行わないようにコンパイラーにヒントを与えます。
ここでの意味は、コンパイラは値を再利用してはいけないということですすでにレジスタにロードされていますが、レジスタの値はメモリに格納された値と同じであることが保証されていないため、再度メモリにアクセスします。
キャッシュメモリに関する残りの部分は、プログラマとは直接関係ありません。
つまり、CPUのキャッシュメモリとRAM=との同期は、まったく別の主題です。
私の提案は、仮想メモリマネージャーによって非キャッシュとしてページをマークすることです。
Windowsでは、これは設定 PAGE_NOCACHE
VirtualProtect
を呼び出す場合。
やや異なる目的のために、 SSE 2命令 には_mm_stream_xyz
キャッシュの汚染を防ぐための手順ですが、ここではあなたのケースには当てはまらないと思います。
どちらの場合でも、Cでやりたいことをportableする方法はありません。 OSの機能を使用する必要があります。
Wikipediaには、x86ファミリーのCPUに適用されるMTRR(Memory Type Range Registers) に関するかなり良い記事があります。
要約すると、Pentium Pro Intel(およびAMDのコピー)以降、これらのMTRレジスタは、メモリの範囲に対して非キャッシュ、ライトスルー、ライトコンバイン、ライトプロテクト、またはライトバック属性を設定できました。
Pentium IIIから始まりますが、私が知る限り、64ビットプロセッサでのみ有効で、MTRRを尊重しますが、CPUがメモリの各ページのメモリタイプを設定できるようにするページ属性テーブルによってオーバーライドできます。
私が知っているMTRRの主な用途はグラフィックRAMです。書き込み結合としてマークする方がはるかに効率的です。これにより、キャッシュは書き込みを保存し、すべてのメモリ書き込み順序付けルールを緩和して、グラフィックカードへの非常に高速なバースト書き込みを可能にします。
ただし、目的のために、MTRRまたはPAT設定のいずれかを非キャッシュまたはライトスルーにする必要があります。
_Uncachedキーワードを使用すると、MQXなどの組み込みOSで役立つ場合があります
#define MEM_READ(addr) (*((volatile _Uncached unsigned int *)(addr)))
#define MEM_WRITE(addr,data) (*((volatile _Uncached unsigned int *)(addr)) = data)
volatile
は、CPUとメモリ間のキャッシュを気にすることなく、必要なときにいつでもデータが読み取られるようにします。ただし、キャッシュされたデータではなく、メモリから実際のデータを読み取る必要がある場合は、2つのオプションがあります。
2番目のオプションの詳細は、OSやCPUによって異なります。
あなたが言うように、キャッシュはプログラマーに対して透過的です。システムは、アドレスを介してオブジェクトにアクセスした場合、最後に書き込まれた値が常に表示されることを保証します。廃止された値がキャッシュにある場合に発生する可能性がある「唯一の」ことは、実行時のペナルティです。