Ошибка заключалась в том, что было рассчитано общее время ЦП всех используемых ядер/потоков. Чтобы получить среднее время процессора для каждого потока, это значение необходимо разделить на количество потоков. Другим способом ее решения может быть измерение времени стены (т. е. разницы фактического времени дня до и после операции). Если используется время стены, то операционная система может запускать другую программу между ними, и это также включается в время стены. Чтобы проиллюстрировать это, вместе со сравнением для случая строгой последовательности я публикую этот код:
#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
Как видите, результирующее скалярное произведение неверно, но это отвечает на первоначальный вопрос.
person
neckutrek
schedule
26.05.2015
nthreads
на 4 и посмотрите, что произойдет. Если ваша архитектура поддерживает только 8 потоков, я не знаю, почемуomp_get_max_threads()
вернет 16. Что касается того, почему он замедляется для 8 против 1, когда вы запускаете его на 8 потоках, вы используете гиперпоточность. То есть у вас есть только 4 физических ядра, каждое из которых имеет по два потока. Каждый раз, когда вы используете оба потока на ядре для одного и того же процесса, это называется гиперпоточностью. - person   schedule 26.05.2015omp_get_wtime()
вместоclock()
? На какой ОС вы проводили эти тесты? - person Z boson   schedule 26.05.2015omp_get_wtime()
дает мне время 1,07 секунды вместо 6,65 секунды, если я использую часы() (для N = 1e9). Кто из них прав? Я использую ArchLinux. Я измеряю вне распараллеленной части программы. - person neckutrek   schedule 26.05.2015omp_get_wtime()
возвращает время стены. Это то, что ты хочешь.Clock
не возвращает время стены с несколькими потоками, за исключением библиотек MSFT C в Windows. Ваша проблема решена сейчас? - person Z boson   schedule 26.05.2015omp_get_wtime()
. В системе, где другие пользователи, вероятно, будут выполнять задания, которые могут иметь значение. В соответствии с этой link объединенное время процессора всех потоков как возвращаемое clock(), обычно больше, чем время настенных часов, измеренное omp_get_wtime(), за исключением случаев, когда ваше приложение в основном спит или ждет. Кажется, это не согласуется с вашим результатом, поэтому это был бы интересный вопрос. - person Z boson   schedule 26.05.2015