Предоставить частный предварительно выделенный буфер потока для параллельного цикла for()?

Моя программа содержит цикл for(), который построчно обрабатывает некоторые необработанные данные изображения, которые я хочу распараллелить с помощью OpenMP следующим образом:

...
#if defined(_OPENMP)
        int const  threads = 8;
        omp_set_num_threads( threads );
        omp_set_dynamic( threads );
#endif
        int line = 0;
#pragma omp parallel private( line )
        {
            // tell the compiler to parallelize the next for() loop using static
            // scheduling (i.e. balance workload evenly among threads),
            // while letting each thread process exactly one line in a single run
#pragma omp for schedule( static, 1 )
            for( line = 0 ; line < max; ++line ) {
                // some processing-heavy code in need of a buffer
            }
        } // end of parallel section
....

Вопрос в следующем:

Можно ли предоставить отдельный (предварительно выделенный) буфер (указатель) для каждого потока команды, выполняющей мой цикл, используя стандартную прагму/функцию OpenMP (таким образом устраняя необходимость выделения нового буфера для каждого цикла? )?

Заранее спасибо.

Бьорн


person Bjoern    schedule 20.10.2011    source источник


Ответы (2)


Я могу понять вас неправильно, но я думаю, что это должно сделать это:

#pragma omp parallel 
{
    unsigned char buffer[1024]; // private

    // while letting each thread process exactly one line in a single run
    #pragma omp for // ... etc
    for(int line = 0; line < max; ++line ) {
          //...
    }
}

Если вы действительно имели в виду, что хотите использовать один и тот же буфер для разных параллельных блоков, вам придется прибегнуть к локальному хранилищу потока. (Boost, а также C++11 имеют средства, упрощающие это (и более переносимые), чем прямое использование TlsAlloc и другие).

Обратите внимание, что этот подход возлагает на программиста часть бремени проверки безопасности потоков, потому что вполне возможно, чтобы разные omp parallel секции выполнялись одновременно, особенно когда они вложены друг в друга.

Учтите, что параллельные блоки могут вкладываться друг в друга во время выполнения, даже если они не вложены лексически. На практике это обычно не очень хороший стиль и часто приводит к плохой производительности. Тем не менее, это то, что вы должны знать, когда делаете это).

person sehe    schedule 20.10.2011
comment
Проблема здесь в том, что необходимый буфер может быть слишком большим для стека, особенно когда мы используем несколько потоков. Работает ли добавление «буфера» к «omp parallel private» #pragma для указателей? - person Bjoern; 20.10.2011
comment
IIRC private работает для /имен/ (идентификаторы). Тем не менее, я сомневаюсь, что вам нужно указывать это. По моему опыту, переменные, объявленные внутри параллельного раздела, относятся к потоку. Так что да, не стесняйтесь просто объявить std::vector<...> buffer или обновить его. Не забудьте удалить :) - person sehe; 20.10.2011
comment
Большое спасибо! Попробую, завтра первым делом =). - person Bjoern; 20.10.2011

Существует threadprivate: http://msdn.microsoft.com/en-us/library/2z1788dd

static int buffer[BUFSIZE];
#pragma omp threadprivate(buffer)

Эта прагма работает с глобальной/статической переменной, поэтому вам не нужно беспокоиться о переполнении стека. (В таком случае переполнения стека совсем неплохо увеличить размер стека, настроив параметр компоновщика.)

Обратите внимание, что у компиляторов могут быть разные детали реализации для threadprivate. Например, компилятор VS 2010 не может сделать make threadprivate, если у переменной есть конструктор. Однако компилятор Intel C/C++ отлично справляется с этой задачей.

Использование отдельных omp parallel и omp for также является хорошей идеей, как показано выше. Однако использование threadprivate позволяет использовать omp parallel for напрямую.

К вашему сведению: даже если вам нужно выделить собственное локальное хранилище потока, во многих случаях вам фактически не нужно вызывать функцию, специфичную для ОС, такую ​​как TlsAlloc. Вы можете просто выделить массив из N структур данных. А затем получить к ним доступ, используя omp_get_thread_num, который дает идентификатор потока от 0 до N-1. Конечно, вы должны учитывать ложное совместное использование, вставляя заполнение, чтобы гарантировать, что каждая структура данных должна быть выровнена с другой строкой кэша (в основном современные кэши ЦП имеют 64-байтную строку кэша).

person minjang    schedule 20.10.2011
comment
+1 за подход omp_get_thread_num. Это решение больше похоже на то, что я изначально имел в виду. Похоже, мы все-таки собираем здесь достойный вопрос =)... - person Bjoern; 21.10.2011