PARALLEL.RU

Дискуссионный клуб по параллельным вычислениям
Текущее время: 21 ноя 18 19:24

Часовой пояс: UTC + 4 часа [ Летнее время ]




Начать новую тему Ответить на тему  [ Сообщений: 14 ] 
Автор Сообщение
 Заголовок сообщения: Замер времени работы программы
СообщениеДобавлено: 6 дек 11 22:40 
Не в сети

Зарегистрирован: 6 дек 11 22:11
Сообщения: 3
Всем привет!
Я написал программу с использованием MPI (Message Passing Interface).
Мне нужно замерить время работы всей программы и отдельных ее частей. Для замера времени использовал функцию MPI_Wtime(). Когда тестировал программу на кластере, она выдавала не такие результаты, какие мне были нужны. Когда тестировал программу дома - результаты были хорошие (т.е. какие нужны). Тогда я стал разбираться в чем же дело, писать различные тесты и т.д. И вот один из тестов показал мне странный результат: я замерил время работы ДВУХ операций сложения чисел типа double и время работы ЧЕТЫРЕХ операций сложения чисел типа double. В результате получилось, что время работы четырех сложений почти такое же как и для двух сложений. Если подумать, то время работы двух сложений должно быть почти в два раза меньше, чем четырех. Может я что-то не учитываю или неправильно понимаю. Очень нужна помощь, т.к. сроки поджимают! У кого есть какие либо предположения или может кто-то знает в чем дело - поделитесь знаниями пожалуйста. Код описанного теста ниже (он простой и его не так много, как кажется). Результат двух операций сложения выводится как "Oper Time 1", четырех операций - "Oper Time 2".


Код:
#include <mpi.h>
#include <iostream>
#include <stdio.h>
#include <windows.h>

using namespace std;


//-----------------------------------------------------------------------------------------

int main(int argc, char* argv[])
{
   double N = 100000000;
   double StartOper1, EndOper1, StartOper2, EndOper2;

   int namelen, numprocs, rank;
   char processor_name[MPI_MAX_PROCESSOR_NAME];

   MPI_Init(&argc,&argv);
   
    MPI_Comm_size(MPI_COMM_WORLD,&numprocs);
    MPI_Comm_rank(MPI_COMM_WORLD,&rank);
    MPI_Get_processor_name(processor_name,&namelen);   

   cout<< "Process "<<rank<<" of "<<numprocs<<" is on "<<processor_name<<endl;   
   
   double Time1, Time2;
   double Time3, Time4;
   double TimeAVG = 0;
   double TimeAVG1 = 0;

//----------------------------------------------------------------------------------------
   double i;

   double x = 0;   
   double y = 0;
   double z = 0;

   StartOper1 = MPI_Wtime();
   for (i = 0; i<N; i++){
      Time3 = MPI_Wtime();
      x = 1;
      y = 4;   
      Time1 = MPI_Wtime();
      z = x + y;
      x = z + y;
      Time2 = MPI_Wtime();
      
      Time4 = MPI_Wtime();

      TimeAVG = TimeAVG + Time2 - Time1;
      TimeAVG1 = TimeAVG1 + Time4 - Time3;

   };
   EndOper1 = MPI_Wtime();

   cout<<endl;
   cout<< "All Time 1 = "<<(EndOper1 - StartOper1) <<endl;
   cout<< "Oper Time 1 = "<<TimeAVG <<endl;
   cout<< "Circle Time 1 = "<<TimeAVG1 <<endl;

//----------------------------------------------------------------------------------------

   StartOper2 = 0;
   EndOper1 = 0;
   TimeAVG = 0;
   TimeAVG1 = 0;

   StartOper2 = MPI_Wtime();
   for (i = 0; i<N; i++){
      Time3 = MPI_Wtime();
      x = 5;
      y = 7;
      Time1 = MPI_Wtime();
      z = x + y;
      x = z + y;
      y = z + x;
      x = y + y;
      Time2 = MPI_Wtime();
      
      Time4 = MPI_Wtime();

      TimeAVG = TimeAVG + Time2 - Time1;
      TimeAVG1 = TimeAVG1 + Time4 - Time3;

   };
   EndOper2 = MPI_Wtime();

   cout<<endl;
   cout<< "All Time 2 = "<<(EndOper2 - StartOper2) <<endl;
   cout<< "Oper Time 2 = "<<TimeAVG <<endl;
   cout<< "Circle Time 2 = "<<TimeAVG1 <<endl;

   MPI_Finalize();
   
   return 0;
};


Еще видел на форуме темы про какую-то синхронизацию времени - что-то связанное с MPI_WTIME_IS_GLOBAL. Если кто-то может, объясните, пожалуйста, что это и зачем надо.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Замер времени работы программы
СообщениеДобавлено: 6 дек 11 23:20 
Не в сети

Зарегистрирован: 3 дек 11 0:45
Сообщения: 13
Две операции сложения плюс две операции присвоения - итого в сумме 4 машинные инструкции.
Четыре операции сложения + четыре присвоения = 8 машинных инструкций.
Современные процессоры могут выполнять несколько инструкций за один такт.
Какими бы способами вы не замеряли время выполнения нескольких инструкций, основное значение вам даст сама процедура замера этого времени. Вы код MPI_Wtime() смотрели? Сколько инструкций там выполняется? Если хотите получить более точные значения - организуйте цикл сперва пустой, чем на большее число операций, тем лучше. Замерьте время время его выполнения. Примерно как вы и сделали, только время измеряйте за пределами цикла, а не внутри. Причём, лучше запустив несколько раз и найдя среднее значение времени. Потом добавьте в цикл те несколько операций. Запустите опять и из полученного значения вычтете время выполнения пустого цикла. Ну при этом, конечно, не забывайте, что работаете не на системе реального времени и периодически происходит переключение потоков.
MPI_WTIME_IS_GLOBAL если = 1, значит время для всех процессов в MPI_COMM_WORLD синхронизировано, то есть часы были сверены и приведены к единому значению, и значение времени, полученное на одной машине, будет соответствовать полученному на другой. В MPICH это реализовано, в OpenMPI нет. Хотя может сейчас и изменилось. Значение это можно получить через MPI_Comm_get_attr. А вот как синхронизировать время, я чего-то не понял. Или этот процесс происходит автоматически, если системы это позволяют. Можно, попробовать MPI_Comm_set_attr, но не знаю, какой результат это даст.
У меня MPI_Comm_get_attr возвращает 5797424. Ошибки не выдаёт, тип булевый. Значит время синхронизировано.
Попробовал изменить значение, не получилось. Видимо, это значение для контроля.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Замер времени работы программы
СообщениеДобавлено: 7 дек 11 2:13 
Не в сети

