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

 
Renat Fatkhullin:


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

カッコイイ!ありがとうございました。私も興味があります。

そして、sqrtは実に速い。ナノ秒以下 :)) 。ファンタスティック!

 
Nikolai Semko:

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

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

この時点で、多くの古い最適化技術はもはや機能しなくなりました。


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

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

しかし、私が理解していないのは、配列のサイズがその項目にアクセスする速度に影響 するのか、ということです。

あなたの計算にはきれいな証拠がなく、非常にポイ捨てされたコードに基づいて仮定しています。

あなたは、コードに含まれ、結果に大きな影響を与える大量の補助計算を無視しているのです。

 
Renat Fatkhullin:


あなたの計算にはきれいな証拠がなく、非常に混乱したコードに基づいて仮定しています。

コードに含まれ、結果に大きな影響を与える大量の補助計算を無視しているのです。

sqrtを配列からの単純な読み込みに変更しただけで、全体の計算速度が4分の1になったことだけは確かです。そして、このかなり大規模な各画素の計算におけるsqrtの全体の割合は、ほとんど10%以上、あるいは数%にもならない。

明らかな異常事態!

配列の要素への アクセス時間はsqrt時間の10倍というミスまでしてしまいました。sqrtがとても速く、全体の速度低下が大きいことを考えると、ずっとです。

 
Nikolai Semko:

sqrtを単純に配列から読み出すように変更しただけで、全体の計算速度が1/4に低下していることだけはわかりますね。そして、このかなり大規模な各画素の計算におけるsqrtの割合の合計は、ほとんど10%以上、あるいは数%にもならない。

明らかな異常事態!

その理由を説明しました。90年代の数学の最適化は、もはや通用しないのだ。

しかし、あなたは現実のポイ捨てされたコードをもとに、次のような主張をし続けています。植物学的に言えば、彼らは間違っている。

このレベルの技術的な[un]純度では、私は問題を議論することはありません。

 
Renat Fatkhullin:

その理由を説明しました。90年代の数学の最適化は、もはや通用しないのだ。

しかし、あなたは本当にポイ捨てされたコードに基づいて、次のような主張をし続けています。もっともな欠陥がある。

このレベルの技術的な[un]純度では、私は問題を議論することはありません。

数学の最適化というのがよくわからないのですが。そして、私は断言したわけではなく、ブレーキの元は何だろうと思っただけです。

このオリジナルコードのどこに乱雑さと最適化があるのでしょうか?

#include <Canvas\Canvas.mqh>

void OnStart()
  {
   ChartSetInteger(0,CHART_FOREGROUND,true);
   CCanvas C;
   int Width=(ushort)ChartGetInteger(0,CHART_WIDTH_IN_PIXELS);                               // получаем Ширину окна
   int Height=(ushort)ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS);                             // получаем Высоту окна
   if(!C.CreateBitmapLabel(0,0,"CanvasExamlple",0,0,Width,Height,COLOR_FORMAT_XRGB_NOALPHA)) // создаем канвас размером текущего окна
   Print("Error creating canvas: ",GetLastError()); 
   uint i=0,j=100000;
   int size=Width*Height;
   uchar h[25600];
   for (int w=0;w<25600;w++) 
   h[w]= uchar(128+128*sin(double(w)/256));//создаем массив для ускорения работы
   double X1=0,Y1=0,X2=0,Y2=0,X3=0,Y3=0,X4=0,Y4=0;
   while(!IsStopped())
     {
      int pos=int(i%size);
      if(pos==0)
        {
         C.Update();
         //Sleep(30);
         X1= Width/2-(sin((double)j/100)*(double)Width/2);
         Y1= Height/2-(cos((double)j/140)*(double)Height/2);
         X2= Width/2+(cos((double)j/80)*(double)Width/2);
         Y2= Height/2+(sin((double)j/20)*(double)Height/2);
         X3= Width/2+(cos((double)j/85)*(double)Width/2);
         Y3= Height/2+(sin((double)j/65)*(double)Height/2);
         X4= Width/2+(cos((double)j/152)*(double)Width/2);
         Y4= Height/2+(sin((double)j/42)*(double)Height/2);
         j++;
        }
      int X=pos%Width;
      int Y=int(pos/Width);
      double D1=sqrt((X1-X)*(X1-X)+(Y1-Y)*(Y1-Y));
      double D2=sqrt((X2-X)*(X2-X)+(Y2-Y)*(Y2-Y));
      double D3=sqrt((X3-X)*(X3-X)+(Y3-Y)*(Y3-Y));
      double D4=sqrt((X4-X)*(X4-X)+(Y4-Y)*(Y4-Y));
      double d= (D1+D2)/(D1+D2+D3+D4);
      C.PixelSet(X,Y,XRGB(h[int(d*11520)],h[int(d*17920)],h[int(d*6400)]));
      i++;
     }
   C.Destroy();
  }

このような汎用的なコードも使えますが、ループや配列の使用により、画像フレームのスピードはほぼ半減します。

#include <Canvas\Canvas.mqh>
#property script_show_inputs 
input int N=8; // количество центов гравитации

