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

 
fxsaber #:

Добавил его сюда.

Действительно, ваша версия самая быстрая. Поздравляю!

TimeToStructFast

Это самый быстрый вариант, который я смог найти.

Еще один очень быстрый вариант - использование вычислительного календаря (фиктивного календаря, начинающегося 1 марта), который позволяет избежать проблемы с високосными годами.

Это новый алгоритм и очень быстрый. Посмотреть его можно здесь https://www.youtube.com/watch?v=0s9F4QWAl-E&t=1790s.

bool TimeToCalendar(datetime time, MqlDateTime& dt_struct)
  {
   uint  K = 536895458;
   uint  L = 1468000  ;
   uint  m = 146097   ;
   uint  q = 2939745  ;
   uint  r = 2141     ;
   uint  p = 197913   ;
   ulong t = (ulong)time;
   int   n = (int)(t / 86400)                   ;  // Unix day
   uint  N = ((uint)n) + K                      ;  // Computational calendar day
   uint  a = 4 * N + 3                          ;
   uint  c = a / m                              ;
   uint  e = a % m / 4                          ;
   uint  b = 4 * e + 3                          ;
   ulong f = ((ulong)q) * b                     ;
   uint  z = (uint)(f >> 32)                    ;
   uint  h = ((uint)f) / q / 4                  ;
   uint  d = r * h + p                          ;
   uint  Y = 100 * c + z                        ;
   uint  M = d >> 16                            ;
   uint  D = ((ushort)d) / r                    ;
   uint  J = h >= 306                           ;  // Map from Computational to Gregorian calendar
   int Y_G = int((Y - L) + J)                   ;
   int M_G = int(J ? M - 12 : M)                ;
   int D_G = int(D + 1)                         ;
   int HH  = (int)((t / 3600) % 24)             ;
   int MM  = (int)((t / 60) % 60)               ;
   int SS  = (int)(t % 60)                      ;
   int dow = (n + 4) % 7                        ;
   int doy = n - ((Y_G * 5844 - 11512676) >> 4) ;
 //int doy = (((n << 2) + 2) % 1461) / 4;  // slower here!

   dt_struct.year           = Y_G;
   dt_struct.mon            = M_G;
   dt_struct.day            = D_G;
   dt_struct.hour           = HH;
   dt_struct.min            = MM;
   dt_struct.sec            = SS;
   dt_struct.day_of_week    = dow;
   dt_struct.day_of_year    = doy;

   return (true);
  }

Источник: https: //searchfox.org/mozilla-central/source/js/src/jsdate.cpp#263

https://github.com/torvalds/linux/blob/276010551664f73b6f1616dde471d6f0d63a73ba/kernel/time/timeconv.c#L77

 
amrali #:

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

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

Пожалуйста, с указанием ссылки на эту тему. 
 
amrali #:

Это самый быстрый вариант, который я смог найти.

Еще один очень быстрый вариант - использование вычислительного календаря (фиктивного календаря, начинающегося 1 марта), который позволяет избежать проблемы с високосными годами.

Это новый алгоритм и очень быстрый. Посмотреть его можно здесь https://www.youtube.com/watch?v=0s9F4QWAl-E&t=1790s.

Источник: https: //searchfox.org/mozilla-central/source/js/src/jsdate.cpp#263

https://github.com/torvalds/linux/blob/276010551664f73b6f1616dde471d6f0d63a73ba/kernel/time/timeconv.c#L77

1 - 37.72 ns, контрольная сумма = 231124376201  // TimeToStruct
2 - 21.38 ns, контрольная сумма = 231124376201  // amrali
2 - 15.79 ns, контрольная сумма = 231124376201  // TimeToStruct2100
2 - 10.71 ns, контрольная сумма = 231124376201  // TimeToStruct2100 fxsaber
2 - 9.29 ns, контрольная сумма = 231124376201  // TimeToStruct2100 amrali2
2 - 11.04 ns, контрольная сумма = 231124376201  // TimeToCalendar
 

Реальный тест в реальном времени:

TimeToStruct            2024.11.26 17:03:55.501    Core 02    EURUSD,H1: 8667407 ticks, 37169 bars generated. Environment synchronized in 0:00:00.092. Test passed in 0:01:43.931.
TimeToStruct2100Old     2024.11.26 17:10:52.902    Core 02    EURUSD,H1: 8667407 ticks, 37169 bars generated. Environment synchronized in 0:00:00.081. Test passed in 0:01:41.173.
TimeToCalendar          2024.11.26 17:25:37.672    Core 02    EURUSD,H1: 8667407 ticks, 37169 bars generated. Environment synchronized in 0:00:00.088. Test passed in 0:01:40.153.
TimeToStructFast        2024.11.26 17:31:59.339    Core 02    EURUSD,H1: 8667407 ticks, 37169 bars generated. Environment synchronized in 0:00:00.091. Test passed in 0:01:41.471.
TimeToStruct2100Last    2024.11.26 17:37:43.948    Core 02    EURUSD,H1: 8667407 ticks, 37169 bars generated. Environment synchronized in 0:00:00.084. Test passed in 0:01:40.996.
TimeToStruct2100Cache   2024.11.26 17:42:40.774    Core 02    EURUSD,H1: 8667407 ticks, 37169 bars generated. Environment synchronized in 0:00:00.091. Test passed in 0:01:40.585.