Зарегистрирован: 6 дек 11 22:11
Сообщения: 3
Попробовал изменить код исходя из ваших рекомендаций. Вот что получилось
Код:
#include <mpi.h>
#include <iostream>
#include <stdio.h>
#include <windows.h>

using namespace std;

#pragma optimize("", off)
//-----------------------------------------------------------------------------------------

int main(int argc, char* argv[])
{
   double N = 10000000;
   double StartOper1, EndOper1, StartOper2, EndOper2;

   double TEMP;
   double i;
   
   double x = 1;   
   double y = 1;
   double z = 0;

   MPI_Init(&argc,&argv);

//----------------------------------------------------------------------------------------
   StartOper1 = MPI_Wtime();
   for (i = 0; i<N; i++){
   };
   EndOper1 = MPI_Wtime();

   TEMP = EndOper1 - StartOper1;
   cout<<endl;
   cout<< "Empty circle Time 1 = "<<(EndOper1 - StartOper1) <<endl;

   StartOper1 = MPI_Wtime();
   for (i = 0; i<N; i++){
      z = x + y;
      x = z + y;
   };
   EndOper1 = MPI_Wtime();
   
   cout<<endl;
   cout<< "All Time 1 = "<<(EndOper1 - StartOper1) <<endl;
   cout<< "Oper Time 1 = "<<(EndOper1 - StartOper1) - TEMP <<endl;

//----------------------------------------------------------------------------------------

   StartOper2 = MPI_Wtime();
   for (i = 0; i<N; i++){
   };
   EndOper2 = MPI_Wtime();

   TEMP = EndOper2 - StartOper2;
   cout<<endl;
   cout<< "Empty circle Time 2 = "<<TEMP <<endl;

   StartOper2 = MPI_Wtime();
   for (i = 0; i<N; i++){
      z = x + y;
      x = z + y;
      y = z + x;
      x = y + y;
   };
   EndOper2 = MPI_Wtime();

   cout<<endl;
   cout<< "All Time 2 = "<<(EndOper2 - StartOper2) <<endl;
   cout<< "Oper Time 2 = "<<(EndOper2 - StartOper2) - TEMP <<endl;   

   MPI_Finalize();
   
   return 0;
};


результат получился такой:

Empty circle Time 1 = 0.153204

All Time 1 = 0.155395
Oper Time 1 = 0.00219044

Empty circle Time 2 = 0.154022

All Time 2 = 3.83317
Oper Time 2 = 3.67914

Мягко говоря очень странный результат. Но если поменять местами циклы в программе (код ниже), то результат получается очень даже правильный и логичный (см. ниже).
Код:
#include <mpi.h>
#include <iostream>
#include <stdio.h>
#include <windows.h>

using namespace std;

#pragma optimize("", off)
//-----------------------------------------------------------------------------------------

int main(int argc, char* argv[])
{
   double N = 10000000;
   double StartOper1, EndOper1, StartOper2, EndOper2;

   double TEMP;
   double i;
   
   double x = 1;   
   double y = 1;
   double z = 0;

   MPI_Init(&argc,&argv);

//----------------------------------------------------------------------------------------
   StartOper2 = MPI_Wtime();
   for (i = 0; i<N; i++){
   };
   EndOper2 = MPI_Wtime();

   TEMP = EndOper2 - StartOper2;
   cout<<endl;
   cout<< "Empty circle Time 2 = "<<TEMP <<endl;

   StartOper2 = MPI_Wtime();
   for (i = 0; i<N; i++){
      z = x + y;
      x = z + y;
      y = z + x;
      x = y + y;
   };
   EndOper2 = MPI_Wtime();

   cout<<endl;
   cout<< "All Time 2 = "<<(EndOper2 - StartOper2) <<endl;
   cout<< "Oper Time 2 = "<<(EndOper2 - StartOper2) - TEMP <<endl;      
   
//----------------------------------------------------------------------------------------
   StartOper1 = MPI_Wtime();
   for (i = 0; i<N; i++){
   };
   EndOper1 = MPI_Wtime();

   TEMP = EndOper1 - StartOper1;
   cout<<endl;
   cout<< "Empty circle Time 1 = "<<(EndOper1 - StartOper1) <<endl;

   StartOper1 = MPI_Wtime();
   for (i = 0; i<N; i++){
      z = x + y;
      x = z + y;
   };
   EndOper1 = MPI_Wtime();
   
   cout<<endl;
   cout<< "All Time 1 = "<<(EndOper1 - StartOper1) <<endl;
   cout<< "Oper Time 1 = "<<(EndOper1 - StartOper1) - TEMP <<endl;


   MPI_Finalize();
   
   return 0;
};


Результат:

Empty circle Time 2 = 0.151582

All Time 2 = 3.79057
Oper Time 2 = 3.63899

Empty circle Time 1 = 0.153596

All Time 1 = 1.97511
Oper Time 1 = 1.82151

С чем может быть связано такое различие из-за простой перестановки кода?
Тестировал много раз - результаты не меняются (ну с учетом погрешности конечно).


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Замер времени работы программы
СообщениеДобавлено: 7 дек 11 7:41 
Не в сети

