Ваш полный тест очень неправильный. Вы посчитали время для обеих частей кода и для второй; не время первого раздела. Кроме того, вторая строка printf измерила время первого printf.
Первый запуск очень медленный, потому что здесь есть время запуска потока, инициализация памяти и эффекты кэширования. Кроме того, эвристика omp может быть настроена автоматически после нескольких параллельных областей
Моя версия вашего теста:
$ cat test.c
#include <stdio.h>
#include <omp.h>
void test( int n, int j)
{
int i ;
double t_a = 0.0, t_b = 0.0, t_c = 0.0 ;
t_a = omp_get_wtime() ;
#pragma omp parallel
{
for(i=0;i<n;i++) { }
}
t_b = omp_get_wtime() ;
for(i=0;i<n;i++) {
#pragma omp parallel
{ }
}
t_c = omp_get_wtime() ;
printf( "[%i] directive outside for-loop: %lf\n", j, 1000*(t_b-t_a)) ;
printf( "[%i] directive inside for-loop: %lf \n", j, 1000*(t_c-t_b)) ;
}
int main(void)
{
int i, n, j=3 ;
double t_1 = 0.0, t_2 = 0.0, t_3 = 0.0;
printf( "Input n: " ) ;
scanf( "%d", &n ) ;
while( j --> 0 ) {
t_1 = omp_get_wtime();
#pragma omp parallel
{
for(i=0;i<n;i++) { }
}
t_2 = omp_get_wtime();
for(i=0;i<n;i++) {
#pragma omp parallel
{ }
}
t_3 = omp_get_wtime();
printf( "[%i] directive outside for-loop: %lf\n", j, 1000*(t_2-t_1)) ;
printf( "[%i] directive inside for-loop: %lf \n", j, 1000*(t_3-t_2)) ;
test(n,j) ;
}
return 0 ;
}
Я сделал 3 прогона для каждого n внутри самой программы.
Результаты:
$ ./test
Input n: 1000
[2] directive outside for-loop: 5.044824
[2] directive inside for-loop: 48.605116
[2] directive outside for-loop: 0.115031
[2] directive inside for-loop: 1.469195
[1] directive outside for-loop: 0.082415
[1] directive inside for-loop: 1.455855
[1] directive outside for-loop: 0.081297
[1] directive inside for-loop: 1.462352
[0] directive outside for-loop: 0.080528
[0] directive inside for-loop: 1.455786
[0] directive outside for-loop: 0.080807
[0] directive inside for-loop: 1.467101
Затрагивается только первый запуск test()
. Все следующие результаты одинаковы для test
и main()
.
Более качественные и стабильные результаты получены при таком запуске (я использовал gcc-4.6.1 и статическую сборку)
$ OMP_WAIT_POLICY=active GOMP_CPU_AFFINITY=0-15 OMP_NUM_THREADS=2 ./test
Input n: 5000
[2] directive outside for-loop: 0.079412
[2] directive inside for-loop: 4.266087
[2] directive outside for-loop: 0.031708
[2] directive inside for-loop: 4.319727
[1] directive outside for-loop: 0.047563
[1] directive inside for-loop: 4.290812
[1] directive outside for-loop: 0.033733
[1] directive inside for-loop: 4.324406
[0] directive outside for-loop: 0.047004
[0] directive inside for-loop: 4.273143
[0] directive outside for-loop: 0.092331
[0] directive inside for-loop: 4.279219
Я установил две переменные среды производительности omp и ограничил число потоков до 2.
Также. У вас "параллельный" цикл неправильный. (и я воспроизвел эту ошибку в моем варианте ^^^) Переменная i находится здесь:
#pragma omp parallel
{
for(i=0;i<n;i++) { }
}
Вы должны иметь это как
#pragma omp parallel
{
for(int local_i=0;local_i<n;local_i++) { }
}
ОБНОВЛЕНИЕ7 Ваш результат для n = 1000:
[2] directive inside for-loop: 0.001188
[1] directive outside for-loop: 0.021092
[1] directive inside for-loop: 0.001327
[1] directive outside for-loop: 0.005238
[1] directive inside for-loop: 0.001048
[0] directive outside for-loop: 0.020812
[0] directive inside for-loop: 0.001188
[0] directive outside for-loop: 0.005029
[0] directive inside for-loop: 0.001257
0,001 или 0,02 для вашего кода - это .... секунды, умноженные на 1000, поэтому это миллисекунда (мс). И это ... около 1 микросекунды или 20 микросекунд. Гранулярность некоторых системных часов (user time
или system time
выходных полей утилиты time
) составляет от 1 миллисекунды, 3 мс или 10 мс. 1 микросекунда - 2000-3000 тактов процессора (для процессора 2-3 ГГц). Таким образом, вы не можете измерить такой короткий интервал времени без специальной настройки. Вы должны:
- отключить энергосбережение ЦП (Intel SpeedStep, AMD ???), которое может перевести ЦП в режим пониженного энергопотребления путем понижения его тактовой частоты (частоты);
- отключить динамический разгон процессора (Intel TurboStep);
- Измерять время без помощи ОС, например, читая TSC (
rdtsc
asm инструкция)
- Отключить переупорядочение команд на ЦП вне очереди (только атом не является процессором текущего поколения) до и после
rdtsc
, добавив инструкцию cpuid
(или другую инструкцию, которая отключит переупорядочение)
- Выполните запуск на полностью бесплатной системе (загрузка процессора 0% на оба процессора перед началом теста)
- переписать тест неинтерактивным способом (не ждите ввода от пользователя с помощью
scanf
, передайте n через argv[1]
)
- Не используйте Xserver и медленный терминал для вывода результатов
- Уменьшить число прерываний (физически отключить сеть; не воспроизводить фильм в фоновом режиме, не касаться мыши и клавиатуры)
- Выполнить много прогонов (я имею в виду не перезапуск программы, а перезапуск измеренной части программы; в моей программе j = 100) и добавить статистический расчет по результатам.
- Не запускайте printf так часто (между измерениями); это загрязнит тайники и TLB. Сохраняйте результаты внутри и выводите их после всех измерений.
ОБНОВЛЕНИЕ 8: В качестве статистики я имею в виду: принять несколько значений, 7 или более. Откажитесь от первого значения (или даже 2-3 первых значений, если у вас есть большое количество измеренных значений). Сортировать их. Откажитесь ... 10-20% от максимальных и минимальных результатов. Рассчитать среднее. Буквально
double results[100], sum=0.0, mean = 0.0;
int count = 0;
// sort results[5]..results[100] here
for(it=20; it< 85; it ++) {
count++; sum+= results[it];
}
mean = sum/count;