如何有效地合并来自 TBB 线程的结果

How to efficiently merge results from TBB threads

我正在研究对象的视觉检测,我使用 Opencv 的级联分类器。它运作良好,但对我来说太慢了。我使用 Vtune 获取所有热点,我发现在 140 秒的执行时间(CPU 时间,实际约为 60 秒),有 123 秒的开销时间。 cvCascadeClassifier 使用 TBB 速度更快,但似乎所有 TBB 线程等待的时间都超过了应有的时间。 有代码:

void operator()(const Range& range) const
{
    Ptr<FeatureEvaluator> evaluator = classifier->featureEvaluator->clone();

    Size winSize(cvRound(classifier->data.origWinSize.width * scalingFactor), cvRound(classifier->data.origWinSize.height * scalingFactor));

    int y1 = range.start * stripSize;
    int y2 = min(range.end * stripSize, processingRectSize.height);
    for( int y = y1; y < y2; y += yStep )
    {
        for( int x = 0; x < processingRectSize.width; x += yStep )
        {
            if ( (!mask.empty()) && (mask.at<uchar>(Point(x,y))==0)) {
                continue;
            }

            double gypWeight;
            int result = classifier->runAt(evaluator, Point(x, y), gypWeight);

            #if defined (LOG_CASCADE_STATISTIC)
            logger.setPoint(Point(x, y), result);
            #endif
            if( rejectLevels )
            {
                if( result == 1 )
                    result =  -(int)classifier->data.stages.size();
                if( classifier->data.stages.size() + result < 4 )
                {
                    mtx->lock();
                    rectangles->push_back(Rect(cvRound(x*scalingFactor), cvRound(y*scalingFactor), winSize.width, winSize.height));
                    rejectLevels->push_back(-result);
                    levelWeights->push_back(gypWeight);
                    mtx->unlock();
                }
            }
            else if( result > 0 )
            {
                mtx->lock();
                rectangles->push_back(Rect(cvRound(x*scalingFactor), cvRound(y*scalingFactor),
                                           winSize.width, winSize.height));
                mtx->unlock();
            }
            if( result == 0 )
                x += yStep;
        }
    }
}

我认为问题出在结果的合并上。互斥锁过多,线程需要等待的次数过多。这部分代码被调用了很多时间,线程很少(在我的例子中是 3 个)。我尝试为每个线程创建本地向量(我没有尝试使用列表,因为 Rect 类型非常小)并在最后合并所有这些向量。该解决方案减少了开销时间(在 CPU 时间的 140 秒上少于 10 秒)但我想要更多。

这是我的问题: 有没有办法有效地合并来自不同 TBB 线程的结果(也就是减少开销时间)?

编辑: 就我而言,我在链接过程中发现了一个错误。创建本地向量并在末尾使用互斥量合并效果很好。现在,我在 140 秒的 CPU 时间上有 0.1 秒的开销。这是一个特殊情况,只有很少的元素非常小。安东的回答似乎更笼统

您可以尝试使用TBB concurrent_vectorgrow_by 接口可以帮助您减少插入的开销:您可以在堆栈上创建小的(例如 16 个元素)数组并将其中的所有元素合并到 concurrent_vector.

您还可以将 push_back 替换为 emplace_back,使用由 concurrent_vector 驱动的 C++11。

还有另一种可能更有效的组合结果的方法。使用 combinable or "ets" 类 以便为每个 thread/task 收集 .local() 结果(不要直接使用线程),然后使用 .combine()[= 合并结果13=]