Замена TBB parallel_for на OpenMP

Я пытаюсь придумать эквивалентную замену циклу Intel TBB parallel_for, который использует tbb::blocked_range с использованием OpenMP. Копаясь в Интернете, мне удалось найти упоминание только об одном человеке, который делал что-то подобное; патч, отправленный в проект Open Cascade, в котором цикл TBB выглядел так (но не использовал tbb::blocked_range):

tbb::parallel_for_each (aFaces.begin(), aFaces.end(), *this);

и эквивалент OpenMP был:

int i, n = aFaces.size();
#pragma omp parallel for private(i)
for (i = 0; i < n; ++i)
    Process (aFaces[i]);

Вот цикл TBB, который я пытаюсь заменить:

tbb::parallel_for( tbb::blocked_range<size_t>( 0, targetList.size() ), DoStuff( targetList, data, vec, ptr ) );

Он использует класс DoStuff для выполнения работы:

class DoStuff
{
private:
    List& targetList;
    Data* data;
    vector<things>& vec;
    Worker* ptr;

public:
    DoIdentifyTargets( List& pass_targetList, 
                       Data* pass_data, 
                       vector<things>& pass_vec, 
                       Worker* pass_worker) 
        : targetList(pass_targetList), data(pass_data), vecs(pass_vec), ptr(pass_worker)
    {
    }

    void operator() ( const tbb::blocked_range<size_t> range ) const
    {
        for ( size_t idx = range.begin(); idx != range.end(); ++idx )
        {
            ptr->PerformWork(&targetList[idx], data->getData(), &Vec);
        }
    }
};

Насколько я понимаю, на основании этой ссылки, TBB разделит заблокированный диапазон на более мелкие подмножества и дать каждому потоку один из диапазонов для циклического прохождения. Поскольку каждый поток получит свой собственный класс DoStuff, который имеет кучу ссылок и указателей, это означает, что потоки, по сути, совместно используют эти ресурсы.

Вот что я придумал в качестве эквивалентной замены в OpenMP:

int index = 0;
#pragma omp parallel for private(index)
for (index = 0; index < targetList.size(); ++index)
{
    ptr->PerformWork(&targetList[index], data->getData(), &Vec);
}

Из-за обстоятельств, не зависящих от меня (это всего лишь один компонент в гораздо более крупной системе, охватывающей +5 компьютеров), просмотр кода с помощью отладчика, чтобы точно увидеть, что происходит,... маловероятен. Я работаю над удаленной отладкой, но это не очень многообещающе. Все, что я знаю точно, это то, что приведенный выше код OpenMP как-то делает что-то не так, как TBB, и ожидаемые результаты после вызова PerformWork для каждого индекса не получаются.

Учитывая приведенную выше информацию, есть ли у кого-нибудь идеи о том, почему код OpenMP и TBB функционально не эквивалентен?


person NobodyNothing    schedule 03.01.2014    source источник
comment
Насколько вы абстрагировались от исходного кода в своем примере? tbb::blocked_range работает с обратным диапазоном, а for — нет.   -  person Ben Jackson    schedule 03.01.2014
comment
И еще одна возможность заключается в том, что ваше изменение типа с size_t на int на самом деле является проблемой, если у вас огромное количество элементов.   -  person Ben Jackson    schedule 03.01.2014
comment
Все, что я сделал с кодом, это изменил имена классов и переменных, чтобы они были более общими, кроме того, что это то же самое. Ожидаемое количество элементов составляет всего около 100-500, а OpenMP требует использования знакового целого числа для индексной переменной, поэтому вариантов не так много, даже если это было проблемой. Если у меня заработает удаленная отладка, я обязательно проверю количество элементов, но я очень сомневаюсь, что этого достаточно, чтобы переполнить подписанный int.   -  person NobodyNothing    schedule 03.01.2014
comment
Вы пытались запустить цикл последовательно и наоборот, как предложил Бен? Кроме того, вам не нужно делать idx закрытым, фиксировать длину перед циклом и использовать ее в сравнении, но объявлять idx как обычно в вашем цикле. (с телефона, извините за опечатки)   -  person Rick    schedule 03.01.2014
comment
@ Рик, почему важно фиксировать длину перед циклом?   -  person Z boson    schedule 07.01.2014
comment
это не должно иметь значения, возможно, делая индекс закрытым, он всегда был его начальным значением. Прошло некоторое время с тех пор, как я прочитал спецификацию   -  person Rick    schedule 08.01.2014


Ответы (1)


Следуя совету Бена и Рика, я протестировал следующий цикл без прагмы omp (последовательно) и получил ожидаемые результаты (очень медленно). После повторного добавления прагмы параллельный код также работает должным образом. Похоже, проблема заключалась либо в объявлении индекса как частного вне цикла, либо в объявлении numTargets как частного внутри цикла. Или оба.

    int numTargets = targetList.size();
    #pragma omp parallel for
    for (int index = 0; index < numTargets; ++index)
    {
        ptr->PerformWork(&targetList[index], data->getData(), &vec);
    }
person NobodyNothing    schedule 06.01.2014
comment
index будет закрытым в любом случае, поэтому вам не нужно явно объявлять его закрытым (но это не повредит). Одной из особенностей OpenMP является поддержка ansi C, которая не допускает смешанных объявлений. Обычно все, что находится за пределами параллельного блока, является общим, но это исключение из правил. Так что это не могло быть вашей проблемой. Если и была проблема, то она могла быть связана с вызовом targetList.size(), но я не знаю почему. - person Z boson; 07.01.2014