Caractéristiques du langage mql5, subtilités et techniques - page 122

 
Je suis tombé sur une situation où l'échange peut être accéléré.
#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))  
}


Résultat

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


La troisième variante (switch) est régulièrement plus lente que la deuxième (pointeurs de fonction). Quelle en est la raison ?


ZS Lentement. Le troisième commutateur est plus rapide que le deuxième. Tout va bien.


Ainsi, si vous avez un tableau immuable de pointeurs vers des fonctions, il sera plus rapide si vous le remplacez par switch.

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


Ainsi, si nous avons un tableau immuable de pointeurs vers des fonctions, il est plus rapide de le remplacer par switch.

Dans ce cas, c'est logique, car le tableau a été rempli dynamiquement, ce qui implique des contrôles constants de la validité des pointeurs.

Mais si MQL supportait l'initialisation des tableaux par des pointeurs constants, ce serait probablement la même chose.

p.s. Votre code n'est pas du tout lisible. Bien sûr, je comprends que vous trouviez toutes ces subtilités avec les macros commodes, puisque vous les avez écrites vous-même, donc vous savez ce qu'elles font. Mais pour un lecteur externe, c'est juste un puzzle. Je pense qu'il est peu probable que vous compreniez vous-même ce que vous avez fait ici en six mois... Faites au moins un commentaire ou quelque chose...

 
Alexey Navoykov:

p.s. Votre code est complètement illisible. Bien sûr, je comprends que vous soyez à l'aise avec tous ces artifices avec les macros, parce que vous les avez vous-même écrites, donc vous savez ce qu'elles font. Mais pour un lecteur externe - c'est juste un puzzle. Je pense qu'il est peu probable que vous compreniez vous-même ce que vous avez fait là dans six mois... Faites au moins un commentaire ou quelque chose...

Sinon, vous seriez dans le pétrin. De plus, j'expérimentais le nombre de transitions. Sans les macros, cela aurait été difficile. À propos des commentaires supplémentaires - j'en tiendrai compte à l'avenir.

 
fxsaber:

Sinon, ça aurait été le bordel. De plus, j'ai expérimenté avec le nombre de transitions. Sans les macros, cela aurait été difficile. À propos des commentaires supplémentaires - j'en tiendrai compte à l'avenir.

Parfois, il est beaucoup plus facile de démonter un écheveau compréhensible que de commencer à démonter un puzzle compact et d'abandonner immédiatement cette activité inutile.

 
Artyom Trishkin:

Il est parfois plus facile de démêler un texte compréhensible que de commencer à décortiquer un puzzle compact et d'abandonner immédiatement cette tâche inutile.

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

En plus de ce qui précède, l'une des causes les plus courantes d'exécutions non concordantes dans le testeur est une initialisation défectueuse ou un manque d'initialisation.


Si l'initialisation des variables manquantes est simple, celle des tableaux est un peu plus compliquée. Le plus souvent, c'est la découverte de situations où le nombre d'éléments du tableau augmente qui peut indiquer un endroit problématique.


Afin d'éviter ces problèmes potentiels, vous pouvez insérer ces chaînes au début du conseiller expert.

// Помогает найти причину потенциальной ошибочной инициализации
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 la situation est détectée, les informations détaillées seront écrites dans le journal et l'exécution sera arrêtée.


SZZ Exemple d' application.

 
J'ai compris pour le redimensionnement, j'utilise souvent la même méthode moi-même, mais qu'en est-il de l'initialisation ? Comment cela peut-il être faux ?
 
Alexey Navoykov:
Je comprends, j'utilise souvent cette méthode moi-même, mais qu'en est-il de l'initialisation ? Comment peut-il être invalide ?

Par exemple, ArrayResize et ArrayInitialize sont confondus. Ou, par exemple, l'indicateur effectue ArrayInitialize du tampon à OnInit, pensant par erreur que le tampon est initialisé.

 
fxsaber:

Par exemple, ArrayResize et ArrayInitialize sont confondus.

C'est une erreur très enfantine. Cela vaut-il la peine de faire un tel effort pour la trouver ?

 
Ilya Malev:

C'est une erreur enfantine. Ça vaut le coup de la chercher ?

Trouver une erreur demande un effort. Surtout lorsque le code est volumineux et n'est pas le vôtre.