Mehrdad の例の DISSASEMBLER VIEW (両方のバージョン)
私のようにアセンブリ コードを読むのが苦手な人のために、Mehrdad の優れた証明をもう少し掘り下げてみます。このコードは、デバッグ中に Visual Studio でキャプチャできます。[Debug] -> [Windows] -> [Dissasembly] をクリックします。
REFを使用したバージョン
ソースコード:
namespace RefTest
{
class Program
{
static void Test(ref object o) { GC.KeepAlive(o); }
static void Main(string[] args)
{
object temp = args;
Test(ref temp);
}
}
}
アセンブリ言語 (x86) (異なる部分のみ表示):
object temp = args;
00000030 mov eax,dword ptr [ebp-3Ch]
00000033 mov dword ptr [ebp-40h],eax
Test(ref temp);
00000036 lea ecx,[ebp-40h] //loads temp address's address on ecx?
00000039 call FD30B000
0000003e nop
}
参照なしのバージョン
ソースコード:
namespace RefTest
{
class Program
{
static void Test(object o) { GC.KeepAlive(o); }
static void Main(string[] args)
{
object temp = args;
Test(temp);
}
}
}
アセンブリ言語 (x86) (異なる部分のみ表示):
object temp = args;
00000035 mov eax,dword ptr [ebp-3Ch]
00000038 mov dword ptr [ebp-40h],eax
Test(temp);
0000003b mov ecx,dword ptr [ebp-40h] //move temp address to ecx?
0000003e call FD30B000
00000043 nop
}
コメント行を除いて、コードは両方のバージョンで同じです。ref がある場合、関数の呼び出しの前に LEA 命令があり、ref がない場合は、より単純な MOV 命令になります。この行を実行した後、LEA はオブジェクトへのポインターへのポインターを使用して ecx レジスターをロードしましたが、MOV はオブジェクトへのポインターを使用して ecx をロードしました。これは、最初のケースの FD30B000 サブルーチン (Test 関数を指している) が、オブジェクトに到達するためにメモリへの追加アクセスを行わなければならないことを意味します。この関数の生成された各バージョンのアセンブリ コードを調べると、ある時点 (実際には 2 つのバージョン間で異なる唯一の行) で余分なアクセスが行われていることがわかります。
static void Test(ref object o) { GC.KeepAlive(o); }
...
00000025 mov eax,dword ptr [ebp-3Ch]
00000028 mov ecx,dword ptr [eax]
...
refのない関数はオブジェクトに直接行くことができますが:
static void Test(object o) { GC.KeepAlive(o); }
...
00000025 mov ecx,dword ptr [ebp-3Ch]
...
それが役に立ったことを願っています。