3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

配列のアクセス方法による速さの違い

Posted at

はじめに

C#でマネージドオブジェクトとして配列にアクセスするときと、アンマネージドオブジェクトとして配列にポインタでアクセスするときの速さを比べました。
ここでは書き込みしか計測していないので、読み込みの場合は違った結果になるかもしれません。

結論

算術複合代入演算子でポインタを更新してアクセスする方法が最速でした。
また、インデクサー演算子を使った普通の配列へのアクセスが、ポインター要素アクセス演算子を使ったポインタによる配列へのアクセスよりも速くなることがあります。

環境

Windows 10 Home, AMD Ryzen 5 3500 6-Core Processor(3.59 GHz), 16GB Memory, Visual Studio 2022, .NET 6.0

計測コード

private static readonly int Count = 1000; // 関数の実行を繰り返す回数
private static readonly int Size = 100000; // 配列の大きさ

private static int[] VSA { get; } = new int[Size]; // マネージド配列
private static IntPtr VSPH = Marshal.AllocHGlobal(sizeof(int) * Size); // アンマネージド配列
private static int* VSP { get; } = (int*)VSPH.ToPointer(); // アンマネージド配列のポインタ

// 普通に配列にアクセスする方法
private static void Array()
{
    int[] a = VSA;
    for (int i = 0; i < Size; i++)
    {
        a[i] = i;
    }
}

// ポインター要素アクセス演算子でアクセスする方法
private static void Pointer1()
{
    int* p = VSP;
    for (int i = 0; i < Size; i++)
    {
        p[i] = i;
    }
}

// ポインタで、算術演算子で目的のアドレスに移動する方法
private static void Pointer2()
{
    int* p = VSP;
    for (int i = 0; i < Size; i++)
    {
        *(p + i) = i;
    }
}

// ポインタをインクリメント演算子で更新する方法
private static void Pointer3()
{
    int* p = VSP;
    for (int i = 0; i < Size; i++)
    {
        *p = i;
        p++;
    }
}

// ポインタを算術複合代入演算子で更新する方法
private static void Pointer4()
{
    int* p = VSP;
    for (int i = 0; i < Size; i++)
    {
        *p = i;
        p += 1;
    }
}

// エントリポイント
public static void Entry()
{
    Console.WriteLine($"Array:   \t{Program.Measure(Count, Array)}");
    Console.WriteLine($"Pointer1:\t{Program.Measure(Count, Pointer1)}");
    Console.WriteLine($"Pointer2:\t{Program.Measure(Count, Pointer2)}");
    Console.WriteLine($"Pointer3:\t{Program.Measure(Count, Pointer3)}");
    Console.WriteLine($"Pointer4:\t{Program.Measure(Count, Pointer4)}");

    Marshal.FreeHGlobal(VSPH);
}

// 関数の処理にかかる時間を計測する
public static TimeSpan Measure(int count, Action action)
{
    DateTime time = DateTime.Now;
    for (int i = 0; i < count; i++)
    {
        action();
    }
    return DateTime.Now - time;
}

結果

CountとSizeを以下のように変えて計測しました。単位はミリ秒です。

Count = 10, Size = 10000000

方法 1回目 2回目 3回目 平均
Array 313.9766 327.8773 307.7491 316.5343
Pointer1 366.2786 432.1988 385.5325 394.6699
Pointer2 317.5178 321.1072 334.8939 324.5063
Pointer3 282.9098 269.9905 258.7676 270.5559
Pointer4 243.4862 245.5820 243.0119 244.0267

Count = 1000, Size = 100000

方法 1回目 2回目 3回目 平均
Array 327.2435 292.3494 276.8912 298.8280
Pointer1 321.7174 427.6671 351.0425 366.8090
Pointer2 330.6075 327.0324 329.7217 329.1205
Pointer3 248.2612 266.3699 265.2462 259.9591
Pointer4 240.4266 242.9126 242.5092 241.9494

Count = 100000, Size = 1000

方法 1回目 2回目 3回目 平均
Array 314.5075 279.0266 275.8633 289.7991
Pointer1 371.4749 414.7517 335.8634 374.0300
Pointer2 334.1162 308.4300 320.1084 320.8848
Pointer3 267.7154 313.6996 289.6043 290.3397
Pointer4 243.0017 245.0434 249.3512 245.7987

Count = 10000000, Size = 10

方法 1回目 2回目 3回目 平均
Array 355.4642 279.7824 279.7824 305.0096
Pointer1 356.0908 385.8575 385.8575 375.9352
Pointer2 348.3576 319.1117 319.1117 328.8603
Pointer3 247.0751 265.7220 265.7220 259.5063
Pointer4 245.3749 245.7401 245.7401 245.6183

Pointer4は安定して圧倒的に速いです。

おわりに

私としてはこの結果は意外です。
Pointer3, 4はループ毎にポインタを更新しており、アドレスに整数値を加算するという処理に加えてポインタを書き換えるという処理を行うと思うのでPointer1, 2よりも遅いと思っていました。
Pointer1, 3はそれぞれPointer2, 4のシンタックスシュガーで、内部的には同じ処理だと思ってたので速さが違うとは思いませんでした。
ポインタを使うよりも普通に配列を使った方が速いことがあるのも驚きです。

何事もやってみないとわかりませんね。

3
1
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?