3

Array.Copy を使用して配列の一部を別の部分にコピーするいくつかのテストを行いました。

最初のテストでは、値型の配列で Array.Copy を使用しました

struct ValueApple {
    public int Redness;
}

ValueApple[] a1 = ...
ValueApple[] a2 = ...

Array.Copy(a1, a2, a1.Length);

次に、2 番目のテストでは、参照型の配列で Array.Copy を使用しました

class ReferenceApple {
    public int Redness;
}

ReferenceApple[] a1 = ...
ReferenceApple[] a2 = ...

Array.Copy(a1, a2, a1.Length);

値型の配列間のコピーは、参照型の配列間のコピーよりも高速であることを示す結果が得られました

だから私の質問は:

本当 ?値型の配列からのコピーは、参照型の配列からのコピーよりも高速ですか?その理由は?


次に、私の2番目の質問は次のようになります。

値型の配列からコピーする方が速い場合。参照型の値型ラッパーを作成すると、パフォーマンスが向上しませんか?

例えば

struct ValueWrapper<T> {
    public T Value;
}

class ReferenceApple { }

ValueWrapper<ReferenceApple>[] a1 = new ValueWrapper<ReferenceApple>[20];
ValueWrapper<ReferenceApple>[] a2 = new ValueWrapper<ReferenceApple>[40];

Array.Copy(a1, a2, 20);

これも、プレーンなReferenceApple[] array を使用するよりも高速である必要がありますか?

4

1 に答える 1

4

ValueApple[]ReferenceApple[]; _ 特定の違いは、ターゲットプラットフォームに大きく依存します。x86では、参照型配列に少しオーバーヘッドがあるように見えますが、x64ではそれほど目立ちません(バージョンが2倍のデータをコピーしているにもかかわらず、どちらのx86バージョンよりも高速です)。ReferenceApple[]

x86

ValueApple: 3295ms
ValueApple CopyTo: 3283ms
ReferenceApple: 4345ms
ReferenceApple CopyTo: 4234ms

x64

ValueApple: 1819ms
ValueApple CopyTo: 1864ms
ReferenceApple: 2251ms
ReferenceApple CopyTo: 2335ms

私はそれがコードを曲げる価値があるとは言いません。また、「アレイコピーは私の本物のボトルネックですか?」と尋ねる必要があります。そうでない場合、このテストはまったく役に立ちません。だから最初に:あなたのボトルネック何であるかを見つけてください。

構造体の配列のトリックが役立つ場合もありますが、通常は、長寿命の巨大なデータを含む非常にエッジケースの高いメモリ/ガベージコレクションのシナリオに関連します。20/40アイテムの配列を持つ通常のコードではありません。

上記の数値は以下に基づいています:

using System;
using System.Diagnostics;
struct ValueApple
{
    public int Redness;
}
class ReferenceApple
{
    public int Redness;
}

static class Program {
    static void Main()
    {
        const int LOOP = 50000000;

        ValueApple[] a1 = new ValueApple[20];
        ValueApple[] a2 = new ValueApple[40];
        var watch = Stopwatch.StartNew();
        for (int i = 0; i < LOOP; i++)
        {
            Array.Copy(a1, a2, 20);
        }
        watch.Stop();
        Console.WriteLine("ValueApple: {0}ms", watch.ElapsedMilliseconds);

        watch = Stopwatch.StartNew();
        for (int i = 0; i < LOOP; i++)
        {
            a1.CopyTo(a2, 0);
        }
        watch.Stop();
        Console.WriteLine("ValueApple CopyTo: {0}ms", watch.ElapsedMilliseconds);

        ReferenceApple[] a3 = new ReferenceApple[20];
        ReferenceApple[] a4 = new ReferenceApple[40];
        watch = Stopwatch.StartNew();
        for (int i = 0; i < LOOP; i++)
        {
            Array.Copy(a3, a4, 20);
        }
        watch.Stop();
        Console.WriteLine("ReferenceApple: {0}ms", watch.ElapsedMilliseconds);

        watch = Stopwatch.StartNew();
        for (int i = 0; i < LOOP; i++)
        {
            a3.CopyTo(a4, 0);
        }
        watch.Stop();
        Console.WriteLine("ReferenceApple CopyTo: {0}ms", watch.ElapsedMilliseconds);

        Console.WriteLine("(done)");
        Console.ReadKey();
    }
}

ラップされたものとされないものの場合:

私はあなたの結果を疑問視します。私の番号:

Wrapper<T>: 2175ms
Direct: 2231ms
Wrapper: 2165ms

(最後は同じものの非ジェネリックバージョンです)

これは基本的に「ほぼ同じ、ランダムなCPUアクティビティを与えるか取る」です。5,000万回の反復で数ミリ秒かかることは、コードを形を崩して曲げる価値がないことは確かです...

重要なのは、そのようなテストがリリースモードであり、デバッガーの外部で実行されていることを確認することです。

私のテストコード:

using System;
using System.Diagnostics;
struct ValueWrapper<T> {
    public T Value;
}
struct ValueWrapper
{
    public Apple Value;
}
class Apple { }

static class Program {
    static void Main()
    {
        const int LOOP = 50000000;

        ValueWrapper<Apple>[] a1 = new ValueWrapper<Apple>[20];
        ValueWrapper<Apple>[] a2 = new ValueWrapper<Apple>[40];
        var watch = Stopwatch.StartNew();
        for (int i = 0; i < LOOP; i++)
        {
            Array.Copy(a1, a2, 20);
        }
        watch.Stop();
        Console.WriteLine("Wrapper<T>: {0}ms", watch.ElapsedMilliseconds);

        Apple[] a3 = new Apple[20];
        Apple[] a4 = new Apple[40];
        watch = Stopwatch.StartNew();
        for (int i = 0; i < LOOP; i++)
        {
            Array.Copy(a3, a4, 20);
        }
        watch.Stop();
        Console.WriteLine("Direct: {0}ms", watch.ElapsedMilliseconds);

        ValueWrapper[] a5 = new ValueWrapper[20];
        ValueWrapper[] a6 = new ValueWrapper[40];
        watch = Stopwatch.StartNew();
        for (int i = 0; i < LOOP; i++)
        {
            Array.Copy(a5, a6, 20);
        }
        watch.Stop();
        Console.WriteLine("Wrapper: {0}ms", watch.ElapsedMilliseconds);

        Console.WriteLine("(done)");
        Console.ReadKey();
    }
}
于 2012-12-21T12:55:18.603 に答える