我编写了以下并行代码,用于检查向量向量中的所有元素。我只存储满足给定条件的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;
}
答案 0 :(得分:1)
很高兴看到你能否给出一些规模大的&#39;内部向量只是为了看问题有多糟糕。
但我认为,问题是:
for(auto& thread : parallelThreads)
thread.join();
这个位按顺序遍历所有线程并等到它们完成,然后才查看下一个。对于线程池,您希望等到每个线程完成。这可以通过使用condition_variable来完成每个线程来完成。在完成之前,他们必须通知您可以等待的condition_variable。
看看你的实现,这里更大的问题是你的工作线程的消费不平衡。
要在所有线程上获得更均衡的负载,您需要展平数据结构,以便不同的工作线程可以处理相对类似大小的数据块。我不确定你的数据来自哪里,但是在处理大数据集的应用程序中使用向量向量并不是一个好主意。将现有的矢量矢量处理成单个矢量,或者如果可能的话,读取数据。如果您需要处理的行号,可以保留一个起始范围的向量,您可以从中找到行号。
一旦你有一个大的向量,你可以将它分解为相同大小的块以提供给工作线程。其次,你不想在堆栈上构建向量并将它们推送到另一个向量中,因为很可能你遇到了在线程工作期间分配内存的问题。分配内存是一个全局状态更改,因此需要一定程度的锁定(尽管可以避免使用适当的地址分区)。根据经验,只要您正在寻找性能,就应该从性能关键部分中删除动态分配。
在这种情况下,也许你的线程宁愿标记&#39;元素是令人满意的条件,而不是建立令人满意的元素的向量。一旦完成,你可以只迭代好的,而无需推送和复制任何东西。这样的解决方案可以减少浪费。
事实上,如果我是你,我会先试着在一个线程上解决这个问题,然后再做上面的建议。如果你摆脱了向量矢量结构,并有条件地迭代元素(这可能就像使用C ++ 11标准库提供的xxxx_if算法一样简单),你可能会得到足够的性能。并且只有在那时才值得考虑将这项工作的大块委托给工作者线程。在编码的这一点上,使用工作线程的理由很少,只是为了过滤它们。做尽可能少的书写和移动,你获得了很多表现。并行化在某些情况下才有效。