Características del lenguaje mql5, sutilezas y técnicas - página 122

 
Me he topado con una situación en la que se puede acelerar el cambio
#define  MACROS2 \
  MACROS(0)     \
  MACROS(1)     \
  MACROS(2)     \
  MACROS(3)     \
  MACROS(4)     \
  MACROS(5)     \
  MACROS(6)     \
  MACROS(7)     \
  MACROS(8)     \
  MACROS(9)

double Math( const double Value ) { return(MathSqrt(Value)); }

typedef double(*SUMFUNC)( void );

#define  MACROS(A) double SumFunc##A() { return(Math(A + 1)); }
  MACROS2
#undef  MACROS

SUMFUNC SumFunc[10];

bool Init()
{
#define  MACROS(A) SumFunc[A] = SumFunc##A;
   MACROS2
#undef  MACROS
  
  return(true);
}

const bool Init = Init();

// Через switch
void SwitchFunc1( const int Value, double &Sum )
{    
  switch (Value)
  {
  #define  MACROS(A) case A: Sum += Math(A + 1); break;
    MACROS2
  #undef  MACROS    
  }
}

// Через массив указателей на функции
void SwitchFunc2( const int Value, double &Sum )
{
  Sum += SumFunc[Value]();
}

// Через switch2
void SwitchFunc3( const int Value, double &Sum )
{    
  switch (Value)
  {
  #define  MACROS(A) case A: Sum += SumFunc##A(); break;
    MACROS2
  #undef  MACROS    
  }
}

typedef void(*SWITCHFUNC)( const int, double& );

void Bench( SWITCHFUNC SwitchFunc )
{
  double Sum = 0;
  
  MathSrand(0);
  
  for (int i = 0; i < 1 e8; i++)
    SwitchFunc(MathRand() % 10, Sum);
    
  Print(Sum);
}

#define  BENCH(A)                                                              \
{                                                                             \
  const ulong StartTime = GetMicrosecondCount();                              \
  A;                                                                          \
  Print("Time[" + #A + "] = " + (string)(GetMicrosecondCount() - StartTime)); \
}  

void OnStart()
{
  BENCH(Bench(SwitchFunc1))
  BENCH(Bench(SwitchFunc2))  
  BENCH(Bench(SwitchFunc3))  
}


Resultado

224677638.4805779
Time[Bench(SwitchFunc1)] = 635995
224677638.4805779
Time[Bench(SwitchFunc2)] = 1646031
224677638.4805779
Time[Bench(SwitchFunc3)] = 1593283


La tercera opción (interruptor) es constantemente más lenta que la segunda (punteros de función). ¿Cuál es la razón de esto?


ZS Despacio. El tercer interruptor es más rápido que el segundo. Todo está bien.


Por lo tanto, si tienes un array inmutable de punteros a funciones, será más rápido si lo sustituyes por switch.

Почему Switch/Case, а не If/Else If?
  • 2009.06.22
  • OB OB
  • qaru.site
Этот вопрос в основном указывается на C/С++, но я думаю, что другие языки также актуальны. Я не могу понять, почему используется параметр switch/case вместо if/else if. Мне очень нравится использование goto's и приводит к тому же виду беспорядочного кода, в то время как те же результаты могут быть достигнуты с if/else, если более организованным...
 
fxsaber:


Así, si tenemos un array inmutable de punteros a funciones, es más rápido si lo sustituimos por switch.

Bueno, en este caso es lógico, porque el array se ha rellenado dinámicamente, lo que implica comprobaciones constantes de la validez del puntero. Aunque se podría optimizar, claro...

Pero si MQL soportara la inicialización de arrays mediante punteros constantes, probablemente sería lo mismo.

p.d. Tu código no es legible en absoluto. Por supuesto, comprendo que todos estos entresijos con las macros te resulten cómodos, ya que las has escrito tú mismo, así que sabes lo que hacen. Pero para un lector externo, es sólo un rompecabezas. Creo que es poco probable que usted mismo entienda lo que ha hecho aquí en seis meses ) Al menos haga un comentario o algo ...

 
Alexey Navoykov:

