замедление выполнения кода в полупараллельном OpenMP

#c #performance #parallel-processing #openmp #pragma

#c #Производительность #параллельная обработка #openmp #pragma

Вопрос:

добрый день. Я хочу реализовать внутренний продукт тремя способами: 1 — последовательный, 2 — полупараллельный, 3 — полностью параллельный

полупараллельное означает параллельное умножение и последовательное суммирование.

вот мой код:

 int main(int argc, char *argv[]) {
    int *x, *y, *z, *w, xy_p, xy_s, xy_ss, i, N=5000;
    double s, e;
    x = (int *) malloc(sizeof(int)*N);
    y = (int *) malloc(sizeof(int)*N);
    z = (int *) malloc(sizeof(int)*N);
    w = (int *) malloc(sizeof(int)*N);

    for(i=0; i < N; i  ) {
        x[i] = rand();
        y[i] = rand();
        z[i] = 0;
    }

    s = omp_get_wtime();

    xy_ss = 0;

    for(i=0; i < N; i  )
    {
        xy_ss  = x[i] * y[i];
    }

    e = omp_get_wtime() - s;
    printf ( "[**] Sequential execution time is:n.10f and <A,B> is %dn", e, xy_ss );


    s = omp_get_wtime();

    xy_s = 0;

    #pragma omp parallel for shared ( N, x, y, z ) private ( i )
    for(i = 0; i < N; i  )
    {
        z[i] = x[i] * y[i];
    }
    for(i=0; i < N; i  )
    {
        xy_s  = z[i];
    }

    e = omp_get_wtime() - s;
    printf ( "[**] Half-Parallel execution time is:n.10f and <A,B> is %dn", e, xy_s );


    s = omp_get_wtime();

    xy_p = 0;

    # pragma omp parallel shared (N, x, y) private(i)
    # pragma omp for reduction (   : xy_p )
    for(i = 0; i < N; i  )
    {
        xy_p  = x[i] * y[i];
    }
    e = omp_get_wtime() - s;
    printf ( "[**] Full-Parallel execution time is:n.10f and <A,B> is %dn", e, xy_p );
}
  

итак, у меня есть некоторый вопрос:
сначала я хочу знать: корректен ли мой код?!!!!
второй: почему полупараллельный быстрее последовательного?!
в-третьих: является ли 5000 подходящим размером для параллелизма?
и, наконец, почему последовательный является самым быстрым? из-за 5000?!
пример вывода:

Время последовательного выполнения составляет: 0.0000196100, а точка равна -1081001655

Время полупараллельного выполнения составляет: 0.0090819710, а точка равна -1081001655

Время полностью параллельного выполнения составляет: 0.0080959420, а точка равна -1081001655

и для N = 5000000

Время последовательного выполнения составляет: 0,0150297650 и равно -1629514371

Время полупараллельного выполнения составляет: 0,0292110600 и равно -1629514371

Время полностью параллельного выполнения составляет: 0,0072323760 и равно -1629514371

в любом случае, почему полупараллельный является самым медленным?

Комментарии:

1. 5000 умножений не стоят параллельных вычислений. Стоимость создания потока (или взятия потока из пула) и присоединения к потоку намного выше, чем фактический расчет. Вы можете выполнить упражнение и проверить сами, каков минимальный размер точечного произведения, чтобы параллель была прибыльной.

2. Спасибо, но я тестировал на 5000000, и полностью параллельный — самый быстрый, а полупараллельный — самый медленный! Почему полупараллельный не лучше последовательного?!

3. Циклы For для seq и parallel отличаются от полупараллельных — они создают разный ассемблерный код. Полупараллельный генерирует вдвое больший объем кода, и вы все равно платите за порождение потоков. Увеличьте размер операции до чего-то более реалистичного, например, до 50 миллионов или около того, и проверьте еще раз.

4. Когда вы разделяете циклы, в вашем самом большом случае у вас будут потери кэша и удвоится количество промахов кэша в вашем «полупараллельном» случае. Платформа NUMA может усугубить проблему. В вашем меньшем случае векторизация с сокращением omp simd должна быть более эффективной, чем tthreading. В зависимости от вашего компилятора и настроек опций, возможно, вы уже добились этой оптимизации. В типичных реализациях omp_wtime не будет точным для таких коротких временных интервалов.