The dawn of modern programmers

現代プログラマーの朝焼け

配列コピーのパフォーマンス比較

通常のエンタープライズアプリケーションではまずないことですが、現在配列の操作を多用しています。
その中でも配列のコピーをよく使用するのですがアルゴリズムで煮詰まってきたのでパフォーマンス検証を行いました。
検証した配列コピーの方法は以下の通りです。

  • Array.Copy() メソッドによる配列のコピー
  • for 文による配列のコピー
  • unsafe ステートメント内でのポインタによる配列のコピー

以下が検証で使用したコードです。

using System;
using System.Diagnostics;

namespace ArrayCopyPerformance
{
    class Program
    {
        static void Main(string[] args)
        {
            // 8KB
            int size = 1024 * 8;

            Console.WriteLine("TEST START...");

            MeasurePerformanceOfArrayCopyByMethod(size);
            MeasurePerformanceOfArrayCopyByForLoop(size);
            MeasurePerformanceOfArrayCopyByFixed(size);

            Console.WriteLine("TEST END.");
            Console.ReadLine();
        }

        static void MeasurePerformanceOfArrayCopyByMethod(int size)
        {
            byte[] source = new byte[size];
            byte[] dest = new byte[size];

            Stopwatch stopWatch = new Stopwatch();
            stopWatch.Start();

            Array.Copy(source, dest, size);

            stopWatch.Stop();

            Console.WriteLine("  BY 'Array.Copy' METHOD\t: TIME={0} ticks", stopWatch.ElapsedTicks);
        }

        static void MeasurePerformanceOfArrayCopyByForLoop(int size)
        {
            byte[] source = new byte[size];
            byte[] dest = new byte[size];

            Stopwatch stopWatch = new Stopwatch();
            stopWatch.Start();

            for (int i = 0; i < size; i++)
            {
                dest[i] = source[i];
            }

            stopWatch.Stop();

            Console.WriteLine("  BY 'for' LOOP STATEMENT\t: TIME={0} ticks", stopWatch.ElapsedTicks);

        }

        static unsafe void MeasurePerformanceOfArrayCopyByFixed(int size)
        {
            Stopwatch stopWatch = new Stopwatch();
            stopWatch.Start();

            fixed (byte* source = new byte[size])
            {
                fixed (byte* dest = new byte[size])
                {
                    byte* pSource = source;
                    byte* pDest = dest;
                    for (int i = 0; i < size; i++)
                    {
                        *pDest++ = *pSource++;
                    }
                }
            }

            stopWatch.Stop();

            Console.WriteLine("  BY 'fixed' UNSAFE STATEMENT\t: TIME={0} ticks", stopWatch.ElapsedTicks);
        }
    }
}

結果は以下の通り

TEST START...
BY 'Array.Copy' METHOD : TIME=16856 ticks
BY 'for' LOOP STATEMENT : TIME=120276 ticks
BY 'fixed' UNSAFE STATEMENT : TIME=191368 ticks
TEST END.

パフォーマンスを考慮しないといけない局面では「できるだけ Array.Copy() メソッドを使う」というソリューションが出ました。
Array.Copy() メソッドの処理が高速なのはランタイムに実装されたメソッドを使用しているためです。
(IL DASM で System.Runtime.ConstrainedExecution.ReliabilityContractAttribute が宣言されていることを確認しました。)