Библиотеки: Benchmark

 

Benchmark:

Измерение времени выполнения функций/методов, выражений и частей кода.

Автор: fxsaber

 
// Возвращает true в некоторых ситуациях, когда чарт не виден.
bool IsInvisible( long chartID = 0 )
{
  bool Res = ::ChartGetInteger(chartID, CHART_IS_MINIMIZED);
  
  if (!Res && !::ChartGetInteger(chartID, CHART_IS_MAXIMIZED) && ::ChartGetInteger(chartID, CHART_IS_DOCKED))
  {
    if (!chartID)
      chartID = ::ChartID();

    for (long Chart = ::ChartFirst(); (Chart != -1) && !Res; Chart = ::ChartNext(Chart))
      Res = (Chart != chartID) && ::ChartGetInteger(Chart, CHART_IS_MAXIMIZED);
  }
  
  return(Res);
}


На первый взгляд кажется, что Benchmark-библиотека не при чем. Но добавим альтернативный вариант той же функции.

#include <fxsaber\MultiTester\MTTester.mqh> // https://www.mql5.com/ru/code/26132

// Возвращает true в некоторых ситуациях, когда чарт не виден.
// Учитывает еще окно Терминала и графики Оптимизатора.
bool IsInvisible2( const long chartID = 0 )
{
  static const long TerminalHandle = MTTESTER::GetTerminalHandle();
  static const long ChartsHandle = user32::GetDlgItem(TerminalHandle, 0xE900);
  
  bool Res = ::ChartGetInteger(chartID, CHART_IS_MINIMIZED) || user32::IsIconic(TerminalHandle);
  
  if (!Res && !::ChartGetInteger(chartID, CHART_IS_MAXIMIZED) && ::ChartGetInteger(chartID, CHART_IS_DOCKED))
  {
    const long hwnd = ::ChartGetInteger(chartID, CHART_WINDOW_HANDLE);

    for (long handle = user32::GetWindow(ChartsHandle, GW_CHILD); handle && !Res; handle = user32::GetWindow(handle, GW_HWNDNEXT))
      Res = (hwnd != handle) && user32::IsZoomed(handle);
  }
  
  return(Res);
}


И сравним их показатели и производительность.

#include <fxsaber\Benchmark.mqh> // https://www.mql5.com/ru/code/31279
#define _B2(A) _B(A, 1)

const bool Init = EventSetTimer(1);

void OnTimer()
{
  Alert((string)_B2(IsInvisible()) + " - " + (string)_B2(IsInvisible2()));
}


Результат.

2020.10.02 00:30:51.114 Alert: Time[Test9.mq5 46 in OnTimer: IsInvisible()] = 877 mсs.
2020.10.02 00:30:51.114 Alert: Time[Test9.mq5 46 in OnTimer: IsInvisible2()] = 3 mсs.
2020.10.02 00:30:51.114 Alert: true - true
2020.10.02 00:30:52.116 Alert: Time[Test9.mq5 46 in OnTimer: IsInvisible()] = 1117 mсs.
2020.10.02 00:30:52.116 Alert: Time[Test9.mq5 46 in OnTimer: IsInvisible2()] = 14 mсs.
2020.10.02 00:30:52.116 Alert: true - true
2020.10.02 00:30:53.123 Alert: Time[Test9.mq5 46 in OnTimer: IsInvisible()] = 1939 mсs.
2020.10.02 00:30:53.123 Alert: Time[Test9.mq5 46 in OnTimer: IsInvisible2()] = 5 mсs.
2020.10.02 00:30:53.123 Alert: true - true
2020.10.02 00:30:54.121 Alert: Time[Test9.mq5 46 in OnTimer: IsInvisible()] = 1082 mсs.
2020.10.02 00:30:54.121 Alert: Time[Test9.mq5 46 in OnTimer: IsInvisible2()] = 4 mсs.
2020.10.02 00:30:54.121 Alert: true - true
2020.10.02 00:30:55.121 Alert: Time[Test9.mq5 46 in OnTimer: IsInvisible()] = 1455 mсs.
2020.10.02 00:30:55.121 Alert: Time[Test9.mq5 46 in OnTimer: IsInvisible2()] = 15 mсs.
2020.10.02 00:30:55.121 Alert: true - true
2020.10.02 00:30:56.119 Alert: Time[Test9.mq5 46 in OnTimer: IsInvisible()] = 1024 mсs.
2020.10.02 00:30:56.119 Alert: Time[Test9.mq5 46 in OnTimer: IsInvisible2()] = 5 mсs.
2020.10.02 00:30:56.119 Alert: true - true


Становится понятно, что альтернативный вариант гораздо быстрее. И если боевой советник может использовать DLL, то лучше не использовать чистый MQL-вариант.


Пробуем выяснить, где загвоздка у штатного варианта. Прописываем в исходный код несколько символов.

    for (long Chart = _B2(::ChartFirst()); (Chart != -1) && !Res; Chart = _B2(::ChartNext(Chart)))
      Res = (Chart != chartID) && _B2(::ChartGetInteger(Chart, CHART_IS_MAXIMIZED));

