Выходной контейнер из openmp параллельного цикла - PullRequest
0 голосов
/ 26 октября 2018

Какой стиль критической секции лучше при сборе выходного контейнера?

// Insert into the output container one object at a time.
vector<float> output;
#pragma omp parallel for
for(int i=0; i<1000000; ++i)
{
    float value = // compute something complicated
    #pragma omp critical
    {
        output.push_back(value);
    }
}

// Insert object into per-thread container; later aggregate those containers.
vector<float> output;
#pragma omp parallel
{
    vector<float> per_thread;
    #pragma omp for
    for(int i=0; i<1000000; ++i)
    {
        float value = // compute something complicated
        per_thread.push_back(value);
    }
    #pragma omp critical
    {
        output.insert(output.end(), per_thread.begin(), per_thread.end());
    }
}

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

// Insert into the output container one object at a time.
vector<float> output;
#pragma omp parallel for
for(int i=0; i<1000000; ++i)
{
    int k = // compute number of items
    for( int j=0; j<k; ++j)
    {
        float value = // compute something complicated
        #pragma omp critical
        {
            output.push_back(value);
        }
    }
}

// Insert object into per-thread container; later aggregate those containers.
vector<float> output;
#pragma omp parallel
{
    vector<float> per_thread;
    #pragma omp for
    for(int i=0; i<1000000; ++i)
    {
        int k = // compute number of items
        for( int j=0; j<k; ++j)
        {
            float value = // compute something complicated
            per_thread.push_back(value);
        }
    }
    #pragma omp critical
    {
        output.insert(output.end(), per_thread.begin(), per_thread.end());
    }
}

1 Ответ

0 голосов
/ 27 октября 2018

Если вы всегда вставляете ровно один элемент на параллельную итерацию, правильный путь:

std::vector<float> output(1000000);
#pragma omp parallel for
for(int i=0; i<1000000; ++i)
{
    float value = // compute something complicated
    output[i] = value;
}

Потокобезопасно назначать различные элементы std::vector (что гарантировано, потому что все i различны). И в этом случае нет существенной ложной информации.

Если вы не вставляете ровно один элемент в параллельную итерацию, то любая версия в основном верна.

Ваша первая версия, использующая critical в цикле, может быть очень медленной - обратите внимание, что если вычисления действительно медленные, то все равно в целом все может быть в порядке.

Контейнер для каждой нити / ручное сокращение обычно в порядке. Конечно, это делает порядок результата недетерминированным. Вы можете упростить это, используя пользовательское сокращение.

...