C#、複素数クラスの罠

CompileAssemblyFromSourceという素敵メソッドを発見。
C#ならこういうことができると思っていた。
テキストボックスにz*z*z+cなどと入力してマンデルブロ集合を表示させたいと
前から思っていたが、これで簡単に、そしてC言語に近いくらい高速に実現できる。

さて、今のzやcは複素数なので、
複素数クラスを作り演算子オーバーロードする必要がある。*1
これはC++で経験があるので、すぐできる。そしてすぐ動作した。しかし遅い。
z*z+cというのを複素数クラスを使わないで実装した場合と比べて、10倍近く遅い!
何だこれ。C#は速いと思っていたけど、クラスとか使うとこんな遅いのか。

いやいや、returnでnewしまくって、それを捨てまくってるから遅いんじゃないか。
こんな大差で遅いというのは、何か改善方法があるはず。
そもそもC++複素数クラスを作ったら(マシン語レベルで)どう処理されるんだ?
そういうことを考えながら色々調べているうちに、構造体がメンバ関数を持てることを知る。

C++構造体,第5章 C#のデータ型 (3/4):連載 改訂版 C#入門 - @IT
structじゃなくてclass使えって、みんな言ってた気がしたから、
常にclass使えば無難だと思い込んでたよ。
たった16byteの複素数クラスを参照渡しするのは、そりゃあ効率悪いよな。

演算子オーバーロードという、C++の夢のような機能を知ったときから、
演算子オーバーロードと言えばクラスしか思い浮かばなくなっていた。
しかし実際には、C#のstructでも演算子オーバーロードはできる。
private class complexというコードのclassをstructに変更して実行ボタンを押した。
他に何の修正もせず動作したことにも驚いたが、圧倒的な速さに体が軽くなった。
結局、structを使えば、ほぼオーバーヘッドなしで複素数を扱えるようになった。
これはすごい。本気のアセンブラに近い速度を、こんな高級な言語で実現できるなんて。
同じ内容のclassが8.5倍遅かったけど、やはり値渡しのときの最適化技術がすごいんだろうな。

まだ知らないことだらけだけど、C#について書かれた文書を読めるようになってきた。
.NET Frameworkが必要なのが嫌だと思ってたけど、これはC#いいヤツだと言わざるをえない。
更に恐ろしいことには、おそらくVBでも同等の速度が出るのだ。
VB6.0の吐くネイティブコードは、VC++6.0と比べるとはっきり遅かったからなあ。
まあ、C#と同じ論理構造のコードだから、VBの意味がないっちゃないが。

JavaScriptより書きやすく感じるのも怖いところで、C#のが当然圧倒的に速いから、
今後JavaScriptを書く気力が出てくる気がしない。

*1:ひええ、C#には元々Complex構造体があったよ(System.Numerics.Complex)。
http://msdn.microsoft.com/ja-jp/library/system.numerics.complex.aspx