p.d. Su código es completamente ilegible. Por supuesto, entiendo que te sientas cómodo con todos estos artificios con las macros, porque tú mismo las escribiste, así que sabes lo que hacen. Pero para un lector externo - es sólo un rompecabezas. Creo que es poco probable que usted mismo entienda lo que hizo allí en seis meses ) Al menos haga un comentario o algo ...

De lo contrario, serías un desastre. Además, estaba experimentando con el número de transiciones. Sin las macros habría sido difícil. En cuanto a los comentarios adicionales, los tendré en cuenta en el futuro.

 
fxsaber:

De lo contrario, habría sido un desastre. Además, experimenté con el número de transiciones. Sin las macros habría sido difícil. En cuanto a los comentarios adicionales, los tendré en cuenta en el futuro.

A veces es mucho más fácil desmontar un desorden comprensible que empezar a desmontar un puzzle compacto y abandonar inmediatamente esta actividad inútil.

 
Artyom Trishkin:

A veces es mucho más fácil descifrar un escrito comprensible que empezar a descifrar un rompecabezas compacto y abandonar inmediatamente la inútil tarea que tenemos entre manos.

double Math( const double Value ) { return(MathSqrt(Value)); }

typedef double(*SUMFUNC)( void );

double SumFunc0() { return(Math(0 + 1)); }
double SumFunc1() { return(Math(1 + 1)); }
double SumFunc2() { return(Math(2 + 1)); }
double SumFunc3() { return(Math(3 + 1)); }
double SumFunc4() { return(Math(4 + 1)); }
double SumFunc5() { return(Math(5 + 1)); }
double SumFunc6() { return(Math(6 + 1)); }
double SumFunc7() { return(Math(7 + 1)); }
double SumFunc8() { return(Math(8 + 1)); }
double SumFunc9() { return(Math(9 + 1)); }

SUMFUNC SumFunc[10];

bool Init()
{
  SumFunc[0] = SumFunc0;
  SumFunc[1] = SumFunc1;
  SumFunc[2] = SumFunc2;
  SumFunc[3] = SumFunc3;
  SumFunc[4] = SumFunc4;
  SumFunc[5] = SumFunc5;
  SumFunc[6] = SumFunc6;
  SumFunc[7] = SumFunc7;
  SumFunc[8] = SumFunc8;
  SumFunc[9] = SumFunc9;
  
  return(true);
}

const bool Init = Init();

// Через switch
void SwitchFunc1( const int Value, double &Sum )
{    
  switch (Value)
  {
  case 0: Sum += Math(0 + 1); break;
  case 1: Sum += Math(1 + 1); break;
  case 2: Sum += Math(2 + 1); break;
  case 3: Sum += Math(3 + 1); break;
  case 4: Sum += Math(4 + 1); break;
  case 5: Sum += Math(5 + 1); break;
  case 6: Sum += Math(6 + 1); break;
  case 7: Sum += Math(7 + 1); break;
  case 8: Sum += Math(8 + 1); break;
  case 9: Sum += Math(9 + 1); break;
  }
}

// Через массив указателей на функции
void SwitchFunc2( const int Value, double &Sum )
{
  Sum += SumFunc[Value]();
}

// Через switch2
void SwitchFunc3( const int Value, double &Sum )
{    
  switch (Value)
  {
  case 0: Sum += SumFunc0(); break;
  case 1: Sum += SumFunc1(); break;
  case 2: Sum += SumFunc2(); break;
  case 3: Sum += SumFunc3(); break;
  case 4: Sum += SumFunc4(); break;
  case 5: Sum += SumFunc5(); break;
  case 6: Sum += SumFunc6(); break;
  case 7: Sum += SumFunc7(); break;
  case 8: Sum += SumFunc8(); break;
  case 9: Sum += SumFunc9(); break;
  }
}

typedef void(*SWITCHFUNC)( const int, double& );

void Bench( SWITCHFUNC SwitchFunc )
{
  double Sum = 0;
  
  MathSrand(0);
  
  for (int i = 0; i < 1 e8; i++)
    SwitchFunc(MathRand() % 10, Sum);
    
  Print(Sum);
}

#define  BENCH(A)                                                              \
{                                                                             \
  const ulong StartTime = GetMicrosecondCount();                              \
  A;                                                                          \
  Print("Time[" + #A + "] = " + (string)(GetMicrosecondCount() - StartTime)); \
}  

void OnStart()
{
  BENCH(Bench(SwitchFunc1))
  BENCH(Bench(SwitchFunc2))  
  BENCH(Bench(SwitchFunc3))  
}
 

Además de lo anterior, una de las causas más comunes de las ejecuciones erróneas en el probador es la inicialización defectuosa o la falta de ella.


Si la inicialización de las variables es sencilla, con los arrays es un poco más complicado. Lo más frecuente es encontrar situaciones en las que el número de elementos de una matriz aumenta, lo que puede indicar un lugar problemático.


Para detectar estos posibles problemas, puede insertar estas cadenas al principio del Asesor Experto

// Помогает найти причину потенциальной ошибочной инициализации
template <typename T>
int ArrayResize2( T &Array[], const int NewSize, const string Str )
{
  const int PrevSize = ArraySize(Array);
  const int Res = ArrayResize(Array, NewSize);
  
  if ((PrevSize < NewSize) || (Res != NewSize))
  {
  #define  TOSTRING(A) ", " + #A + " = " + (string)(A)
    Print(Str + (string)Res + TOSTRING(PrevSize) + TOSTRING(NewSize));
  #undef  TOSTRING     
  
    TesterStop();
  }
  
  return(Res);
}

// Вариант с Reserve != 0 не предусмотрен
#define ArrayResize(A,B) ArrayResize2(A, B, __FUNCSIG__ + ", Line = " + (string)__LINE__ + \
                                            ": ArrayResize(" + #A + ", " + #B + ") = ")
// Помогает найти причину потенциальной ошибочной инициализации
template <typename T1, typename T2>
int ArrayInitialize2( T1 &Array[], const T2 Value, const string Str )
{
  const int Res = ArrayInitialize(Array, Value);
  
  if (!ArraySize(Array) || !Res)
  {
  #define  TOSTRING(A) ", " + #A + " = " + (string)(A)
    Print(Str + (string)Res + TOSTRING(ArraySize(Array)));
  #undef  TOSTRING     
  
    TesterStop();
  }
  
  return(Res);
}

#define ArrayInitialize(A,B) ArrayInitialize2(A, B, __FUNCSIG__ + ", Line = " + (string)__LINE__ +  \
                                                    ": ArrayInitialize(" + #A + ", " + #B + ") = ")


Si se detecta la situación, se escribirá la información detallada en el registro y se detendrá la ejecución.


SZZ Ejemplo de aplicación.

 
Entiendo lo del redimensionamiento, yo mismo suelo utilizar el mismo método, pero ¿qué pasa con la inicialización? ¿Cómo puede estar mal?
 
Alexey Navoykov:
Lo entiendo, yo mismo utilizo a menudo este método, pero ¿qué pasa con la inicialización? ¿Cómo puede ser inválido?

Por ejemplo, ArrayResize y ArrayInitialize se confunden. O, por ejemplo, el indicador realiza ArrayInitialize del buffer en OnInit, pensando erróneamente que el buffer está inicializado.

 
fxsaber:

Por ejemplo, ArrayResize y ArrayInitialize se confunden.

Es un error muy infantil. ¿Vale la pena tanto esfuerzo para encontrarlo?

 
Ilya Malev:

Es un error infantil. ¿Merece la pena el esfuerzo de encontrarlo?

Encontrar cualquier error requiere un esfuerzo. Especialmente cuando el código es grande y no es propio.