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

 
Andrei Iakovlev #:

У меня не совпадает на GBPUSD, изменил только первый принт.

ну если контрольная сумма одинакова с расчетом через структуру, тогда по MN1 есть дырки истории.

Может история не загрузилась еще, или диапазон проверки больше исторических данных



 

Можно резюмировать 
Набор быстрых функций без применения структуры для получения параметров даты и времени до 2100 года по одному входному параметру datetime:

char GetSeconds(datetime time) { // return 0..59
   return char(time%60);
}
//+------------------------------------------------------------------+
char GetMinutes(datetime time) { // return 0..59
   return char((time/60)%60);
}
//+------------------------------------------------------------------+
char GetHours(datetime time) { // return 0..23
   return char((time/(60*60))%24);
}
//+------------------------------------------------------------------+
char GetDayOfMonth(datetime time) { // return 1...31
   static const int dm[12] = {0,31,61,92,122,153,184, 214, 245, 275, 306, 337};
   static int last_days = 0;
   static char last_result = 0;
   int days = int(time/(24*60*60));
   if (last_days!=days) {
      last_days = days;
      int d1 = (days+306+365)%1461;
      int y = d1/365;
      char m = 0;
      if (d1==1460) {
         last_result = 29;
         return 29;
      }
      int d = d1-y*365+1;
      if (d!=31) if (d==276) m=9;
         else m = char (d/30.68);
      last_result = char(d-dm[m]);
   }
   return last_result;
}
//+------------------------------------------------------------------+
char GetDayOfWeek(datetime time) {  // return 0 - Sunday, 1 - Monday,  .... 6 - Saturday
   return char(((time/(60*60*24))+4)%7);
}
//+------------------------------------------------------------------+
char GetMonth(datetime time) { // return 1...12
   static int last_days = 0;
   static char last_result = 0;
   int days = int(time/(24*60*60));
   if (last_days!=days) {
      last_days = days;
      int d1 = (days+306+365)%1461;
      char m = 3;
      if (d1==1460) {
         last_result = 2;
         return 2;
      }
      int d = d1-(d1/365)*365+1;
      if (d!=31) if (d==276) {
            last_result = 12;
            return 12;
         } else m = char (d/30.68)+3;
      if (m>12) m-=12;
      last_result = m;
   }
   return last_result;
}
//+------------------------------------------------------------------+
int GetYear(const datetime time) { // return 1970 ... 2099
   return((int)((time / (3600*24) * 4 + 2) / 1461) + 1970);
}
//+------------------------------------------------------------------+
 

With the help from codes by fxsaber here and here and some codes from this webpage source code, I was able to devise a very fast function to get year, month and day of year.

I updated the previous test done by Nokali before.

#define DAY (24 * 3600)
#define SIZE 10000000
//+------------------------------------------------------------------+
void OnStart() {
   ulong sum = 0;
   datetime t[];
   ArrayResize(t, SIZE);
   for(int i = 0;i<SIZE;i++) {
      t[i] = datetime(randUlong(ulong(365)*128*3600*24));
   }
   ulong tt = GetMicrosecondCount();
   for(int i=0; i<SIZE; i++) {
      int mm =GetMonth3(t[i]);
      sum+=mm;
   }
   tt = GetMicrosecondCount()-tt;
   Print("1 - " + DoubleToString(tt*1000.0/SIZE,2) + " ns, контрольная сумма = " + string(sum)+"   // fxsaber");
   ///////////
   sum = 0;
   tt = GetMicrosecondCount();
   for(int i=0; i<SIZE; i++) {
      uchar mm =GetMonth5(t[i]);
      sum+=mm;
   }
   tt = GetMicrosecondCount()-tt;
   Print("2 - " + DoubleToString(tt*1000.0/SIZE,2) + " ns, контрольная сумма = " + string(sum)+"   // C++ lib");
   ///////////
   sum = 0;
   tt = GetMicrosecondCount();
   for(int i=0; i<SIZE; i++) {
      uchar mm =GetMonth(t[i]);
      sum+=mm;
   }
   tt = GetMicrosecondCount()-tt;
   Print("3 - " + DoubleToString(tt*1000.0/SIZE,2) + " ns, контрольная сумма = " + string(sum)+"   // nikolai");
   ///////////
   sum = 0;
   tt = GetMicrosecondCount();
   for(int i=0; i<SIZE; i++) {
      int mm =GetMonthFast(t[i]);
      sum+=mm;
   }
   tt = GetMicrosecondCount()-tt;
   Print("4 - " + DoubleToString(tt*1000.0/SIZE,2) + " ns, контрольная сумма = " + string(sum)+"   // amrali");
   ///////////
   sum = 0;
   tt = GetMicrosecondCount();
   for(int i=0; i<SIZE; i++) {
      MqlDateTime dt;
      TimeToStruct(t[i], dt);
      int mm =dt.mon;
      sum+=mm;
   }
   tt = GetMicrosecondCount()-tt;
   Print("5 - " + DoubleToString(tt*1000.0/SIZE,2) + " ns, контрольная сумма = " + string(sum)+"  // TimeToStruct()");

}
//+------------------------------------------------------------------+
ulong randUlong(ulong max=ULONG_MAX) {
   return(((ulong)rand()<<60)|((ulong)rand()<<45)|((ulong)rand()<<30)|((ulong)rand()<<15)|(ulong)rand())%max;
}
//+------------------------------------------------------------------+
int GetMonth3( const datetime time ) {
   int time2 = (int)(time / DAY);
   const int Year = ((time2 << 2) + 2) / (365 * 4 + 1); // to 2100

   const bool Flag = ((Year & 3) == 2);
   time2 -= Year * 365 + ((Year + 2) >> 2);

   return (time2 < 59) ? (time2 + Flag) / 31 + 1
          : ((time2 >= 90) ? (time2 * 500 + 1532 * 11) / (1532 * 10) : 3);
}
//+------------------------------------------------------------------+
uchar GetMonth5(const datetime time) {
   const int dy4 =  365 * 4 + 1;
   int total_days = (int)(time / DAY);// + 306+365;
   const int td = (total_days << 2) + 2;

   const int Year = td / dy4; // to 2100
   const bool LeapYear = ((Year & 3) == 2);
   int days = (td % dy4)/4;
   const uchar JAN_AND_FEB_DAY_LEAP_YEAR = 59;
   const uchar TABLE_DAY_OF_YEAR[] = {
      1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,  // 31 январь
      2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,        // 28 февраль
      3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,  // 31 март
      4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,    // 30 апрель
      5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
      6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
      7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
      8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
      9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
      10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,
      11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
      12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,
   };
   return (LeapYear && days >= JAN_AND_FEB_DAY_LEAP_YEAR) ? TABLE_DAY_OF_YEAR[days - 1] : TABLE_DAY_OF_YEAR[days];
}
//+------------------------------------------------------------------+
char GetMonth(datetime time) { // return 1...12
   int days = int(time/(24*60*60));
   int d1 = (days+306+365)%1461;
   char m = 3;
   if (d1==1460) {
      return 2;
   }
   int d = d1-(d1/365)*365+1;
   if (d!=31) if (d==276) return 12;
      else m = char (d/30.68)+3;
   if (m>12) m-=12;
   return m;
}
//+------------------------------------------------------------------+
int GetMonthFast(datetime time)   // return 1...12
  {
   int days    = (int)(time / (60 * 60 * 24));
   int year    = ((days * 4 + 2) / 1461) + 1970;
   int yearday = days - ((year * 5844 - 11512676) >> 4);
   int isleap  = (year % 4 == 0);
   int leapadj = ((yearday < (59 + isleap)) ? 0 : (2 - isleap));
   int month   = ((((yearday + leapadj) * 12) + 373) / 367);
   return (month);
  }
//+------------------------------------------------------------------+

Results:

2024.04.20 03:30:56.494 TestDate2 (EURUSD,H1)   1 - 4.05 ns, контрольная сумма = 65197478   // fxsaber
2024.04.20 03:30:56.544 TestDate2 (EURUSD,H1)   2 - 4.91 ns, контрольная сумма = 65197478   // C++ lib
2024.04.20 03:30:56.576 TestDate2 (EURUSD,H1)   3 - 3.21 ns, контрольная сумма = 65197478   // nikolai
2024.04.20 03:30:56.598 TestDate2 (EURUSD,H1)   4 - 2.19 ns, контрольная сумма = 65197478   // amrali
2024.04.20 03:30:56.779 TestDate2 (EURUSD,H1)   5 - 18.13 ns, контрольная сумма = 65197478  // TimeToStruct()
2024.04.20 03:30:59.462 TestDate2 (EURUSD,H1)   1 - 4.00 ns, контрольная сумма = 65189469   // fxsaber
2024.04.20 03:30:59.511 TestDate2 (EURUSD,H1)   2 - 4.90 ns, контрольная сумма = 65189469   // C++ lib
2024.04.20 03:30:59.544 TestDate2 (EURUSD,H1)   3 - 3.23 ns, контрольная сумма = 65189469   // nikolai
2024.04.20 03:30:59.566 TestDate2 (EURUSD,H1)   4 - 2.20 ns, контрольная сумма = 65189469   // amrali
2024.04.20 03:30:59.747 TestDate2 (EURUSD,H1)   5 - 18.12 ns, контрольная сумма = 65189469  // TimeToStruct()

