ceil(), round(), floor() 함수의 실행 속도

 

예상치 못한 간단하고 유용한 발견을 프로그래머와 공유하고 싶습니다.

반올림 함수:

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

매우 느린 것으로 밝혀졌습니다. 내 MQL5 테스트에 따르면 반올림 프로세스의 속도를 4-5배 높이려면 다음 함수를 간단한 대안으로 바꿀 수 있습니다.

 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

왜냐하면 이러한 함수는 종종 크고 중첩된 루프에서 사용되며 성능 향상은 상당히 중요할 수 있습니다.

아마도 함수를 호출 한다는 사실만으로도 매우 시간이 많이 소요될 것입니다(스택에 다른 데이터, 주소 등을 저장). 그리고 이 경우 함수 없이 할 수 있습니다.

성능 테스트가 포함된 스크립트 파일을 첨부합니다.

파일:
TestSpeed.mq5  3 kb
 
Nikolai Semko :

예상치 못한 간단하고 유용한 발견을 프로그래머와 공유하고 싶습니다.

반올림 함수:

매우 느린 것으로 밝혀졌습니다. 내 MQL5 테스트에 따르면 반올림 프로세스의 속도를 4-5배 높이려면 다음 함수를 간단한 대안으로 바꿀 수 있습니다.

왜냐하면 이러한 함수는 종종 크고 중첩된 루프에서 사용되며 성능 향상은 상당히 중요할 수 있습니다.

아마도 함수를 호출 한다는 사실만으로도 매우 시간이 많이 소요될 것입니다(스택에 다른 데이터, 주소 등을 저장). 그리고 이 경우 함수 없이도 할 수 있습니다.

성능 테스트가 포함된 스크립트 파일을 첨부합니다.

이 라인으로 나만

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

동의하지 않는다. 수학 규칙에 따르면 소수 부분이 0.5보다 작으면 반올림됩니다. 그러나 0.5를 45.27에 더하면 더 큰 값으로 반올림됩니다.

 
Alexey Viktorov :

이 라인으로 나만

동의하지 않는다. 수학 규칙에 따르면 소수 부분이 0.5보다 작으면 반올림됩니다. 그러나 0.5를 45.27에 더하면 더 큰 값으로 반올림됩니다.


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

이 라인으로 나만

동의하지 않는다. 수학 규칙에 따르면 소수 부분이 0.5보다 작으면 반올림됩니다. 그러나 0.5를 45.27에 더하면 더 큰 값으로 반올림됩니다.


무엇을 혼동하고 있습니까? 예를 들어 인증 코드를 구체적으로 삽입했습니다.

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

내가 틀렸다면 Print( "oops..." ,x);

시도 - 모든 것이 정상입니다.

 
Alexey Viktorov :

이 라인으로 나만

동의하지 않는다. 수학 규칙에 따르면 소수 부분이 0.5보다 작으면 반올림됩니다. 그러나 0.5를 45.27에 더하면 더 큰 값으로 반올림됩니다.


그리고 가져가서 확인하면? ))) int(45.27 + 0.5)는 어떻게 46을 줄까요? 동일한 45개가 유지됩니다.

 
Lilita Bogachkova :


나는 속도에 대해 말하는 것이 아니다.

 
Nikolai Semko :

당신은 무엇을 혼란스럽게 생각합니다. 예를 들어 인증 코드를 구체적으로 삽입했습니다.

내가 틀렸다면 Print 문이 실행될 것입니다 ( "oops..." ,x);

시도 - 모든 것이 정상입니다.


하지만 배열 에 데이터가 미리 채워져 있지 않으면 속도가 변경된다는 점은 여전히 흥미롭습니다.


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


그리고 채우기

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

그리고 가져가서 확인하면? ))) int(45.27 + 0.5)는 어떻게 46을 줄까요? 동일한 45개가 유지됩니다.

동의합니다. 속도를 줄이세요. 말을 되돌려...

 
Lilita Bogachkova :

하지만 배열 에 데이터가 미리 채워져 있지 않으면 속도가 변경된다는 점은 여전히 흥미롭습니다.


그리고 채우기

모든 것이 간단합니다. 컴파일러는 다음 명령을 무시합니다.
 y0[i]+= 0 ; // !!!!!!!!!!!!!!
아무것도 바뀌지 않기 때문입니다. 어레이가 초기화되지 않은 상태로 유지됩니다. 이미 초기화된 배열이 더 빨리 액세스되는 것 같습니다. 첫 번째 경우 t1을 계산할 때 두 번째 주기에서 초기화가 발생하므로 t1은 t2보다 큽니다. 그리고 두 번째 경우에는 첫 번째 주기에서 초기화가 발생합니다. 따라서 t1과 t2는 동일합니다.
 

내 생각에는 "#define"이 더 편리합니다.

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

왜 오래 캐스팅하지 않습니까? 오버플로가 가능하지만 int 오버플로가 훨씬 쉽습니다.