2015-05-25 2 views
0

Я пытаюсь распараллелить операцию с точечным продуктом, и я измеряю время выполнения операции, выполняемую на различных количествах ядер, используя OpenMP. Я получаю результат, что если N = 1e9, то для 1 ядра время процессора составляет 5,6 секунды, для 8 ядер - 6 секунд и 16 ядер - 10,8 секунды. Почему время вычисления возрастает, когда я использую больше ядер?Parallelizing Dot Product, больше ядер делает его медленнее?

Вот мой код:

#include <stdlib.h> 
#include <stdio.h> 
#include <time.h> 
#include <omp.h> 

#define DATA_TYPE float 

const int N = 1e9; 

int main() 
{ 
    int i, nthreads, tid; 
    DATA_TYPE x_par, *y, *z, cput_par; 
    clock_t start, end; 

    y = (DATA_TYPE*)malloc(sizeof(DATA_TYPE)*N); 
    z = (DATA_TYPE*)malloc(sizeof(DATA_TYPE)*N); 

    for (i=0; i<N; i++) { 
     y[i] = i * 1.0; 
     z[i] = i * 2.0; 
    } 

    x_par = 0; 

    //nthreads = omp_get_max_threads(); 
    nthreads = 1; 
    printf("n threads = %d\n", nthreads); 
    start=clock(); 
    omp_set_num_threads(nthreads); 
    #pragma omp parallel for reduction(+:x_par) 
     for (i=0; i<N; i++) 
     { 
      x_par += y[i] * z[i]; 
     } 
    end=clock(); 
    cput_par = ((double)(end-start)/(double)(CLOCKS_PER_SEC)); 
    printf("Parallel time use: %f\n", cput_par); 
    printf("x_par = %f\n", x_par); 

    return 0; 
} 
+0

У вас есть 16 физических ядер, или вы гиперпотоки? [Hyperthreading не подходит для производительности] (https://www.pugetsystems.com/labs/articles/Hyper-Threading-may-be-Killing-your-Parallel-Performance-578/) –

+0

omp_get_max_threads() возвращает 16 на моя машина. Мое оборудование - это Intel Xeon E5520 с четырьмя ядрами и 8 потоками. Однако время вычисления для использования 8 потоков вместо 1 все равно немного увеличивается. Зачем? –

+0

Попробуйте изменить 'nthreads' до 4 и посмотреть, что произойдет. Если ваша архитектура поддерживает только 8 потоков, я не знаю, почему 'omp_get_max_threads()' будет возвращать 16. Что касается того, почему она замедляется на 8 против 1, когда вы запускаете ее на 8 потоках, вы оказываете гиперпоточность. То есть у вас есть только 4 физических ядра, каждая из которых имеет по два потока. Каждый раз, когда вы используете оба потока для ядра для одного и того же процесса, он называется гиперпотоком. –

ответ

1

Неисправность была общее процессорное время всех ядер/потоков, используемых рассчитывали. Чтобы получить среднее значение cpu-time, заданное каждому потоку, значение нужно разделить на количество потоков. Другим способом его решения может быть измерение времени стены (то есть разница фактического времени дня до и после операции). Если используется время стены, операционная система может запускать другую программу между ними, и тогда она также включается в стену. Чтобы проиллюстрировать это, наряду со сравнением для строгого последовательного случае, я отправляю этот код:

#include <stdlib.h> 
#include <stdio.h> 
#include <sys/time.h> //gettimeofday() 
#include <time.h> 
#include <omp.h> 

#define DATA_TYPE float 

const int N = 1e9; 

int main() 
{ 
    int i, nthreads, tid; 
    DATA_TYPE x_seq, x_par, *y, *z; 
    struct timeval time; 
    double tstart_cpu, tend_cpu, tstart_wall, tend_wall; 
    double walltime_seq, walltime_par, cputime_seq, cputime_par; 

    nthreads = 8; 
    printf("- - -DOT PROCUCT: OPENMP - - -\n"); 
    printf("Vector size   : %d\n", N); 
    printf("Number of threads used: %d\n", nthreads); 


    // INITIALIZATION 

    y = (DATA_TYPE*)malloc(sizeof(DATA_TYPE)*N); 
    z = (DATA_TYPE*)malloc(sizeof(DATA_TYPE)*N); 

    for (i=0; i<N; i++) { 
     y[i] = i * 1.0; 
     z[i] = i * 2.0; 
    } 

    x_seq = 0; 
    x_par = 0; 


    // SEQUENTIAL CASE 

    gettimeofday(&time, NULL); 
    tstart_cpu = (double)clock()/CLOCKS_PER_SEC; 
    tstart_wall = (double)time.tv_sec + (double)time.tv_usec * .000001; 

    for (i=0; i<N; i++) x_seq += y[i] * z[i]; 

    tend_cpu = (double)clock()/CLOCKS_PER_SEC; 
    gettimeofday(&time, NULL); 
    tend_wall = (double)time.tv_sec + (double)time.tv_usec * .000001; 

    cputime_seq = tend_cpu-tstart_cpu; 
    walltime_seq = tend_wall - tstart_wall; 
    printf("Sequential CPU time: %f\n", cputime_seq); 
    printf("Sequential Walltime: %f\n", walltime_seq); 
    printf("Sequential result : %f\n", x_seq); 


    // PARALLEL CASE 

    gettimeofday(&time, NULL); 
    tstart_cpu = (double)clock()/CLOCKS_PER_SEC; 
    tstart_wall = (double)time.tv_sec + (double)time.tv_usec * .000001; 

    omp_set_num_threads(nthreads); 
    #pragma omp parallel for reduction(+:x_par) 
    for (i=0; i<N; i++) 
    { 
     x_par += y[i] * z[i]; 
    } 

    tend_cpu = (double)clock()/CLOCKS_PER_SEC; 
    gettimeofday(&time, NULL); 
    tend_wall = (double)time.tv_sec + (double)time.tv_usec * .000001; 

    cputime_par = tend_cpu - tstart_cpu; 
    walltime_par = tend_wall - tstart_wall; 
    cputime_par /= nthreads; // take the average cpu time per thread 
    printf("Parallel CPU time : %f\n", cputime_par); 
    printf("Parallel Walltime : %f\n", walltime_par); 
    printf("Parallel result : %f\n", x_par); 


    // SPEEDUP 

    printf("Speedup (cputime) : %f\n", cputime_seq/cputime_par); 
    printf("Speedup (walltime) : %f\n", walltime_seq/walltime_par); 

    return 0; 
} 

и типичный бег его выходы:

- - -DOT PROCUCT: OPENMP - - - 
Vector size   : 1000000000 
Number of threads used: 8 
Sequential CPU time: 4.871956 
Sequential Walltime: 4.878946 
Sequential result : 38685626227668133590597632.000000 
Parallel CPU time : 0.751475 
Parallel Walltime : 0.757933 
Parallel result : 133586303067416523805032448.000000 
Speedup (cputime) : 6.483191 
Speedup (walltime) : 6.437172 

Как вы можете видеть, что в результате точка продукт неверно, но это отвечает на начальный вопрос.

Смежные вопросы