Зарегистрирован: 3 дек 11 0:45
Сообщения: 13
Вот у меня типичное время выполнения для первого варианта:
Цитата:
Empty circle Time 1 = 0.0813891
All Time 1 = 0.0861892
Oper Time 1 = 0.00480005
Empty circle Time 2 = 0.0967799
All Time 2 = 0.124288
Oper Time 2 = 0.0275085

А вот для второго:
Цитата:
Empty circle Time 2 = 0.0826099
All Time 2 = 0.19816
Oper Time 2 = 0.11555
Empty circle Time 1 = 0.0955658
All Time 1 = 0.0882255
Oper Time 1 = -0.00734032

То есть цикл для двух операций вообще становится отрицательным по времени. Заметьте, время выполнения пустых циклов осталось почти тем же, в соответствии с порядком.
Во втором примере простая вставка кода:
Код:
   
z = x + y;
x = z + y;
z = x + y;
x = z + y;
z = x + y;
x = z + y;
z = x + y;
x = z + y;
z = x + y;
x = z + y;
z = x + y;
x = z + y;

перед вторым пустым циклом изменила результат на следующий:
Цитата:
Empty circle Time 2 = 0.0804259
All Time 2 = 0.198502
Oper Time 2 = 0.118076
Empty circle Time 1 = 0.080218
All Time 1 = 0.0946925
Oper Time 1 = 0.0144745

Но а если операции в цикле заменить все на:
z = x + y;
z1 = x1 + y1;
и
z = x + y;
z1 = x1 + y1;
z2 = x2 + y2;
z3 = x3 + y3;
то результат получается такой:
Цитата:
Empty circle Time 2 = 0.0934057
All Time 2 = 0.121505
Oper Time 2 = 0.0280991
Empty circle Time 1 = 0.090994
All Time 1 = 0.119714
Oper Time 1 = 0.0287199

Тут надо про низкоуровневую оптимизацию для современных процессоров почитать. Кэш, конвейер, линии предвыборки, суперскалярность
Цитата:
Отсюда ещё вывод: на скорость выполнения цикла влияет а) выравнивание начала цикла (т.е. точки, на которую осуществляется переход) в адресном пространстве б) расположение тела цикла относительно линии кэша (пересекает/ не перескает границу).
Не веришь - попробуй сам.
http://forum.ixbt.com/topic.cgi?id=40:1636-2


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Замер времени работы программы
СообщениеДобавлено: 7 дек 11 8:58 
Не в сети

Зарегистрирован: 25 июн 10 17:36
Сообщения: 35
Откуда: Тюмень
JuniorProger писал(а):
Код:
...
Time3 = MPI_Wtime();
x = 1;
y = 4;   
Time1 = MPI_Wtime();
...

ну, вообще-то и такое можно считать, тлько MPI_Wtime здесь уже не помошник,

поможет подсчет тактов,
делать это можно по разному, вот один из примеров: http://art-coding.blogspot.com/2010/05/blog-post.html
могу только добавить, что на SMP следует съем RDTSC привязать к определенному ядру, либо весь код привязать к ядру (это будет точнее), иначе могут появляться артефакты в виде отрицательных замеров, исходя из самой природы SMP


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Замер времени работы программы
СообщениеДобавлено: 7 дек 11 23:02 
Не в сети

Зарегистрирован: 3 дек 11 0:45
Сообщения: 13
http://iproc.ru/programming/windows-timers/
Вот ещё о измерении интервалов времени в виндовз. Там говориться и про GetPerformanceFrequency. Именно при помощи неё и реализуется под виндовз MPI_Wtime(), на юниксах - gettimeofday(). Разрешающая способность у этой функции порядка микросекунды. GetPerformanceFrequency можно использовать в связке со sleep для определения частоты процессора, так как у sleep разрешающая способность на три порядка меньше.
Если подсчитывать время выполнения двух четырёх операций при помощи rdtsc, то тоже надо делать поправку на процедуру измерения времени, так как при использовании ассемблерных вставок код будет примерно такой (для х86):
Код:
   LARGE_INTEGER lstart, lend;
   __asm{
      rdtsc
      mov      lstart.LowPart, eax
      mov      lstart.HighPart, edx
   }
   x=y+z;
   y=z+x;
    __asm{
      rdtsc
      mov      lend.LowPart, eax
      mov      lend.HighPart, edx
   }
cout<<"Cycle count: "<<lend.QuadPart-lstart.QuadPart<<endl;

У меня такой код даёт в среднем от 229 до 272, иногда и за 300.
С закомментированными
x=y+z;
y=z+x;
226-260, один раз выдал 90.
Вставка перед первым измерением простой последовательности:
Код:
x=y+z;
y=z+x;
x=y+z;
y=z+x;
x=y+z;
y=z+x;
x=y+z;
y=z+x;
x=y+z;
y=z+x;
x=y+z;
y=z+x;
x=y+z;
y=z+x;
x=y+z;
y=z+x;
x=y+z;
y=z+x;
x=y+z;
y=z+x;
x=y+z;
y=z+x;
x=y+z;
y=z+x;
x=y+z;
y=z+x;
x=y+z;
y=z+x;
x=y+z;
y=z+x;
x=y+z;
y=z+x;
x=y+z;
y=z+x;

привела к тому, что типичное значение стало в районе 320, а иногда и стало заходить за 400.
Это замеры проводились с включённой системой снижения частонты и напряжения питания ядер при отсутствии загруженности - AMD OverDrive. С отключённой, расскомментированный код, в среднем 80, закомментированный практически так же.

__________________________________________________________________________
Решил проверить, сколько же тиков занимает выполнение самой команды rdtsc, без учёта возможных задержек на обращение к оперативке.
Код:
LARGE_INTEGER lstart, lend;
   __asm{
           push   ebx
   push   ecx
   rdtsc
   mov      ebx, eax
   mov      ecx, edx      
   rdtsc
   sub      eax, ebx
   sbb      edx, ecx
   mov      lend.LowPart, eax
   mov      lend.HighPart, edx
   pop   ecx
   pop   ebx
   }   
   cout<<"Cycle count: "<<lend.QuadPart<<endl;