void OnStart()
  {
   ChartSetInteger(0,CHART_FOREGROUND,true);
   CCanvas C;
   int Width=(ushort)ChartGetInteger(0,CHART_WIDTH_IN_PIXELS);                               // get Window width
   int Height=(ushort)ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS);                             // get Window height 
   if(!C.CreateBitmapLabel(0,0,"CanvasExamlple",0,0,Width,Height,COLOR_FORMAT_XRGB_NOALPHA)) // create canvas with the size of the current window
      Print("Error creating canvas: ",GetLastError());
   uint i=0,j=100000;
   int size=Width*Height;
   uchar h[];
   ArrayResize(h,25600);
   for(int w=0;w<25600;w++) h[w]=uchar(128+128*sin(double(w)/256)); //create an array to speed up the work
                                                                  
   int k[]; ArrayResize(k,N*2);
   for(int w=0;w<N*2;w++) k[w]=20+rand()%200;
   double XP[],YP[],D[],D1[];
   ArrayResize(XP,N);
   ArrayResize(YP,N);
   ArrayResize(D,N);
   ArrayInitialize(XP,0);
   ArrayInitialize(YP,0);
  
   while(!IsStopped())
     {
      int pos=int(i%size);
      if(pos==0)
        {
         C.Update();
         for(int w=0;w<N;w++)
           {
            XP[w]= Width/2-(sin((double)j/k[2*w])*(double)Width/2);
            YP[w]= Height/2-(cos((double)j/k[2*w+1])*(double)Height/2);
           }
         j++;
        }
      int X=pos%Width;
      int Y=int(pos/Width);

      for(int w=0;w<N;w++) D[w]= sqrt((XP[w]-X)*(XP[w]-X)+(YP[w]-Y)*(YP[w]-Y));
      double S1=0,S2=0;
      for(int w=0;w<N/2;w++) S1+=D[w];
      for(int w=0;w<N;w++) S2+=D[w];
      double d=S1/S2;
      
      C.PixelSet(X,Y,XRGB(h[int(d*11520)],h[int(d*17920)],h[int(d*6400)]));
      i++;
     }
   C.Destroy();
  }
//+------------------------------------------------------------------+
 

数学の最適化:sqrtの代わりに配列を使おうとする。

雑念が見えない、これがパフォーマンステストのルールを誤解している根源です。事前計算された配列と計算を比較する場合、余分なものをすべて削除する必要があります。絶対に不要なものばかり。

いろいろと主張されていますね。受け手の側から文章を読み、作者の働かない防御策を浄化する力が必要です。

 
Renat Fatkhullin:

数学の最適化:sqrtの代わりに配列を使おうとする。

あ、sqrtは地獄だ。この特殊な機能の場合、意味がないことはもうわかっている。やってみて何が悪い?でも、今ならわかるんです。

ちなみに、このサンプルではh[]配列を使うことで、sin()を使うよりもかなりの速度向上が期待できます。

質問は別のことです。

大きな配列のセルにアクセスすることで、数%しか期待できなかった計算時間が数倍になるのはなぜか?

ポイ捨てを見ないのが性能試験のルールを誤解している根源です。事前計算された配列と計算を比較テストする場合、余計なものはすべて削除 する必要があります。絶対に不要なものばかり。

あのね、レナート、あなたからビンタされるのは恥ずかしいことではなく、光栄なことでさえあるのよ。

私の理解では、ゴミは不要なものであり、取り除くべきものである。しかし、この特別なケースでは、クリーンアップするものは何もなく、さもなければコードが動作しなくなります。

事前計算された配列 vs 計算」のテストではなく、フレーム形成のスピードを解析して いるのです。

私の場合、弱小のノートパソコン で、25fpsと6fpsの違いはテストしなくてもわかるので、テストは必要ない。

主張が重厚です。受信者側から文章を読み、作者の不戦勝をクリアする力が必要です。

私はいくつかの仮定をしただけで、断言はしていません。

- "配列SQRT[x]からの読み込みはsqrt(x)関数より速いと考えるのが論理的です。"

- "コンパイラは何か奇妙な方法で大きな配列にアクセスしており、ループのターンごとに配列のことを「忘れて」、毎回何らかのサービスのインデックス付けを行っているようだ "と仮定することができるかもしれません。"

というのも、私は独学でプログラマをやっていますが、独学で学んだからこそ、プロセッサの内部処理についてある程度理解しており、かつて私が好きだったプログラムはNumega SoftIceで、アセンブラレベルのエイリアンのコードを大量に掘った時期がありましたから。

- コンパイラには、配列へのアクセスに関するアルゴリズムのバグがあり、(おそらく十分に大きな配列でのみ発生する)、この方向にベクトルを向けて努力すれば、あなたのチームは簡単に見つけることができます。そして、 私の例が あなたの助けになるでしょう。

と主張することもできる。))

 

テーブルをループする場合、次にアクセスされるたびに、プロセッサのキャッシュにある可能性が高い。そのため、より高速に動作します。

アクセス間にコードがある場合、キャッシュミスの可能性があります。これは、大きなテーブルと小さなテーブルの取り扱いの違いも説明しています。

そのほかにも、自明でないケースもある。そのため、最終的に簡略化されていないバージョンのコードをテストする必要があります。

 
みんな、関数テンプレートで、enum 型の関数が呼び出さ れたことをどうやって見分けるの?
 
Vladimir Pastushak:
みんなは、enum型を持つ関数が呼び出される ことを決定するために、関数テンプレートでどのように私に教えてください?
enum は整数の列挙である。
0,1,2,3 ...等々