Edit:

GetMonthFast() uses branchless code, no if statements, and I think memoization is not required because it takes only 2 nanosec to execute.

May be the comparisons with TimeToStruct() is not fair enough (it has to calculate all the other members of the struct), but it was included here to verify checksums.
 
amrali #:

With the help from codes by fxsaber here and here and some codes from this webpage source code, I was able to devise a very fast function to get year, month and day of year.

I updated the previous test done by Nokali before.

Results:

Edit:

GetMonthFast() uses branchless code, no if statements, and I think memoization is not required because it takes only 2 nanosec to execute.

May be the comparisons with TimeToStruct() is not fair enough (it has to calculate all the other members of the struct), but it was included here to verify checksums.

Вау! Браво!
Спасибо, Amrali

 

Conclusion:

(1) The decoding of datetime variables into the date components (year, month and day) is fast enough. The TimeToStruct() function is doing a very good job at extraction.

(2) The encoding routines for converting date components into datetime variables are really slow, encoding benchmarks here  had showed speed-ups of100x times or more.

I think the StructToTime () and StringToTime () need some consideration from the developers , because these functions are frequently used in experts / indicators on the platform to encode datetime .

 
amrali #:

Заключение:

(1) Декодирование переменных datetime в компоненты даты (год, месяц и день) происходит достаточно быстро. Функция TimeToStruct() очень хорошо справляется с извлечением данных.

(2) Процедуры кодирования для преобразования компонентов даты в переменные даты и времени работают очень медленно, тесты кодирования здесь показали ускорение в 100 раз и более.

Я думаю, что StructToTime ( ) и StringToTime () требуют некоторого рассмотрения со стороны разработчиков , поскольку эти функции часто используются в экспертах/индикаторах на платформе для кодирования даты и времени .

Да, явный семантический баг, влияющий на производительность, в функции StructToTime()



 

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

StructToTime.mqh

#ifndef  STRUCTTOTIME_UNIQUE_HEADER_ID_H
#define  STRUCTTOTIME_UNIQUE_HEADER_ID_H
class GlobalApiHook
  {
public:
   static datetime   StructToTime_Hook(MqlDateTime &st)
     {
      datetime date = CreateDateTime5(st.year, st.mon, st.day);
      return date + (st.hour * 3600 + st.min * 60 + st.sec);
     }

private:
   static datetime   CreateDateTime5(const int Year, const int Month, const int Day)
     {
      static const uint Months[] = {0, 11512676, 11512180, 11511728, 11511232, 11510750, 11510256,
                                       11509774, 11509280, 11508781, 11508302, 11507806, 11507326};
      return (((Year * 5844 - Months[Month]) >> 4) + Day - 1) * (24 * 60 * 60);
     }
  };

#define StructToTime(st)       GlobalApiHook::StructToTime_Hook(st)
#endif

ваш существующий код:

// add this line to your existing code
#include "StructToTime.mqh"

void OnStart()
  {
   MqlDateTime st = {2024, 12, 31, 09, 24, 59};

   datetime t = StructToTime(st);

   Print("Time: ", t);
  }

// Time: 2024.12.31 09:24:59
 
amrali #:

some codes from this webpage source code

Спасибо, интересный исходник.

I was able to devise a very fast function to get year, month and day of year.

Отлично, что собрали лучшие решения воедино. Небольшое ускорение.

int GetMonthFast2(datetime time)   // return 1...12
  {
   int days    = (int)(time / (60 * 60 * 24));
   int year    = ((days << 2) + 2) / 1461;
   int yearday = days - ((year * 1461 + 1) >> 2);
   int isleap  = ((year & 3) == 2);
   int leapadj = ((yearday < (59 + isleap)) ? 0 : (2 - isleap));
   int month   = ((((yearday + leapadj) * 12) + 373) / 367);
   return (month);
  }
 
amrali #:

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

Почему не стали использовать CreateDateTime6?


Наверное, имеет смысл сделать временное решение и для TimeToStruct.

 
fxsaber #:

Почему они не использовали CreateDateTime6?

Я обнаружил, что производительность в случайных бенчмарках не отличается от CreateDateTime5() или CreateDateTime4().
Любой из трех вышеперечисленных вариантов - отличный вариант.
Причина обращения: