Оптимизация производительности цикла C for с помощью openmp - PullRequest
1 голос
/ 01 ноября 2019

У меня есть простой for -петл, который вычисляет array[n] в зависимости от соответствующей строки в массиве X[n][d].

array *function(X, n, d){
    double *array = calloc(n,sizeof(double));
    //#pragma omp parallel
    {
        //#pragma omp parallel for if(n>15000)
        for( i=0 ; i<n-1; i++)
        {
            //#pragma omp parallel for shared(X,j, i) reduction(+: sum)
            //#pragma omp parallel for if(d>100) reduction(+:distances[:n]) private(j)
            for ( j=0; j< d; j++)
            {
                array[i] += (pow(X[(j+1)*n-1]-X[j*n+i], 2));
            }
            array[i] = sqrt(array[i]);
        }
    }
    return array;
}

Считайте n равным n=100000 и d могут иметь предопределенное значение от d=2 до d=100. function() вызывается несколько раз (2^k) на каждой k-й итерации. Таким образом, шаблон таков: на первой итерации он вызывается один раз, на второй итерации он вызывается дважды, на третьей итерации - четыре раза и т. Д. Также n уменьшается на единицу в каждой итерации (n-=1).

Я пробовал разные комбинации директив openmp, которые я добавил в качестве примера в пример кода, но независимо от того, что я пробовал, код работает одинаково или лучше без директив openmp.

Какими хорошими способами / методами можно улучшить временную производительность вышеуказанного цикла, используя openmp?

1 Ответ

0 голосов
/ 03 ноября 2019

Трудно сказать без чего-либо, чтобы проверить это, но я бы попробовал что-то вроде этого:

double* function( double* X, int n, int d ) {
    double *array = malloc( n * sizeof( double ) );
    #pragma omp parallel for schedule( static )
    for( int i = 0 ; i < n - 1; i++ ) {
        double sum = 0;
        for( int j = 0; j < d; j++ ) {
           double dist = X[( j + 1 ) * n - 1] - X[j * n + i];
           sum += dist * dist;
        }
        array[i] = sqrt( sum );
    }
    return array;
}

Я не уверен, что это будет более эффективным, чем ваш код, но у него естьНесколько улучшений, которые должны повлиять на производительность:

  1. Чтобы избежать инициализации массива равным 0 в последовательной части, а также, возможно, обеспечить лучшую оптимизацию от компилятора, я заменил calloc() простымmalloc() и использовал локальную переменную sum для накопления частичных сумм.
  2. Я поместил прагму распараллеливания как можно дальше, чтобы максимизировать параллелизм.
  3. Я использоваллокальная dist переменная для хранения временного расстояния между двумя текущими значениями X и просто умножения его на себя, избегая дорогостоящего вызова гораздо более сложной функции pow().

Теперь, в зависимости от того, какой результат вы получите, вы можете рассмотреть возможность добавления оператора if( n > NMIN ) того же типа, который вы изначально использовали для директивы #pragma omp parallel for. Значение для этого NMIN будет определяться в соответствии с вашими измеренными показателями. Другим возможным направлением для оптимизации было бы размещение директивы parallel вне функции. Это избавит вас от многочисленных запусков / остановок. Однако вам нужно добавить #pragma omp single перед вызовом malloc() и удалить parallel из существующей директивы.

...