C++とC# unsafeで速度差を計測してみる

何となく、unsafeコードの速度が気になったので、.NET Framework 4.0上でのC#コードと、Visual C++ 2010のコードの速度差を計測してみました。
ちなみに、このコードは、いわゆる画像処理の速度差を計測することを前提とするものなので、w/h/strideといった画像処理っぽいコードになっています。また、コードが厳密には一緒ではないことは、言語の特性にも大いに依存し、言語の特性を無視してまで最適化を行うことは趣旨に反すると思うので、そこは、C++C#の味だとして続けることにします。

C#版(speedtest.cs)。

// Compile: csc /o /unsafe speedtest.cs
using System;
using System.Diagnostics;

class SpeedTest
{
  static void test1()
  {
    int w = 4321;
    int stride = (w + 3) & ~3;
    int h = 6789;
    var a = new byte[stride * h];
    for (int y = 0; y < h; y++)
    {
      int offset = y * stride;
      for (int x = 0; x < w; x++)
      {
        a[x + offset] = (byte)(x ^ y);
      }
    }
  }
  
  static void test2()
  {
    int w = 4321;
    int stride = (w + 3) & ~3;
    int h = 6789;
    var a = new byte[stride * h];
    unsafe
    {
      fixed (byte* p0 = a)
      {
        for (int y = 0; y < h; y++)
        {
          byte* p = p0 + y * stride;
          for (int x = 0; x < w; x++)
          {
            p[x] = (byte)(x ^ y);
          }
        }
      }
    }
  }

  static void time(Action action, int count = 100)
  {
    var tw = new Stopwatch();
    tw.Start();
    for (int i = 0; i < count; i++)
      action();
    tw.Stop();
    Console.WriteLine(tw.ElapsedMilliseconds);
  }

  static void Main(string[] args)
  {
    time(test1);
    time(test2);
  }
}

C++版(speedtest.cpp)。

// Compile: cl /MD /Ox speedtest.cpp
#include <stdio.h>
#include <windows.h>
typedef unsigned char byte;

static void test2()
{
  int w = 4321;
  int stride = (w + 3) & ~3;
  int h = 6789;
  byte* a = new byte[stride * h];
  byte* p0 = a;
  for (int y = 0; y < h; y++)
  {
    byte* p = p0 + y * stride;
    for (int x = 0; x < w; x++)
    {
      p[x] = (byte)(x ^ y);
    }
  }
  delete[] a;
}

void time(void (*action)(), int count = 100)
{
  DWORD start = GetTickCount();
  for (int i = 0; i < count; i++)
    action();
  printf("%u\n", GetTickCount() - start);
}

int main()
{
  time(test2);
}

C#版(AnyCPU on Windows 7 x64)の結果。

6663
5436

Unsafeコード、つまりポインタ版が、1.2倍程度速いという結果。逆に言うと、配列の範囲チェックはこの程度の影響しかないとも言える。

さて、C++版(x64 on Windows 7 x64)の結果。

5210

ん。C++版は、C#ポインタ版に比べても4%程度しか速くない!
思ったよりもC#のコードはかなり速いということか。差がこの程度ということは、メモリ管理とかの差もあんまり問題にならないということだろう。

C#でunsafeコードが使えないコンテキストでも、C++に比べて、130%程度の処理時間にしかならない。
ライブラリの充実度や、コードのポータビリティ(Silverlightなど)を考えると、この程度のトレードオフならば許容できると思う。