И сразу видим причину.

2020.10.02 00:45:14.113 Alert: Time[Test9.mq5 36 in IsInvisible: ::ChartGetInteger(Chart,CHART_IS_MAXIMIZED)] = 878 mсs.
2020.10.02 00:45:14.114 Alert: Time[Test9.mq5 36 in IsInvisible: ::ChartGetInteger(Chart,CHART_IS_MAXIMIZED)] = 943 mсs.
2020.10.02 00:45:14.114 Alert: Time[Test9.mq5 36 in IsInvisible: ::ChartGetInteger(Chart,CHART_IS_MAXIMIZED)] = 297 mсs.
2020.10.02 00:45:14.116 Alert: Time[Test9.mq5 36 in IsInvisible: ::ChartGetInteger(Chart,CHART_IS_MAXIMIZED)] = 1787 mсs.
2020.10.02 00:45:14.116 Alert: Time[Test9.mq5 35 in IsInvisible: ::ChartNext(Chart)] = 2 mсs.
2020.10.02 00:45:14.117 Alert: Time[Test9.mq5 36 in IsInvisible: ::ChartGetInteger(Chart,CHART_IS_MAXIMIZED)] = 980 mсs.
2020.10.02 00:45:14.117 Alert: Time[Test9.mq5 35 in IsInvisible: ::ChartNext(Chart)] = 2 mсs.
2020.10.02 00:45:14.117 Alert: Time[Test9.mq5 36 in IsInvisible: ::ChartGetInteger(Chart,CHART_IS_MAXIMIZED)] = 59 mсs.
2020.10.02 00:45:14.118 Alert: Time[Test9.mq5 36 in IsInvisible: ::ChartGetInteger(Chart,CHART_IS_MAXIMIZED)] = 803 mсs.
2020.10.02 00:45:14.119 Alert: Time[Test9.mq5 36 in IsInvisible: ::ChartGetInteger(Chart,CHART_IS_MAXIMIZED)] = 1059 mсs.

CHART_IS_MAXIMIZED тормозит для чужих чартов. Баг-репорт готов! С библиотекой это было очень просто.

 

Из-за тормозов GetMicrosecondCount перелопатил другие варианты. В итоге остановился на одном, результативность которого исследовал с помощью библы.

#include <fxsaber\Benchmark\Benchmark.mqh> // https://www.mql5.com/ru/code/31279

#import "winmm.dll"
  uint timeGetTime( void ); // Альтернативный счетчик времени.
#import

ulong GetNextCount( void )
{
  uint TickCount = 0;
  const uint PrevTickCount = winmm::timeGetTime();
  
  do
  {
    TickCount = winmm::timeGetTime();
  } while ((TickCount == PrevTickCount));
  
  return(TickCount - PrevTickCount);
}

const bool Init = EventSetTimer(1); // Синхронизация

void OnTimer()
{  
  GetNextCount(); // Синхронизация
    
  Print("winmm::timeGetTime Step = " + (string)(_B(GetNextCount(), 1) * 1000) + " mcs.");
}


Результат.

        Alert: Time[Test9.mq5 117 in OnTimer: GetNextCount()] = 999 mсs.
        winmm::timeGetTime Step = 1000 mcs.
        Alert: Time[Test9.mq5 117 in OnTimer: GetNextCount()] = 998 mсs.
        winmm::timeGetTime Step = 1000 mcs.
        Alert: Time[Test9.mq5 117 in OnTimer: GetNextCount()] = 999 mсs.
        winmm::timeGetTime Step = 1000 mcs.
        Alert: Time[Test9.mq5 117 in OnTimer: GetNextCount()] = 998 mсs.
        winmm::timeGetTime Step = 1000 mcs.
        Alert: Time[Test9.mq5 117 in OnTimer: GetNextCount()] = 996 mсs.
        winmm::timeGetTime Step = 1000 mcs.
        Alert: Time[Test9.mq5 117 in OnTimer: GetNextCount()] = 996 mсs.
        winmm::timeGetTime Step = 1000 mcs.

Отлично видно, насколько точен альтернативный миллисекундый счетчик времени. Можно использовать, т.к. еще и не тормозит (проверял).

Microsecond Resolution Time Services for Windows
Microsecond Resolution Time Services for Windows
  • Arno Lentfer
  • www.windowstimestamp.com
A substantial amount of time and effort has been spent on the attempt to get a proper high resolution time service implemented for Windows. However, the performance of these implementations is still not satisfactory. The complexity arises from the variety of Windows versions running on an even greater variety of hardware platforms. Proper...
 
fxsaber:

Из-за тормозов GetMicrosecondCount перелопатил другие варианты. В итоге остановился на одном, результативность которого исследовал с помощью библы.


Результат.

Отлично видно, насколько точен альтернативный миллисекундый счетчик времени. Можно использовать, т.к. еще и не тормозит (проверял).

никогда не пользовался 

#import "winmm.dll"
  uint timeGetTime( void ); // Альтернативный счетчик времени.
#import

