2009/12/23

C#のコードを高速化するための方法

以前のものの続き。
C#ならではのコーディングの際に注意するところまとめ。

○クラスと構造体の選択方法

オブジェクトのインスタンス生成はかなり重たい処理であるよう。

で、C#にはオブジェクトをヒープ内でなく、
スタックに積むための「構造体」という仕組みがあるのですが、
http://www.atmarkit.co.jp/fdotnet/special/java2cs/java2cs_01.html

構造体は値型であるためそのサイズが大きい場合にはかえってその受け渡し時の
コピーに負荷がかかってしまうよう。
http://www.atmarkit.co.jp/fdotnet/directxworld/directxworld06/directxworld06_04.html

構造体を使うかクラスを使うかの境界線はそのサイズで決まるようなの
ですが、その境界は意外と低くて16バイト以上のものはクラスを使ったほうがいいよう。

クラスと構造体の選択
http://msdn.microsoft.com/ja-jp/library/ms229017.aspx
実検証結果
http://mag.autumn.org/Content.modf?id=20061014194823

実際には構造体を使ったほうが得をする場合は
結構限られている感じをうけます。


○キャストはすごく重そうだ

型キャストはちょっと意外なぐらい重い。
http://www.atmarkit.co.jp/fdotnet/dotnettips/005castandas/castandas.html

上記のリンクにもあるように、参照型のキャストにはasを使ったほうが速い。
でも、asを使ってもそれなりに重い処理ではある。

Collectionはキャスト必須になってしまうので、
なるべく代わりにGenericを使ったほうが良い。

http://www.atmarkit.co.jp/fdotnet/csharp20/csharp20_02/csharp20_02_01.html
http://life-hack.jp/blog/charly/95

イベントハンドラでsenderを繰り返し使うと重くなるので、
senderは早めにas演算子でなんらかの型に変換しちゃったほうが良いみたい。


○多次元配列は比較的重い

多次元配列(x[i,j])よりジャグ配列(x[i][j])使ったほうが速いそうです。
http://d.hatena.ne.jp/EmK/20081130/1228025602
重い数値計算とかやる場合に影響するかも。

C++の多次元配列は実質的には単なるポインタ経由のアクセスなんで
べらぼうに速いですね。やっぱり。


○文字列の連結処理

頻繁な文字列の連結はメモリ中のコピーを繰り返すので遅い
http://www.atmarkit.co.jp/fdotnet/dotnettips/029strcat/strcat.html
ベンチマーク結果
http://sonic64.com/2005-11-30.html

stringbuilderを使うと高速化できる。
そこまで重い処理ではないので、ホントに大量の連結
を繰り返す場合にだけ対策が必要。


○画像処理

BitmapのGetPixel, SetPixelは画像処理に使うにはちょっと重すぎる。
http://www.atmarkit.co.jp/fdotnet/csharp_abc/csharp_abc_021/csharp_abc05.html

本気で画像処理するときには、unsafeになるけど
ポインタを使うしかないと思う。


○.net Frameworkの起動が遅い

Windows XPだと初回のアプリ起動時に.net Framework自体を起動しに行く。
http://ameblo.jp/norixp/entry-10013581738.html

対策するとしたら、
上のリンクのように別アプリでスプラッシュウィンドウを出す、とか、
スタートアップに空の.netアプリを入れとくか。

Vista以降だと最初から起動してるから
これがなくて速いんでしょうか?

2009/12/22

C#って遅いのか?

C#って遅いのかどうか?といういろいろ荒れがちなセンシティブな話。
でも結構高速化するときに結局どうすりゃいいのかわからなくて困るので
他力本願で収集した内容を分かる範囲でまとめてみる。

1) C#ってVMで中間言語を解釈しながら動いてるわけじゃない

ときどき間違って説明されていることがありますが、
C#って昔のVBのインタプリタとかJAVAのVMとは違って
中間言語を逐次実行してるわけではない。

