C# Assembly Duel – Properties vs. Getters and Setter Methods

In this post we look at the differences between properties and getter/setter methods in C# at the assembly level.


Disclaimer: These are done in the name of fun and research, I’m not out to prove that one way of doing something is better than another, and I also want to point out that by using the disassembly window, we are looking at assembly generated for debug, for an x64 intel chipset, so your mileage will vary both in release code and for your specific chipset.

In the last post where I talked about the disassembly window in Visual Studio, a couple people reached out and told me they liked the idea of comparing methodologies at the assembly level to see the differences between the two, so I decided to start a series I’m dubbing assembly duels. In this first installment, we’ll be looking at the overhead of using a property in C# vs. the overhead of using a standard getter or setter method, so let’s jump straight into it.

The test class

For this comparison, we’ll need a class, let’s create a 2D vector class:

public class Vec2D

    private int x;
    private int y;


So we’ve got two simple member variables, an x and a y which represent x and y coordinates. Now, let’s write the properties to get and set these, and the methods to get and set them:

public int X
    get { return x; }
    set { x = value; }
public int Y
    get { return y; }
    set { y = value; }

public int GetX()
    return x;

public int GetY()
    return y;

Pretty standard stuff here, we’ve got a property for x and y, which have a very basic get and set, and we have two methods, GetX and GetY, which return the member variables x and y. For the usage, I’m going to simply assign a few variables the result of each getter/setter:

var vec2D = new Vec2D();
vec2D.X = 10;
vec2D.Y = 10;

var xProp = vec2D.X;
var yProp = vec2D.Y;

var xGetter = vec2D.GetX();
var yGetter = vec2D.GetY();

Pretty simple again, I create an instance of the class, initialize the x and y values with the setter portion of the properties, and then assign 4 variables with the various methods of getting the members. Now let’s break open the disassembly window and see what we get:

            var xProp = vec2D.X;
02DB04C7  mov         ecx,dword ptr [ebp-40h]  
02DB04CA  cmp         dword ptr [ecx],ecx  
02DB04CC  call        02DB0050  
02DB04D1  mov         dword ptr [ebp-58h],eax  
02DB04D4  mov         eax,dword ptr [ebp-58h]  
02DB04D7  mov         dword ptr [ebp-44h],eax  
            var yProp = vec2D.Y;
02DB04DA  mov         ecx,dword ptr [ebp-40h]  
02DB04DD  cmp         dword ptr [ecx],ecx  
02DB04DF  call        02DB0060  
02DB04E4  mov         dword ptr [ebp-5Ch],eax  
02DB04E7  mov         eax,dword ptr [ebp-5Ch]  
02DB04EA  mov         dword ptr [ebp-48h],eax  

            var xGetter = vec2D.GetX();
02DB04ED  mov         ecx,dword ptr [ebp-40h]  
02DB04F0  cmp         dword ptr [ecx],ecx  
02DB04F2  call        02DB0070  
02DB04F7  mov         dword ptr [ebp-60h],eax  
02DB04FA  mov         eax,dword ptr [ebp-60h]  
02DB04FD  mov         dword ptr [ebp-4Ch],eax  
            var yGetter = vec2D.GetY();
02DB0500  mov         ecx,dword ptr [ebp-40h]  
02DB0503  cmp         dword ptr [ecx],ecx  
02DB0505  call        02DB0078  
02DB050A  mov         dword ptr [ebp-64h],eax  
02DB050D  mov         eax,dword ptr [ebp-64h]  
02DB0510  mov         dword ptr [ebp-50h],eax

Very interesting results here, you’ll notice that the assembly generated for each type of getter is literally identical, minus specific memory addresses, so are getter methods and properties the same thing under the hood? Well, at a surface level they appear to be, but notice the call command. The call command jumps to another portion of assembly, so to determine if they are identical, we also need to analyze what’s actually at each call. First, let’s break down the assembly above.

First we take the contents of the ecx register, and put them in the pointers memory. Next we see a command “cmp”, cmp means conditional jump, you can think of jump as a goto statement (which in assembly are not only not bad practice, they are literally required) the difference between cmp and a normal goto is cmp evaluates the data on the left and right side, and only jumps when it evaluates to true. Next, we get to a call method, which we’ll get to, and then three mov commands happen.

Getting into the meat of each method

Now let’s really drill down, the call method for the x member properties getter is as follows:

            get { return x; }
