MQL4およびMQL5でDigits()をバイパスして任意の数値(引用符だけでなく)の小数点以下桁数を取得 - ページ 14

 

構造体の配列(sizeof(int)の倍数の長さ)をint[]配列と相互変換する高速な実装について、ブレインストーミングを行うことはできますか?

実用的な用途としては、リソースを介した高速なデータ交換が挙げられます。私のバリアントは 普遍的すぎるから遅くなるんだ。


MT5で登場した機能

StructToCharArray
CharArrayToStruct

MT4には存在しませんし、おそらく今後も存在しないでしょう。だから、これらの機能がある場合とない場合(役に立つ場合)で問題を解決しなければならない。

この結果は、ここのフォーラムの多くのユーザーにとって有益なものとなることでしょう。


この問題に対する私の解決策で、ブレインストーミングのための非常にシンプルなテンプレートを書きました。

// Решения задачи: https://www.mql5.com/ru/forum/287618/page14#comment_9806429

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

// Инициализация исходных данных
int InitData( MqlTick &Ticks[], const int Amount = 1 e6, const double Price = 1.2345)
{
  const int Size = ArrayResize(Ticks, Amount);
  
  for (int i = 0; i < Size; i++)
    Ticks[i].bid = Price;
    
  return(Size);
}

MqlTick TicksIn[];                  // Массив с исходными данными.
const int Init = InitData(TicksIn); // Инициализировали его.

// Проверка, что два массива совпадают.
bool IsCompare( const MqlTick &Ticks1[], const MqlTick &Ticks2[] )
{
  const int Size = ArraySize(Ticks1);  
  bool Res = (Size == ArraySize(Ticks2));
  
  for (int i = 0; (i < Size) && Res; i++)
    Res = (Ticks1[i].bid == Ticks2[i].bid);
    
  return(Res);
}

void OnStart()
{
  MqlTick TicksOut[];
  int Array[];
  
  BENCH(TicksToIntArray_fxsaber1(TicksIn, Array));  // Замерили конвертацию MqlTick[] -> int[].
  BENCH(IntArrayToTicks_fxsaber1(Array, TicksOut)); // Замерили конвертацию int[] -> MqlTick[].
  
  Print(IsCompare(TicksIn, TicksOut)); // Убедились, что тики на входе и выходе совпадают.
}

// Варианты реализаций
/***********************************************/
#include <TypeToBytes.mqh> // https://www.mql5.com/ru/code/16280

template <typename T1, typename T2>
int ArrayToArray( const T1 &Source[], T2 &Target[] )
{
  return(_ArrayCopy(Target, Source) / sizeof(T2));
}

// Перевод массива тиков в массив int[].
int TicksToIntArray_fxsaber1( const MqlTick &Ticks[], int &Array[] )
{
  return(ArrayToArray(Ticks, Array));
}

// Перевод массива int[] в массив тиков.
int IntArrayToTicks_fxsaber1( const int &Array[], MqlTick &Ticks[] )
{
  return(ArrayToArray(Array, Ticks));
}
/***********************************************/


結果

Time[TicksToIntArray_fxsaber1(TicksIn,Array)] = 2036468
Time[IntArrayToTicks_fxsaber1(Array,TicksOut)] = 2253915
true
 
別のバリアント
template <typename T>
union INTEGER
{
  T Data;
  int Integer[sizeof(T) / sizeof(int)];
};

// Перевод массива тиков в массив int[].
int TicksToIntArray_fxsaber2( const MqlTick &Ticks[], int &Array[] )
{
  INTEGER<MqlTick> TickInteger;
  
  const int Size1 = ArraySize(Ticks);
  const int Size2 = ArrayResize(Array, Size1 * sizeof(MqlTick) / sizeof(int));
  int j = 0;
  
  for (int i = 0; (i < Size1) && (j < Size2); i++)
  {
    TickInteger.Data = Ticks[i];
    
    j += ArrayCopy(Array, TickInteger.Integer, j);
  }    
  
  return(j);
}

// Перевод массива int[] в массив тиков.
int IntArrayToTicks_fxsaber2( const int &Array[], MqlTick &Ticks[] )
{
  INTEGER<MqlTick> TickInteger;
  
  const int Size1 = ArraySize(Array);
  const int Size2 = ArrayResize(Ticks, Size1 * sizeof(int) / sizeof(MqlTick));
  int j = 0;
  
  for (int i = 0; (i < Size1) && (j < Size2); j++)
  {
    i += ArrayCopy(TickInteger.Integer, Array, 0, i, sizeof(MqlTick) / sizeof(int));

    Ticks[j] = TickInteger.Data;        
  }    
  
  return(j);
}


結果

Time[TicksToIntArray_fxsaber2(TicksIn,Array)] = 91967
Time[IntArrayToTicks_fxsaber2(Array,TicksOut)] = 79630
true


2番目の選択肢よりかなり速いもの。スピードアップの方法はないのでしょう。

 

構造体の配列 (sizeof(int) の倍数の長さ) を int[] 配列と相互変換する高速な実装はありますか?

こんな感じ

#property strict

template< typename S, typename T >
union UDT{
  UDT(){for(int i=0;i<sizeof(S)/sizeof(T);i++)t[i]=0;}
  UDT(S&src){s=src;}
  UDT(T&src[],int k=0){for(int i=0;i<sizeof(S)/sizeof(T);i++)t[i]=src[i+k];}
  void to(S&dst){dst=s;}
  void to(T&dst[],int k=0){for(int i=0;i<sizeof(S)/sizeof(T);i++)dst[i+k]=t[i];}
  S s; T t[sizeof(S)/sizeof(T)];};

template< typename S, typename T >
void StructToArray(S&src[],T&dst[]){
  ArrayResize(dst,ArraySize(src)*(sizeof(S)/sizeof(T)));
  for(int i=0;i<ArraySize(dst)/(sizeof(S)/sizeof(T));i++){UDT<S,T>u(src[i]);u.to(dst,i*(sizeof(S)/sizeof(T)));}}

template< typename S, typename T >
void ArrayToStruct(T&src[],S&dst[]){
  ArrayResize(dst,ArraySize(src)/(sizeof(S)/sizeof(T)));
  for(int i=0;i<ArraySize(dst);i++){UDT<S,T>u(src,i*(sizeof(S)/sizeof(T)));u.to(dst[i]);}}

void OnStart()
 {
  MqlRates rates[],r2[];
  int l[];
  CopyRates(_Symbol,_Period,0,10,rates);
  for(int i=0;i<10;i++)printf("open=%f, high=%f, low=%f, close=%f, time=%s",rates[i].open,rates[i].high,rates[i].low,rates[i].close,TimeToStr(rates[i].time));
  Print("");
  StructToArray(rates,l);
  ArrayToStruct(l,r2);
  for(int i=0;i<ArraySize(r2);i++)printf("open=%f, high=%f, low=%f, close=%f, time=%s",r2[i].open,r2[i].high,r2[i].low,r2[i].close,TimeToStr(r2[i].time));
 }  


 
Ilya Malev:

こんな感じ。

https://www.mql5.com/ru/forum/287618/page14#comment_9807465
TicksToIntArray_fxsaber2
Time[TicksToIntArray(TicksIn,Array)] = 735252
IntArrayToTicks_fxsaber2
Time[IntArrayToTicks(Array,TicksOut)] = 591458
true

https://www.mql5.com/ru/forum/287618/page14#comment_9808274
TicksToIntArray_antfx1
Time[TicksToIntArray(TicksIn,Array)] = 398796
IntArrayToTicks_antfx1
Time[IntArrayToTicks(Array,TicksOut)] = 296646
true

よくぞ言ってくれました。コードに手を入れてみます、ありがとうございました。


ArrayCopyは 本当に遅いみたいですね。


ZY 走るたびに、結果が大きく変わるんです。例えば、Testsの順番を変えると、ほとんどすべてが逆転してしまうんです。どうやら、より客観的な速度測定が必要なようです。

ファイル:
 
あまり考えずに、デバッグもせずに、最初に思いついたことを書いたので、不具合も多いかもしれません。私自身、このような課題に遭遇したのは初めてです(労働組合とは幅広くお付き合いしていますが)。
 
fxsaber:

ZZZさん、ArrayCopyはちょっと引き気味ですね。

あるローカルなタスクで、少数のエレメントをコピーするような測定をしたことを覚えています。16要素まではforループの方がArrayCopyより はるかに速く、要素数が多くなるとArrayCopyの方が速くなりました。もちろん、ループを全く使わない最速のバリエーション(前ページの私の関数のように)もあります。

 
Ilya Malev:

一番早いのは、ループを全く使わない方法です(前ページの私の関数のように)。

理解できない。

 
fxsaber:

理解できない。

私が言いたいのは、for(int i=0; i<5; i++) dst[i]=src[i]; は dst[0]=src[0];dst[1]=src[1];dst[2]=src[2];dst[3]=src[3];dst[4]=src[4] より遅く動くということです。

というのは、ループ管理に関連する追加操作のことを考えれば、ごく当たり前のことです)

そして、CopyArrayは、今確認したところ、両者よりもはるかに高速に動作します。もちろん状況にもよるのかもしれませんが。

 

はい、この方がずっと 速く動作します(可能な限りArrayCopyで 置き換え、その他は同じです)。

template< typename S, typename T >
union UTS2{
  UTS2(){for(int i=0;i<sizeof(S)/sizeof(T);i++)t[i]=0;}
  UTS2(S&src){s=src;}
  UTS2(T&src[],int k=0){ArrayCopy(t,src,0,k,sizeof(S)/sizeof(T));}//for(int i=0;i<sizeof(S)/sizeof(T);i++)t[i]=src[i+k];}
  void to(S&dst){dst=s;}
  void to(T&dst[],int k=0){ArrayCopy(dst,t,k,0,sizeof(S)/sizeof(T));}//for(int i=0;i<sizeof(S)/sizeof(T);i++)dst[i+k]=t[i];}
  S s; T t[sizeof(S)/sizeof(T)];};

template< typename S, typename T >
void StructToArray2(S&src[],T&dst[]){
  ArrayResize(dst,ArraySize(src)*(sizeof(S)/sizeof(T)));
  for(int i=0;i<ArraySize(dst)/(sizeof(S)/sizeof(T));i++){UTS2<S,T>u(src[i]);u.to(dst,i*(sizeof(S)/sizeof(T)));}}

template< typename S, typename T >
void ArrayToStruct2(T&src[],S&dst[]){
  ArrayResize(dst,ArraySize(src)/(sizeof(S)/sizeof(T)));
  for(int i=0;i<ArraySize(dst);i++){UTS2<S,T>u(src,i*(sizeof(S)/sizeof(T)));u.to(dst[i]);}}

だから、テストもせずに最初に思いついたことを書いたってばよ))

 
Ilya Malev:

そして、CopyArrayは、今確認したところ、両方のオプションよりずっと速く動作するようです。もちろん状況次第なのかもしれませんが。

Cish memmove()の原理でArrayCopy()を行った場合。

ArrayCopy()の実行速度は、メモリの確保速度に依存すると思います。中間バッファのメモリがコピーできる状態であれば、ArrayCopy()は非常に速く実行されますが、メモリが確保されていない場合は、OSにメモリの確保を 要求するようになるでしょう。

大きなデータ量でArrayCopy()を1回呼び出し、交換用のバッファメモリを準備し、次に小さなデータ量でArrayCopy()をループさせ、速度を測定してみるとよいでしょう。