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

 

エディタにおける長年のバグを修正しました。

- ファイルを新しい名前で保存します(例:name_v1.2)。
- カーソルを置く
- alt+gを押す

- 古いファイルが開かれ、そこに編集がジャンプする(

 
Vict:

総じて、期待もしていなかった。

キャッシュラインに収まらない要素にたどり着き、直接打ち込もうとしたが失敗した(やろうと思えば成功したのだろうが、飽きた)、など、ちょっと複雑すぎるコードになってしまった。16回のコラプスのうち、キャッシュラインに収まらない要素に対して行われたのは1回だけですが、それでも顕著な結果が得られています。

SZY:この場合、より客観的には、1つのショートを削除するのではなく、2つのショートを挿入することによってRIGHT_ALIGNEDを行う(したがって、両方のケースでキャッシュラインの2つの更新を達成することになります)。高速化はより緩やかになりますが、それでも約1.5倍です。

失礼ですが、アライメントはどこで使うのでしょうか?この例はそういうことではありません。

П.С.コメントもなく、粗雑な形でコードを掲載することは、友人に対して失礼にあたります。

 

正しくは、サードパーティーのライブラリ、特にdotnetでMQL構造体オブジェクトを使用するためのアライメントが追加されました。

パック構造/クラスのフィールドにアライメントが追加されたのは、dotnetをサポートするためのdotnetライブラリが追加されたときでした。

短く簡単に言うと、次のような仕組みです。

各タイプ(char, short, int, ...)には、デフォルトのアライメント(それぞれ1, 2, 4バイト)があります。
構造のフィールドでは,2つのアラインメントのうち最小のものが選択されます:デフォルトのものと,ユーザーによって定義されたもの(packを通じて)です.

同時に,パックされたオブジェクトのサイズは,配列内のオブジェクトフィールドのアドレッシングが常に「正しく」なるように設定されています(デフォルトでは1バイトがパックとともに設定されています).
後者のため、パックが構造体のサイズを 揃えるという誤った印象を与えてしまうのだ。



例えば

struct A pack(8)
  {
   double d;
   char   c;
  };

void OnStart()
  {
   Print(sizeof(A));
   
  }

結果 16、最初のフィールドdへのアドレッシングが常に8バイトでアラインされるよう

 
fxsaber:

自分で走らせても、顕著な差は出ていません。

元のアイデアを改良しました(最初のコードでは、アドレスのカウントが誤っていました)。もし差し支えなければ、あなたにとって面白い結果になるのではないでしょうか。

#define  WRONG_ALIGNED
#define  CACHE_LINE_SIZE 64

struct Data {
#ifdef  WRONG_ALIGNED
   ushort pad;
#else
   uint pad;
#endif
   uint ar[CACHE_LINE_SIZE/sizeof(int)+1];
};

#import "msvcrt.dll"
  long memcpy(uint &, uint &, long);
#import
#define  getaddr(x) memcpy(x, x, 0)

void OnStart()
{
   Data data[32768];
   ZeroMemory(data);
   
   srand(GetTickCount());
   
   ulong start_time = GetMicrosecondCount();
   
   for(unsigned i = 0; i < 10000; ++ i) {
      int rndnum = rand();
      while (++rndnum < 32768) {
         int index = int(CACHE_LINE_SIZE - getaddr(data[rndnum].ar[0]) % CACHE_LINE_SIZE) / sizeof(int);
         ++ data[rndnum].ar[index];
         ++ data[rndnum].pad;
      }
   }
      
   Alert(GetMicrosecondCount() - start_time);
   
   Print(data[100].ar[0]);
   Print(data[100].pad);
}
/*
WRONG_ALIGNED:
6206397
6185472

RIGHT_ALIGNED
4089827
4003213
*/
本質的には、WRONG_ALIGNEDの有無にかかわらず同じことが起こる - 私たちは2つの隣接するキャッシュライン(常に正しいアドレスにパッドへの書き込み)に書き込みながら、唯一の違いは、WRONG_ALIGNEDとケース(常にではない)があることです arのいずれかのエントリが完全にキャッシュラインに取得されませんuintに発生したとき、私は約1.5倍の安定した違いを持っています。
 
Vict:

元のアイデアを解決した(最初のコードではアドレスを正しくカウントしていなかった)。もし差し支えなければ、あなたの場合の結果を見るのも面白いかもしれませんね。

基本的にはWRONG_ALIGNEDの有無にかかわらず同じことが起こります - それぞれの間、我々は2つの隣接するキャッシュライン(常に正しいアドレスにパッドエントリ)に書き込みます。唯一の違いは、WRONG_ALIGNEDではarのエントリのいずれかがuintで発生する場合(常にではない)があり、これはキャッシュライン全体をヒットしない、私は約1.5回安定して差を持っていることです。

説明してください、このセリフで何を得ようとしているのでしょうか?前の例では、ゴミのようなコードでした。

int index = int(CACHE_LINE_SIZE - getaddr(data[rndnum].ar[0]) % CACHE_LINE_SIZE) / sizeof(int);
 
Francuz:

このセリフで何を得ようとしているのか、説明してください。前の例では、ゴミのようなコードでした。

現在のキャッシュライン(pad が存在するもの)から位置を探し、ar[] にそのようなインデックスを取り、それを持つ要素は次のキャッシュラインにある(もしかしたら要素は WRONG_ALIGNED で二つのキャッシュラインにあるかもしれない)

 
Vict:

現在のキャッシュライン(pad があるもの)の位置を見つけ、それを持つ要素が次のキャッシュラインにあるような ar[] のインデックスを取ります(要素は WRONG_ALIGNED で 2 つのキャッシュラインにあるかもしれません)。

今度はオフセットの話です。しかし、あなたが示したのは純粋に合成の例であり、現実には決して起こりえないことです。また、実際の例では、速度向上はせいぜい1%程度でしょう。そんな微々たる加速度で大騒ぎするのはやめた方がいい。

П.С.また、レジスタサイズの計算も間違っています。
 
Francuz:

今は、変位について話しています。しかし、あなたが示したものは、現実には決して見られない純粋な合成例です。実際の例では、速度向上はせいぜい1%程度です。そんな微々たる加速度で大騒ぎするのはやめたほうがいい。

いや、これはかなりリアルな例です。キャッシュラインの界面にある「問題箇所」に書き込まれるのは、全体の25%に過ぎない。それはいくらですか?例えば、長いdouble配列がある場合、1つのキャッシュラインには4つの値しか格納できず、アライメントを気にしない(コンパイラがやってくれない)場合、25%のdouble配列に問題があることになります(私の例ではそうなっています)。アライメントを語るには、もっといろいろなニュアンスがあるのですが、私は詳しくないので割愛します。

さて、この家の主人。

P.S. また、レジスタの大きさを間違えていますね。
私は彼を全く数えていませんでした ))
 
Vict:

いいえ、これは完全に現実的な例です。その中で、キャッシュライン接合部の「問題」箇所に発生するエントリーは25%のみである。多いのでしょうか?例えば、長いdouble配列があったとして、1つのキャッシュラインには4つの値しかなく、アライメントを気にしない(コンパイラがそれをしない)場合、私の例のように問題のあるdoubleの25%を取得することになります。もっといろいろなニュアンスがあり、アライメントを語ることができるのですが、私はこの問題に十分な知識を持っていないので、それについては触れません。

まあ、あなたがボスなんですけどね。

もう一度言いますが、あなたはレジスターのサイズに惑わされているのではありませんか?

 
Francuz:

もう一度言いますが、レジスターの大きさを混同していませんか?

ジャスティファイ