mql5言語の特徴、微妙なニュアンスとテクニック - ページ 122

 
スイッチが 高速化される状況に遭遇した
#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))  
}


結果

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


3番目のバリエーション(スイッチ)は、2番目のバリエーション(関数ポインタ)よりも着実に遅くなっています。その理由は何でしょうか?


ZS ゆっくりと。3番目のスイッチは、2番目のスイッチよりも高速です。すべて順調です。


つまり、関数へのポインタの不変配列がある場合、それをswitchに置き換えると高速になります。

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


つまり、関数へのポインタの不変配列がある場合、それをswitchに置き換えると高速になります。

この場合、配列は動的に埋められるので、ポインタの有効性を常にチェックすることになり、論理的です。 もちろん、最適化することも可能ですが......。

しかし、もしMQLが定数ポインタによる配列の初期化を サポートしていれば、おそらく同じになるはずです。

p.s. あなたのコードは全く読めません。もちろん、自分で書いたのだから、マクロが何をするものかは分かっているので、こうした複雑なことを便利に感じるのは分かります。 しかし、外部の読者にとっては、ただのパズルに過ぎないのです。あなた自身、半年後にはここで何をしたのか理解できていないと思います)せめてコメントか何かをしてください.

 
Alexey Navoykov:

p.s. あなたのコードは全く読めません。もちろん、自分が書いたものだから、マクロが何をするものかわかっているから、こうした工夫に抵抗がないのはわかります。 しかし、外部の読者にとっては、ただのパズルに過ぎないのです。あなた自身、半年もすれば何をしたのかわからなくなると思います)せめてコメントか何かをしてください.

そうでなければ、あなたは混乱してしまうでしょう。さらに、トランジションの数を試行錯誤していました。マクロがなければ難しかったと思います。追加コメントについて-今後に生かします。

 
fxsaber:

そうでなければ、混乱するところだったでしょう。さらに、トランジションの数を実験してみたんです。マクロがなければ難しかったと思います。追加コメントについて-今後に生かします。

コンパクトなパズルを分解し始めてすぐにこの無駄な活動を放棄するよりも、理解しやすいスプロールを分解する方がはるかに簡単なことがあります。

 
Artyom Trishkin:

コンパクトなパズルの解析を始めてすぐに無駄な作業を放棄するよりも、理解できる文章を解きほぐす方がずっと簡単な場合もあるのです。

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

上記に加えて、テスターで不一致のランが発生する最も一般的な原因の1つは、誤った初期化またはその欠如です。


足りない変数の初期化は簡単ですが、配列は少し複雑です。多くの場合、配列の要素 数が増える状況を見つけることが、問題の場所を示している可能性があります。


このような潜在的な問題を捕捉するために、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 + ") = ")


もし、この状況に陥った場合は、詳細情報をログに書き込み、実行を停止します。


SZZ アプリケーションの です。

 
リサイズについては理解できましたし、私自身も同じ方法をよく使いますが、初期化についてはどうでしょうか。どうして間違っているのでしょうか?
 
Alexey Navoykov:
なるほど、私自身もよく使う方法ですが、初期化についてはどうでしょうか。どうして無効なんだ?

例えば、ArrayResizeとArrayInitializeが 混在している。あるいは、例えば、インジケータが OnInit でバッファの ArrayInitialize を実行し、バッファが初期化されたと誤認している。

 
fxsaber:

例えば、ArrayResizeとArrayInitializeが混在している。

まあ、これは非常に幼稚なエラーですが、それを見つけるためにこれほどの努力をする価値があるのでしょうか?

 
Ilya Malev:

まあ、幼稚な間違いなんだけどね。 探す労力はあるかな?

どんなエラーでも見つけるのは努力が必要です。特に、コードが大きく、自分のものではない場合。

理由: