web-dev-qa-db-ja.com

C ++キャッシュ対応プログラミング

c ++でCPUのキャッシュサイズを決定する方法はありますか?私は大量のデータを処理するアルゴリズムを持っているので、このデータをキャッシュに収まるようにチャンクに分割したいと思います。これは可能ですか?キャッシュサイズを考慮したプログラミングに関する他のヒントを教えてもらえますか(特にマルチスレッド/マルチコアのデータ処理に関して)。

ありがとう!

56
Mat

すべてのプログラマーがメモリについて知っておくべきこと 」によると、Ulrich DrepperによるLinuxでは次のことができます。

メモリ要件の式が得られたら、それをキャッシュサイズと比較できます。前述のように、キャッシュは他の複数のコアと共有される場合があります。現在{間違いなく近いうちにより良い方法があるでしょう!}ハードコーディングの知識なしで正しい情報を得る唯一の方法は/ sysファイルシステムを通してです。表5.2では、ハードウェアに関してカーネルが公開しているものを見てきました。プログラムはディレクトリを見つけなければなりません:

/sys/devices/system/cpu/cpu*/cache

これは セクション6:プログラマーにできること にリストされています。

彼はまた、図6.5のすぐ下に、OSから取得できない場合にL1Dキャッシュサイズを決定するために使用できる短いテストについて説明しています。

彼の論文で私が出会ったもう1つのことがあります。sysconf(_SC_LEVEL2_CACHE_SIZE)はLinuxのシステムコールであり、十分に文書化されていないように見えますが、L2キャッシュサイズを返すことになっています。

15

C++自体はCPUキャッシュを「気」にしないため、言語に組み込まれたキャッシュサイズのクエリはサポートされていません。 Windows用に開発している場合は、 GetLogicalProcessorInformation()-function があり、これを使用してCPUキャッシュに関する情報を照会できます。

11
kusma

大きな配列を事前に割り当てます。次に、各要素に順次アクセスし、各アクセスの時間を記録します。キャッシュミスが発生すると、アクセス時間が急増するのが理想的です。その後、L1キャッシュを計算できます。うまくいかないかもしれませんが、試す価値はあります。

8
ben

このスレッドを見ることができます: http://software.intel.com/en-us/forums/topic/296674

短い答えはこの他のスレッドにあります:

最新のIA-32ハードウェアでは、キャッシュラインサイズは64です。値128は、Intel Netburst Microarchitecture(Intel Pentium Dなど)のレガシーであり、64バイトラインが128バイトセクターにペアリングされます。セクター内の行がフェッチされると、ハードウェアはセクター内の他の行も自動的にフェッチします。したがって、誤った共有の観点から見ると、Netburstプロセッサーでの有効な行サイズは128バイトです。 ( http://software.intel.com/en-us/forums/topic/292721

4
Daniel Munoz

何をしようとしているのかにもよりますが、ライブラリに任せることもできます。マルチコア処理について言及しているので、 Intel Threading Building Blocks をご覧ください。

TBBにはキャッシュ対応メモリアロケーターが含まれています。具体的には、cache_aligned_allocator(リファレンスドキュメントでは、直接リンクが見つかりませんでした)。

4

興味深いことに、私はしばらく前にこれを行うプログラムを作成しました(ただし、Cで作成しましたが、C++コードに簡単に組み込むことができると確信しています)。

http://github.com/wowus/CacheLineDetection/blob/master/Cache%20Line%20Detection/cache.c

Get_cache_line関数は興味深いもので、配列アクセスのタイミングデータの最大スパイクの直前の位置を返します。それは私のマシンで正しく推測されました!他に何かあれば、それはあなたがあなた自身のものを作るのを助けることができます。

もともと私の興味をそそったこの記事に基づいています: http://igoro.com/archive/gallery-of-processor-cache-effects/

4
Clark Gaebel

cPU(x86)のcpuidを読み取り、ルックアップテーブルでキャッシュサイズを決定します。この表には、CPUの製造元がプログラミングマニュアルで公開しているキャッシュサイズを入力する必要があります。

4
Tobias Langner

IIRC、GCCには__builtin_prefetchヒント。

http://gcc.gnu.org/onlinedocs/gcc-3.3.6/gcc/Other-Builtins.html

これに関する優れたセクションがあります。基本的に、それは提案します:

__builtin_prefetch (&array[i + LookAhead], rw, locality);

ここで、rwは0(読み取りの準備)または1(書き込みの準備)値であり、locality0〜3の数字を使用します。ゼロはローカリティなしで、3は非常に強いローカリティです。

両方ともオプションです。 LookAheadは、先読みする要素の数です。メモリアクセスが100サイクルで、展開されたループが2サイクル離れている場合、LookAheadは50または51に設定できます。

1
Max

区別する必要がある2つのケースがあります。コンパイル時または実行時にキャッシュサイズを知る必要がありますか?

コンパイル時のキャッシュサイズの決定

一部のアプリケーションでは、たとえばホストマシンで直接コードをコンパイルできる場合など、コードが実行される正確なアーキテクチャを知っています。その場合は、サイズのルックアップを簡素化し、それをオプションとしてハードコーディングします(ビルドシステムで自動化できます)。現在のほとんどのマシンでは、L1キャッシュラインは64バイトである必要があります。

その複雑さを回避したい場合、または未知のアーキテクチャでのコンパイルをサポートする必要がある場合は、C++ 17機能 std :: hardware_constructive_interference_size をフォールバックとして使用できます。キャッシュラインのコンパイル時の見積もりを提供しますが、 その制限に注意してください 。キャッシュラインのサイズは一般にアーキテクチャに依存するため、コンパイラはバイナリを作成するタイミングを完全に推測できないことに注意してください。

実行時のキャッシュサイズの決定

実行時には、正確なマシンを知っているという利点がありますが、OSから情報を読み取るにはプラットフォーム固有のコードが必要です。出発点として適切なのは、主要なプラットフォーム(Windows、Linux、MacOS)をサポートする this answer のコードスニペットです。同様に、実行時にL2キャッシュサイズを読み取ることもできます。

起動時にベンチマークを実行し、どれが最高のパフォーマンスを発揮したかを測定することで、キャッシュラインを推測しようとすることを避けることをお勧めします。うまくいくかもしれませんが、CPUが他のプロセスで使用されている場合にもエラーが発生しやすくなります。

両方のアプローチを組み合わせる

1つのバイナリを出荷し、後でキャッシュサイズが異なるさまざまなアーキテクチャの機能を実行するマシンを出荷する必要がある場合は、キャッシュサイズごとに専用のコードパーツを作成し、動的に(アプリケーションの起動時に)最適なものを選択できます1。

0
Philipp Claßen

C++ 17では、std :: hardware_destructive_interference_sizeを使用してL1キャッシュサイズを決定できます。 https://en.cppreference.com/w/cpp/thread/hardware_destructive_interference_size を参照してください。現在のところ、Microsoft Visual Studio 2019でのみサポートされています。