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

 
アラン・ヴェルレーエン

それを実証するベンチマークコードを提供してもらえますか?

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

double Bench1( const int Size, const string Str )
{
  double Tmp = 0;
  
  for (int i = 0; i < Size; i++)
    Tmp += (double)Str;

  return(Tmp);
}

double Bench2( const int Size, const string Str )
{
  double Tmp = 0;;
  
  for (int i = 0; i < Size; i++)
    Tmp += StringToDouble(Str);

  return(Tmp);
}

double Bench3( const int Size, const string Str )
{
  double Tmp = 0;
  
  for (int i = 0; i < Size; i++)
    Tmp += StringToDouble2(Str); // https://www.mql5.com/ru/forum/170952/page83#comment_7121066

  return(Tmp);
}

void OnStart()
{  
  const string Str = "123.456";
  
  BENCH(Print(Bench1(1 e7, Str)));
  BENCH(Print(Bench2(1 e7, Str)));
  BENCH(Print(Bench3(1 e7, Str)));
}


結果(リリース)

1234559999.924436
Time[Print(Bench1(1 e7,Str))] = 1656182
1234559999.924436
Time[Print(Bench2(1 e7,Str))] = 1639179
1234559999.924436
Time[Print(Bench3(1 e7,Str))] = 147382


そして、プロファイリングモードで実行すると、このような結果が得られます。

1234559999.924436
Time[Print(Bench1(1 e7,Str))] = 1757705
1234559999.924436
Time[Print(Bench2(1 e7,Str))] = 1877177
1234559999.924436
Time[Print(Bench3(1 e7,Str))] = 4578266

残念ながら、この場合、プロファイラを信用することはできません。

 
fxsaber:


結果(リリース)


HH そして、プロファイリングモードで実行すると、このようになります。

この場合、プロファイラを信用できないだけでなく、Bench1はRelease版に比べて10倍も高速に動作しています

ありがとうございます。

結果(リリース)

Time [Bench1( 1 e7,Str)] = 1680754
Time [Bench2( 1 e7,Str)] = 1646789
Time [Bench3( 1 e7,Str)] = 143408     more then 10 times faster !!! 

戦略別Expert Advisorでテストしています。

2018.04.16 14:24:28.049    Core 1    OnTester result 39725470 (µs bench1)
2018.04.16 14:26:14.629    Core 1    OnTester result 39270950 (µs bench2)
2018.04.16 14:27:13.566    Core 1    OnTester result 20467067 (µs bench3)

さらに2倍高速化(ただし10倍以上はない、これはコンパイラの最適化によるものであることは間違いない)。

ファイル:
170952.mq5  6 kb
 
アラン・ヴェルレーエン

ストラテジーアドバイザーでテスト。

2018.04.16 14:24:28.049    Core 1    OnTester result 39725470 (µs bench1)
2018.04.16 14:26:14.629    Core 1    OnTester result 39270950 (µs bench2)
2018.04.16 14:27:13.566    Core 1    OnTester result 20467067 (µs bench3)

それでも2倍は速い(ただし、10倍以上はない、これは明らかにコンパイラの最適化によるもの)。

OnTickの計算だけでなく、ティックを生成するための時間をまだ計測していることになります。

以下はOnTickの計測のみです。

#define  PROFILER_OnTick // Замеряет чистое время выполнения всех OnTick - немного замедляет общую работу
#include <TesterBenchmark.mqh> // https://www.mql5.com/ru/code/18804

input int bench=1;// between 1 and 3

void OnTick()
{
  static const string Str = "123.456";

  switch(bench)
  {
    // https://www.mql5.com/ru/forum/170952/page84#comment_7121207
    case 1 : Bench1(1, Str); break;
    case 2 : Bench2(1, Str); break;
    case 3 : Bench3(1, Str); break;
  }  
}


ベンチ1

i = 0 Pass = 0 OnTester = 3.729 s.: OnTick Profiler: Count = 7197033, Interval = 1.895 s., 3796990.9 unit/sec , Agent = C:\Program Files\Alpari Limited MT5\Tester\Agent-127.0.0.1-3000 build = 1795
i = 1 Pass = 1 OnTester = 3.843 s.: OnTick Profiler: Count = 7197033, Interval = 1.950 s., 3690523.1 unit/sec , Agent = C:\Program Files\Alpari Limited MT5\Tester\Agent-127.0.0.1-3000 build = 1795


