Особенности языка mql5, тонкости и приёмы работы - страница 277

 
amrali #:

Уже давно я заметил, что с компилятором MQL при выполнении бенчмарков происходит что-то странное,

Если количество сравниваемых функций превышает определенный предел ?!, то вы не получаете правильных результатов (т.е. функции в конце mq5 файла получают лучшие результаты с наименьшим временем выполнения, а верхние функции имеют плохие результаты). Такое уже случалось со мной, поэтому лучше ограничить сравниваемые функции до 3/4 максимума. Если у кого-то есть объяснение этому странному явлению, пожалуйста, просветите меня.

Это произошло и здесь, когда версия Доминика была добавлена в файл скрипта бенчмарка.

Чтобы получить более наглядное представление, я просто сохранил в исходном коде только две функции, и вот мои результаты в сравнении:

Одинаковая производительность, и обеим удалось опуститься ниже 5 наносекунд.

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

Не было ли это связано с алгоритмом радиксной сортировки?

В качестве объяснения я пришел к выводу, что дело в подкачке памяти. Но это было неудовлетворительно, так как я только добавлял комментарии.

Редактировать:
Итак, вывод: функции работают одинаково, примерно.
 
Dominik Egert #:
О, я помню ту дискуссию, где я сказал, что добавление комментария меняет поведение функций в одном и том же mqh-файле...

Разве это не было связано с алгоритмом радиксной сортировки?

В качестве объяснения я пришел к выводу, что это связано с подкачкой памяти. Но это было неудовлетворительно, так как я только добавил комментарии.

Редактировать:
В итоге, функции работают одинаково, примерно.

Да, точно, мы столкнулись с той же проблемой при тестировании радиксной сортировки. Проблема до сих пор существует.

Я не знаю, проблема ли это с оптимизациями, применяемыми компилятором, или процессор увеличивает тактовую частоту (прогрев, турбо-ускорение или что-то еще...).

 
amrali #:

Да, точно, мы столкнулись с той же проблемой при тестировании radix sort. Проблема все еще существует.

Я не знаю, является ли это проблемой применяемых компилятором оптимизаций, или процессор увеличивает тактовую частоту (прогрев, турбо-ускорение или что-то еще...).


Вот версия с минимальным объемом памяти, но почему-то я не могу заменить const uint t на переменную из структуры. - Если я это делаю, то получаю ошибку "array out of range". - Есть идеи?


bool TimeToStructMQLplus(const datetime timestamp, MqlDateTime& dt_struct)
{
    static const int Months[] = { 0, 11512692, 11512196, 11511744, 11511248, 11510766, 11510272, 11509790, 11509296, 11508797, 11508318, 11507822, 11507342 };

    const uint t             = (uint)(timestamp);
    dt_struct.day_of_week   = (int)(t / 86400);
    dt_struct.mon           = (dt_struct.day_of_week << 2) | 2;

    dt_struct.day_of_year   = (dt_struct.mon % 1461) >> 2;
    dt_struct.year          = (dt_struct.mon / 1461) + 1970;
    dt_struct.day           = !(dt_struct.year & 3);

    dt_struct.mon           = ((((dt_struct.day_of_year + ((dt_struct.day_of_year < (dt_struct.day + 59)) ? 0 : (2 - dt_struct.day))) * 12) + 373) / 367);
    dt_struct.day           = dt_struct.day_of_week - (int)((dt_struct.year * 5844 - Months[dt_struct.mon]) >> 4);
    #ifndef  WITHOUT_HOURS
        dt_struct.hour      = (int)(t / 3600) % 24;
        dt_struct.min       = (int)(t / 60) % 60;
        dt_struct.sec       = (int)(t % 60);

    #endif //#ifndef WITHOUT_HOURS
    dt_struct.day_of_week   = (dt_struct.day_of_week + 4) % 7;
    return (true);
}


EDIT:

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


Результаты из прикрепленного файла:

Compiler Version: 4620 X64 Regular, optimization - true
12 th Gen Intel Core i7-12700 K, AVX2 + FMA3
With hours (dt.hour+ dt.min+ dt.sec - on), random datetimes[].
1970.01.01 00:00:23 - 2097.11.29 23:59:51
 3.95 ns, checksum = 1224148774003212   // TimeToStructMQLplus
 4.00 ns, checksum = 1224148774003212   // TimeToStructFast
19.98 ns, checksum = 1224148774003212  /// MQL's TimeToStruct()
Файлы:
 
Dominik Egert #:


Вот версия с минимальным объемом памяти, но по какой-то причине я не могу заменить const uint t на переменную из структуры. - Если я это делаю, то получаю ошибку "массив вне диапазона". - Есть идеи?

Вот так (возможно, для цифровых часов с ограниченной памятью):

bool TimeToStructMQLplus(const datetime timestamp, MqlDateTime& dt_struct)
{
    static const int Months[] = { 0, 11512692, 11512196, 11511744, 11511248, 11510766, 11510272, 11509790, 11509296, 11508797, 11508318, 11507822, 11507342 };

  //const uint t             = (uint)(timestamp);
  //dt_struct.day_of_week   = (int)(t / 86400);
    dt_struct.sec           = (int)(timestamp);
    dt_struct.day_of_week   = (int)((uint)dt_struct.sec / 86400);
    dt_struct.mon           = (dt_struct.day_of_week << 2) | 2;

    dt_struct.day_of_year   = (dt_struct.mon % 1461) >> 2;
    dt_struct.year          = (dt_struct.mon / 1461) + 1970;
    dt_struct.day           = !(dt_struct.year & 3);

    dt_struct.mon           = ((((dt_struct.day_of_year + ((dt_struct.day_of_year < (dt_struct.day + 59)) ? 0 : (2 - dt_struct.day))) * 12) + 373) / 367);
    dt_struct.day           = dt_struct.day_of_week - (int)((dt_struct.year * 5844 - Months[dt_struct.mon]) >> 4);
    #ifndef  WITHOUT_HOURS
        dt_struct.hour      = (int)((uint)dt_struct.sec / 3600) % 24;
        dt_struct.min       = (int)((uint)dt_struct.sec / 60) % 60;
        dt_struct.sec       = (int)((uint)dt_struct.sec % 60);

    #endif //#ifndef WITHOUT_HOURS
    dt_struct.day_of_week   = (dt_struct.day_of_week + 4) % 7;
    return (true);
}

после 2038 года счетчик секунд (временная метка unix) достигнет INT_MAX, поэтому вы должны продолжать использовать тип uint для секунд (чтобы избежать отрицательных значений).

Обратите внимание, что поля MqlDateTime - это ints, поэтому вам придется приводить повторно используемое поле. Приведение к тому же количеству битов или большему (расширяющее приведение) не изменяет биты. Меняется только интерпретация знакового бита (2'complement).

 
amrali #:

Вот она (возможно, для цифровых часов с ограниченной памятью):

после 2038 года счетчик секунд (временная метка unix) достигнет INT_MAX, поэтому вы должны продолжать использовать тип uint для секунд (чтобы избежать отрицательных значений).

Обратите внимание, что поля MqlDateTime - это ints, поэтому вам придется приводить повторно используемое поле. Приведение к тому же количеству битов или большему (расширяющее приведение) не изменяет биты. Меняется только интерпретация знакового бита (2'complement).


Спасибо. Да, я видел это сегодня утром. Вчера я был слишком измотан.

Теперь эта функция занимает 2 строки кэша, а вытаскивать нужно только одну, так как вторая уже будет содержать структуру MqlDateTime. Всего 92 байта, MqlDateTime, предварительно загруженный стеком функции, плюс datetime. Все вместе - 40 байт. А статический массив - 13*4 = 52 байта.

Я не думаю, что можно еще больше оптимизировать эту функцию в ее нынешнем виде.
 
Dominik Egert #:

Спасибо. Да, я видел его сегодня утром. Вчера я был слишком измотан.

Теперь эта функция занимает 2 строки кэша, а вытаскивать нужно только одну, так как вторая уже содержит структуру MqlDateTime. Всего 92 байта, MqlDateTime, предварительно загруженный стеком функции, плюс datetime. Все вместе - 40 байт. А статический массив - 13*4 = 52 байта.

Я не думаю, что можно еще больше оптимизировать эту функцию в ее нынешнем виде.
У вас на компьютере не хватает памяти?!

Редактировать:
Переменные будут оптимизированы в регистры процессора, а не в кэш.
 
amrali #:
На вашем компьютере не хватает памяти?!

Редактировать:
Код будет оптимизирован в регистры процессора, а не в кэш.

Да, разницы больше нет. - Или, по крайней мере, не поддается измерению.

Это было из любопытства.

EDIT: В виртуальной машине он действительно показывает небольшую тенденцию к минимально лучшей производительности, когда кэш-линия и память оптимизированы. - Хотя этот тест очень синтетический. И это всего лишь тенденция... не гарантия, так что это может быть связано с планировщиком ОС, или с тем, что находится между кодом и процессором...

BTW: Я удалил закомментированные строки из функции, что дало ей немного edge..... - Почему так происходит, но поскольку мы находимся в области пикосекунд, то и выводы из этого сделать невозможно.
 

Я удалю эту функцию из бенчмарка, так как она ненадежна:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
// https://www.mql5.com/ru/forum/170952/page247#comment_52620994
// true - оптимизация компилятора включена, false - выключена.
bool IsOptimizationCompiler( void )
{
  static ulong PrevValue = INT_MAX;

  if (PrevValue == INT_MAX)
  {
    const ulong StartTime = GetMicrosecondCount();

    for (int i = 0; i < 1 e5; i++)
      const double j = MathSin(0);

    PrevValue = GetMicrosecondCount() - StartTime;
  }

  return(PrevValue < 5);
}

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnStart() {
   Print("\nCompiler Version: " + (string)__MQLBUILD__ + " " + __CPU_ARCHITECTURE__ +
         ", optimization - " + (string)IsOptimizationCompiler());
}

Я скомпилировал его на своей машине как обычный X64, без оптимизаций, после чего получил такой результат

2024.11.30 19:20:17.616 IsOptimizationCompiler (EURUSD,H1)      
2024.11.30 19:20:17.616 IsOptimizationCompiler (EURUSD,H1)      Compiler Version: 4647 X64 Regular, optimization - true
 

Небольшое исправление (приведение datetime к uint), но увеличивающее производительность в два раза:

bool TimeToJulian(datetime time, MqlDateTime& dt_struct)
  {
   uint t = (uint) time;  // cast datetime to uint for speed-up

Обновленные бенчмарки v1.10:

Compiler Version: 4620 X64 Regular
13 th Gen Intel Core i7-13700 KF, AVX2 + FMA3
1970.01.01 00:03:02 - 2097.11.29 23:55:39, random datetimes[]
 4.04 ns, checksum = 161301871646119   // TimeToStruct2100
 3.91 ns, checksum = 161301871646119   // TimeToStructFast
 4.10 ns, checksum = 161301871646119   // TimeToCalendar
 4.62 ns, checksum = 161301871646119   // TimeToJulian
18.77 ns, checksum = 161301871646119  /// MQL's TimeToStruct()
Файлы:
 
amrali #:

Я удалю эту функцию из бенчмарка, так как она не является надежной:

Я скомпилировал его на своей машине как обычный X64, без оптимизаций, после чего получил такой результат

Да, эта функция рассчитана на определенный уровень мощности процессора.

Нам нужен подход, при котором мы могли бы достоверно сказать, что этот код был удален оптимизатором.

Я думал о каком-то более надежном подходе, но не уверен, что его можно использовать.