エラー、バグ、質問 - ページ 2160

 
Andrey Khatimlianskii:

MQの立場からすると、どうやら正しいようだ。いつものように、より便利なものを私たちに決めてください。

トレーディング、自動売買システム、トレーディング戦略のテストに関するフォーラム

プロフィールにメッセージはありません。

レナート・ファットフーリン さん 2018.03.08 10:31

互換モードで動作する機能を減らすため、一時的に削除しました。

移行作業が完了次第、新しい機能を追加していく予定です。

これは、非常に大規模で機能豊富な新しいシステムです。


新しいチャットルームを導入しています。

 
Andrey Khatimlianskii:

MQの立場からすると、どうやら正しいようだ。いつも通り、どうすればより快適に過ごせるかを考えてくれました。

テレパシーのレベルへ...。)))))

 

メインインジケーターの不明確なバグ。H1 以下のタイムフレームで、端末起動 時のみ表示されます。 エラーのテキスト "S-v5 (EURUSD,M10) array out of range in 'S-v5.mq5' (211,54)" .描画は正しいが、時系列フラグがすべてのバッファに設定されているが、逆順である。

テンプレートの構成-メインインジケータ(#1)(エラーが発生する場所)、追加インジケータ(#2)(メインインジケータの複数のハンドルを異なるパラメータ(時間枠)で組み合わせたもの)、インジケータ(#1)からの信号でメインチャートに矢印を表示するインジケータ(#3)、インジケータ#2からの信号でメインチャートに矢印を表示するインジケータ(#4)です。

タイムフレームの切り替え、テンプレートの再適用は、2から4または唯一の番号1で、市場の概要とこのテンプレートのアプリケーションから新しいシンボルの出力を任意の2つの指標を削除した場合 - エラーは再現されず、すべての指標のレンダリングが正しいです。

 

コンパイルエラー

#import "Test1.ex5"
       void f1();
#import "Test2.dll"
       void f2();
#import
typedef void (*fn)();
void OnStart()
{
    fn fn1 =        f1; //нормально
    fn fn2 =        f2; //Error: 'f2' - pointer to this function type is not supported yet
    fn fn3 = Test2::f2; //нормально
}
 

エラーメッセージのため、広範囲なコードで原因を把握することができない

struct A {
    void f( int    ) {}
    void g( int fn ) { f( fn ); } //Error: ')' - unexpected token ???
};
typedef void (*fn)();

以下略

struct A {
    void f( int    ) {}
    void g( int fn ) { f( fn ); } //Error: 'fn' - identifier already used
};
struct fn {};
 

エラーメッセージ なし

typedef void (*fn)();
void f( int i ) { Print( __FUNCTION__, ":", i ); }
void OnStart()
{
    f( fn() );
}

しかも、実行中で、結果まで出ている(!)。

このバリアントでは

typedef int (*fn)();
void OnStart() { Print( fn() ); }

エラー行にジャンプしない (Enterで)


 

コンパイルエラー

    void g(  int ) {}
    void g( uint ) {}
#import "Test.ex5"
    void f(  int );
    void f( uint );
#import
typedef void (*fn1)(  int );
typedef void (*fn2)( uint );
void OnStart()
{
    fn1 g1 = g; /*нормально и результат*/ g1( 1 ); //совпадает с ожидаемым
    fn2 g2 = g; /*нормально и результат*/ g2( 1 ); //совпадает с ожидаемым
    fn1 f1 = f; //Error: 'f' - cannot resolve function address
    fn2 f2 = f; /*нормально и результат*/ f2( 1 ); //совпадает с ожидаемым
                                                   //при наличии Test.ex5
}
 

このサンプルを 作成する際、スピードアップを図るために、全く偶然に一つの奇妙なことに遭遇し、そのまま棚上げにしました。

そして今、この異変に対処しようとすると、さらに混乱することになる。

そこで、計算では平方根関数sqrt()を使っているのですが、これをダブル配列にして回避することにしました。

整数から必ず平方根を取るので、配列を作るとこんな感じになります。

   double SQRT[];
   int ss=Width*Width+Height*Height;
   ArrayResize(SQRT,ss);
   for(double w=0;w<ss;w++) SQRT[(int)w]=sqrt(w);

配列SQRT[x]から読み出す方が、sqrt(x)関数を使うより速いと考えるのは論理的です。
そして、それを確認するのがチェックコードです。

   double Sum1=0,Sum2=0;
   ulong t=GetMicrosecondCount();
   for(int i=0;i<ss;i++) Sum1+=sqrt(double(i));   // Находим сумму квадратных корней всех целых чисел от 0 до ss
   t=GetMicrosecondCount()-t;
   Print("Время на сумму "+IntegerToString(ss)+" значений функций = "+IntegerToString(t)+" мкс, Сумма = "+DoubleToString(Sum1));
   t=GetMicrosecondCount();
   for(int i=0;i<ss;i++) Sum2+=SQRT[(int)(i)];    // Находим сумму квадратных корней всех целых чисел от 0 до ss через сложение элементов массива SQRT[]
   t=GetMicrosecondCount()-t;
   Print("Время на сумму "+IntegerToString(ss)+" значений массива = "+IntegerToString(t)+" мкс, Сумма = "+DoubleToString(Sum2));

その結果、1.5倍程度の速度向上が確認されました。

しかし、コード中のsqrt()関数を配列の要素を読み込むSQRT[]に置き換えると、高速化するどころか、ひどいラグが発生してしまいます。

SQRT[]配列から項目を読み取るには、sqrt()を実行するよりも何倍も、おそらく一桁も長い時間がかかります。

そして、どこで速度漏れが発生するのかがわからない。上記のチェックコードは、そうでないことを物語っています。

コンパイラは大きな配列に奇妙な方法でアクセスし、ループのターンごとにそれを「忘れて」いるようで、そのたびに独自のサービスインデックスを実行すると仮定できます。

しかし、これは論理的なバグか、戦略的なコンパイラのバグか、どちらかです。そして、この速度漏れは非常に重大で、しかも目立たないので発見しにくいので、明らかに何か手を打つべきでしょう。

この奇妙な現象を実証するために、スクリプトコードを添付します。

デフォルトの実行(arr=false)ではsqrt()が計算されますが、arrをtrueに変更すると、配列から平方根の値が取得されます。

どうしたんだ?なぜ遅いのか?

ファイル:
Swirl.mq5  11 kb
 
Nikolai Semko:

この例を 作成する際、スピードアップを図るため、全く偶然に1つの奇妙なことに遭遇し、そのまま棚上げしてしまいました。


配列SQRT[x]から読み出す方がsqrt(x)を取るより速いと考えるのは論理的な ことです。

ダイナミックアレイから すると、事実とは違う。

また、ルートの取得は、すでにSQRTSD プロセッサのコマンドレベルにあります。つまり、ダイナミックアレイの悪名高いアクセスコストを使っても、プロセッサの実装に勝つことはできない。


そして、このことは認証コードによって確認 されます。

その結果、1.5倍程度の速度向上が確認されました。

確定しているのかどうか疑問です。

この種のコードは、まるでループ最適化の教科書に載っているようなものだ。非常に単純なケースなので、コードオプティマイザは甘く見てしまうかもしれません。

   for(int i=0;i<ss;i++) 
      Sum2+=SQRT[i];

つまり、意図的に完全に最適化されたケースを適用する場合の性能測定は、テストの目的と明確に関連付ける必要があるのだ。

この場合のテストは間違っています。

配列SQRT[]から項目を読み取るのに、sqrt()関数を実行するよりも何倍も、おそらく一桁も時間がかかる。

そして、どこで速度漏れが発生するのかがわからない。結局のところ、上記のテストコードではそうなっていない。

最終的なアセンブラコードを見ないと、そっちに傾いてしまうんです。

  1. sqrt は実際に高速です (ネイティブコードへの非常に効率的なジェネレータがあり、sqrt は純粋な SQRTSD に変わります)
  2. チェッカーのコードが単純すぎるため、最適化によって遅延を補う。


コード全体をよく確認してみましょう。本当の理由は何なのか、それを知ることは興味深いことです。

SQRTSD — Compute Square Root of Scalar Double-Precision Floating-Point Value
  • www.felixcloutier.com
Computes square root of the low double-precision floating-point value in xmm3/m64 and stores the results in xmm1 under writemask k1. Also, upper double-precision floating-point value (bits[127:64]) from xmm2 is copied to xmm1[127:64].
 
Renat Fatkhullin:

事実ではなく、動的な 配列から。


最終的なアセンブラコードを見ないと、そっちに傾いてしまうんです。

  1. sqrt は実際に高速です (ネイティブコードで非常に効率的なジェネレータがあり、sqrt は純粋な SQRTSD に変わります)。
  2. チェッカーのコードが単純すぎるため、最適化によって遅延を補う。

静的な配列も試してみましたが、同じでした。

sqrtも高速ですが、この関数が配列の 要素を読み込むだけよりも10倍も高速になるとは信じがたいです。

さらに、私の例では、キャンバスのサイズが50x50と縮小されています(このために、入力パラメータSizeがあり、0ではなく50を設定する必要があります)。

で、アレイが5000個とかなり小さくなるため、スピードのイメージは大きく変わります。もう、そんな強いコントラストはありません。

しかし、私は理解していない:配列のサイズは、そのアイテムにアクセスする速度に影響を与えるのでしょうか?

チェックコードのシンプルさについては、おっしゃるとおりなのかもしれません。

もう少し複雑にしてみたのですが、結果はまったく違いました。

for(int i=0;i<ss;i++) Sum1+=i*sqrt(double(i));
....
for(int i=0;i<ss;i++) Sum2+=i* SQRT[(int)(i)];