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

 
// ArrayResize с освобождением памяти
template <typename T>
int ArrayResize2( T &Array[], const int NewSize, const int Reserve = 0 )
{
  T ArrayTmp[];
  
  ArraySwap(Array, ArrayTmp);
  const int Res = ArrayResize(Array, NewSize, Reserve);
  
  if (Res > 0)
    ArrayCopy(Array, ArrayTmp, 0, 0, MathMin(Res, ArraySize(ArrayTmp)));
    
  return(Res);
}
 
fxsaber:

ありがとうございます。ArraySwap関数を使うことは、私には思いつかなかったでしょう。

この関数は、サイズの増加と減少の両方、つまりArrayResizeの完全なアナログとして動作すると理解しています。

ArraySwap、ArrayResize、ArrayCopyの 3つの関数を呼び出すのではなく、配列が拡大されたときに標準のArrayResize関数を呼び出すだけでよいのではないでしょうか?


p.sMqlTickの 配列を1,000,000から500,0000に減らすときに、あなたのバージョンと私のバージョンを2つコピーしてテストしました。あなたのバージョンでは22ミリ秒です。私のは37-38でやってます。
 
pivomoe:

ArraySwap、ArrayResize、ArrayCopyの 3つの関数を呼び出すよりも、配列が大きくなったときに標準のArrayResizeを単純に呼び出す方が良いのではありませんか?

考えたけどやらなかったのは、実はもっと微妙なところがあるからです。だから、饒舌なバージョンにこだわることにしたんです。

その機微のひとつが、これです。コンストラクタ/デストラクタを持つ構造体の配列を取ると、ArrayResizeは値の差に等しい量だけ、どちらかの方向に呼び出します。

しかし、ArrayCopyを適用 すると、すべてのコンストラクタが呼び出されます。ArrayTmpの削除は、すべてのデストラクタの呼び出しになります。つまり、ArrayResize2 は本当の ArrayResize ではないのです。

 
このようにArrayResize( arr, new_size, -1)と書けばよいことがわかりました。しかし、あなたのバージョンの方がまだ速く、約22ミリ秒対37ミリ秒です。
 
EAのフレームモードから外れてしまい、再度フレームモードに戻す必要がある場合があります。次のExpert Advisorは、この方法を示しています。
// Создание mqd-Файла из Тестера, чтение mqd-файла из Терминала во фрейм/стандартном режиме работы советника.

input int Range = 0; // 0..10

void OnTesterInit( void ) {}
void OnTesterDeinit( void ) {}

#define  TOSTRING(A) #A + " = " + (string)(A) + " "

void OnTesterPass( void )
{
  ulong Pass;
  string Name;
  long ID;
  double Value;

  while (FrameNext(Pass, Name, ID, Value)) // Прочли очередной проход из mqd-файла.
    Print(TOSTRING(Pass) + TOSTRING(Name) + TOSTRING(Value)); // Вывели данные mqd-файла
}

double OnTester( void )
{
  if (MQLInfoInteger(MQL_OPTIMIZATION))
  {
    uchar Data[];

    FrameAdd(TerminalInfoString(TERMINAL_DATA_PATH), 0, MathRand(), Data); // Отправили данные в mqd-файл Терминала.
  }

  return(0);
}

void OnInit()
{
  if (MQLInfoInteger(MQL_TESTER))
  {
    // OnInit для Тестера
  }
  else if (FrameFirst()) // Удалось инициализировать mqd-файл.
  {
    OnTesterInit();
    OnTesterPass();
  }
}

void OnDeinit( const int )
{
  if (MQLInfoInteger(MQL_TESTER))
  {
    // OnDeinit для Тестера
  }
  else
    OnTesterDeinit();
}

void OnTick()
{
  static const bool IsTester = MQLInfoInteger(MQL_TESTER);
  
  if (!IsTester)
    return;
    
  // OnTick для Тестера.
}


最適化後、以下のようになります。

Pass = 0 Value = 25534.0 
Pass = 1 Value = 12915.0 
Pass = 7 Value = 25534.0 
Pass = 8 Value = 12915.0 
Pass = 6 Value = 6528.0 
Pass = 5 Value = 2523.0 
Pass = 3 Value = 22229.0 
Pass = 2 Value = 9767.0 
Pass = 4 Value = 7748.0 
Pass = 9 Value = 25534.0 
Pass = 10 Value = 12915.0 


フレームモードのExpert Advisorをオフにして、標準モードで実行すると、最適化中に取得したのと同じデータが表示されます。

この方法なら、何度でもOptimizationの 結果に戻ることができます。


SZY フレームモードのターミナルで開いたチャートでは、Expert Advisorを実行することはできないんだ。そのため、標準モードで実行する場合は、フレームを開いていないチャートで実行する必要があります。

 
extern はハードコードされたマクロになりました
#undef extern
#define extern // macro redefinition
従って、MT5のmq4コードを変更せずに動作させることは、必ずしも可能ではありません。
 

fxsaber:
extern теперь является жестко заданным макросом

従って、MT5のmq4コードを変更せずに動作させることは、必ずしも可能ではありません。

ドキュメントに変更はございません。もう少し詳しく説明してください。

 
Alexey Viktorov:

ドキュメントに変更はございません。もう少し詳しく説明してください。

このようなコード

#property script_show_inputs

#define extern input // macro redefinition

extern int i = 0;

void OnStart() {}

は常に警告を発生させます。ありえない」というのは、ちょっと厳しいですね。オーバーライドすることが可能なので、そのような状況では警告だけで必ずハングアップします。

 
fxsaber:

そのニュアンスのひとつがこれです。コンストラクタ/デストラクタを持つ構造体の配列を取る場合、どちらかの方向のArrayResizeは、値の差に等しい数でそれらを呼び出します。

しかし、ArrayCopyを適用すると、すべてのコンストラクタが呼び出されます。ArrayTmpの削除は、すべてのデストラクタの呼び出しになります。つまり、ArrayResize2 は本当の ArrayResize ではないのです。

ArrayReallocateと呼ぶべきかもしれませんが、このような強制的な再コピーに意味があるとは思えません。不必要な速度低下を招くことになる。唯一、クラスオブジェクトの 配列のポインタをリセットする(以前の値を無効にする)必要がある場合のみ、どこかに計上されていればいいのでしょう(でも、むしろひつような気もするのですが......)。
 
Alexey Navoykov:
それならArrayReallocateと呼ぶ方が正しいかもしれませんが、このような強制的な再コピーに意味があるとは思えません。不要なブレーキ

メモリを解放する ことが唯一の理由です。

理由: