Перейти к содержимому


Фотография
- - - - -

Время исполнения (как измерять)


  • Please log in to reply
Сообщений в теме: 26

#21 kanua

kanua

    Свой

  • Пользователи
  • PipPip
  • 372 сообщений
  • МестоположениеКиев

Отправлено 13 July 2013 - 01:09

 
Это стандартная виндовая функция возвращает системный таймер, точность 10...16 мс.
Вот я тут гораздо гораздее написал:
 

#import "Kernel32.dll"
int QueryPerformanceFrequency(int &a[]);
int QueryPerformanceCounter(int &a[]);
//+------------------------------------------------------------------+
//| PreciseTime(0) - начало отсчета; точность - микросекунда; |
//| PreciseTime(1) - окончание отсчета, возврат времени в мс; |
//+------------------------------------------------------------------+
double PreciseTime(int c)
{
static int start[2], end[2], freq[2];
if(c==0) QueryPerformanceCounter(start);
else
{
QueryPerformanceCounter(end);
if(QueryPerformanceFrequency(freq)==0) return(0);
double res=end[0]-start[0];
return(NormalizeDouble(res/freq[0]*1000.0,3));
}
}/////////////////////////////////////////////////////////////////////

 

Надо иметь ввиду, что использование функции QueryPerformanceCounter в таком виде предполагает постоянство частоты процессора, а современные процессоры могут её самостоятельно менять, например, для экономии энергии. Особенно это актуально для ноутбуков. Поэтому лучше проверять частоту вначале и в конце измеряемого диапазона времени, а потом сравнивать эти значения, ну, или брать среднее. Как-то так.


  • 1

#22 kanua

kanua

    Свой

  • Пользователи
  • PipPip
  • 372 сообщений
  • МестоположениеКиев

Отправлено 13 July 2013 - 02:54

 #import "Kernel32.dll"
int QueryPerformanceFrequency(int &a[]);
int QueryPerformanceCounter(int &a[]);
//+------------------------------------------------------------------+
//| PreciseTime(0) - начало отсчета; точность - микросекунда; |
//| PreciseTime(1) - окончание отсчета, возврат времени в мс; |
//+------------------------------------------------------------------+
double PreciseTime(int c)
{
static int start[2], end[2], freq[2];
if(c==0) QueryPerformanceCounter(start);
else
{
QueryPerformanceCounter(end);
if(QueryPerformanceFrequency(freq)==0) return(0);
double res=end[0]-start[0];
return(NormalizeDouble(res/freq[0]*1000.0,3));
}
}/////////////////////////////////////////////////////////////////////

Нет, так нельзя. В строке double "res=end[0]-start[0];" игнорируются значения end[1], start[1], которые могут быть не одинаковыми. Кроме того, надо учесть, что в MT4 end[0], start[0] воспринимаются как знаковые целые!


Сообщение отредактировал kanua: 13 July 2013 - 03:51

  • 1

#23 kazakov.v

kazakov.v

    Не новичок

  • Пользователи
  • Pip
  • 48 сообщений

Отправлено 13 July 2013 - 07:42

Нет, так нельзя. В строке double "res=end[0]-start[0];" игнорируются значения end[1], start[1], которые могут быть не одинаковыми. Кроме того, надо учесть, что в MT4 end[0], start[0] воспринимаются как знаковые целые!

Все верно. Так же, как GetTickCount через 49 суток начнет отсчет с нуля, здесь "малый" круг в зависимости от частоты составит около 20 минут. А "знаковость" проявится через полкруга, т.е. минут через 10 величина интервала станет отрицательной.

И, да, частота не меняется. Она очевидно как-то связана с номинальной частотой процессора (примерно в 1000 раз меньше), но от нагрузки не меняется.


  • 0

#24 Sergey Kovalyov

Sergey Kovalyov

    Свой

  • Пользователи +
  • PipPip
  • 1062 сообщений
  • МестоположениеKiev

Отправлено 13 July 2013 - 07:42

Вот я тут гораздо гораздее написал:

=)

Это круто, но DLL разрешать не все хотят. Да и меряем мы числа порядка 1000ms, на этом фоне 16 ms туда-сюда не особо играет роли, тем более, что эти 16ms могут быть и из-за "пинг плавает". =)
  • 1

#25 kazakov.v

kazakov.v

    Не новичок

  • Пользователи
  • Pip
  • 48 сообщений

Отправлено 13 July 2013 - 11:12

... 16 ms туда-сюда не особо играет роли...

 

Да это я по случаю делал в visual studio: мучаю тут параллельные вычисления через amp, но там проблемка возникает - компилятор не вставляет отладочную информацию, ну и профилировщик соответственно детальную информацию не дает. А время исполнения хочется померять. Можно, конечно, в цикле функцию покрутить, но кто его знает этот direct x, может он там кеширует что ни будь - лажануться можно в итоге...


  • 1

#26 kanua

kanua

    Свой

  • Пользователи
  • PipPip
  • 372 сообщений
  • МестоположениеКиев

Отправлено 15 July 2013 - 05:30

Нет, так нельзя. В строке double "res=end[0]-start[0];" игнорируются значения end[1], start[1], которые могут быть не одинаковыми. Кроме того, надо учесть, что в MT4 end[0], start[0] воспринимаются как знаковые целые!

Все верно. Так же, как GetTickCount через 49 суток начнет отсчет с нуля, здесь "малый" круг в зависимости от частоты составит около 20 минут. А "знаковость" проявится через полкруга, т.е. минут через 10 величина интервала станет отрицательной.
И, да, частота не меняется. Она очевидно как-то связана с номинальной частотой процессора (примерно в 1000 раз меньше), но от нагрузки не меняется.

Ну, во-первых, 49 суток - это не 10 минут. Вероятность исполнения ордера за 1 секунду в момент переполнения функции GetTickCount равна 1 с / 49 суток = 1/4233600 (т.е. 1 ордер из 4 млн.), а вот "знаковость" end[0], start[0] будут проявляться значительно чаще, вероятность равна 1 с / 10 мин = 1/600 (т.е. 1 ордер на 600).
 
Во-вторых, я не знаю, как посчитано время круга 20 минут, а полкруга 10 минут. У меня на двуядерном процессоре AMD Athlon 64 X2 4800+ с текущей частотой около 2511 МГц (взято из CPU-Z) частота тиков freq[0]=25000000. Т.е. полный круг займёт 4294967296/25000000 сек = 172 сек, а полкруга - 86 секунд! И это действительно так, я проверял. А это значит, что время исполнения каждого 86-го ордера будет вычислено неправильным. Учитывая, что некоторые ордера могут исполняться до 20 секунд (пост #101), время исполнения такого каждого 5-го ордера будет неверным.
 
Предлагаю свой код, который учитывает проблемы переполнения и "знаковости" в функции GetElapsedTime:

#import "Kernel32.dll"
int QueryPerformanceFrequency(int &a[]);
int QueryPerformanceCounter(int &a[]);
#import

int start_tick[2], cur_tick[2], start_freq[2], cur_freq[2];
bool MeasureOrderExecutionTime;

void StartTimer() // запускает (перезапускает) таймер
{
   QueryPerformanceFrequency(start_freq);
   QueryPerformanceCounter(start_tick);
}

double GetElapsedTime() // возвращает текущее значение таймера в мс
{
   QueryPerformanceCounter(cur_tick);
   QueryPerformanceFrequency(cur_freq);
   double ticks=cur_tick[0]-start_tick[0];
   // следующие 2 строчки учитывают проблему "знаковости"
   if (cur_tick[0]<0) ticks=ticks+4294967296.0;
   if (start_tick[0]<0) ticks=ticks-4294967296.0;
   // следующая строка учитывает возможное изменение старших 32 бит
   // длинного целого ("переполнение")
   ticks=ticks+(cur_tick[1]-start_tick[1])*4294967296.0;
   return(ticks*2000.0/(start_freq[0]+cur_freq[0]));
             // учтено возможное изменение частоты
}

void init()
{
   MeasureOrderExecutionTime=QueryPerformanceFrequency(start_freq)!=0;
   if (!MeasureOrderExecutionTime)
      Print("Измерение времени исполнения торговых приказов недоступно!");
   <...>
}

int start()
{
   <...>
   if (MeasureOrderExecutionTime) StartTimer();
   ticket = OrderSend(<...>);
   if (MeasureOrderExecutionTime)
          Print("Время исполнения = "+GetElapsedTime()+" мс");
   <...>
}

Код также предусматривает возможное изменение частоты тиков (start_freq и cur_freq). Хотя я на своём Desktop-е, как и уважаемый kazakov.v, не обнаружил изменений значений частоты, возвращаемой функцией QueryPerformanceFrequency, это ещё не означает, что реальная частота тиков не меняется (ведь частота процессора у меня несколько плавает, что видно из CPU-Z). Постоянство значения, возвращаемой функцией QueryPerformanceFrequency, при плавающей частоте тиков может быть связано с аппаратными или программными особенностями системы. Про это можно прочитать здесь и в замечаниях ("Remarks") описания функции QueryPerformanceCounter. Не факт, что такое положение останется навсегда для всех систем, поэтому лучше перестраховаться усреднением начальной и текущей частоты ( (start_freq[0]+cur_freq[0])/2 ).
 
Функция GetElapsedTime может быть использована, не только для измерения времени исполнения приказа, но и для измерения значительно более длительных промежутков времени.


  • 1

#27 Sergey Kovalyov

Sergey Kovalyov

    Свой

  • Пользователи +
  • PipPip
  • 1062 сообщений
  • МестоположениеKiev

Отправлено 04 November 2013 - 18:28

Вот я тут гораздо гораздее написал:

Пригодилось. =)

Тестили Protrader на серваке на той же площадке, где и торговый сервер. Вариант с GetTickCount давал то нули, то 15,16. А по правильному вышло вот так:

latency.png
  • 0


Количество пользователей, читающих эту тему: 0

0 пользователей, 0 гостей, 0 анонимных