#c #c #multithreading #performance #openmp
#c #c #многопоточность #Производительность #openmp
Вопрос:
Я хочу использовать OpenMP для распараллеливания калькулятора for-loop, который выполняет что-то вроде:
B = (int*)malloc(sizeof(int) * N); //N is known
for(i=0;i<500000;i )
{
for(j=0;j<M;j ) B[j]=i j; //M is different from N, but M <= N;
some operations on B which produce a variable L;
printf("%dn",L);
}
Мне не нужно перераспределять B, поскольку его значения будут определены для каждой итерации соответственно. Операции будут использовать только от B [0] до B[M-1]. Это экономит много времени при выделении и инициализации B.
Чтобы использовать openmp, я изменил код на это:
#pragma omp parallel num_threads(32) private(i,j,B,M,L)
{
B = (int*)malloc(sizeof(int) * N); //N is known
#pragma omp parallel for
for(i=0;i<500000;i )
{
for(j=0;j<M;j ) B[j]=i j; //M is different from N, but M <= N;
some operations on B which produce a variable L;
printf("%dn",L);
}
}
Он работает очень медленно по сравнению с первым, поскольку создает новый массив B для каждого потока (так 500000 раз).
Есть ли способ избежать этого с помощью openmp?
Ответ №1:
Основная проблема заключается в том, что итерации цикла не назначаются потокам, как вы хотели. Поскольку вы снова добавили предложение parallel
в #pragma omp for
, и предполагая, что у вас отключен вложенный параллелизм, который по умолчанию отключен, каждый из потоков, созданных во внешней parallel
области, будет выполнять «последовательно» код в этой области, а именно:
#pragma omp parallel for
for(i=0;i<500000;i ){
...
}
Следовательно, каждый поток будет выполнять все 500000
итерации внутреннего цикла, которые вы намеревались распараллелить. Следовательно, удаление параллелизма и добавление дополнительных накладных расходов (например, создание потоков) к последовательному коду. Тем не менее, можно легко решить эту проблему, просто удалив второе parallel
предложение, а именно:
#pragma omp parallel num_threads(32) private(i,j,B,M,L)
{
B = (int*)malloc(sizeof(int) * N); //N is known
#pragma omp for
for(i=0;i<500000;i ){
...
}
}
В зависимости от настройки, в которой будет выполняться код (например, в NUMA
архитектуре или нет, является ли malloc
используемая функция (или нет) потоковым распределителем памяти, среди прочего), может быть целесообразно профилировать вашу параллельную область, чтобы проверить, окупается (или нет) перемещение выделениямассива 2D
за пределы этой области. Пример того, как может выглядеть альтернативная версия:
int total_threads = 32;
int** B = malloc(sizeof(*int) * total_threads);
for(int i = 0; i < total_threads; i ){
B[i] = malloc(N * sizeof(int));
}
#pragma omp parallel num_threads(32) private(i,j,M,L)
{
int threadID = omp_get_thread_num();
#pragma omp for
for(i=0;i<500000;i )
{
for(j=0;j<M;j )
B[threadID][j]=i j; //M is different from N, but M <= N;
some operations on B which produce a variable L;
printf("%dn",L);
}
}
// you might need to reduce all the values from all threads
// to main thread array.