По прежнему держиться в районе 80 тиков. В то время как обычные инструкции сложения, присвоения и тд. выполняются за один, а при независимости данных и по несколько инструкций за тик. Так что для измерения времени выполнения нескольких инструкций rdtsc тоже не годиться. Возможно, на других процессорах он будет реализован как-то по другому и давать меньшую задержку, но опять же, если мы хотим получать стабильные результаты на разных системах это не пойдёт.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Замер времени работы программы
СообщениеДобавлено: 8 дек 11 4:58 
Не в сети

Зарегистрирован: 6 дек 11 22:11
Сообщения: 3
Большое спасибо всем за ответы!
Сначала напишу ответ ко всем сообщениям:
Моя программа выполняет Быстрое Преобразование Фурье (БПФ или FFT). Мне нужно измерить время работы программы в случае последовательного алгоритма, когда все входные данные обрабатываются на одном процессоре, и параллельного алгоритма, когда часть (части) данных обрабатывается (обрабатываются) на нескольких процессорах. Нужно было использовать MPI. Программу нужно протестировать дома (у меня двухъядерный процессор) и на кластере в универе. Дома все работает отлично и показывает необходимое мне ускорение (примерно 1,81 раза) параллельного алгоритма относительно последовательного. На кластере ускорение примерно 1,42. На кластере стоит Линукс. Есть компиляторы gcc и итнтеловский (intel).
Мне нужно измерить время работы всей программы (так сказать астрономическое) с учетом пересылок данных на другой процессор.

Алгоритм последовательного случая такой:
1. процесс 0 замеряет время функцией MPI_Wtime() - "Замер 0".
2. процесс 0 обрабатывает данные - "Обработка 0".
3. процесс 0 замеряет время функцией MPI_Wtime() - "Замер 1".
4. процесс 0 вычитает из "Замер 1"-"Замер 0" получается время, потраченное на "Обработка 0" оно же время всей программы.

Алгоритм параллельного случая такой:
1. процесс 0 (он главный) замеряет время функцией MPI_Wtime() - "Замер 0".
2. процесс 0 отсылает данные другому с номером 1.
3. процесс 0 замеряет время функцией MPI_Wtime() - "Замер 1".
4. процесс 0 обрабатывает свою часть данных - "Обработка 0", а процесс 1 обрабатывает свои полученные от 0 процесса данные - "Обработка 1".
5. процесс 0 замеряет время функцией MPI_Wtime() - "Замер 2".
6. процесс 0 получает обработанные данные от процесса 1.
7. процесс 0 делает конечное преобразование
8. процесс 0 замеряет время функцией MPI_Wtime() - "Замер 3".
9. процесс 0 вычитает из "Замер 2"-"Замер 1" получается время, потраченное на "Обработка 0".
10. процесс 0 вычитает из "Замер 3"-"Замер 0" получается время, потраченное на всю программу.

Количество операций в "Обработка 0" для последовательного варианта почти в 2 раза больше.

Далее я смотрел соотношение времени работы алгоритмов целиком "соотношение 0" (последовательное/параллельное), а также соотношение времени, затраченного на "Обработка 0" (последоват "Обработка 0" / параллел "Обработка 0"). Первое отношение (примерные): дома 1,81 на кластере 1,42. Второе отношение (примерные): дома 1,88 на кластере 1,55.

Теперь конкретно:
mega, ознакомился со статьей про __rdtsc() - спасибо. Можно эту функцию применить к описанным мной алгоритмам (сделать эти замеры времени)? Покажет ли она астрономическое время? Наверное нет.

Subrealist, спасибо, что потестил мой тест. Я думал что никто его даже и смотреть не станет, а ты потестил! Прочитал обсуждение на ixbt.com по ссылке, которую ты дал. Т.к. я почти не знаю Ассемблер и всяких других штук, про которые там пишут, то мало чего понял. Есть опыт работы с функциями GetPerformanceFrequency и GetPerformanceCounter - знаю про них. А откуда информация
Цитата:
Вот ещё о измерении интервалов времени в виндовз. Там говориться и про GetPerformanceFrequency. Именно при помощи неё и реализуется под виндовз MPI_Wtime(), на юниксах - gettimeofday().
Можно ссылку? И еще, если есть реализация MPI_Wtime() под виндовз и линукс, можешь скинуть ссылку?

Хочу подытожить следующими вопросами:
Почему дома получились одни соотношения времен, а на кластере совсем уж другие? Ведь различия не в сотых, а в десятых и, причем, большие. А если взять и поменять тип исходных данных на другой (с float на double) то соотношения становятся намного хуже - параллельный вариант работает почти как последовательный по времени :shock:
Как лучше замерять время (астрономическое) в описанном мной алгоритме? Стоит использовать MPI_Wtime() или лучше "тиковые" функции? Каким компилятором лучше пользоваться на кластере gcc или интеловским?

P.S. Написал много,а в голове есть еще восросы :) Но надеюсь кто-нибудь дочитает до этой строчки :)


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Замер времени работы программы
СообщениеДобавлено: 8 дек 11 9:37 
Не в сети

Зарегистрирован: 25 июн 10 17:36
Сообщения: 35
Откуда: Тюмень
Subrealist писал(а):
Если подсчитывать время выполнения двух четырёх операций при помощи rdtsc, то тоже надо делать поправку на процедуру измерения времени, так как при использовании ассемблерных вставок код будет примерно такой (для х86):
Код:
   LARGE_INTEGER lstart, lend;
   __asm{
      rdtsc
      mov      lstart.LowPart, eax
      mov      lstart.HighPart, edx
   }
   x=y+z;
   y=z+x;
    __asm{
      rdtsc
      mov      lend.LowPart, eax
      mov      lend.HighPart, edx
   }
cout<<"Cycle count: "<<lend.QuadPart-lstart.QuadPart<<endl;