ベンチ3

i = 0 Pass = 0 OnTester = 2.280 s.: OnTick Profiler: Count = 7197033, Interval = 0.631 s., 11404799.6 unit/sec , Agent = C:\Program Files\Alpari Limited MT5\Tester\Agent-127.0.0.1-3000 build = 1795
i = 1 Pass = 1 OnTester = 2.340 s.: OnTick Profiler: Count = 7197033, Interval = 0.640 s., 11242184.6 unit/sec , Agent = C:\Program Files\Alpari Limited MT5\Tester\Agent-127.0.0.1-3000 build = 1795


3倍の差で。BenchX関数を何度も呼び出すから10倍速くなるわけではないのです。StringToDouble2そのものは確かに10倍速いです。

 
fxsaber:

OnTickの計算だけでなく、ティックを生成するための時間を静止画で計測しているのです。

...

その通りです。

10倍速いというのは本当に驚きですが、あなたの関数は、文字列が有効なdouble値を含んでいることが分かっている場合にしか使えませんね。

2018.04.16 17:14:16.183 170952_180416 (EURUSD, H1) StringToDouble2 (abcdef) = 5456784.0

 
アレクセイ・ヴャジミキン

ありがとうございます。しかし、このスクリプトも保存が正しくありません。

私のモニターでは、右側の制限に縦線が引かれており、スクリーンショットはその線をはるかに超えています。

サービスデスクからの回答を引用します。

この場合、ChartScreenShotの ヘルプは文字通りに解釈してください。

align_mode=ALIGN_RIGHT

[in] 細かいスクリーンショットの出力のモード。ENUM_ALIGN_MODE 列挙型の値。ALIGN_RIGHT は、右の境界線にアライメントする(端から出力する) ことを示す。ALIGN_LEFTは左寄せを指定する。

つまり、アラインメント ALIGN_RIGHT を指定すると、グラフは強制的に右のボーダーにスクロールされ、これは、以下のコマンドを実行するのと同じことになります。

ChartNavigate(0,CHART_END,0);

この動作は、まだChartNavigate()関数がなかった何年も前(つまり歴史的に)に確立されたものです。align_mode=ALIGN_RIGHTを設定すると、グラフの右側のボーダーが正確に削除されることが保証されます。

ChartNavigate()関数が追加されても、ChartScreenShot 関数の動作は変更されないようにしました。

従って、期待する効果(グラフが右端にスクロールされない)を得たい場合は、align_modeパラメータにALIGN_LEFT値を使用します。

 
アラン・ヴェルレーエン

この関数は、文字列が有効な double 値を含んでいることが分かっている場合にのみ使用することができます。

2018.04.16 17:14:16.183 170952_180416 (EURUSD, H1) StringToDouble2 (abcdef) = 5456784.0

修正され、オリジナルのように動作するようになった

#define  PRINT(A) Print(#A + " = " + (string)(A))

void OnStart()
{    
  const string Str[] = {"123.456", "-asdf1234", "12as", ".34 a", "..23", "1.."};

  for (int i = 0; i < ArraySize(Str); i++)
  {
    PRINT(Str[i]);
    
    PRINT((double)Str[i]);
    PRINT(StringToDouble2(Str[i])); // https://www.mql5.com/ru/forum/170952/page83#comment_7121066
    
    Print("");
  }
}
 

ハイライトされたconstを削除すると、関数の実行時間が2倍になります。これは、コンパイラが常に最適なコードを作成するわけではなく、このようなヒントが必要であることを示している。

 
fxsaber:

マークされたconstを削除すると、関数の実行時間が2倍になる。これは、コンパイラが常に最適なコードを作成するわけではなく、このようなヒントが必要であることを示しています。

面白いですね、ありがとうございます。

一度回答があったコードを編集しないでください、更新した通知が届きません。

 
fxsaber

マークされたconstを削除すると、関数の実行時間が2倍になる。これは、コンパイラが常に最適なコードを作成するわけではなく、このようなヒントが必要であることを示しています。

非常に興味深い...
なぜこのようなことが起こるのか、何かお考えはありますか?

その仕組みとは?

 

MQL5で撮影したスクリーンショットの幅を、ある期間のすべてのバーを含むように決定するために、以下のソリューションを提供します。

スクリーンショットの幅は、チャートの近似度によって補正する必要があることが判明した。

実際の「係数」は、スケールのあるバリアントとないバリアントで(特に私の場合)異なることが判明しました。

 if (Use_Shakala==false)
 
 {
   ChartSetInteger(0,CHART_SHOW_PRICE_SCALE ,0);//Показывать или нет ценовую шкалу
   int TotalPixel=(int)ChartGetInteger(0,CHART_WIDTH_IN_PIXELS);   // Ширина графика в пикселях
   int WidthBar=int(1<<ChartGetInteger(0,CHART_SCALE));            // сколько пикселей между барами
   int FirstBar=(int)ChartGetInteger(0,CHART_FIRST_VISIBLE_BAR);   // номер первого (левого) бара на экране
   int VisibleBars=(int)ChartGetInteger(0,CHART_VISIBLE_BARS);     // количество видимых баров на экране
   int BarNr= FirstBar-(Shift_Start-Shift_Stop)*(-1);              // номер искомого бара, допустим на 12 баров правее самого левого бара 
   int LeftPixelOfBar=((FirstBar-BarNr)>=VisibleBars || FirstBar<BarNr)?(-1):((FirstBar-BarNr)*WidthBar);  // левый пиксель искомого бара, если бара нет на экране тогда -1
   int RightPixelOfBar =(LeftPixelOfBar<0)?(-1):LeftPixelOfBar+WidthBar-1;                                 //правый пиксель искомого бара, если бара нет на экране тогда -1
   if (RightPixelOfBar>=TotalPixel) RightPixelOfBar=TotalPixel-1;  // проверяем не за пределами ли экрана 

      if (Zoom==0)ZoomX=6;
      if (Zoom==1)ZoomX=5;
      if (Zoom==2)ZoomX=5;
      if (Zoom==3)ZoomX=4;
      if (Zoom==4)ZoomX=2;
      if (Zoom==5)ZoomX=0;

  pp=WidthBar*((Shift_Start-Shift_Stop)*(-1)+2)+ZoomX;
}

  if (Use_Shakala==true)
  {
   ChartSetInteger(0,CHART_SHOW_PRICE_SCALE ,1);//Показывать или нет ценовую шкалу
   int TotalPixel=(int)ChartGetInteger(0,CHART_WIDTH_IN_PIXELS);   // Ширина графика в пикселях
   int WidthBar=int(1<<ChartGetInteger(0,CHART_SCALE));            // сколько пикселей между барами
   int FirstBar=(int)ChartGetInteger(0,CHART_FIRST_VISIBLE_BAR);   // номер первого (левого) бара на экране
   int VisibleBars=(int)ChartGetInteger(0,CHART_WIDTH_IN_BARS);     // количество видимых баров на экране
   int BarNr= FirstBar-(Shift_Start-Shift_Stop)*(-1);              // номер искомого бара, допустим на 12 баров правее самого левого бара 
   int LeftPixelOfBar=((FirstBar-BarNr)>=VisibleBars || FirstBar<BarNr)?(-1):((FirstBar-BarNr)*WidthBar);  // левый пиксель искомого бара, если бара нет на экране тогда -1
   int RightPixelOfBar =(LeftPixelOfBar<0)?(-1):LeftPixelOfBar+WidthBar-1;                                 //правый пиксель искомого бара, если бара нет на экране тогда -1
   if (RightPixelOfBar>=TotalPixel) RightPixelOfBar=TotalPixel-1;  // проверяем не за пределами ли экрана 

      if (Zoom==0)ZoomX=1;
      if (Zoom==1)ZoomX=1;
      if (Zoom==2)ZoomX=1;
      if (Zoom==3)ZoomX=3;
      if (Zoom==4)ZoomX=2;
      if (Zoom==5)ZoomX=0;    
  pp=WidthBar*((Shift_Start-Shift_Stop)*(-1)+2-0.5)+ZoomX+Schkala;
  
  }
//pp 		 - ширина скриншота
//Shift_Start    - номер левого бара, который целиком должен попасть на скрин
//Shift_Stop     - номер правого бара, который целиком должен попасть на скрин
//Schkala        - ширина цифровой шкалы по методу fxsaber
//Zoom           - степень приближение экрана
理由: