我有一個簡單的函式來計算“n”個數字的總和。
我正在嘗試使用執行緒來并行實作總和。代碼如下,
void Add(double &sum, const int startIndex, const int endIndex)
{
sum = 0.0;
for (int i = startIndex; i < endIndex; i )
{
sum = sum 0.1;
}
}
int main()
{
int n = 100'000'000;
double sum1;
double sum2;
std::thread t1(Add, std::ref(sum1), 0, n / 2);
std::thread t2(Add, std::ref(sum2), n / 2, n);
t1.join();
t2.join();
std::cout << "sum: " << sum1 sum2 << std::endl;
// double serialSum;
// Add(serialSum, 0, n);
// std::cout << "sum: " << serialSum << std::endl;
return 0;
}
但是,代碼運行速度比串行版本慢得多。如果我修改函式使其不包含sum 變數,那么我將獲得所需的加速(接近 2 倍)。
我在網上閱讀了一些資源,但似乎都表明變數不能被多個執行緒訪問。我不明白為什么這個例子會是這樣。
有人可以澄清我的錯誤嗎?
uj5u.com熱心網友回復:
這里的問題是硬體。
你可能知道 CPU 有快取來加速操作。這些快取比記憶體快很多倍,但它們以稱為快取線的單元作業。您的系統上可能有 64 個位元組。您的 2 個雙打每個 8 位元組大,并且幾乎肯定最終會在堆疊上的相同 64 位元組區域中。CPU 中的每個核心通常都有自己的 L1 快取,而更大的快取可以在核心之間共享。
現在,當一個執行緒訪問sum1核心時,會將相關的快取線加載到快取中。當第二個執行緒訪問sum2其他核心時,嘗試將相同的快取線加載到它自己的快取中。x86 架構非常好地試圖幫助您,它會要求第一個快取交出快取線,因此兩個執行緒總是看到相同的資料。
因此,雖然您有 2 個單獨的變數,但它們位于同一快取行中,并且在每次訪問時,快取行都會從一個內核反彈到另一個內核并回傳。這是一個相當緩慢的操作。這稱為虛假共享。
因此,您需要在 和 之間進行一些分離sum1,sum2以使這項作業快速進行。請參閱std::hardware_破壞性_interference_size了解您需要達到的距離。
另一種可能更簡單的方法是修改作業函式以使用區域變數:
void Add(double &sum, const int startIndex, const int endIndex)
{
double t = 0.0;
for (int i = startIndex; i < endIndex; i )
{
t = t 0.1;
}
sum = t;
}
您仍然有錯誤共享,并且兩個執行緒將爭奪對sum1和的訪問權限sum2。但是現在它只發生一次并且變得無關緊要。
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/442165.html
下一篇:為什么要避免C 中的冗余宣告?
