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

 
fxsaber #:

Посмотрите эту реализацию.

Посмотрел. Проверил. 
Удивительно, но мой вариант, логика которого была описана здесь, оказался самый быстрый. Портированная функция из библиотеки C++ оказалась самой медленной.

#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)+"   // my");

}
//+------------------------------------------------------------------+
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;
}
//+------------------------------------------------------------------+

результат теста:

2024.04.19 03:07:06.966 TestDate (EURUSD,M15)   1 - 5.08 ns, контрольная сумма = 65186859   // fxsaber
2024.04.19 03:07:07.029 TestDate (EURUSD,M15)   2 - 6.25 ns, контрольная сумма = 65186859   // C++ lib
2024.04.19 03:07:07.073 TestDate (EURUSD,M15)   3 - 4.43 ns, контрольная сумма = 65186859   // my
2024.04.19 03:07:08.371 TestDate (EURUSD,M15)   1 - 5.10 ns, контрольная сумма = 65175027   // fxsaber
2024.04.19 03:07:08.435 TestDate (EURUSD,M15)   2 - 6.44 ns, контрольная сумма = 65175027   // C++ lib
2024.04.19 03:07:08.479 TestDate (EURUSD,M15)   3 - 4.39 ns, контрольная сумма = 65175027   // my
2024.04.19 03:07:09.636 TestDate (EURUSD,M15)   1 - 5.18 ns, контрольная сумма = 65199325   // fxsaber
2024.04.19 03:07:09.698 TestDate (EURUSD,M15)   2 - 6.13 ns, контрольная сумма = 65199325   // C++ lib
2024.04.19 03:07:09.741 TestDate (EURUSD,M15)   3 - 4.32 ns, контрольная сумма = 65199325   // my
2024.04.19 03:07:10.858 TestDate (EURUSD,M15)   1 - 5.25 ns, контрольная сумма = 65183166   // fxsaber
2024.04.19 03:07:10.921 TestDate (EURUSD,M15)   2 - 6.33 ns, контрольная сумма = 65183166   // C++ lib
2024.04.19 03:07:10.966 TestDate (EURUSD,M15)   3 - 4.46 ns, контрольная сумма = 65183166   // my
2024.04.19 03:07:12.117 TestDate (EURUSD,M15)   1 - 5.33 ns, контрольная сумма = 65187155   // fxsaber
2024.04.19 03:07:12.183 TestDate (EURUSD,M15)   2 - 6.67 ns, контрольная сумма = 65187155   // C++ lib
2024.04.19 03:07:12.227 TestDate (EURUSD,M15)   3 - 4.39 ns, контрольная сумма = 65187155   // my

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

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;
}
Файлы:
TestDate.mq5  9 kb
 

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

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

Nikolai Semko, 2024.04.19 09:23

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);
}

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

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

fxsaber, 2024.04.16 12:46

int GetMonth3( const datetime time )
{
  const int time2 = (int)time / DAY;
  
  return((time2 < D'1970.03.01' / DAY) ? time2 / 31 + 1
                                 : ((time2 >= D'1970.04.01' / DAY) ? ((time2 * 500 + 1532 * 11) / (1532 * 10)) : 3));
}


x64:

1 - 6.30 ns, контрольная сумма = 63803263   // fxsaber
2 - 8.20 ns, контрольная сумма = 65195523   // C++ lib
3 - 9.44 ns, контрольная сумма = 65195523   // my
 

Я проверил days_from_civil() и epoch_days_fast() из присланных вами ссылок.

Они не самые быстрые (возможно, это компилятор MQL5).

 
amrali #:

Я проверил days_from_civil() и epoch_days_fast() из присланных вами ссылок.

Они не самые быстрые (возможно, это компилятор MQL5).

Там интерес в пояснениях.

 
fxsaber #:


x64:

этот вариант не рабочий. Контрольная сумма не верная. Ограничен одним месяцем. Видимо Вы забыли.
Видно разные процессоры. У меня Intel 13 поколения


 
Nikolai Semko #:

этот вариант не рабочий. Контрольная сумма не верная.

Извините, забыл.

Там решалась другая задача - поиск дня месяца. Не пробовал GetMonth-задачу.

 
fxsaber #:

Там решалась другая задача - поиск дня месяца. Не пробовал GetMonth-задачу.

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

Обновленные контрольные показатели

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

//+------------------------------------------------------------------+
//| Create a datetime value from the given year, month and day.      |
//| https://www.mql5.com/en/forum/393227/page251#comment_53067868    |
//+------------------------------------------------------------------+
datetime CreateDateTime4(const int year, const int month, const int day)
  {
   static const int dm[] = {0, 0, 31, 59, 90, 120, 151, 181, 212,243, 273, 304, 334};
   int leap_days = (year - 1968) / 4 - (year % 4 == 0 && month < 3);
   return datetime((year - 1970) * 365 + leap_days + dm[month] + day - 1) * 86400;
  }
//+------------------------------------------------------------------+
//| https://www.mql5.com/en/forum/393227/page254#comment_53104384    |
//+------------------------------------------------------------------+
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) * 86400;
  }
//+------------------------------------------------------------------+
//| Create a datetime value from the given year, month and day.      |
//+------------------------------------------------------------------+
datetime CreateDateTime6(const int year, const int month, const int day)
  {
   datetime jd = (365 * (year - 1)) + (year - 1) / 4 +
                 (367 * month - 362) / 12 +
                 (month <= 2 ? 0 : (year % 4 == 0 ? -1 : -2)) +
                 (day - 719178);

   return (jd * (60 * 60 * 24));
  }

Контрольная точка:

void OnStart()
  {
   int k = 5000;
   ulong sum1 = 0;
   ulong sum2 = 0;
   ulong sum3 = 0;
   ulong sum4 = 0;
   ulong start = GetMicrosecondCount();
   for(int i = 0; i < k; i++)
      for(int y = 1970; y <= 2099; y++)
         for(int m = 1; m <= 12; m++)
            for(int d = 1; d <= 28; d++)
              {
               MqlDateTime st = {y, m, d};
               sum1 += StructToTime(st);  // <-------------------
              }
   PrintFormat("MQL StructToTime() -> %.1f msec", (GetMicrosecondCount()-start)/1000.);
   start = GetMicrosecondCount();
   for(int i = 0; i < k; i++)
      for(int y = 1970; y <= 2099; y++)
         for(int m = 1; m <= 12; m++)
            for(int d = 1; d <= 28; d++)
              {
               sum2 += CreateDateTime4(y, m, d);  // <-------------------
              }
   PrintFormat("CreateDateTime4()  -> %.1f msec", (GetMicrosecondCount()-start)/1000.);
   start = GetMicrosecondCount();
   for(int i = 0; i < k; i++)
      for(int y = 1970; y <= 2099; y++)
         for(int m = 1; m <= 12; m++)
            for(int d = 1; d <= 28; d++)
              {
               sum3 += CreateDateTime5(y, m, d);  // <-------------------
              }
   PrintFormat("CreateDateTime5()  -> %.1f msec", (GetMicrosecondCount()-start)/1000.);
   start = GetMicrosecondCount();
   for(int i = 0; i < k; i++)
      for(int y = 1970; y <= 2099; y++)
         for(int m = 1; m <= 12; m++)
            for(int d = 1; d <= 28; d++)
              {
               sum4 += CreateDateTime6(y, m, d);  // <-------------------
              }
   PrintFormat("CreateDateTime6()  -> %.1f msec", (GetMicrosecondCount()-start)/1000.);
//--- Display results.
   Print("sum1: ", sum1);
   Print("sum2: ", sum2);
   Print("sum3: ", sum3);
   Print("sum4: ", sum4);
  }

Результаты:

X64:
2024.04.19 10:57:28.649 benchmark_CreateDateTime (EURUSD_Custom+3,H1)   MQL StructToTime() -> 6542.2 msec
2024.04.19 10:57:28.709 benchmark_CreateDateTime (EURUSD_Custom+3,H1)   CreateDateTime4()  -> 59.5 msec
2024.04.19 10:57:28.737 benchmark_CreateDateTime (EURUSD_Custom+3,H1)   CreateDateTime5()  -> 28.3 msec
2024.04.19 10:57:28.739 benchmark_CreateDateTime (EURUSD_Custom+3,H1)   CreateDateTime6()  -> 2.2 msec
2024.04.19 10:57:28.739 benchmark_CreateDateTime (EURUSD_Custom+3,H1)   sum1: 447943668480000000
2024.04.19 10:57:28.739 benchmark_CreateDateTime (EURUSD_Custom+3,H1)   sum2: 447943668480000000
2024.04.19 10:57:28.739 benchmark_CreateDateTime (EURUSD_Custom+3,H1)   sum3: 447943668480000000
2024.04.19 10:57:28.739 benchmark_CreateDateTime (EURUSD_Custom+3,H1)   sum4: 447943668480000000

AVX2:

2024.04.19 11:00:49.809 benchmark_CreateDateTime (EURUSD_Custom+3,H1)   MQL StructToTime() -> 6377.5 msec
2024.04.19 11:00:49.830 benchmark_CreateDateTime (EURUSD_Custom+3,H1)   CreateDateTime4()  -> 20.5 msec
2024.04.19 11:00:49.843 benchmark_CreateDateTime (EURUSD_Custom+3,H1)   CreateDateTime5()  -> 13.6 msec
2024.04.19 11:00:49.844 benchmark_CreateDateTime (EURUSD_Custom+3,H1)   CreateDateTime6()  -> 0.8 msec
2024.04.19 11:00:49.844 benchmark_CreateDateTime (EURUSD_Custom+3,H1)   sum1: 447943668480000000
2024.04.19 11:00:49.844 benchmark_CreateDateTime (EURUSD_Custom+3,H1)   sum2: 447943668480000000
2024.04.19 11:00:49.844 benchmark_CreateDateTime (EURUSD_Custom+3,H1)   sum3: 447943668480000000
2024.04.19 11:00:49.844 benchmark_CreateDateTime (EURUSD_Custom+3,H1)   sum4: 447943668480000000

Впечатляющее ускорение! Более чем в 3000 раз быстрее, чем родной способ (StructToTime).

Также быстрее, чем все варианты, выложенные ранее.

Компилятор MQL творит волшебство!

предыдущий бенчмарк здесь

Documentation on MQL5: Constants, Enumerations and Structures / Data Structures / Date Type Structure
Documentation on MQL5: Constants, Enumerations and Structures / Data Structures / Date Type Structure
  • www.mql5.com
The date type structure contains eight fields of the int type: Note The day number of the year day_of_year for the leap year, since March, will...
 
amrali #:

Впечатляющее ускорение! Более чем в 3000 раз быстрее, чем родной способ (StructToTime).

Также быстрее, чем все варианты, выложенные ранее.

Компилятор MQL творит волшебство!

предыдущий бенчмарк здесь

Если отключить оптимизацию компилятора.

CreateDateTime4()  -> 2896.8 msec
CreateDateTime5()  -> 1238.9 msec
CreateDateTime6()  -> 2697.4 msec


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

 
Nikolai Semko #:
Контрольная сумма с iBarShift не будет совпадать, т.к.  iBarShift работает с реальными барами. Констрольная сумма будет совпадать только на MN1 и W1 таймфреймах, т.к. в истории таких баров нет дырок.

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

2024.04.19 23:50:58.534 timeToStartMonth (GBPUSD,H1)    ======== LOOP = 10000 ======== STEPS = 100000 seconds ======== GBPUSD ======== PERIOD_MN1 ========
2024.04.19 23:50:58.535 timeToStartMonth (GBPUSD,H1)    контрольная сумма - 12123055555200, время выполнения 1 итерации = 44.90 наносекунд - Расчет через структуру MqlDateTime
2024.04.19 23:50:58.535 timeToStartMonth (GBPUSD,H1)    контрольная сумма - 12123055555200, время выполнения 1 итерации = 13.80 наносекунд - Быстрый расчет
2024.04.19 23:50:58.538 timeToStartMonth (GBPUSD,H1)    контрольная сумма - 12125912112000, время выполнения 1 итерации = 322.20 наносекунд - Расчет через iBarShift
2024.04.19 23:50:58.538 timeToStartMonth (GBPUSD,H1)    ========================================================================