Это тест горячего старта со всеми данными в памяти.

Общее количество вызовов функции :

2024.11.26 17:48:38.190    Core 02    2024.09.29 23:59:59   TTS Call = 191969676.

 

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

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

amrali, 2024.11.26 15:58

Самая быстрая замена встроенной функции TimeToStruct(),

я рекомендую использовать эту функцию:

bool TimeToStructFast(datetime time, MqlDateTime& dt_struct)
  {
//   static const int Months[13] = {0, -1, 30, 58, 89, 119, 150, 180, 211,242, 272, 303, 333};  
   static const uint Months[] = {0, 11512676, 11512180, 11511728, 11511232, 11510750, 11510256,
                                    11509774, 11509280, 11508781, 11508302, 11507806, 11507326};
   uint t       = (uint)time;
   int  n       = (int)(t / (24 * 3600));
   int  tn      = (n << 2) + 2;
   int  dow     = (n + 4) % 7;
   int  doy     = (tn % 1461) / 4;
   int  year    = (tn / 1461) + 1970; // to 2100
   int  isleap  = ((year & 3) == 0);
   int  leapadj = ((doy < (isleap + 59)) ? 0 : (2 - isleap));
   int  mon     = ((((doy + leapadj) * 12) + 373) / 367);
//   int  day     = doy - Months[mon] - (isleap && doy > 59);
   int  day     = n - (int)((year * 5844 - Months[mon]) >> 4); // https://www.mql5.com/ru/forum/170952/page254#comment_53104383
   int  HH      = (int)(t / 3600) % 24;
   int  MM      = (int)(t / 60) % 60;
   int  SS      = (int)(t % 60);

   dt_struct.year           = year;
   dt_struct.mon            = mon;
   dt_struct.day            = day;
   dt_struct.hour           = HH;
   dt_struct.min            = MM;
   dt_struct.sec            = SS;
   dt_struct.day_of_week    = dow;
   dt_struct.day_of_year    = doy;
   return (true);
  } 

Так заметно (AVX) быстрее.

 
Alain Verleyen #:

Реальный тест в реальном времени:

TimeToStruct            2024.11.26 17:03:55.501    Core 02    EURUSD,H1: 8667407 ticks, 37169 bars generated. Environment synchronized in 0:00:00.092. Test passed in 0:01:43.931.
TimeToStruct2100Old     2024.11.26 17:10:52.902    Core 02    EURUSD,H1: 8667407 ticks, 37169 bars generated. Environment synchronized in 0:00:00.081. Test passed in 0:01:41.173.
TimeToCalendar          2024.11.26 17:25:37.672    Core 02    EURUSD,H1: 8667407 ticks, 37169 bars generated. Environment synchronized in 0:00:00.088. Test passed in 0:01:40.153.
TimeToStructFast        2024.11.26 17:31:59.339    Core 02    EURUSD,H1: 8667407 ticks, 37169 bars generated. Environment synchronized in 0:00:00.091. Test passed in 0:01:41.471.
TimeToStruct2100Last    2024.11.26 17:37:43.948    Core 02    EURUSD,H1: 8667407 ticks, 37169 bars generated. Environment synchronized in 0:00:00.084. Test passed in 0:01:40.996.
TimeToStruct2100Cache   2024.11.26 17:42:40.774    Core 02    EURUSD,H1: 8667407 ticks, 37169 bars generated. Environment synchronized in 0:00:00.091. Test passed in 0:01:40.585.

Это тест горячего старта со всеми данными в памяти.

Общее количество вызовов функции :

2024.11.26 17:48:38.190    Core 02    2024.09.29 23:59:59   TTS Call = 191969676.

Спасибо Алену за тест. Замена на встроенную TimeToStruct() работает всего в 4-5 раз быстрее, как я уже сообщал.

  4.29 ns, checksum = 42266650915760   // TimeToStructFast
  5.16 ns, checksum = 42266650915760   // TimeToStructFast (Cached)
 19.16 ns, checksum = 42266650915760  /// MQL's TimeToStruct()

Однако замена TimeYear(), TimeDayOfWeek() как минимум в 10-20 раз быстрее, чем использование MQL'овской TimeToStruct().

  1.47 ns, checksum = 20510890332002139   // TimeDayOfWeek()
 18.91 ns, checksum = 20510890332002139  /// MQL's TimeToStruct()

  0.73 ns, checksum = 20512672180581744   // TimeYear()
 18.45 ns, checksum = 20512672180581744  /// MQL's TimeToStruct()

Этот код выполняется медленно при использовании оберток встроенного StructToTime.

