2

複雑なインデックス付けで多次元配列を使用するのはかなり一般的なケースです。すべてのインデックスが int であると、列と行 (またはあなたが持っているもの) を簡単に混同することができ、コンパイラが問題を特定する方法がないため、非常に混乱し、エラーが発生しやすくなります。実際には、行と列の 2 種類のインデックスが必要ですが、型レベルでは表現されません。

ここに私が欲しいものの小さなイラストがあります:

var table = new int[RowsCount,ColumnsCount];
Row row = 5;
Column col = 10;
int value = table[row, col];

public void CalcSum(int[,] table, Column col)
{
    int sum = 0;
    for (Row r = 0; r < table.GetLength(0); r++)
    {
        sum += table[row, col];
    }
    return sum;
}

CalcSum(table, col); // OK
CalcSum(table, row); // Compile time error

要約:

  • インデックスが混同されていないか静的にチェックする必要があります (一種の型チェック)
  • 重要!int をインデックスを含むカスタム オブジェクトにラップしてからアンラップするのはパフォーマンスに問題があるため、実行時に効率的である必要があります。
  • ネイティブの多次元配列でインデックスとして機能するために、暗黙的に int に変換できる必要があります。

これを達成する方法はありますか?typedef完璧な解決策は、プレーンintにコンパイルするだけのコンパイル時のチェックとして機能するようなものです。

4

1 に答える 1

1

x64 ジッターでは 2 倍の速度低下しか得られません。興味深い最適化されたコードを生成します。構造体を使用するループは次のようになります。

00000040  mov         ecx,1 
00000045  nop         word ptr [rax+rax+00000000h] 
00000050  lea         eax,[rcx-1] 
                s.Idx = j;
00000053  mov         dword ptr [rsp+30h],eax 
00000057  mov         dword ptr [rsp+30h],ecx 
0000005b  add         ecx,2 
            for (int j = 0; j < 100000000; j++) {
0000005e  cmp         ecx,5F5E101h 
00000064  jl          0000000000000050 

コードが変わっているため、これには何らかの注釈が必要です。まず、オフセット 45 の奇妙な NOP は、ループの開始時に命令を整列させるために存在します。これにより、オフセット 64 での分岐が高速になります。53 の命令は完全に不必要に見えます。ここで見られるのはループのアンローリングです。5bの命令がループ カウンターを 2 ずつインクリメントする方法に注意してください。ただし、オプティマイザーは、ストアが不要であることを確認できるほどスマートではありません。

そして何よりも、見るべき ADD 命令がないことに注意してください。つまり、コードは実際には「合計」の値を計算しません。これは、ループの後のどこでも使用していないためです。オプティマイザーは、計算が役に立たないことを確認して、完全に削除します。

2 番目のループでは、はるかに優れた仕事をします。

000000af  xor         eax,eax 
000000b1  add         eax,4 
            for (int j = 0; j < 100000000; j++) {
000000b4  cmp         eax,5F5E100h 
000000b9  jl          00000000000000B1 

「合計」計算と「i」変数割り当てが完全に削除されました。また、for() ループ全体を削除することもできますが、ジッター オプティマイザーでは決して行われず、遅延が意図的なものであると想定されます。

メッセージが明確になっていることを願っています。人為的なベンチマークから推測することは避け、実際のコードのみをプロファイリングしてください。オプティマイザーが計算を破棄しないように、実際に「合計」の値を表示することで、よりリアルにすることができます。ループの後に次のコード行を追加します。

        Console.Write("Sum = {0} ", sum);

そして、もう違いがないことがわかります。

于 2012-11-09T12:18:05.583 に答える