Subrealist, т.е. по вашему, из-за 4-х неблокируемых операций mov следует отказаться от rdtsc в пользу QueryPerformanceCounter?

я вас огорчу, QueryPerformanceCounter - это функция ядра,
и помимо того, что в ней гораздо больше операций (сам вызов функции я даже не рассматриваю):
Код:
mov         edi,edi 
push        ebp 
mov         ebp,esp 
push        ecx 
push        ecx 
lea         eax,[ebp-8] 
push        eax 
push        dword ptr [ebp+8] 
call        dword ptr ds:[7C8013DCh] 
test        eax,eax 
jl          7C841A8E 
cmp         dword ptr [ebp-8],0 
je          7C841A98 
xor         eax,eax 
inc         eax 
leave 
ret         4 

она еще может спокойно ждать переключения контекста, причем неоднократного еще до того как войдет в sysenter (после которого идет еще неизвестно сколько кода)

а __rdtsc - это intrinsic-функция ( асемблер тут был и не нужен ), которая запирает текущий аппаратный (TSC) счетчик в паре регистров,

и если мы опять же хотим получать стабильные результаты на разных системах, то повторяю:
mega писал(а):
на SMP следует съем RDTSC привязать к определенному ядру, либо весь код привязать к ядру (это будет точнее)


точнее RDTSC будет только его аналог: железный, независимый высокоточный счетчик - отдельное устройство, работающее на своей частоте, и даже в этом случае издержки на получение его текущего состояния будут всегда выше издержек на съем RDTSC


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Замер времени работы программы
СообщениеДобавлено: 9 дек 11 6:40 
Не в сети

Зарегистрирован: 3 дек 11 0:45
Сообщения: 13
mega писал(а):
Subrealist, т.е. по вашему, из-за 4-х неблокируемых операций mov следует отказаться от rdtsc в пользу QueryPerformanceCounter?

Вовсе не стоит. Вы меня не так поняли. Раннее я писал в этом топике, что какие бы мы способы не применяли для измерения времени выполнения нескольких операций сама процедура замера времени у нас займёт больше времени, чем сами операции. Для десятков операций ситуация может быть другой. Работали бы мы в системе реального времени, или в режиме ядра, когда можно было бы запретить все прерывания, тогда да, мы могли бы более точно измерить задержку на процедуру измерения и вычесть её из полученного результата. А в пользу QueryPerformanceCounter для замера времени выполнения по сравнению с rdtsc я ничего не писал. Наоборот, я прямо указал, что она позволяет измерять интервалы лишь от микросекунды. А за микросекунду на современных процессорах происходят тысячи тиков. Я лишь советовал использовать QueryPerformanceCounter в связке со sleep для более точного измерения частоты процессора, так как у неё точность на три порядка больше, чем у sleep. Естественно, сами тики процессора при этом должны измеряться rdtsc. Не QueryPerformanceCounter их же мерять.
mega писал(а):
а __rdtsc - это intrinsic-функция ( асемблер тут был и не нужен ), которая запирает текущий аппаратный (TSC) счетчик в паре регистров,

Ну не совсем не нужен. По крайней мере ассемблерный код более наглядно показывает, какой минимальный набор инструкций нужно выполнить для получения числа тиков, раз уж речь идёт о выяснении онного всего для нескольких операций. К тому же, ассемблерный код позволяет точнее установить задержку, даваемую rdtsc, без учёта возможных задержек на сохранение результата в памяти, загрузку кэш-линий и прочее, через непосредственное сохранение результата в регистры.
О привязке к ядру. Когда в продаже появились первые многоядерные процессоры, некоторые популярные игры стали давать сбой. Именно из-за того, что вызывали rdtsc на разных ядрах. Тогда производители стали делать так, что rdtsc стала возвращать одинаковое значение для всех ядер. Как там с этим дело обстоит сейчас, я не знаю. Но, покрайней мере, следует учитывать тот факт, что на разных процессорах значение, возвращаемое rdtsc может вычисляться по разному.
Я совершенно не подвергаю критике использование rdtsc для наиболее точного замера производительности, хотя на системах, где множитель частоты в зависимости от загруженности может динамически менятся в несколько раз (например, у меня он меняется от 4 до 17), это находится под вопросом. Я лишь протестировал предложенный вами способ:
mega писал(а):
JuniorProger писал(а):
Код:
...
Time3 = MPI_Wtime();
x = 1;
y = 4;   
Time1 = MPI_Wtime();
...

ну, вообще-то и такое можно считать, тлько MPI_Wtime здесь уже не помошник,

поможет подсчет тактов,
делать это можно по разному, вот один из примеров: http://art-coding.blogspot.com/2010/05/blog-post.html

И пришёл к выводу, что для подобного он не годится. Возможно я использовал не правильный код. Если вы считаете что это так и для вас не составит труда, пожалуйста, приведите правильный, с интересом его протестирую
Цитата:
и если мы опять же хотим получать стабильные результаты на разных системах, то повторяю:
mega писал(а):
на SMP следует съем RDTSC привязать к определенному ядру, либо весь код привязать к ядру (это будет точнее)

