this によるメソッド呼び出しは速い

.NET Framework 4.0 で以下のfuncの呼び出しは、this のほうが速い。

遅い方。

var me = this;

me.func();

速いほう。

this.func();

「そりゃ変数代入の分だけ遅い」ではなくて、呼び出し単体で遅いのである。

利用は、NullReferenceException チェックのため。

機械語を見ると分かる。

速いほうは func の呼び出し命令列が下記のようになっている。

00000027 8B 4D F8             mov         ecx,dword ptr [ebp-8]
0000002a E8 DD A1 CB FF       call        FFCBA20C

一行目は、thisポインタをECXレジスタに入れている。.NET JITコンパイラは、ECXレジスタをthisポインタのやり取りに使うようだ。

二行目で、メソッドを呼び出している。

遅い方は、下記のようになっている。

0000004e 8B 4D F4             mov         ecx,dword ptr [ebp-0Ch]
00000051 39 09                cmp         dword ptr [ecx],ecx
00000053 E8 B4 A1 CB FF       call        FFCBA20C

一行目と三行目は速いほうと変わらない。問題は二行目が増えていること。

二行目はECXレジスタのメモリから4バイト読み出し、それをECXレジスタと比較している。

こうすれば、ECXレジスタが 0 (NULL) だったときにプロセッサが割り込みを発生させることから、NullReferenceException を起こすことができる。もしこのCMP命令がないと、インスタンスフィールドへアクセスするまでNullReferenceExceptionが発生しなくなるため、プログラムの動作が不確定になる。(たとえば、static 変数を修正した後インスタンスフィールドを修正するコードは、処理途中で意図せず例外が起こってしまう。)実際、.NET 2.0 とか 3.0 あたりでは this についての NULL チェックをしていなかったような記憶があり、変な挙動になっていた。

さて、まあ遅いと言ってもたかが一命令、しかも読み出しである。キャッシュヒットすればたいしたことないだろう。

もしかしたらIntelのすごい技術によって実質的に0サイクルになったりするかもしれない(そこら辺は詳しくないので分からない)。

これが問題になることは?

マルチプロセッサ環境でキャッシュコヒーレンスの維持に負荷が掛かってる場合なんかどうだろう。改めて調べてみたい。