[英]C++ Using `.reserve()` to pad `std::vector`s as a way of protecting against multithreading cache invalidation and false sharing
我有一個程序,其結構如下所示。 基本上,我有一個對象矢量。 每個對象都有成員向量,其中一個是包含更多向量的結構向量。 通過多線程,對象被並行操作,進行涉及訪問和修改成員向量元素的計算。 一個對象一次僅被一個線程攻擊,並被復制到該線程的堆棧以進行處理。
問題是該程序無法擴展到16個核心。 我懷疑並且被告知該問題可能是虛假共享和/或緩存失效。 如果這是真的,似乎原因必須是矢量分配內存彼此太接近,因為我的理解是兩個問題(簡單來說)是由不同處理器同時訪問的近端內存地址引起的。 這種推理是否有意義,是否可能發生這種情況? 如果是這樣,似乎我可以通過使用.reserve()填充成員向量來解決這個問題,以增加額外的容量,在向量數組之間留下大的空內存空間。 那么,這一切都有意義嗎? 我完全在這里吃午飯嗎?
struct str{
vector <float> a; vector <int> b; vector <bool> c; };
class objects{
vector <str> a; vector <int> b; vector <float> c;
//more vectors, etc ...
void DoWork(); //heavy use of vectors
};
main(){
vector <object> objs;
vector <object> p_objs = &objs;
//...make `thread_list` and `attr`
for(int q=0; q<NUM_THREADS; q++)
pthread_create(&thread_list[q], &attr, Consumer, p_objs );
//...
}
void* Consumer(void* argument){
vector <object>* p_objs = (vector <object>*) argument ;
while(1){
index = queued++; //imagine queued is thread-safe global
object obj = (*p_objs)[index]
obj.DoWork();
(*p_objs)[index] = obj;
}
那么,在線程0中復制的最后一個向量是objs[0].c
。 在線程1中復制的第一個向量是objs[1].a[0].a
。 因此,如果它們的兩個分配數據塊都碰巧占用相同的高速緩存行(64個字節,或者它實際上是該CPU的任何內容),那么你就會有錯誤的共享。
當然,涉及的任何兩個向量也是如此,但僅僅為了一個具體的例子,我假裝線程0首先運行並在線程1開始分配之前進行分配,並且分配器傾向於使連續分配相鄰。
reserve()
可能會阻止您實際操作的塊的部分占用相同的緩存行。 另一種選擇是每線程內存分配 - 如果這些向量的塊是從不同的池分配的,那么它們不可能占用相同的行,除非池有。
如果你沒有每線程分配器,那么問題可能是內存分配器上的爭用,如果DoWork
重新分配了這些向量。 或者它可能是DoWork
使用的任何其他共享資源的爭用。 基本上,假設每個線程花費1 / K的時間做一些需要全局獨占訪問的事情。 然后它似乎可以合理地並行化到一定數量J <= K,此時獲取獨占訪問顯着地加速到加速,因為核心在空閑時間上花費了相當大的時間。 除了K核之外,額外核心幾乎沒有任何改進,因為共享資源無法更快地工作。
在荒謬的一端,想象一下花費1 / K的時間進行全局鎖定的工作,以及等待I / O的時間(K-1)/ K. 然后問題看起來幾乎與K個線程(無論核心數量)並行地令人尷尬地平行,此時它停止了。
所以,在你排除真正的分享之前,不要專注於虛假分享;-)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.