加入线程时的性能问题

时间:2016-12-30 17:41:42

标签: c++ c++11 vector stl

我编写了以下并行代码,用于检查向量向量中的所有元素。我只存储满足给定条件的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;
}

1 个答案:

答案 0 :(得分:1)

很高兴看到你能否给出一些规模大的&#39;内部向量只是为了看问题有多糟糕。

但我认为,问题是:

for(auto& thread : parallelThreads)
    thread.join();

这个位按顺序遍历所有线程并等到它们完成,然后才查看下一个。对于线程池,您希望等到每个线程完成。这可以通过使用condition_variable来完成每个线程来完成。在完成之前,他们必须通知您可以等待的condition_variable。

看看你的实现,这里更大的问题是你的工作线程的消费不平衡。

要在所有线程上获得更均衡的负载,您需要展平数据结构,以便不同的工作线程可以处理相对类似大小的数据块。我不确定你的数据来自哪里,但是在处理大数据集的应用程序中使用向量向量并不是一个好主意。将现有的矢量矢量处理成单个矢量,或者如果可能的话,读取数据。如果您需要处理的行号,可以保留一个起始范围的向量,您可以从中找到行号。

一旦你有一个大的向量,你可以将它分解为相同大小的块以提供给工作线程。其次,你不想在堆栈上构建向量并将它们推送到另一个向量中,因为很可能你遇到了在线程工作期间分配内存的问题。分配内存是一个全局状态更改,因此需要一定程度的锁定(尽管可以避免使用适当的地址分区)。根据经验,只要您正在寻找性能,就应该从性能关键部分中删除动态分配。

在这种情况下,也许你的线程宁愿标记&#39;元素是令人满意的条件,而不是建立令人满意的元素的向量。一旦完成,你可以只迭代好的,而无需推送和复制任何东西。这样的解决方案可以减少浪费。

事实上,如果我是你,我会先试着在一个线程上解决这个问题,然后再做上面的建议。如果你摆脱了向量矢量结构,并有条件地迭代元素(这可能就像使用C ++ 11标准库提供的xxxx_if算法一样简单),你可能会得到足够的性能。并且只有在那时才值得考虑将这项工作的大块委托给工作者线程。在编码的这一点上,使用工作线程的理由很少,只是为了过滤它们。做尽可能少的书写和移动,你获得了很多表现。并行化在某些情况下才有效。