При запуске через менеджер процессов MPICH, да и других подобных систем, наверное, тоже, об этом можно не беспокоится. Он осуществляет привязку процесса к ядру сам.
[quote=JuniorProger]Можно ссылку? И еще, если есть реализация MPI_Wtime() под виндовз и линукс, можешь скинуть ссылку?[/quote]
Ссылку - уже не помню где смотрел, узнать такую информацию не сложно. То, что я написал по этому поводу, оказалось не совсем верно. Реализация таймера для MPI_Wtime() под одной и той же операционной системой может быть различна. Тип используемого счётчика можно указывать при конфигурировании. Смотрим исходный код. Первый файл для реализации MPI_Wtime() mpich2-1.4.1p1\src\mpi\timer\wtime.c, смотрим, что вызывается и дальше по цепочке, вызывая поиск в папке с исходным кодом.
Попробуй используя хотя бы rdtsc вычислить задержки на коммуникацию дома и на кластере. Только не забывай, что сама процедура измерения тоже занимает время) При исполнении на одном компьютере в качестве средств обмена может использоваться общая память, при выполнении на разных сообщение идёт через сокеты, но характер использования сокетов в разных типах каналов может быть различен. И какая бы скоростная сеть не была, но сама отправка сообщения через сокеты требует достаточно времени, по сравнению с передачей через общую память, хотя бы потому, что приходится переключатся в режим ядра и обратно, а это процедура затратная. Поищи про sock & nemesis. Я подробностей не знаю. Даже при выполнении на одном компьютере сокеты должны давать большую задержку. Тип используемых каналов можно указамть через wmpiconfig, причём отдельно для межузлового и внутриузововго сообщения. Астрономическое время через rdtsc получить можно, для этого надо определить частоту процессора, причём делать это надо, как я писал, не просто через sleep, а используя sleep лишь для создания задержки, а сам промежуток времени измерять через QueryPerformanceFrequency, QueryPerformanceCounter, но при этом надо не забывать, что частота процессора может плавать, а если устоновленно специальное по, то оно в зависимости от загрузки может, вообще менять множитель, изменяя частоту в несколько раз. Измеряешь частоту, подсчитываешь тики, делишь их на частоту. Вот тебе и астрономическое время. Но если речь идёт о времени выполнения порядка секунды, то MPI_Wtime себя вполне оправдает.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Замер времени работы программы
СообщениеДобавлено: 12 дек 11 14:49 
Не в сети

Зарегистрирован: 25 июн 10 17:36
Сообщения: 35
Откуда: Тюмень
Subrealist писал(а):
... какие бы мы способы не применяли для измерения времени выполнения нескольких операций сама процедура замера времени у нас займёт больше времени, чем сами операции. Для десятков операций ситуация может быть другой.

а я пытаюсь донести до вас мысль о том, что не так важно, сколько операций займет процедура замера времени,
важнее то, чтобы число этих операций было постоянным,

а издержки всегда будут
Subrealist писал(а):
Я лишь советовал использовать QueryPerformanceCounter в связке со sleep для более точного измерения частоты процессора, так как у неё точность на три порядка больше, чем у sleep.

а вот тут уже интереснее :)
что имеется ввиду?

и я честно говоря не понял, какая зависимость между точностью sleep и QueryPerformanceCounter, и откуда эти данные по точности?
Subrealist писал(а):
... ассемблерный код более наглядно показывает, какой минимальный набор инструкций нужно выполнить для получения числа тиков, раз уж речь идёт о выяснении онного всего для нескольких операций. К тому же, ассемблерный код позволяет точнее установить задержку, даваемую rdtsc, без учёта возможных задержек на сохранение результата в памяти, загрузку кэш-линий и прочее, через непосредственное сохранение результата в регистры.

я бы согласился, если бы вместо y=x+z стоял набор ассемблерных команд
Subrealist писал(а):
Тогда производители стали делать так, что rdtsc стала возвращать одинаковое значение для всех ядер. Как там с этим дело обстоит сейчас, я не знаю. Но, покрайней мере, следует учитывать тот факт, что на разных процессорах значение, возвращаемое rdtsc может вычисляться по разному.

ничего не слышал о том, чтобы rdtsc возвращала одинаковые значения для всех ядер ( думаю, аппаратно реализовать такое очень сложно, это все равно что "разработать многоядерный процессор", что не оправдывает саму его природу: разместить типовые процессоры на одном кристалле ), возможно вы имели ввиду, что "производители игр" стали запирать вызов rdtsc на одном ядре, чтобы она возвращала значение только по этому ядру :), но об этом я и говорил
Subrealist писал(а):
Я лишь протестировал предложенный вами способ...И пришёл к выводу, что для подобного он не годится

а вот я не смог к такому выводу придти, потому что просто не увидел никаких сравнительных данных в вашем тесте, увидел только голословное утверждение, основанное на одном значении:
Subrealist писал(а):
320, а иногда и стало заходить за 400

единственное заключение, которое отсюда можно вынести - это то что 320 безусловно меньше 400 :)

сделайте парный "замер времени" (со sleep) и "замер времени выполнения нескольких операций" пару сотен раз, на нескольких машинах, с фиксированной привязкой к каждому ядру (только не уповая на менеджер процессов, а честно, закрепив маску через SetProcessAffinityMask, MPI тут строго говоря не нужен и даже вреден для чистоты эксперимента), выведите полученные значения в табличку, *.csv, например, чтобы народ мог посмотреть на график и убедиться в том, что ваши "потери на операциях mov" имеют хоть какие-то обоснования для последующих расчетов

ну естественно, сделайте то же самое на базе какой-нибудь другой операции замера, например, той же MPI_Wtime и/или GetPerformanceCounter, ( кстати, параллельно с этим тестом можно определить и точность GetPerformanceFrequency относительно rdtsc со Sleep )

тогда и будет что обсуждать


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Замер времени работы программы
СообщениеДобавлено: 12 дек 11 20:58 
Не в сети

Зарегистрирован: 3 дек 11 0:45
Сообщения: 13
mega писал(а):
а я пытаюсь донести до вас мысль о том, что не так важно, сколько операций займет процедура замера времени,
важнее то, чтобы число этих операций было постоянным,
а издержки всегда будут

Ага, если процедура замера у нас занимает от 75 до 85 тиков, а операции занимают 4 тика, а может и меньше, то конечно же, число тиков для самой процедуры мы можем считать постоянной
mega писал(а):
а вот тут уже интереснее :)
что имеется ввиду?
и я честно говоря не понял, какая зависимость между точностью sleep и QueryPerformanceCounter, и откуда эти данные по точности?

