Как эффективно объединять результаты потоков TBB

Я работаю над визуальным обнаружением объектов и использую каскадный классификатор от Opencv. Работает хорошо, но для меня слишком медленно. Я использовал Vtune, чтобы получить все точки доступа, и я обнаружил, что при 140-секундном выполнении (время процессора, реальное время было около 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 очень мал) для каждого потока и объединить все эти векторы в конце. Это решение снижает накладные расходы (менее 10 с при 140 с процессорного времени), но хотелось бы большего.

Это мой вопрос: есть ли способ эффективно объединить результаты из разных потоков TBB (т. е. сократить время накладных расходов)?

РЕДАКТИРОВАТЬ: В моем случае я обнаружил ошибку во время связывания. Создание локальных векторов и слияние в конце с помощью мьютекса работает хорошо. Теперь у меня есть 0,1 с накладных расходов на 140 с процессорного времени. Это частный случай с несколькими элементами, которые очень малы. Ответ Антона кажется более общим


person P1t_    schedule 17.10.2016    source источник


Ответы (2)


Есть другой и, возможно, более эффективный способ объединения результатов. Используйте комбинируемые или "ets" для сбора .local() результатов для каждого потока/задачи (не используйте потоки напрямую). а затем объедините результаты, используя .combine()

person Anton    schedule 17.10.2016

Вы можете попробовать использовать TBB concurrent_vector. Интерфейс grow_by может помочь вам уменьшить накладные расходы на вставку: вы можете создать небольшой (например, 16 элементов) массив в стеке и объединить все элементы из него в concurrent_vector.

Также вы можете заменить push_back на emplace_back на С++ 11 с concurrent_vector.

person ntfs.hard    schedule 17.10.2016