03850AB0  push        ebp  
03850AB1  mov         ebp,esp  
03850AB3  push        edi  
03850AB4  push        esi  
03850AB5  push        ebx  
03850AB6  sub         esp,34h  
03850AB9  mov         esi,ecx  
03850ABB  lea         edi,[ebp-38h]  
03850ABE  mov         ecx,0Bh  
03850AC3  xor         eax,eax  
03850AC5  rep stos    dword ptr es:[edi]  
03850AC7  mov         ecx,esi  
03850AC9  mov         dword ptr [ebp-3Ch],ecx  
03850ACC  cmp         dword ptr ds:[36742C0h],0  
03850AD3  je          03850ADA  
03850AD5  call        73B236D0  
03850ADA  xor         edx,edx  
03850ADC  mov         dword ptr [ebp-40h],edx  
03850ADF  nop  
03850AE0  mov         eax,dword ptr [ebp-3Ch]  
03850AE3  mov         eax,dword ptr [eax+4]  
03850AE6  mov         dword ptr [ebp-40h],eax  
03850AE9  nop  
03850AEA  jmp         03850AEC  
03850AEC  mov         eax,dword ptr [ebp-40h]  
03850AEF  lea         esp,[ebp-0Ch]  
03850AF2  pop         ebx  
03850AF3  pop         esi  
03850AF4  pop         edi  
03850AF5  pop         ebp  
03850AF6  ret  

It’s not important to understand all this stuff, just notice two things, one: another call happens, and two, ret at the bottom returns us back to where we came, this occurs by simply popping the stack. As it turns out, we can’t actually view what occurs when the inner call happens, what we do know is it goes to the memory address 73B236D0, because the memory address is so massively far from our own jitted codes memory address range, it hints to us that it’s unmanaged code, but there’s not a really easy way to tell for sure, so let’s accept this and move on. Command count for the properties getter: 31 (I think).

Another disclaimer: Just because something may use more assembly instructions, doesn’t make it less efficient, to really, truly see how efficient something is in terms of the assembly executing is going to be tough in Visual Studio, as it only tells us that something took <= 1ms at best. At the assembly level you're dealing with clock cycles, which are incredibly fine granularity, much finer certainly that milliseconds. To determine runtime speed, you would really need to know ahead of time about how long a particular processor takes to execute a particular instruction, which will depend greatly from processor to processor, and it will even vary if you compare the same processor against the same processor, because some may have higher clocked speeds than others. (ex: two intel i7 7700k processors can be clocked at many different speeds, so all of them will run differently)

Okay so let’s drill down into the getter method now:

033606B8  push        ebp  
033606B9  mov         ebp,esp  
033606BB  push        edi  
033606BC  push        esi  
033606BD  push        ebx  
033606BE  sub         esp,34h  
033606C1  mov         esi,ecx  
033606C3  lea         edi,[ebp-38h]  
033606C6  mov         ecx,0Bh  
033606CB  xor         eax,eax  
033606CD  rep stos    dword ptr es:[edi]  
033606CF  mov         ecx,esi  
033606D1  mov         dword ptr [ebp-3Ch],ecx  
033606D4  cmp         dword ptr ds:[33142C0h],0  
033606DB  je          033606E2  
033606DD  call        73B236D0  
033606E2  xor         edx,edx  
033606E4  mov         dword ptr [ebp-40h],edx  
033606E7  nop  
            return x;
033606E8  mov         eax,dword ptr [ebp-3Ch]  
033606EB  mov         eax,dword ptr [eax+4]  
033606EE  mov         dword ptr [ebp-40h],eax  
033606F1  nop  
033606F2  jmp         033606F4

Here there’s certainly a lot less going on, notice though, that in this method as well we have a call command that calls to 73B236D0, which is the same memory address as the one the property called to. This tells us one thing for certain, whatever happens at that memory address doesn’t really matter, because both methods of getting a member are going to incur the same overhead of whatever goes on at that memory address. The nop (no-op) command is likely something the debugger put in, and even if it isn’t, no-ops incur virtually zero overhead on pretty much any processor or chipset, so we can basically call that free, so ignoring that command, we have 23 instructions executing for this method of getting a member.

What we learned

There are a few takeaways here. First off, properties for sure use more assembly instructions to execute, does that make them more inefficient? I don’t know, I haven’t gone in and studied how many clock cycles each instruction takes to execute, however my guess is it runs at roughly the same speed, give or take. The second takeaway is that we can say with certainty, that properties and getter and setter methods are absolutely not implemented the same way under the hood, they use very different sets of commands for doing their respective jobs, but we also know that at some point, both call into the exact same unmanaged code to do something, what that something is, we aren’t sure. Again, the idea behind these deep dives is exposure, it’s not to say whether something is good or bad, it’s just to see what happens under the hood. I hope this was at least mildly interesting, and if you enjoyed it make sure to like or leave a comment on the post and consider following the blog, I’ll definitely be doing more of these.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s

%d bloggers like this: