DISSASEMBLER VIEW OF Mehrdad's example (BOTH VERSIONS)
I'll try to dig a little deeper on Mehrdad's nice proof, for those like me that are not very good reading assembly code. This code can be captured in Visual Studio when we're debbuging, clicking Debug -> Windows -> Dissasembly.
VERSION USING REF
Source Code:
namespace RefTest
{
class Program
{
static void Test(ref object o) { GC.KeepAlive(o); }
static void Main(string[] args)
{
object temp = args;
Test(ref temp);
}
}
}
Assembly language (x86) (only showing the part that differs):
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
}
VERSION WITHOUT REF
Source Code:
namespace RefTest
{
class Program
{
static void Test(object o) { GC.KeepAlive(o); }
static void Main(string[] args)
{
object temp = args;
Test(temp);
}
}
}
Assembly language (x86) (only showing the part that differs):
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
}
Apart from the commented line, the code is the same for both versions: with ref, the call to the function is preceded by a LEA instruction, without ref we've a simpler MOV instruction. After executing this line, LEA has loaded the ecx register with a pointer to a pointer to the object, whereas MOV has loaded ecx with a pointer to the object. This means that the FD30B000 subroutine (pointing to our Test function) in the first case will have to make an extra access to memory to get to the object. If we inspect the assembly code for each produced version of this function, we can see that at some point (in fact the only line that differs between the two versions) the extra access is made:
static void Test(ref object o) { GC.KeepAlive(o); }
...
00000025 mov eax,dword ptr [ebp-3Ch]
00000028 mov ecx,dword ptr [eax]
...
While the function without ref can go straight to the object:
static void Test(object o) { GC.KeepAlive(o); }
...
00000025 mov ecx,dword ptr [ebp-3Ch]
...
Hope it helped.