VB.netの話だけど以下がわかりやすい。C#も同じ。
http://www.atmarkit.co.jp/fdotnet/vbcheer/vbcheer08/vbcheer08.html

だから数値計算とかでベンチマークすると実はかなり速いそうだ。
http://densanshokunin.at.webry.info/200701/article_2.html

でも実際に使っているとなにかとストレスを感じることもあるのは周知の通り。

2) コンパイルの時期に関して
より正確にはここに。
http://msdn.microsoft.com/ja-jp/library/ht8ecch6(VS.80).aspx

> すべての MSIL をネイディブ コードに変換するために時間とメモリを費やすのではなく、
> 実行時に必要になった MSIL を変換し、その結果生成されたネイティブ コードを保存して、
> 以降の呼び出しで利用できるようにしておきます。
> 型が読み込まれると、ローダーはスタブを作成し、その型の各メソッドに結び付けます。
> それ以降は、この JIT コンパイル済みのメソッドを呼び出すと生成済みの
> ネイティブ コードが直接実行され、JIT コンパイルとコードの実行に必要な時間を節約できます。

つまり、フォームを開くのにしてもメソッドを実行するのにしても
「初回だけ」遅い可能性があることになります。
故意に前もって特定のコンパイル実行しておくのも難しいようなので
このへんは気をつけておく必要あり。
最終的にはngen.exeを使った一括先行コンパイルか。試したことないですが。

3)ガベージコレクタの動作に関するオーバーヘッドは読みづらいので注意する必要がある。

ガベージコレクタの動作については以下が詳しい。
http://www.atmarkit.co.jp/fdotnet/directxworld/directxworld06/directxworld06_01.html

でも、結局プロのプログラマでない自分としてはいろいろ調べるのは面倒で、
なるべくobjectのinstanceをpublic宣言して
いろんなところで使いまわす、という腰の引けたコーディングをやりがち。
ホントはもっといいやり方があるはず。

4) System.Windows.Formsって重くないでしょうか。

http://d.hatena.ne.jp/NyaRuRu/20060203#p2
> トップレベルウィンドウとメニューやツールチップなどの一部の特殊ウィンドウのみを
> Win32ウィンドウとし,後はクライアント領域に完全自前描画になったことで,
> やっとInternet ExplorerやFirefox と同じ土俵に上がることができました.
> 要はこれまで WinForms が重かったのはアルゴリズム的問題があったと
> いうことなわけですが,今まで単に「.NET だから重い」と思っていた人は
> もう一度評価し直すべきときがいよいよ来たかと思います.

ちゃんとした検証はできてないですが、
どうもやっぱりFormsの標準コントロールが.net以前と比べて妙に重い気がします。
コントロールをフォーム上に大量配置しただけで露骨に重くなる感じが。

困ったときはすぐダブルバッファリング+自前描画
http://www.atmarkit.co.jp/fdotnet/dotnettips/197doublebuf/doublebuf.html
をやって無理矢理解決してるので、根本的にはどうしたらいいのかさっぱりわからないです。


5) 意外と遅い処理がいろいろあるよう。

C#では意外と遅い特定の処理というのがいろいろあるようで、
そこを気にしないといけないようです。

これについてはまた別途。

2009/12/18

システムの復元でC#のソースファイルが消える!

C#のソースファイルをいくらか無くしてしまいました。

PCを起動してみると突然Windowsがブルースクリーンに。
しょうがないのでセーフモードで起動し、初めて使う
「システムの復元」をすると、なんとか正常に戻った。
なんかシステム関連のファイルだけ以前の状態に戻してくれるんだそうだ。

なるほど、こりゃ凄いや。頻繁に復元ポイント作る設定でよかった。
と思いつついろいろやってると、
どうも最近インストールした自作ソフトのexeファイルが消えてることに気づく。

