.NETで賢くなったVB vs C#

■ 前回の実験では、構造体の構成によるパフォーマンスの相違を検証してみました。
今回は、同じソースをC#に移植して検証、比較的新しいプログラム言語としてC++よりオブジェクト化に適していると言われるC#の実力を計ってみよう。

// シンプルな構造体
public struct stRGB
{
  public byte R, G, B;
}

// 高度な構造体
public struct stRGB
{
  private const float BRK = 0.298912f;
  private const float BGK = 0.586611f;
  private const float BBK = 0.114478f;

  public byte R, G, B;

  // Clear
  public void Clear()
  {
    R = G = B = 0;
  }

  // Brightness
  public float Brightness()
  {
    return (float)((R + G + B) * (1 / 3));
  }

  // GrayScale
  public byte GrayScale()
  {
    return (byte)(R * BRK + G * BGK + B * BBK);
  }

  //
  public static stRGB operator +(stRGB A, stRGB B)
  {
    stRGB res;
    try
    {
      res.R = (byte)(A.R + B.R);
      res.G = (byte)(A.G + B.G);
      res.B = (byte)(A.B + B.B);
      return res;
    }
    catch
    {
      res.R = res.G = res.B = 0;
      return res;
    }
  }

  // ans = a - b
  public static stRGB operator -(stRGB A, stRGB B)
  {
    // omitted
  }

  // ans = a * b
  public static stRGB operator *(stRGB A, stRGB B)
  {
    // omitted
  }

  // ans = a / b
  public static stRGB operator /(stRGB A, int B)
  {
    // omitted
  }
}

 前回のソースもVB9(=Visual Studio 2008版)で制作したものですが、まんま同じものをCS9(=C#=Visual Studio 2008版)に移植しただけです。構文はC++のようですが、構造はVBに似ています。

  C言語は歴史が古いので、「如何に効率的にコードを生成できるか?」と言うことを主眼に作られている言語なので、プログラマの自制心が問われる言語です。 どのような型にも無責任にキャストできてしまうし、どのような数値もポインタとして渡すことが可能なので、めんど臭くなったらついつい近道をしてしまうので無責任なコードがいっぱい出来てしまいます。
 一時期流行になった「バッファ・オーバ・フロー」によるセキュリティホールなどは、関数間の引き渡し時に配列のサイズについて全く責任を持たないC言語の欠陥が原因と言っても過言ではないでしょう。

 そのような問題を解決し、構文的にはC言語ライクにして言語側でインチキ出来にくくしたもの、各機能のパッケージ化を容易にしたものがC#ではないかと思われます。

 まぁ、C#だからと言っても unsafe とか宣言しちゃうとポインタも使えるし、古いベタベタのコードも書けちゃうので、結局はプログラマの自制心が最終的な歯止めと言うことでしょうがね。

  <閑話休題>

 で、実際の処理はこちら、

// シンプルな構造体宣言の場合
private void button1_Click(object sender, EventArgs e)
{
  double ST, ET;
  ST = QPF.GetMilliTime();

  stRGB[] C = new stRGB[VOL];
  stRGB res = C[0];
  for (int n = 1; n < VOL; n++)
  {
    res.R = (byte)((res.R + C[n].R) / 2);
    res.G = (byte)((res.G + C[n].G) / 2);
    res.B = (byte)((res.B + C[n].B) / 2);
  }

  ET = QPF.GetMilliTime() - ST;
  textBox1.Text = ET.ToString("#,##0.000");
}

// 高度な構造体宣言の場合
private void button1_Click(object sender, EventArgs e)
{
  double ST, ET;
  ST = QPF.GetMilliTime();

  stRGB[] C = new stRGB[VOL];
  stRGB res = C[0];
  for (int n = 1; n < VOL; n++)
  {
    res = (res + C[n]) / 2;
  }

  ET = QPF.GetMilliTime() - ST;
  textBox1.Text = ET.ToString("#,##0.000");
}

 例によって、構造体に演算式を含めた場合にはループ内の処理はたったの1行で済んでしまいます。

 で、処理時間は、

シンプルな構造体 87ms
高度な構造体 456ms

 VB9に比較してCS9はシンプルな構造を使うと約2.9倍も速くなります。 でも、構造体が複雑になるとその差は1.5倍程度になり、C#内での動作がより複雑になっているのではないかと想像できます。

※2013/01/18追記 「急いては事をし損じる」と言うわけですね、上記の結果はWindows7(x64)での結果です。 同じWindows7でも32ビット版では結果が異なります。シンプルな構造体のプログラムの32ビット版OSの場合の結果は以下のようになります。

VB9 679ms
C#9 686ms

 動作させるPCが異なりますので、x64:x86相互での比較はできませんが、32ビット版でのVB:C#の比較では、ナントほとんど差がなくなってしまいました。 コンパイラの問題なのかWOW64が問題なのか、64ビット版ではC#にかなりなメリットがあるようです。

 同様に下の文字列操作の32ビット版OSでの結果です。

VB9 6,136ms
C#9 6,031ms

 こちらも、パフォーマンスの差は1%未満で誤差程度、どちらが良いとは言えない数値です。

 

■ 結構C#も速いじゃん、と言う訳で調子に乗ってC言語では全くダメダメだった文字列操作について、VB9 vs CS9で対決!

' VBの文字列操作
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
  Dim A As String = Nothing
  Dim ST, ET As Double

  ST = QPF.GetMilliTime
  For n = 0 To 100000 - 1 '10万回
    A += "/"
  Next
  ET = QPF.GetMilliTime - ST

  Me.TextBox1.Text = ET.ToString("#,##0.000") '+ "ms"
End Sub

// C#の文字列操作
private void button2_Click(object sender, EventArgs e)
{
  double ST, ET;

  ST = QPF.GetMilliTime();
  string A = null;
  for (int n = 0; n < 100000; n++)
  {
    A += "/";
  }

  ET = QPF.GetMilliTime() - ST;
  textBox1.Text = ET.ToString("#,##0.000");
}

 ソースは説明の余地なしという感じですね、string A に10万文字の "/" を1文字ずつ連結していくものです。

 結果です。

VB9 1,871ms
CS9 1,767ms

 あれれっ? 1.05倍程度しか速くなりません。 やはり、文字列操作についてはVBが「一日の長あり」と言うところでしょうか。 C言語がストリングクラスとか言い始めるずーっと昔にBasic言語は文字列を普通に処理できていました。

 別件で解ったことですが、実はC#ではメモリの確保に凄い時間が掛かるのです。 安全なメモリ確保と解放に注力しすぎたのか、ヘタにローカルに配列などを確保すると、そのオーバヘッドを見過ごすことができなくなります。

 次回は、C#でのメモリ確保について検証してみたいと考えています。(2013/01/18)

※上記サンプルプログラムは、テスト目的だけのために書き下ろしたものであって、実際の運用に耐えうるものではありません。 コピペしてお持ち帰りになるのはご自由に、ですが流用した結果に対する責任はご自身で持って下さい。