web-dev-qa-db-ja.com

C揮発性変数とキャッシュメモリ

キャッシュはキャッシュハードウェアによってプロセッサに対して透過的に制御されるため、Cプログラムで揮発性変数を使用する場合、指定された実際のメモリアドレスから毎回プログラムがデータを読み取ることが保証されますが、キャッシュは保証されません。

私の理解は、

  1. 揮発性キーワードは、変数参照を最適化してはならず、コードでプログラムされているように読み取る必要があることをコンパイラーに指示します。

  2. キャッシュはキャッシュハードウェアによって透過的に制御されるため、プロセッサがアドレスを発行するとき、データがキャッシュからのものかメモリからのものかはわかりません。

それで、必要なたびにメモリアドレスを読み取る必要があるという要件がある場合、それがキャッシュからではなく必要なアドレスから参照されていることをどのように確認できますか?

どういうわけか、これら2つの概念はうまく適合していません。その方法を明確にしてください。

(キャッシュにライトバックポリシーがあると想像してください(問題の分析に必要な場合))

マイクロカーネルに感謝します:)

34
Microkernel

ファームウェア開発者はこちら。これは組み込みプログラミングの標準的な問題であり、多くの(非常に経験豊富な)開発者をつまずかせる問題です。

私の仮定は、ハードウェアレジスタにアクセスしようとしていることであり、そのレジスタ値は時間の経過とともに変化する可能性があります(割り込みステータス、タイマー、GPIO表示など)。

volatileキーワードはソリューションの一部にすぎず、多くの場合、必要ない場合があります。これにより、変数は使用されるたびにmemoryから再読み取りされます(コンパイラーによって最適化されたり、複数の用途にわたってプロセッサーレジスターに格納されたりするのではなく)"memory"読み取られているのは実際のハードウェアレジスタであるのに対して、キャッシュされた場所はコードでは認識されず、volatileキーワードの影響を受けません。関数がレジスタを1回だけ読み取る場合は、おそらくvolatileを省略できますが、一般的なルールとして、ほとんどのハードウェアレジスタをvolatileとして定義することをお勧めします。

より大きな問題は、キャッシングとキャッシュの一貫性です。ここで最も簡単な方法は、レジスタがキャッシュされていないアドレス空間にあることを確認することです。つまり、レジスタにアクセスするたびに、キャッシュメモリではなく実際のハードウェアレジスタを読み書きできることが保証されます。より複雑ですがパフォーマンスが向上する可能性のあるアプローチは、キャ​​ッシュされたアドレス空間を使用し、このような特定の状況に対してコードで手動でキャッシュ更新を強制することです。どちらのアプローチでも、これを実現する方法はアーキテクチャに依存し、質問の範囲を超えています。 MTRR(x86の場合)、MMU、ページテーブルの変更などが含まれる可能性があります。

お役に立てば幸いです。何かを見逃した場合はお知らせください。回答を広げます。

32
Andrew Cottrell

あなたの質問からあなたの側には誤解があります。
Volatileキーワードは、あなたが説明するようにキャッシュとは関係ありません。

キーワードvolatileが変数に指定されている場合、この変数はプログラムの他の部分から予期せず変更される可能性があるため、特定の最適化を行わないようにコンパイラーにヒントを与えます。

ここでの意味は、コンパイラは値を再利用してはいけないということですすでにレジスタにロードされていますが、レジスタの値はメモリに格納された値と同じであることが保証されていないため、再度メモリにアクセスします。

キャッシュメモリに関する残りの部分は、プログラマとは直接関係ありません。

つまり、CPUのキャッシュメモリとRAM=との同期は、まったく別の主題です。

7
Cratylus

私の提案は、仮想メモリマネージャーによって非キャッシュとしてページをマークすることです。
Windowsでは、これは設定 PAGE_NOCACHEVirtualProtect を呼び出す場合。

やや異なる目的のために、 SSE 2命令 には_mm_stream_xyzキャッシュの汚染を防ぐための手順ですが、ここではあなたのケースには当てはまらないと思います。

どちらの場合でも、Cでやりたいことをportableする方法はありません。 OSの機能を使用する必要があります。

7
user541686

Wikipediaには、x86ファミリーのCPUに適用されるMTRR(Memory Type Range Registers) に関するかなり良い記事があります。

要約すると、Pentium Pro Intel(およびAMDのコピー)以降、これらのMTRレジスタは、メモリの範囲に対して非キャッシュ、ライトスルー、ライトコンバイン、ライトプロテクト、またはライトバック属性を設定できました。

Pentium IIIから始まりますが、私が知る限り、64ビットプロセッサでのみ有効で、MTRRを尊重しますが、CPUがメモリの各ページのメモリタイプを設定できるようにするページ属性テーブルによってオーバーライドできます。

私が知っているMTRRの主な用途はグラフィックRAMです。書き込み結合としてマークする方がはるかに効率的です。これにより、キャッシュは書き込みを保存し、すべてのメモリ書き込み順序付けルールを緩和して、グラフィックカードへの非常に高速なバースト書き込みを可能にします。

ただし、目的のために、MTRRまたはPAT設定のいずれかを非キャッシュまたはライトスルーにする必要があります。

2
Zan Lynx

_Uncachedキーワードを使用すると、MQXなどの組み込みOSで役立つ場合があります

#define MEM_READ(addr)       (*((volatile _Uncached unsigned int *)(addr)))
#define MEM_WRITE(addr,data) (*((volatile _Uncached unsigned int *)(addr)) = data)
0
James Zhu

volatileは、CPUとメモリ間のキャッシュを気にすることなく、必要なときにいつでもデータが読み取られるようにします。ただし、キャッシュされたデータではなく、メモリから実際のデータを読み取る必要がある場合は、2つのオプションがあります。

  • 上記のデータがキャッシュされていないボードを作成します。 I/Oデバイスをアドレス指定する場合、これはすでに当てはまる可能性があります。
  • キャッシュをバイパスする特定のCPU命令を使用します。これは、可能性のあるSEUエラーをアクティブにするためにメモリをスクラブする必要がある場合に使用されます。

2番目のオプションの詳細は、OSやCPUによって異なります。

0
mouviciel

あなたが言うように、キャッシュはプログラマーに対して透過的です。システムは、アドレスを介してオブジェクトにアクセスした場合、最後に書き込まれた値が常に表示されることを保証します。廃止された値がキャッシュにある場合に発生する可能性がある「唯一の」ことは、実行時のペナルティです。

0
Jens Gustedt