Операции с массивами в параллелизме al oop с openMP - PullRequest
0 голосов
/ 21 февраля 2020

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

Вот один пример:

    curr = (char**)malloc(sizeof(char*)*nx + sizeof(char)*nx*ny);
    next = (char**)malloc(sizeof(char*)*nx + sizeof(char)*nx*ny);

    int i;
    #pragma omp parallel for shared(nx,ny) firstprivate(curr) schedule(static)
    for(i=0;i<nx;i++){
        curr[i] = (char*)(curr+nx) + i*ny;
    }

    #pragma omp parallel for shared(nx,ny) firstprivate(next) schedule(static)
    for(i=0;i<nx;i++){
        next[i] = (char*)(next+nx) + i*ny;
    } 

А вот еще один:

int i,j, sum = 0, probability = 0.2;     
#pragma omp parallel for collapse(2) firstprivate(curr) schedule(static)
for(i=1;i<nx-1;i++){
    for(j=1;j<ny-1;j++) {
        curr[i][j] = (real_rand() < probability);
        sum += curr[i][j];
    }
}

Есть ли проблемы? c ошибка на моем пути? Как я могу улучшить это?

1 Ответ

0 голосов
/ 21 февраля 2020

В первом примере работа, выполняемая каждым потоком, очень мала, а накладные расходы из среды выполнения OpenMP сводят на нет и ускоряют параллельное выполнение. Вы можете попробовать объединить обе параллельные области вместе, чтобы уменьшить накладные расходы, но это не очень поможет:

#pragma omp parallel for schedule(static)
for(int i=0;i<nx;i++){
    curr[i] = (char*)(curr+nx) + i*ny;
    next[i] = (char*)(next+nx) + i*ny;
}

Во втором случае узким местом является вызов drand48(), похороненный где-то в вызове до real_rand() и суммирование. drand48 использует глобальное состояние, которое используется всеми потоками. В однопоточных приложениях состояние обычно сохраняется в кэше данных L1, и там drand48 действительно быстро. В вашем случае, когда один поток обновляет состояние, это изменение распространяется на другие ядра и делает их кэши недействительными. Следовательно, когда другие потоки вызывают drand48, состояние должно быть снова извлечено из памяти (или общего кэша L3). Это приводит к огромным задержкам и делает dran48 намного медленнее, чем при использовании в однопоточной программе. То же самое относится к суммированию в sum, которое также вычисляет неправильное значение из-за гонок данных.

Решение первой проблемы состоит в том, чтобы иметь отдельный PRNG на поток, например, использовать erand48() и передать локальное значение потока для xsubi. Вы также должны заполнить каждый PRNG другим значением, чтобы избежать корреляции псевдослучайных потоков. Решением гонки данных является использование сокращений OpenMP:

int sum = 0;
double probability = 0.2;     
#pragma omp parallel for collapse(2) reduction(+:sum) schedule(static)
for(int i=1;i<nx-1;i++){
    for(int j=1;j<ny-1;j++) {
        curr[i][j] = (real_rand() < probability);
        sum += curr[i][j];
    }
}
...