Код:
unsigned __int64 rdtsc1, rdtsc2, cpufreq;
LARGE_INTEGER count1, count2, freq;
QueryPerformanceFrequency(&freq);
rdtsc1= __rdtsc();
QueryPerformanceCounter(&count1);
Sleep(1000);
rdtsc2= __rdtsc();
QueryPerformanceCounter(&count2);
cpufreq=(rdtsc2-rdtsc1)/((count2.QuadPart-count1.QuadPart)/freq.QuadPart);

Данные по точности из того значения, которое возвращает QueryPerformanceFrequency и того, которое принимает Sleep.
mega писал(а):
я бы согласился, если бы вместо y=x+z стоял набор ассемблерных команд

А что вам мешает его в уме подставить? Или сделать обратную замену ассемблерного кода на intrinsic функцию? В чём проблема то? Мы вроде здесь и сейчас обсуждаем методы замера времени выполнения программы, а не идеологически верный стиль представления кода.
mega писал(а):
( думаю, аппаратно реализовать такое очень сложно, это все равно что "разработать многоядерный процессор", что не оправдывает саму его природу: разместить типовые процессоры на одном кристалле )

А в чём такая уж сложность? Учитывая, что у меня сама голая процедура замера времени занимает 80 тактов, реализована инструкция rdtsc не совсем просто и в микрокоде занимает не одну операцию.
mega писал(а):
а вот я не смог к такому выводу придти, потому что просто не увидел никаких сравнительных данных в вашем тесте, увидел только голословное утверждение, основанное на одном значении:
единственное заключение, которое отсюда можно вынести - это то что 320 безусловно меньше 400 :)

Subrealist писал(а):
Это замеры проводились с включённой системой снижения частонты и напряжения питания ядер при отсутствии загруженности - AMD OverDrive. С отключённой, расскомментированный код, в среднем 80, закомментированный практически так же.

Того, что я писал, о том, что число тиков что с выполнением нескольких операций, что без них составляет практически одинаковое время вы не заметили?
mega писал(а):
сделайте парный "замер времени" (со sleep) и "замер времени выполнения нескольких операций" пару сотен раз, на нескольких машинах, с фиксированной привязкой к каждому ядру (только не уповая на менеджер процессов, а честно, закрепив маску через SetProcessAffinityMask, MPI тут строго говоря не нужен и даже вреден для чистоты эксперимента), выведите полученные значения в табличку, *.csv, например, чтобы народ мог посмотреть на график и убедиться в том, что ваши "потери на операциях mov" имеют хоть какие-то обоснования для последующих расчетов

Я народ не в чём убеждать не пытаюсь). Лишь делюсь с ним своими выводами и соображениями. И по крайней мере, я привёл хоть какие-то результаты проверки предложенного вами метода, в месте с тем кодом, на котором я его проверял. Для меня результат очевиден. Если с ним кто-то не согласен, и ему это интересно, он может без труда мои выводы перепроверить. Или изменить код замера так, что ваш способ будет работать. При чём работать именно для нескольких операций, типа сложения-присвоения, как вы и писали.
mega писал(а):
сделайте парный "замер времени" (со sleep) и "замер времени выполнения нескольких операций" пару сотен раз

Замер времени пары сотен операций дело другое. Метод то был предложен для:
Цитата:
x = 1;
y = 4;

mega писал(а):
тогда и будет что обсуждать

Обсуждать будет что тогда, когда бедет приведён код и результаты его тестирования, подтверждающие, что предложенный вами метод действительно работает относительно того, к чему был вами предложен. А пока в доказательство его работы приводилась лишь несравненная крутость intrinsic-функции __rdtsc и её неоспоримое превосходство над QueryPerformanceCounter, а вся его неработоспособность объясняется отсутствием вызова SetProcessAffinityMask, ну или вставками ассемблерного кода)


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Замер времени работы программы
СообщениеДобавлено: 13 дек 11 11:29 
Не в сети

Зарегистрирован: 25 июн 10 17:36
Сообщения: 35
Откуда: Тюмень
Subrealist писал(а):
Данные по точности из того значения, которое возвращает QueryPerformanceFrequency и того, которое принимает Sleep.

если можно, поподробнее,
из этого, я ничего не понял, честно говоря

какая связь может быть между QueryPerformanceFrequency и Sleep в отношении точности?
Subrealist писал(а):
А что вам мешает его в уме подставить?

в уме его представить вообще-то невозможно,
вернее, конечно можно, но это уже будут исключительно ваши фантазии,
а скомпилирован он будет по строго заданным законам оптимизации, принятым тем или иным компилятором (и программистом, конечно же), так что о том, сколько там будет инструкций и сколько они будут считаться вы опять же можете только фантазировать, пока не увидите листинг машинного кода
Subrealist писал(а):
Или сделать обратную замену ассемблерного кода на intrinsic функцию? В чём проблема то?

вот поэтому я и сказал, что асм тут был лишний
Subrealist писал(а):
А в чём такая уж сложность? Учитывая, что у меня сама голая процедура замера времени занимает 80 тактов, реализована инструкция rdtsc не совсем просто и в микрокоде занимает не одну операцию.

сложность не в реализации инструкции, а в том, чтобы синхронизировать аппаратные счетчики на разных процессорах, учитывая то, что сама rdtsc не должна синхронизироваться даже с кэшем, т.е. она не ждет завершения каких-либо уже начатых или законченных, но не возвращенных операций, а выполняется непосредственно тогда, когда ее позвали, поэтому ее результат по большей части зависит от окружения, в котором она используется,

по этому поводу intel и пишет:
Цитата:
The RDTSC instruction is not a serializing instruction. It does not necessarily wait
until all previous instructions have been executed before reading the counter. Similarly,
subsequent instructions may begin execution before the read operation is
performed. If software requires RDTSC to be executed only after all previous instructions
have completed locally, it can either use RDTSCP (if the processor supports that
instruction) or execute the sequence LFENCE;RDTSC.