а QueryPerformanceCounter  когда то использовал https://docs.microsoft.com/en-us/windows/win32/api/profileapi/nf-profileapi-queryperformancecounter

суть - получаете значение таймера <1 мкс

вроде работает, нагуглил https://stackoverflow.com/questions/1739259/how-to-use-queryperformancecounter

портировал:

#import "Kernel32.dll"
bool QueryPerformanceCounter(long &lpPerformanceCount);
bool QueryPerformanceFrequency(long &lpFrequency);
#import
double PCFreq = 0.0;
long   CounterStart = 0;
//+------------------------------------------------------------------+
void StartCounter()
{
   long li;
   if(!QueryPerformanceFrequency(li)) Print("QueryPerformanceFrequency failed!");

   PCFreq = double(li) / 1000.0;

   QueryPerformanceCounter(li);
   CounterStart = li;
}
//+------------------------------------------------------------------+
double GetCounter()
{
   long li;
   QueryPerformanceCounter(li);
   return double(li - CounterStart) / PCFreq;
}
//+------------------------------------------------------------------+
void OnStart()
{
   StartCounter();
   Sleep(1000);
   double result = GetCounter();
   Print("Sleep(1000) выполнялся ", result, " мс");
}
//+------------------------------------------------------------------+
 
Igor Makanu:

никогда не пользовался 

а QueryPerformanceCounter  когда то использовал https://docs.microsoft.com/en-us/windows/win32/api/profileapi/nf-profileapi-queryperformancecounter

суть - получаете значение таймера <1 мкс

вроде работает, нагуглил https://stackoverflow.com/questions/1739259/how-to-use-queryperformancecounter

портировал:

Также реализован GetMicrosecondsCount.

Форум по трейдингу, автоматическим торговым системам и тестированию торговых стратегий

MT5 и скорость в боевом исполнении

fxsaber, 2020.10.04 11:22

Пробовал заменить на WinAPI-вариант.

Результат идентичный штатному. Видимо, внутренняя реализация такая же. Есть какая-то другая реализация замера в микросекундах?


Поэтому не подходит.

 
fxsaber:

Также реализован GetMicrosecondsCount.


Поэтому не подходит.

не так

QueryPerformanceCounter  - это счетчик тактов процессора

а QueryPerformanceFrequency - это константное значение от Виндовс, которое знает расчетную производительность Вашего ПК


тут в общем вопрос - в чем Вы хотите более точно замерять производительность ПК?

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

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


UPD: кажется GetSystemTimePreciseAsFileTime это системное время https://stackoverflow.com/questions/34557407/queryperformancecounter-or-getsystemtimepreciseasfiletime-when-using-sntp

 
Igor Makanu:

не так

Вчера много часов потратил на изучение вопроса и эксперименты. GetMicrosecondsCount реализован через QPC, который вызывает тормоза на некоторых машинах.

Искался вариант замерителя без тормозов. Нашелся с миллисекундным квантованием.

 
fxsaber:

Вчера много часов потратил на изучение вопроса и эксперименты. GetMicrosecondsCount реализован через QPC, который вызывает тормоза на некоторых машинах.

Искался вариант замерителя без тормозов. Нашелся с миллисекундным квантованием.

возможно так и есть, не занимался этим вопросом

но

меньше чем такт процессора, имхо, не существует дискретности для измерения производительности ПК

поэтому и предлагаю в тактах мерять производительность, т.е. используйте просто QueryPerformanceCounter  - который выдаст текущий счетчик количества тактов тактов ЦПУ

проблема одна - значение будет сильно плавать, что довольно неудобно, но это будет точное значение сколько выполнялся код на конкретном ПК

 

Igor Makanu:

используйте просто QueryPerformanceCounter  - который выдаст текущий счетчик количества тактов тактов ЦПУ

Вы меня не слышите. Эта функция тормозит на некоторых машинах - факт. Почему так происходит - не копал.


Предлагал проверить свои VPS. Отклика не было.

 
fxsaber:

Вы меня не слышите. Эта функция тормозит на некоторых машинах - факт. Почему так происходит - не копал.

понял, тогда жаль

это видимо от материнки зависит, по крайней мере на старом ноутбуке можно было подразогнать его с помощью правки  в БИОСе частоты шины материнки, что влияло на точность системного таймера

ЗЫ: тут хорошо описана в комментариях привязка к QPC   https://overcoder.net/q/1177931/%D1%82%D0%BE%D1%87%D0%BD%D0%BE%D1%81%D1%82%D1%8C-%D1%82%D0%B0%D0%B9%D0%BC%D0%B5%D1%80%D0%B0-c-clock-%D0%BF%D1%80%D0%BE%D1%82%D0%B8%D0%B2-qpc-winapi-%D0%B8%D0%BB%D0%B8-timegettime

 
fxsaber:

Вы меня не слышите. Эта функция тормозит на некоторых машинах - факт. Почему так происходит - не копал.


Предлагал проверить свои VPS. Отклика не было.

вот тесты на ВПС , вроде такая же ситуация

http://demin.ws/blog/russian/2009/03/05/queryperformancecounter-on-multicore-and-virtual-systems/

Причина обращения: