3

2D データ要素の大きな配列がいくつかあります。A と B は同じサイズの寸法ではありません。

A) は 5 から 20 の間です

B) は 1000 から 100000 の間です

初期化時間は、リアルタイム アプリケーションのルックアップ テーブルになるだけなので問題ありません。そのため、値 A と B を知ることによる要素のインデックス作成のパフォーマンスは非常に重要です。現在格納されているデータは、1 バイト値です。

私はこれらの解決策について考えていました:

byte[A][B] datalist1a;

また

byte[B][A] datalist2a;

また

byte[A,B] datalist1b;

また

byte[B,A] datalist2b;

または、固定サイズを知っているので多次元を失い、調べる前に値を掛けるだけです。

byte[A*Bmax + B] datalist3;

また

byte[B*Amax + A] datalist4;

私が必要としているのは、このセットアップがあるときに C# で最も効率的なルックアップに使用するデータ型/配列構造を知ることです。

編集 1 最初の 2 つのソリューションは、マルチ配列ではなく、多次元であると想定されていました。

編集 2 最小次元のすべてのデータは各ルックアップで読み取られますが、大きな次元は一度に 1 回だけインデックス付けに使用されます。

つまり、サンプル B からすべての A を取得します。

4

5 に答える 5

2
  • 多次元 ( [,]) 配列は、重いランダム アクセス シナリオでない限り、ほとんど常に最も低速です。理論的にはそうあるべきではありませんが、これは CLR の奇妙な点の 1 つです。
  • ギザギザ配列 ( [][]) は、ほとんどの場合、多次元配列より高速です。ランダム アクセス シナリオでも。これらにはメモリのオーバーヘッドがあります。
  • 1 次元 ( []) と代数配列 ( [y * stride + x]) は、セーフ コードでのランダム アクセスに最も高速です。
  • アンセーフ コードは、通常、すべてのケースで最速です (繰り返しピン留めしない限り)。
于 2011-08-03T11:09:35.063 に答える
2

Amax または Bmax が 2 の累乗でない限り、ギザギザの配列に賭けます。

ジャグ配列は 2 つのインデックス付きアクセスを必要とするため、非常に高速です。他の形式は、暗黙的または明示的な乗算を意味します。その乗算が単純なシフトでない限り、いくつかのインデックス付きアクセスよりも少し重くなる可能性があると思います。

編集:テストに使用される小さなプログラムは次のとおりです。

class Program
{
    private static int A = 10;
    private static int B = 100;

    private static byte[] _linear;
    private static byte[,] _square;
    private static byte[][] _jagged;



    unsafe static void Main(string[] args)
    {
        //init arrays
        _linear = new byte[A * B];
        _square = new byte[A, B];
        _jagged = new byte[A][];
        for (int i = 0; i < A; i++)
            _jagged[i] = new byte[B];

        //set-up the params
        var sw = new Stopwatch();
        byte b;
        const int N = 100000;

        //one-dim array (buffer)
        sw.Restart();
        for (int i = 0; i < N; i++)
        {
            for (int r = 0; r < A; r++)
            {
                for (int c = 0; c < B; c++)
                {
                    b = _linear[r * B + c];
                }
            }
        }
        sw.Stop();
        Console.WriteLine("linear={0}", sw.ElapsedMilliseconds);

        //two-dim array
        sw.Restart();
        for (int i = 0; i < N; i++)
        {
            for (int r = 0; r < A; r++)
            {
                for (int c = 0; c < B; c++)
                {
                    b = _square[r, c];
                }
            }
        }
        sw.Stop();
        Console.WriteLine("square={0}", sw.ElapsedMilliseconds);

        //jagged array
        sw.Restart();
        for (int i = 0; i < N; i++)
        {
            for (int r = 0; r < A; r++)
            {
                for (int c = 0; c < B; c++)
                {
                    b = _jagged[r][c];
                }
            }
        }
        sw.Stop();
        Console.WriteLine("jagged={0}", sw.ElapsedMilliseconds);

        //one-dim array within unsafe access (and context)
        sw.Restart();
        for (int i = 0; i < N; i++)
        {
            for (int r = 0; r < A; r++)
            {
                fixed (byte* offset = &_linear[r * B])
                {
                    for (int c = 0; c < B; c++)
                    {
                        b = *(byte*)(offset + c);
                    }
                }
            }
        }
        sw.Stop();
        Console.WriteLine("unsafe={0}", sw.ElapsedMilliseconds);

        Console.Write("Press any key...");
        Console.ReadKey();
        Console.WriteLine();
    }
}
于 2011-08-03T10:46:07.870 に答える
1

「どのXが速いか」(すべてのXに対して)に対する唯一の有用な答えは、要件を反映したパフォーマンステストを実行する必要があるということです。

そして、一般的に、考慮することを忘れないでください*

  • プログラムのメンテナンス。これが簡単なものではない場合は、ほとんどの場合、少し遅いが保守可能なプログラムの方が適しています。
  • マイクロベンチマークは欺くことができます。たとえば、コレクションから読み取るだけのタイトループは、実際の作業が行われているときには不可能な方法で最適化される可能性があります。

さらに、最適化する場所を決定するために、完全なプログラムを調べる必要があることを考慮してください。ループを1%高速化すると、そのループには役立つ場合がありますが、実行時間全体の1%しかない場合は、それほど大きな違いはありません。


*ただし、すべてのルールには例外があります。

于 2011-08-03T10:40:22.323 に答える
0

最近のほとんどのコンピューターでは、算術演算はメモリ ルックアップよりもはるかに高速です。キャッシュにないメモリ アドレスをフェッチする場合、または 10 ~ 100 クロックを見ている間違った場所から順不同の実行がプルされる場合、パイプライン化された乗算は 1 クロックです。もう 1 つの問題は、キャッシュの局所性です。byte[B Amax + A] datalist4; A を順番に変化させてアクセスしている場合は、これが最善の策のようです。datalist4[b Amax + a] がアクセスされると、コンピュータは通常、datalist4[b Amax + a+ 64/sizeof(dataListType)]、... +128 ... などの取り込みを開始するか、逆の反復を検出した場合、 datalist4[b Amax + a - 64/sizeof(dataListType)]

それが役立つことを願っています!

于 2018-09-28T01:51:43.637 に答える
-2

HashMapを使用するのが最善の方法かもしれません

辞書

于 2011-08-03T10:44:27.547 に答える