Vitesse d'exécution des fonctions ceil(),round(),floor()

 

Je souhaite partager avec les programmeurs une découverte inattendue, simple et utile.

Les fonctions d'arrondi :

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

se sont avérés très lents. Pour accélérer le processus d'arrondi de 4 à 5 fois (selon mes tests dans MQL5), vous pouvez remplacer ces fonctions par une alternative simple :

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

Comme ces fonctions sont souvent utilisées dans des boucles importantes et imbriquées, le gain de performance peut être très important.

Probablement, le fait d'appeler une fonction prend beaucoup de temps (stockage de différentes données, adresses, etc.). Et dans ce cas, vous pouvez vous passer des fonctions.

Fichier du script avec test de performance joint.

Dossiers :
TestSpeed.mq5  3 kb
 
Nikolai Semko:

Je souhaite partager avec les programmeurs une découverte inattendue, simple et utile.

Les fonctions d'arrondi :

se sont avérés très lents. Pour accélérer le processus d'arrondi de 4 à 5 fois (selon mes tests dans MQL5), vous pouvez remplacer ces fonctions par une alternative simple :

Comme ces fonctions sont souvent utilisées dans des boucles importantes et imbriquées, le gain de performance peut être très important.

Probablement, le fait d'appeler une fonction prend beaucoup de temps (stockage de différentes données, adresses, etc.). Et dans ce cas, vous pouvez vous passer des fonctions.

Fichier script avec test de performance joint.

Seulement moi avec cette ligne

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

Je ne suis pas d'accord avec cette ligne. Selon les règles des mathématiques, si la partie fractionnaire est inférieure à 0,5, l'arrondi se fait à la partie inférieure. Mais si vous ajoutez 0,5 à 45,27, il est arrondi à la hausse.

 
Alexey Viktorov:

Seulement, je ne suis pas d'accord avec cette ligne.

Je ne suis pas d'accord. Selon les règles des mathématiques, si la partie fractionnaire est inférieure à 0,5, elle est arrondie au chiffre inférieur. Mais si vous ajoutez 0,5 à 45,27, il est arrondi à la hausse.


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

Seulement, je ne suis pas d'accord avec cette ligne.

Je ne suis pas d'accord. Selon les règles des mathématiques, si la partie fractionnaire est inférieure à 0,5, elle est arrondie au chiffre inférieur. Mais si vous ajoutez 0,5 à 45,27, c'est arrondi vers le haut.


Vous êtes confus. J'ai volontairement inséré un code de vérification dans l'exemple :

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

Si je me trompais, l'opérateurPrint("oops...",x) serait exécuté ;

Essayez-le - c'est bon.

 
Alexey Viktorov:

Je suis le seul à avoir cette phrase.

ne sont pas d'accord. Selon les règles des mathématiques, si la partie fractionnaire est inférieure à 0,5, elle est arrondie au chiffre inférieur. Mais si vous ajoutez 0,5 à 45,27, le résultat est arrondi à la hausse.


Que diriez-vous de le prendre et de le vérifier ? ))) Comment int(45.27 + 0.5) donnerait-il 46 ? Les mêmes 45 resteront.

 
Lilita Bogachkova:


Je ne parlais pas de vitesse.

 
Nikolai Semko:

Vous devez être confus. J'ai volontairement inséré un code de contrôle dans l'exemple :

si je me trompais, alors l'instructionPrint("oops...",x) serait exécutée ;

Essayez-le - c'est bon.


Mais il est tout de même intéressant de constater que la vitesse change si le tableaun'est pas rempli de donnéesau préalable.


#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)," микросекунд");
  }
//+------------------------------------------------------------------+


et rempli

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

Que diriez-vous d'un test ? ))) Comment int(45.27 + 0.5) donnerait-il 46 ? Les mêmes 45 resteront.

Je suis d'accord, j'ai perdu le fil de mes pensées. Je retire ce que j'ai dit...

 
Lilita Bogachkova:

Mais il est tout de même intéressant de constater que la vitesse change si le tableaun'est pas rempli de donnéesau préalable.


et le remplir

C'est très simple. Le compilateur ignore la commande :
y0[i]+=0; // !!!!!!!!!!!!!!
parce que ça ne change rien. Le tableau reste non initialisé. Il semble que l'accès au tableau déjà initialisé soit plus rapide. Dans le premier cas, l'initialisation est effectuée dans la deuxième boucle lors du calcul de t1, donc t1 est plus grand que t2. Et dans le second cas, l'initialisation a lieu dans la première boucle. Par conséquent, t1 et t2 sont les mêmes.
 

Je trouve "#define" plus pratique

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

Pourquoi ne pas faire un lancer vers le long ? Bien qu'il puisse aussi être bondé, mais il est beaucoup plus facile d'y entrer.