вот и вызывайте intrinsic _mm_lfence перед замером,
у меня пустой замер занимает 24 тика TSC с LFENCE и порядка 500-700 - без нее,
аналогичные результаты и при закреплении расчета на одном ядре - 24 тика, на 4-хядерном Core i3

так что берем поправку "на ветер" и вперед, с музыкой и максимально-доступной точностью,
только надо учесть инертность таких подходов,
можно сказать, это "идеальное", "нереальное", а по вашему, видимо - "астрономическое" время,
которого в современном процессе быть не может, поскольку машинный код может быть и распараллелен по нескольким конвейерам, и размазан по нескольким микрокомандам на этом конвейере, и реальная скорость может измениться уже просто исходя из порядка выполнения операций, а учитывая дополнительную синхронизацию с кешами на расчет "астрономического" времени, погрешности тут неизбежны, причем именно в сторону увеличения времени расчетов

а ваши пара сотен операций - как раз под это определение не подходит,
Subrealist писал(а):
Я народ не в чём убеждать не пытаюсь)

"свежо придание, да верится с трудом"
Subrealist писал(а):
А пока в доказательство его работы приводилась лишь несравненная крутость intrinsic-функции __rdtsc и её неоспоримое превосходство над QueryPerformanceCounter, а вся его неработоспособность объясняется отсутствием вызова SetProcessAffinityMask, ну или вставками ассемблерного кода)

я конечно крут, но не до такой степени )),
свои результаты и представил и объяснил, и я не предлагал сделать замер на паре сотен операций, я предлагал показать статистику на паре сотен итераций, если вы не поняли,

а вы, если честно, меня пока только рассмешили невнятным "из того значения, которое возвращает QueryPerformanceFrequency и того, которое принимает Sleep."


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Замер времени работы программы
СообщениеДобавлено: 16 дек 11 9:57 
Не в сети

Зарегистрирован: 3 дек 11 0:45
Сообщения: 13
mega писал(а):
если можно, поподробнее,
из этого, я ничего не понял, честно говоря

Если по подробнее, то точность QueryPerformanceCounter порядка микросекунды, а интервал у Sleep задаётся в миллисекундах. Ну конечно же, здесь стоит сделать поправку на происходящие прерывания, переключения контекста, но всё же, если при замере частоты руководствоваться значением времени, получаемым при вызовах QueryPerformanceCounter, то можно получить более точное значение, чем полагаясь на значение интервала, передаваемого Sleep и на то, что пробуждение потока наступит в точности через указанное время.
mega писал(а):
в уме его представить вообще-то невозможно,
вернее, конечно можно, но это уже будут исключительно ваши фантазии,
а скомпилирован он будет по строго заданным законам оптимизации, принятым тем или иным компилятором (и программистом, конечно же), так что о том, сколько там будет инструкций и сколько они будут считаться вы опять же можете только фантазировать, пока не увидите листинг машинного кода

Совершенно верно. Поэтому то подставлять вместо операций на си ассемблерный код не имело никакого смысла. Но показать минимальный код, требующийся для выполнения замера времени смысл был.
mega писал(а):
вот поэтому я и сказал, что асм тут был лишний

С тем же успехом можно сказать, что использование функции __rdtsc лишнее, так как она скрывает реализацию метода, в отличии от кода на асм. Но я так не говорю, так как каждый способ представления кода имеет свои достоинства и недостатки.
mega писал(а):
сложность не в реализации инструкции, а в том, чтобы синхронизировать аппаратные счетчики на разных процессорах, учитывая то, что сама rdtsc не должна синхронизироваться даже с кэшем, т.е. она не ждет завершения каких-либо уже начатых или законченных, но не возвращенных операций, а выполняется непосредственно тогда, когда ее позвали, поэтому ее результат по большей части зависит от окружения, в котором она используется,

по этому поводу intel и пишет:
...
вот и вызывайте intrinsic _mm_lfence перед замером,
у меня пустой замер занимает 24 тика TSC с LFENCE и порядка 500-700 - без нее,
аналогичные результаты и при закреплении расчета на одном ядре - 24 тика, на 4-хядерном Core i3

так что берем поправку "на ветер" и вперед, с музыкой и максимально-доступной точностью

только надо учесть инертность таких подходов,
можно сказать, это "идеальное", "нереальное", а по вашему, видимо - "астрономическое" время,
которого в современном процессе быть не может, поскольку машинный код может быть и распараллелен по нескольким конвейерам, и размазан по нескольким микрокомандам на этом конвейере, и реальная скорость может измениться уже просто исходя из порядка выполнения операций, а учитывая дополнительную синхронизацию с кешами на расчет "астрономического" времени, погрешности тут неизбежны, причем именно в сторону увеличения времени расчетов

Вот, вот, вот. А о чём собственно спор был? Я с самого начала это самое и утверждал, что временной интервал для нескольких простых операций точно измерить и чтобы эта точность была стабильной для разного кода и для разных систем попросту невозможно. Для достаточно крупных блоков кода, да, rdtsc лучший метод, но я с этим и не спорил. Или вы как-то по другому поняли смысл мной написаного ранее?


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Замер времени работы программы
СообщениеДобавлено: 23 дек 11 21:29 
Не в сети

Зарегистрирован: 11 дек 02 19:37
Сообщения: 872
Откуда: НИВЦ МГУ
Прошу прощения, но вы пытаетесь линейкой микробов мерить. И удивляетесь точности. А в реальной рпограмме будут вообще другие цифры - это точно. Вы упускаете массу вещей - оптимизацию суперскалярности, например, работу с кешем, и т.п.
Если хочется померить %topic%, то возьмите не 2-4 операции, а всё счётное ядро, запустите его на модельных данных и меряйте. А то, что вы тут пишите - полная фигня, не пригодная для реальной оценки работы вашей программы.


Вернуться к началу
 Профиль  
 
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 14 ] 

Часовой пояс: UTC + 4 часа [ Летнее время ]


Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 2


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Перейти:  
cron
Создано на основе phpBB® Forum Software © phpBB Group
Русская поддержка phpBB