Features of the mql5 language, subtleties and tricks - page 254

 

Forum on trading, automated trading systems and testing trading strategies

Features of mql5 language, subtleties and techniques of work

Vladislav Boyko, 2024.04.18 02:29

Maximum optimisation:

StructToTime() -> 13421 msec
CreateDateTime() -> 125 msec
sum1: 447943668480000000
sum2: 447943668480000000

No optimisation:

StructToTime() -> 12937 msec
CreateDateTime() -> 1860 msec
sum1: 447943668480000000
sum2: 447943668480000000

The performance of imported functions cannot depend on the selected compiler settings.

 

Forum on trading, automated trading systems and testing trading strategies

Features of mql5 language, subtleties and techniques of work

amrali, 2024.04.18 01:50 PM

datetime CreateDateTime(const int iYear, const int iMonth, const int iDay)
  {
// MqlDateTime dt = {iYear, iMonth, iDay}
// return StructToTime(dt);

   static const int dm[] = {0, 0, 31, 59, 90, 120, 151, 181, 212,243, 273, 304, 334};
   int leap_days = (iYear - 1968) / 4 - (iYear % 4 == 0 && iMonth < 3);
   return datetime((iYear - 1970) * 365 + leap_days + dm[iMonth] + iDay - 1) * 86400;
  }

Put this summand into the array.

 
fxsaber array index .

lookup tables are always faster than switch (linear search).

 
fxsaber #:

Alternative.

The encoding of year and month components into a timestamp was the main challenge.

Other components of MqlDateTime struct are trivial to claculate. (multiply by seconds)

 
datetime GetMonthTime5( const int Year, const int Month )
{
  static const uint Months[] = {0, 11512676, 11512180, 11511728, 11511232, 11510750, 11510256,
                                   11509774, 11509280, 11508781, 11508302, 11507806, 11507326};

  return(((Year * 5844 - Months[Month]) >> 4) * 86400);
}

A slightly improved mechanism for applying optimisation produced this. I think this is the fastest calculation.

 
fxsaber #:

A slightly improved mechanism for applying optimisation produced this. I think this is the fastest calculation.

Very fast.
 
amrali #:
Very fast.

To the piggy bank of algorithms on the topic.

Optimizing UTC → Unix Time Conversion For Size And Speed
  • 2020.05.12
  • blog.reverberate.org
How do you convert a UTC timestamp to Unix Time (seconds since the epoch)? Of course the right answer is “you use a standard library function.” But what if you don’t have one available? Or what if you’re the person implementing that library? Converting the time portion is trivial. Unix Time pretends that leap seconds do not exist and makes...
 
fxsaber #:

Check out this implementation.

I did. Checked it.
Surprisingly, my variant, whose logic was describedhere, turned out to be the fastest. The ported function from C++ library turned out to be the slowest.

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

test result:

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

but it is more reasonable to use this variant to use the saved previous values in case the requested day is the same. Therefore, the function will work even faster.

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;
}
Files:
TestDate.mq5  9 kb
 

Forum on trading, automated trading systems and testing trading strategies

Features of mql5 language, subtleties and techniques of work

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

Forum on trading, automated trading systems and testing trading strategies

Peculiarities of mql5 language, subtleties and techniques of work

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
 
fxsaber #:

To the piggy bank of algorithms on the topic.

I checked days_from_civil() and epoch_days_fast() from the links you sent.

These are not the fastest (probably the MQL5 compiler).