4

ソース配列にnullableがないことが保証されていると仮定して、 nullableの配列(たとえばbyte?[]、)をnullableでない配列(たとえば、)にコピーする効率的な方法はありますか(もしあれば、例外をスローしても問題ありません)?byte[]明らかに、インデックスをループして、各要素を個別にコピーできます。

これは動作しません。ArrayTypeMismatchExceptionコンパイルされますが、実行時にスローされます。

 byte?[] sourceNullable = new byte?[]{1,2,3};
 byte[] destNonNullable = new byte[3];

 Array.Copy(sourceNullable,destNonNullable,3);

これは機能しますが、「より良い」ものを探しています

for(int i=0;i<3;i++) {
    destNonNullable[i] = sourceNullable[i] ?? 0;
}

私は答えを喜んで受け入れます:明示的なループの何が問題なのですか? そして、なぜこれを最適化するのに時間を無駄にしているのですか? :)


編集:Linq style を使用してみCast<>()ましたが、はるかに遅いことがわかりました。以下の私のコードからの時間の要約:

for ループ = 585 ミリ秒

Linq キャスト = 3626 ミリ秒

入力imageファイルは、null のセクションで満たされたスパース配列です。

        uint rowsize = 16;
        Stopwatch sw = new Stopwatch();
        sw.Start();
        for (UInt32 address = start & 0xFFFFFFF0; address <= last; address += rowsize)
        {
            Int32 imageOffset = (Int32)(address - start);
            Int32 maxRowLen = (int)rowsize;
            if (maxRowLen + imageOffset > image.Length) maxRowLen = (image.Length - imageOffset);

            if (maxRowLen == 0) throw new Exception("this should not happen");

            int ptr = 0;
            while (ptr < maxRowLen)
            {
                while (ptr < maxRowLen && image[imageOffset + ptr] == null) ptr++;
                int startOffset = ptr;
                while (ptr < maxRowLen && image[imageOffset + ptr] != null) ptr++;
                int stopOffset = ptr;

                if (startOffset < maxRowLen)
                {
 #if false
                    int n = stopOffset - startOffset;
                    byte[] subdata = new byte[n];
                    for (int i = 0; i < n; i++)
                    {
                        subdata[i] = image[imageOffset + startOffset + i] ?? 0;
                    }
 #else
                    byte[] subdata = image.Skip(imageOffset + startOffset).Take(stopOffset - startOffset).Cast<byte>().ToArray();
 #endif
                    IntelHexRecord rec = new IntelHexRecord((uint)(address + startOffset), subdata);
                    records.Add(rec);
                }
            }
        }
        sw.Stop();
        Console.WriteLine("elapsed: {0} ms", sw.ElapsedMilliseconds);
4

1 に答える 1

6

次のように LINQ を使用できます。

byte[] destNonNullable = sourceNullable.Cast<byte>().ToArray();

ただし、これはあなたがしていることよりも速くはありません。コンパイル時に既知の固定バイト数をより高速にコピーする方法が必要な場合は、ループ オーバーヘッドを排除できます。これはそれほど大きくはありませんが、最後の CPU サイクルをこれから絞り出す必要がある場合は機能するはずです。

byte[] destNonNullable = new[] {
    sourceNullable[0].Value
,   sourceNullable[1].Value
,   sourceNullable[2].Value
};

ループを巻き戻すことで、オーバーヘッドを削減することもできます。たとえば、コピーしたいバイト数が 4 で割り切れる場合は、次のようにします。

Debug.Assert(N % 4 == 0); // Otherwise, the loop below wouldn't stop
for (int i = 0 ; i != N ; i += 4) {
    destNonNullable[i] = sourceNullable[i].Value;
    destNonNullable[i+1] = sourceNullable[i+1].Value;
    destNonNullable[i+2] = sourceNullable[i+2].Value;
    destNonNullable[i+3] = sourceNullable[i+3].Value;
}
于 2013-07-26T20:12:18.433 に答える