2016-12-01 7 views
2

最近、インテル®TBBスケーラブル・アロケータに関する問題が発生しました。基本的な使用パターンは、サイズN * sizeof(double)インテルCPU上のキャッシュ・アライメント・メモリ割り当て

  1. いくつかのベクターは、
  2. はランダムな整数MようM >= N/2 && M <= Nを生成割り当てられ、次の通りです。
  3. 各ベクトルの最初のM要素がアクセスされます。
  4. 手順2.を1000回繰り返します。

Mは固定長のパフォーマンスをベンチマークしたくないためランダムに設定しました。代わりに、ある範囲の長さのベクトルにわたって平均的なパフォーマンスを得たいと思っています。

Nの値によってプログラムのパフォーマンスが大きく異なります。これは、私がテストしている機能が大きなNのパフォーマンスを優先させるように設計されているため、珍しくありません。しかし、パフォーマンスとNの関係をベンチマークしようとしたとき、N1016から1017に増加すると、ある点で2つのフォールドの差があることがわかりました。

私の最初の本能は、N = 1016のパフォーマンスの低下は小さなベクトルサイズとは関係がなく、キャッシュを行うことがあるということです。ほとんどの場合、誤った共有があります。テイスティング下の関数は、SIMD命令を使用しますが、スタックメモリは使用しません。第1のベクトルから1つの32バイト要素を読み取り、計算後、第2(および第3)ベクトルに32バイトを書き込む。誤った共有が発生すると、おそらく数十サイクルが失われ、それはまさに私が観察しているパフォーマンスのペナルティです。いくつかのプロファイリングでこれが確認されます。

元々、AVX命令の場合、各ベクトルを32バイト境界に揃えました。この問題を解決するために、私はベクトルを64バイトの境界に揃えました。しかし、私はまだ同じパフォーマンスのペナルティを観察します。 128バイトで整列させると問題が解決します。

もう少し掘りました。インテル®TBBはcache_aligned_allocatorです。そのソースでは、メモリも128バイトで整列されます。

これは私が理解できないものです。私が間違っていなければ、最新のx86 CPUには64バイトのキャッシュラインがあります。 はこれを確認します。以下は、インテルTBBのソースには、128バイトのアライメントがコメントでマークされていた、また、私は機能をチェックするためにCPUIDを使って書いた小さなプログラムから抽出された使用中のCPUの基本的なキャッシュ情報、

Vendor GenuineIntel 
Brand  Intel(R) Core(TM) i7-4960HQ CPU @ 2.60GHz 

==================================================================================================== 
Deterministic Cache Parameters (EAX = 0x04, ECX = 0x00) 
---------------------------------------------------------------------------------------------------- 
Cache level        1   1   2   3   4   
Cache type        Data  Instruction Unified  Unified  Unified  
Cache size (byte)      32K   32K   256K  6M   128M   
Maximum Proc sharing     2   2   2   16   16   
Maximum Proc physical     8   8   8   8   8   
Coherency line size (byte)    64   64   64   64   64   
Physical line partitions    1   1   1   1   16   
Ways of associative      8   8   8   12   16   
Number of sets       64   64   512   8192  8192   
Self initializing      Yes   Yes   Yes   Yes   Yes   
Fully associative      No   No   No   No   No   
Write-back invalidate     No   No   No   No   No   
Cache inclusiveness      No   No   No   Yes   No   
Complex cache indexing     No   No   No   Yes   Yes   
---------------------------------------------------------------------------------------------------- 

ですそれは下位互換性のためだと言った。

なぜ私の場合、64バイトの整列が不十分なのですか?

答えて

2

あなたはconflict missesにヒットしました。 1016から1017に移動すると、アソシエートリストの最後のキャッシュラインを使用し始めます。

キャッシュは32Kの8ウェイなので、各セットは4Kです。 64バイトのキャッシュラインは8倍を保持できます。 しかし、あなたの1017-1024ベクターは4Kではなく8Kを使用しますか? 1024 * sizeof(double)、よく使用するN/2-> Nので、同じベクトルの下位アドレスビットの組み合わせを2回使用します(正確にN/2を除いて)。

すべてのL1キャッシュを使用するまで、競合のヒットは発生しません。 1つのベクトルを読み込みに使用し、2つのベクトルを書き込みに使用します.8Kの長さなので、24Kを使用すると、計算中に8K +余分なデータを使用すると、選択したデータを追い出す可能性が高くなります。

あなたはベクトルの最初の部分だけを使用していますが、決して少なくはありません。

1016から1017に移動すると、L1キャッシュミスが増えることがわかります.1 1024倍を超えると、L1キャッシュの容量ミスが発生するまで、パフォーマンスペナルティが少し消えるはずです。詳細な説明のための「Memory part 5: What programmers can doenter image description here

+0

ありがとう:全8セットはウルリック・ドレパーによって幻想的な記事から>

を使用しているときにスパイクを示し、ここでグラフを画像化

<。これは私が知っていたものですが、もっとはっきりと説明しました。しかし、割り当てられたメモリのサイズを増やさずにこの競合を避ける方法はありますか?私は、ほとんどの場合、2つのベクトルが同じキャッシュラインを共有するのを避けるのに、63バイトの境界で整列することで十分であるという印象を受けました。代わりに、私は128バイトの整列が必要であることを観察した –

+0

グラフとリンクありがとう。私はそれについて最初に読んでいます。 –

+0

アラインメントは、ここでの問題であるset-associativesnesであり、ベクトルによって使用される合計メモリを意味するので重要ではありません。 – Surt