そっかー。実行ファイルだからシステム復元で消されてもしょうがないわな。
まぁ、ソースファイルあるんだしまたコンパイルすりゃいいや。と思って
VC#を開いてみると、ところどころC#のソースコード(拡張子.cs)が消えている!
プロジェクトファイルとかリソースファイルは消えてないのに。
いや、なんでソースファイルの状態巻き戻すかな。

で、いろいと調べた結果、
>http://www.atmarkit.co.jp/fwin2k/operation/srestore/srestore_02.html
Filelist.xmlを調べると、システム復元が対象にしてるファイルの拡張子がわかるそうだ。

・・・うわ。.csが入ってるよ。C Scriptか何かの拡張子とカブってんのかな?
.cとか.cppとか.vbとかは入ってないのに。なんてこった。

急いで再度システム復元を使ってなんとかソースファイルを復元しようとすると、
「システム復元に必要なHDD領域が足りません」とか言われる。
じゃあいらないファイル消してもう一回やるか、と思ったら、
なんとシステム復元ポイントがぜーんぶ無くなってる!

いや、それはないだろーと思って調べてみると、

> http://support.microsoft.com/default.aspx?scid=kb;ja;JP301224
この動作は仕様です。

・・・。うわー。

Windows XP SP3で確認。

2009/12/13

C#でフォームをアクティブにせずに開く方法

.net frameworkで、ポップアップウィンドウを作ろうとすると、
いろいろ面倒な罠がありました。

まず、アクティブにせずにフォームを開く必要があり、
ShowWithoutActivationというプロパティを変更するとよさそうなのですが、
読み取り専用なので、値を変更できない。

すると、このプロパティをoverrideするしかないのですが、

MSDNによると、
http://msdn.microsoft.com/ja-jp/library/system.windows.forms.form.showwithoutactivation(VS.80).aspx

> フォーム内で ShowWithoutActivation をオーバーライドし、
> true が返されるようにプログラミングしても、値を変更することはできません。


なんだよー。と思いつつ調べてると、
http://d.hatena.ne.jp/masa-k/20080609/1213015216
こいつはどうも誤訳で、
> このプロパティはread-onlyなので、変えたいときは
> ShowWindowActivationをoverrideしてtrueを返すようにしてください。
が、正しいよう。

で、なんだーできるんじゃないですかー。と思いつつ試していると、
何故かできない。

で、結局原因は、
http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=401311
「TopMostプロパティをtrueにしているとShowWindowActivationが効かない」
というバグのせいのよう。

ポップアップに使うにはTopMostが効いてないといろいろ不便なので、
結局、ここの下のほうにあるように
http://www.r-nakai.com/archives/99
SetWindowPos APIを使うしかないよう。
(つうかC#でWIN32 APIって使っていいんだ・・・。)

ただ、これをやるとLoadとかのイベントが発生しないようなので、
そこらへんは手で実装してやる必要があるようです。


.net Framework3.5で確認。

2009/12/05

Inkscape 0.47 使ってみました

Inkscape 0.47をしばらく使ってみました。

まだ矢印関連の実装がまだなのが痛いです。
矢印に色をつけたりすると矢印の先だけ色がつかなくて変な感じになったり、
矢印の先端がグリッドに乗らなくて微妙にずれたままだったり、
いろいろ未実装の部分があり。
パス→オブジェクトをパスへ
でなんとか解決できるんだけど、やっぱりまだちょっと仕事に使うのは
時期尚早な感じですね・・・。

Illustatorもどきの第一候補なのでぜひ頑張ってほしいですね。
・・・誰かこの辺直さない?>各位

2009/12/02

Inkscape 0.47

高機能なフリーのドローソフト Inkscapeが
ver 0.47になりました。
http://ja.wikipedia.org/wiki/Inkscape

普通に仕事に使う上では最も致命的だった、
「コピペでofficeにwmp貼り付けできない」
というのが、今回のver upでできるようになってるようです。

・・・が、私の環境ではなぜか左クリックでオブジェクトが選択できなくなってる。
何故だ・・・。