Использование оптимизированных функций здесь очень помогает.

   datetime array[];
   const int aa = CopyTime(_Symbol, PERIOD_H1, 0, Bars(_Symbol, PERIOD_H1), array);   
   if(aa<=0)
     {
      Print("Can't read quotes, error: ", _LastError);
      return;
     }
   const int n = ArraySize(array);
   ENUM_DAY_OF_WEEK weekday = -1;
   for(int i = 0; i < n; ++i)
     {
      if(TimeDayOfWeek(array[i]) > MONDAY)
         continue;
      
      int hour = TimeHour(array[i]);
      //...
      //...
     }

Еще один момент: распаковка (TimeToStruct) всегда быстрее, чем сжатие (StructToTime). Встроенный StructToTime действительно медленнее, чем TimeToStruct().

Если ваш код создает много переменных 'datetime' из компонентов времени, вы обнаружите, что CreateDateTime() намного быстрее, чем MQL'овская StructToTime().

Я столкнулся с проблемой медленной работы StructToTime() при создании переменных времени суток для перехода на летнее время в библиотеке TimeZoneInfo. Заменив ее на эту пользовательскую CreateDateTime(), я значительно улучшил скорость работы.

  1.45 ns, checksum = 40671201835781217   // CreateDateTime
 34.83 ns, checksum = 40671201835781217  /// MQL's StructToTime()
 
fxsaber #:

Так оно заметно (AVX) быстрее.

К сожалению, не быстрее, чем текущая реализация:

  X64 Regular:
  4.90 ns, checksum = 42266650915760   // TimeToStruct2100
  4.29 ns, checksum = 42266650915760   // TimeToStructFast
  5.12 ns, checksum = 42266640915760   // TimeToStructFast2
  4.28 ns, checksum = 42266650915760   // TimeToCalendar
  5.73 ns, checksum = 42266650915760   // * TimeToStruct2100 (Cached)
  5.16 ns, checksum = 42266650915760   // * TimeToStructFast (Cached)
  5.58 ns, checksum = 42266640915760   // * TimeToStructFast2 (Cached)
  5.17 ns, checksum = 42266650915760   // * TimeToCalendar (Cached)
 19.16 ns, checksum = 42266650915760  /// MQL's TimeToStruct()

  AVX:
  5.01 ns, checksum = 50711796193332   // TimeToStruct2100
  4.67 ns, checksum = 50711796193332   // TimeToStructFast
  5.43 ns, checksum = 50711786193332   // TimeToStructFast2
  4.55 ns, checksum = 50711796193332   // TimeToCalendar
  5.63 ns, checksum = 50711796193332   // * TimeToStruct2100 (Cached)
  5.40 ns, checksum = 50711796193332   // * TimeToStructFast (Cached)
  4.73 ns, checksum = 50711786193332   // * TimeToStructFast2 (Cached)
  5.57 ns, checksum = 50711796193332   // * TimeToCalendar (Cached)
 19.38 ns, checksum = 50711796193332  /// MQL's TimeToStruct()

MetaTrader 5 IC Markets (SC) x64 build 4620, запущенный для Raw Trading Ltd.
Windows 11 build 22631, 24 x 13th Gen Intel Core i7-13700KF, AVX2, память 24 / 31 Гб, диск 120 / 199 Гб, UAC, GMT+3

 
amrali #:

К сожалению, не быстрее, чем текущая реализация:

У меня иная картина. Поделитесь исходником скрипта, пожалуйста.

 
fxsaber #:

Вот ошибка от 31 января.

Которая является правильной :-)

Вы упустили, что day_of_year - это индекс, основанный на нуле. Таким образом, doy=31 означает на самом деле 1 февраля.

algo_checker.mq5:

void OnStart()
  {
   int dm[2][13] = {};
   for(int isleap = 0; isleap <= 1; isleap++)
     {
      for(int doy = 0; doy <= 364+isleap; doy++)
        {
         // the core algorithm to calculate month and days
         static const int Months[13] = {0, -1, 30, 58, 89, 119, 150, 180, 211,242, 272, 303, 333};
         int  leapadj = ((doy < (isleap + 59)) ? 0 : (2 - isleap));
         int  mon     = ((((doy + leapadj) * 12) + 373) / 367);
         int  day     = doy - Months[mon] - (isleap && doy > 59);

         PrintFormat("isleap: %d, doy: %.3d, date: %.2d/%.2d", isleap, doy, day, mon);
         dm[isleap][mon]++;
        }
     }
   ArrayPrint(dm);
   Print("done.");
  }
 
fxsaber #:

Еще быстрее.

Браво, кажется, быстрее!

  5.96 ns, checksum = 81874529667266   // TimeToStruct2100
  5.98 ns, checksum = 81874529667266   // TimeToStructFast
  5.27 ns, checksum = 81874519667266   // TimeToStructFast_fxsaber
  5.82 ns, checksum = 81874529667266   // TimeToCalendar
 13.09 ns, checksum = 81874529667266   // TimeToJulian
 25.98 ns, checksum = 81874529667266  /// MQL's TimeToStruct()

Мне следует провести более масштабное тестирование, прежде чем применять эти изменения к моей функции.

Вот улучшенный скрипт для бенчмарков:

Файлы: