Geschwindigkeit der Ausführung der Funktionen ceil(),round(),floor()

 

Ich möchte eine unerwartete, einfache und nützliche Erkenntnis mit Programmierern teilen.

Die Rundungsfunktionen:

floor(), ceil(), round() 
они же
MathFloor(), MathCeil(),MathRound()

Sie haben sich als sehr langsam erwiesen. Um den Rundungsprozess um das 4-5fache zu beschleunigen (nach meinen Tests in MQL5), können Sie diese Funktionen durch eine einfache Alternative ersetzen:

double x=45.27;
int y;
//работает только для положительных чисел!!! 
y=floor(x); -> y=(int)x;
y=ceil(x);  -> y=(x-(int)x>0)?(int)x+1:(int)x;   //более быстрым вариантом является: -> y=(int)(x+0.9999999999999997), но при этом возможна некорректная отработка
y=round(x); -> y=(int)(x+0.5);

Удобнее использовать #define.  Например:
#define _ceil(x) (x-(int)x>0)?(int)x+1:(int)x
y=_ceil(x);

// Для положительных и отрицательных чисел: ( выигрыш в скорости - в 3-4 раза)
#define _ceil(x) (x-(int)x>0)?(int)x+1:(int)x
#define _round(x) (x>0)?(int)(x+0.5):(int)(x-0.5)
#define _floor(x) (x>0)?(int)x:((int)x-x>0)?(int)x-1:(int)x

Da diese Funktionen häufig in großen und verschachtelten Schleifen verwendet werden, kann der Leistungsgewinn beträchtlich sein.

Wahrscheinlich ist der Aufruf einer Funktion recht zeitaufwendig (Speicherung verschiedener Daten, Adressen usw.). Und in diesem Fall können Sie auf Funktionen verzichten.

Datei des Skripts mit Leistungstest im Anhang.

Dateien:
TestSpeed.mq5  3 kb
 
Nikolai Semko:

Ich möchte eine unerwartete, einfache und nützliche Erkenntnis mit den Programmierern teilen.

Die Rundungsfunktionen:

Sie haben sich als sehr langsam erwiesen. Um den Rundungsprozess um das 4-5fache zu beschleunigen (nach meinen Tests in MQL5), können Sie diese Funktionen durch eine einfache Alternative ersetzen:

Da diese Funktionen häufig in großen und verschachtelten Schleifen verwendet werden, kann der Leistungsgewinn beträchtlich sein.

Wahrscheinlich ist der Aufruf einer Funktion recht zeitaufwendig (Speicherung verschiedener Daten, Adressen usw.). Und in diesem Fall können Sie auf Funktionen verzichten.

Skriptdatei mit Leistungstest im Anhang.

Nur ich mit dieser Zeile

y=round(x); -> y=(int)(x+0.5);

Ich bin mit diesem Satz nicht einverstanden. Nach den Regeln der Mathematik wird, wenn der Bruchteil kleiner als 0,5 ist, auf die niedrigere Seite gerundet. Wenn Sie jedoch 0,5 zu 45,27 addieren, wird der Wert nach oben gerundet.

 
Alexey Viktorov:

Nur bin ich mit diesem Satz nicht einverstanden.

Dem stimme ich nicht zu. Nach den Regeln der Mathematik wird der Bruchteil, der kleiner als 0,5 ist, abgerundet. Wenn Sie jedoch 0,5 zu 45,27 addieren, wird der Wert nach oben gerundet.


#define  MUL(x) ((x)+(0.5)) 
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   ulong t0,t1,t2,t3;
   int y0[],y1[],y2[];
   ArrayResize(y0,10000000);
   ArrayResize(y1,10000000);
   ArrayResize(y2,10000000);
   double x=1.45;  

   for(int i=0;i<10000000;i++)
     {
      if ((int)(x+0.5)!=(int)round(x)) Print("ой...",x);
      x+=0.27;
      y0[i]+=0;
      y1[i]+=0;
      y2[i]+=0;
     }
     Print("y0[]: ",y0[9999999]," / y1[]: ",y1[9999999]," / y2[]: ",y2[9999999]);
   x=1.45;
   t0=GetMicrosecondCount();
   for(int i=0;i<10000000;i++)
     {
      y0[i]=(int)MathRound(x);
      x+=0.27;
     }
   t1=GetMicrosecondCount()-t0;
   Print("y0[]: ",y0[9999999]);
   x=1.45;
   t0=GetMicrosecondCount();
   for(int i=0;i<10000000;i++)
     {
      y1[i]=(int)(x+0.5);
      x+=0.27;
     }
   t2=GetMicrosecondCount()-t0;
   Print("y1[]: ",y1[9999999]);
   x=1.45;
   t0=GetMicrosecondCount();
   for(int i=0;i<10000000;i++)
     {
      y2[i]=(int)MUL(x);
      x+=0.27;
     }
   t3=GetMicrosecondCount()-t0;
   Print("y2[]: ",y2[9999999]);
   Print("Цикл округления 10 000 000 раз: (round) = ",IntegerToString(t1),"   альтернатива с (int) = ",IntegerToString(t2),"   альтернатива с (#define) = ",IntegerToString(t3)," микросекунд");
  }
//+------------------------------------------------------------------+
 
Alexey Viktorov:

Nur bin ich mit diesem Satz nicht einverstanden.

Dem stimme ich nicht zu. Nach den Regeln der Mathematik wird der Bruchteil, der kleiner als 0,5 ist, abgerundet. Aber wenn man 0,5 zu 45,27 addiert, wird aufgerundet.


Sie sind verwirrt. Ich habe absichtlich einen Verifizierungscode in das Beispiel eingefügt:

for(int i=0;i<10000000;i++)
     {
      if ((int)(x+0.5)!=(int)round(x)) Print("ой...",x);
      x+=0.27;
     }

Wenn ich mich geirrt hätte, wäre der OperatorPrint("oops...",x) ausgeführt worden;

Probieren Sie es aus - es ist in Ordnung.

 
Alexey Viktorov:

Ich bin der Einzige, der diesen Satz sagt.

nicht zustimmen. Nach den Regeln der Mathematik wird der Bruchteil, der kleiner als 0,5 ist, abgerundet. Wenn man aber 0,5 zu 45,27 addiert, wird der Wert nach oben gerundet.


Wie wär's, wenn Sie es mitnehmen und ausprobieren? ))) Wie würde int(45,27 + 0,5) 46 ergeben? Die gleichen 45 werden bleiben.

 
Lilita Bogachkova:


Ich habe nicht von der Geschwindigkeit gesprochen.

 
Nikolai Semko:

Sie müssen verwirrt sein. Ich habe absichtlich einen Prüfcode in das Beispiel eingefügt:

wenn ich mich geirrt hätte, würde die AnweisungPrint("oops...",x) ausgeführt werden;

Probieren Sie es aus - es ist in Ordnung.


Dennoch ist es interessant, dass sich die Geschwindigkeit ändert, wenn das Arraynichtvorhermit Daten gefüllt wird


#define _round(x) (int)((x)+(0.5)) 
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   ulong t0,t1,t2;
   int y0[],y1[];
   ArrayResize(y0,10000000);

   double x=1.45;  

   for(int i=0;i<10000000;i++)
     {
      if ((int)(x+0.5)!=(int)round(x)) Print("ой...",x);
      x+=0.27;
      y0[i]+=0; // !!!!!!!!!!!!!!
     }

   x=1.45;
   t0=GetMicrosecondCount();
   for(int i=0;i<10000000;i++)
     {
      y0[i]=(int)(x+0.5);
      x+=0.27;
     }
   t1=GetMicrosecondCount()-t0;

   x=1.45;
   t0=GetMicrosecondCount();
   for(int i=0;i<10000000;i++)
     {
      y0[i]=_round(x);
      x+=0.27;
     }
   t2=GetMicrosecondCount()-t0;

   Print("Цикл округления 10 000 000 раз:  с (int) = ",IntegerToString(t1),"   с (#define) = ",IntegerToString(t2)," микросекунд");
  }
//+------------------------------------------------------------------+


und gefüllt

#define _round(x) (int)((x)+(0.5)) 
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   ulong t0,t1,t2;
   int y0[],y1[];
   ArrayResize(y0,10000000);

   double x=1.45;  

   for(int i=1;i<10000000;i++)
     {
      if ((int)(x+0.5)!=(int)round(x)) Print("ой...",x);
      x+=0.27;
      y0[i]+=1; // !!!!!!!!!!!!!!
     }

   x=1.45;
   t0=GetMicrosecondCount();
   for(int i=0;i<10000000;i++)
     {
      y0[i]=(int)(x+0.5);
      x+=0.27;
     }
   t1=GetMicrosecondCount()-t0;

   x=1.45;
   t0=GetMicrosecondCount();
   for(int i=0;i<10000000;i++)
     {
      y0[i]=_round(x);
      x+=0.27;
     }
   t2=GetMicrosecondCount()-t0;

   Print("Цикл округления 10 000 000 раз:  с (int) = ",IntegerToString(t1),"   с (#define) = ",IntegerToString(t2)," микросекунд");
  }
//+------------------------------------------------------------------+
 
Ihor Herasko:

Wie wäre es, wenn Sie das überprüfen? ))) Wie würde int(45,27 + 0,5) 46 ergeben? Die gleichen 45 werden bleiben.

Ich stimme zu, ich habe meinen Gedankengang verloren. Ich nehme es zurück...

 
Lilita Bogachkova:

Dennoch ist es interessant, dass sich die Geschwindigkeit ändert, wenn das Arraynichtvorhermit Daten gefüllt wird


und füllen ihn

Es ist ganz einfach. Der Compiler ignoriert den Befehl:
y0[i]+=0; // !!!!!!!!!!!!!!
denn es ändert sich nichts. Das Array bleibt uninitialisiert. Es sieht so aus, als ob auf das bereits initialisierte Array schneller zugegriffen wird. Im ersten Fall wird die Initialisierung in der zweiten Schleife bei der Berechnung von t1 durchgeführt, so dass t1 größer als t2 ist. Und im zweiten Fall erfolgt die Initialisierung in der ersten Schleife. Daher sind t1 und t2 identisch.
 

Ich finde "#define" praktischer

#define _floor(x) (int)((x)) 
#define _ceil(x)  (int)((x)+(1)) 
#define _round(x) (int)((x)+(0.5)) 
 

Warum wirfst du nicht in die Länge? Obwohl man ihn auch überlaufen lassen kann, ist es viel einfacher, einen Int zu überlaufen.