Caratteristiche del linguaggio mql5, sottigliezze e tecniche - pagina 122

 
Mi sono imbattuto in una situazione in cui lo switch può essere accelerato
#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))  
}


Risultato

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


La terza variante (switch) è costantemente più lenta della seconda (puntatori di funzione). Qual è la ragione di questo?


ZS Lentamente. Il terzo interruttore è più veloce del secondo. Tutto è a posto.


Quindi, se avete un array immutabile di puntatori a funzioni, sarà più veloce se lo sostituite con switch.

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


Quindi, se abbiamo un array immutabile di puntatori a funzioni, è più veloce se lo sostituiamo con switch.

Beh, in questo caso è logico, perché l'array è stato riempito dinamicamente, il che significa controlli costanti della validità del puntatore. Anche se potrebbe essere ottimizzato, naturalmente...

Ma se MQL supportasse l'inizializzazione degli array tramite puntatori costanti, probabilmente sarebbe lo stesso.

p.s. Il tuo codice non è affatto leggibile. Certo, capisco che tu trovi comode tutte queste complessità con le macro, dato che le hai scritte tu stesso, quindi sai cosa fanno. Ma per un lettore esterno, è solo un puzzle. Penso che tu stesso sia improbabile che tu capisca cosa hai fatto qui in sei mesi ) Almeno fai un commento o qualcosa del genere ...

 
Alexey Navoykov:

p.s. Il tuo codice è completamente illeggibile. Naturalmente, capisco che tu sia a tuo agio con tutti questi espedienti con le macro, perché tu stesso le hai scritte, quindi sai cosa fanno. Ma per un lettore esterno - è solo un puzzle. Penso che tu stesso sia improbabile capire cosa hai fatto lì in sei mesi ) Almeno fai un commento o qualcosa del genere ...

Altrimenti saresti un disastro. Inoltre, stavo sperimentando il numero di transizioni. Senza macro sarebbe stato difficile. Riguardo ai commenti aggiuntivi - ne terrò conto in futuro.

 
fxsaber:

Altrimenti sarebbe stato un casino. Inoltre, ho sperimentato il numero di transizioni. Senza macro sarebbe stato difficile. Riguardo ai commenti aggiuntivi - ne terrò conto in futuro.

A volte è molto più facile smontare uno sprawl comprensibile che iniziare a smontare un puzzle compatto e abbandonare immediatamente questa attività inutile.

 
Artyom Trishkin:

A volte è molto più facile decifrare un pezzo di scrittura comprensibile che iniziare ad analizzare un puzzle compatto e abbandonare immediatamente l'inutile compito a portata di mano.

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

Oltre a quanto detto sopra, una delle cause più comuni di corse sbagliate nel tester è l'inizializzazione difettosa o la sua mancanza.


Mentre l'inizializzazione delle variabili mancanti è semplice, gli array sono un po' più complicati. Il più delle volte è il ritrovamento di situazioni in cui il numero di elementi dell'array aumenta che può indicare un luogo problematico.


Per catturare questi potenziali problemi, potete inserire queste stringhe all'inizio dell'Expert Advisor

// Помогает найти причину потенциальной ошибочной инициализации
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 + ") = ")


Se la situazione viene catturata, allora le informazioni dettagliate saranno scritte nel log e l'esecuzione sarà fermata.


SZZ Esempio di applicazione.

 
Capisco il ridimensionamento, io stesso uso spesso lo stesso metodo, ma che dire dell'inizializzazione? Come può essere sbagliato?
 
Alexey Navoykov:
Capisco, io stesso uso spesso questo metodo, ma che dire dell'inizializzazione? Come può essere non valido?

Per esempio, ArrayResize e ArrayInitialize sono confusi. Oppure, per esempio, l'indicatore esegue ArrayInitialize del buffer a OnInit, pensando erroneamente che il buffer sia inizializzato.

 
fxsaber:

Per esempio, ArrayResize e ArrayInitialize sono confusi.

Beh, questo è un errore molto infantile. vale la pena fare un tale sforzo per trovarlo?

 
Ilya Malev:

È un errore infantile, ma vale la pena trovarlo?

Trovare qualsiasi errore richiede uno sforzo. Soprattutto quando il codice è grande e non è il vostro.