2016-12-01 106 views
2

最近我遇到了英特爾TBB可擴展分配器的問題。基本使用模式是作爲以下,緩存對齊英特爾CPU上的內存分配

  1. 大小N * sizeof(double)的幾個矢量分配
  2. 生成一個隨機整數M使得M >= N/2 && M <= N
  3. 訪問每個向量的第一個M元素。
  4. 重複第2步1000次。

我將M設爲隨機,因爲我不想將基準性能定爲固定長度。相反,我想獲得一系列矢量長度的平均性能。

對於不同的值N,程序性能差別很大。這並不罕見,因爲我測試的功能旨在優先考慮大型N的性能。但是,當我試圖對性能和N之間的關係進行基準測試時,我發現在某個點上,當N1016增加到1017時,會有兩倍的差異。

我的第一個直覺是N = 1016的性能變差與較小的向量大小無關,而是它有一定的緩存。很有可能存在虛假分享。品嚐下的功能使用SIMD指令,但不使用堆棧內存。它從第一個向量中讀取一個32字節的元素,並在計算之後,它將第二個(和第三個)向量寫入32個字節。如果發生虛假共享,可能會丟失幾十個週期,這正是我觀察到的性能損失。一些分析證實了這一點。

本來我將每個向量與32字節邊界對齊,用於AVX指令。爲了解決這個問題,我將矢量與64字節的邊界對齊。但是,我仍然觀察到相同的性能損失。通過128字節對齊解決問題。

我做了一些更多的挖掘。英特爾TBB有一個cache_aligned_allocator。在它的源代碼中,內存也由128字節對齊。

這是我不明白。如果我沒有弄錯,現代的x86 CPU有一個64字節的緩存行。 CPUID證實了這一點。以下是正在使用的CPU,從我寫使用CPUID檢查功能的小程序提取的基本緩存信息,

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   
---------------------------------------------------------------------------------------------------- 

此外,英特爾TBB的源代碼,128個字節對齊的特點是評論這是爲了向後兼容。

那麼爲什麼在我的情況下64字節對齊不夠?

回答

2

您遇到conflict misses。 從1016到1017時發生的原因是,您開始使用關聯列表中的最後一個緩存行。

您的緩存是32K 8路,因此每個套件都是4K。您的64字節緩存行可以容納8個雙打。 但你的1017-1024矢量使用8K而不是4K? 1024 * sizeof(double),以及你使用N/2-> N,這樣你的每個向量的使用(除了正好N/2)一些相同的低地址位組合。

在使用完所有L1緩存之前,您不會遇到衝突命中的問題,您現在正在使用您的所有L1緩存。使用1個矢量讀取和2個矢量寫入,全部長8K,所以使用24K,如果在計算過程中使用8K以上的額外數據,您將獲得越來越大的驅逐您選擇的數據的機會。

介意你只使用矢量的第一部分,但它們衝突永遠不會少。

當你從1016跳到1017時,你將能夠觀察到L1緩存缺失的增加。當你超過1024倍時,性能損失應該會在短時間內消失,直到你達到L1緩存容量缺失。

<成像這裏的曲線圖,顯示當所有8個集用於>

從由烏利齊·德雷珀神奇物品在一個尖峯:「Memory part 5: What programmers can doenter image description here

+0

感謝您的詳細說明。這是我知道的,儘管解釋得更清楚。然而,如果不增加分配的內存大小,是否有辦法避免這種衝突呢?我的印象是,通過對齊63字節的邊界足以避免兩個向量在大多數情況下共享相同的緩存行。相反,我觀察到需要128字節的對齊方式 –

+0

感謝您的圖表和鏈接。我會先閱讀一下。 –

+0

對齊並不重要,因爲它是set-associativesnes,這是問題在這裏,意思是向量使用的總內存。 – Surt