[英]Performance issues in joining threads
我編寫了以下並行代碼,用於檢查vector向量中的所有元素。 我只存儲滿足給定條件的vector<vector<int> >
那些元素。 但是,我的問題是vector<vector<int> >
某些向量非常大,而另一些則很小。 由於這個原因,我的代碼需要很長時間才能執行thread.join()。 有人可以建議我如何提高代碼性能。
void check_if_condition(vector<int>& a, vector<int>& satisfyingElements)
{
for(vector<int>::iterator i1=a.begin(), l1=a.end(); i1!=l1; ++i1)
if(some_check_condition(*i1))
satisfyingElements.push_back(*i1);
}
void doWork(std::vector<vector<int> >& myVec, std::vector<vector<int> >& results, size_t current, size_t end)
{
end = std::min(end, myVec.size());
int numPassed = 0;
for(; current < end; ++current) {
vector<int> satisfyingElements;
check_if_condition(myVec[current], satisfyingElements);
if(!satisfyingElements.empty()){
results[current] = satisfyingElements;
}
}
}
int main()
{
std::vector<std::vector<int> > myVec(1000000);
std::vector<std::vector<int> > results(myVec.size());
unsigned numparallelThreads = std::thread::hardware_concurrency();
std::vector<std::thread> parallelThreads;
auto blockSize = myVec.size() / numparallelThreads;
for(size_t i = 0; i < numparallelThreads - 1; ++i) {
parallelThreads.emplace_back(doWork, std::ref(myVec), std::ref(results), i * blockSize, (i+1) * blockSize);
}
//also do work in this thread
doWork(myVec, results, (numparallelThreads-1) * blockSize, myVec.size());
for(auto& thread : parallelThreads)
thread.join();
std::vector<int> storage;
storage.reserve(numPassed.load());
auto itRes = results.begin();
auto itmyVec = myVec.begin();
auto endRes = results.end();
for(; itRes != endRes; ++itRes, ++itmyVec) {
if(!(*itRes).empty())
storage.insert(storage.begin(),(*itRes).begin(), (*itRes).end());
}
std::cout << "Done" << std::endl;
}
很高興看到您是否可以為那些“較大的”內部向量提供一定比例,以查看問題的嚴重程度。
但是,我認為您的問題是這樣的:
for(auto& thread : parallelThreads)
thread.join();
該位依次使所有線程依次通過並等待它們完成,然后才查看下一個線程。 對於線程池,您要等待每個線程完成。 這可以通過對每個線程使用condition_variable完成。 在他們完成之前,他們必須通知condition_variable您可以等待。
查看您的實現,這里更大的問題是工作線程的消耗不平衡。
為了使所有線程上的負載更加均衡,您需要展平數據結構,以便不同的工作線程可以處理大小相對相似的數據塊。 我不確定您的數據來自哪里,但是在處理大型數據集的應用程序中使用向量的向量聽起來並不是一個好主意。 將現有的向量向量處理為一個向量,或者盡可能讀取數據。 如果您需要處理行號,則可以保留一個起始范圍的向量,從中可以找到行號。
一旦有了一個大向量,就可以將其分解成相等大小的塊,以饋入工作線程。 其次,您不想在堆棧上構建向量並將它們推入另一個向量,因為很可能在線程工作期間遇到分配內存的問題。 分配內存是一個全局狀態更改,因此將需要一定程度的鎖定(但可以避免使用適當的地址分區)。 根據經驗,每當您尋求性能時,都應從性能關鍵部件中刪除動態分配。
在這種情況下,您的線程可能寧願“標記”滿足條件的元素,而不是構建滿足條件的元素的向量。 一旦完成,您就可以只遍歷好對象而無需推送和復制任何內容。 這樣的解決方案將減少浪費。
實際上,如果我是您,我會嘗試在單個線程上首先解決上述問題,並執行上面的建議。 如果您擺脫了vector-of-vectors結構,並有條件地遍歷元素(這可能像使用C ++ 11標准庫提供的xxxx_if算法一樣簡單),則最終可能會獲得足夠不錯的性能。 而且只有在這一點上,才值得看一下將這項工作的大部分任務委派給工作線程。 在您的代碼中,此時幾乎沒有理由使用輔助線程來過濾它們。 盡可能少地寫作和移動,您將獲得很多性能。 並行化僅在某些情況下有效。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.