Consider a method that returns a Structure, like this:
Public Function DoWork() As MyStructure
Return New MyStructure(1.5, 1.7, 1.1, 55.9)
End Function
In this case, does .NET create and initialize a MyStructure
value once, or twice?
EDIT: My hunch is that a call to DoWork
must involve .NET pushing the return value on the stack right from the outset. Otherwise, how would Return
get anything back to the calling code? So that's the first initialization I'm talking about.
The second initialization would be at the Return
statement, where arguments 1.5, 1.7, 1.1, 55.9 initialize a new MyStructure
value. On Return
, .NET would then overwrite the existing value on the Stack with the new return value.
The thing is, I know very little about how .NET works under the hood. My conception of how Stacks work is based on vague recollections of trying to code in Pascal, under DOS, in the early 90s. I have no idea how .NET/Windows does things these days!
Just have a look at the generated machine code to see what happens. You first need to change an option to ensure the optimizer is enabled, Tools > Options > Debugging > General > untick the "Suppress JIT optimization" checkbox. Switch to the Release build. Set a breakpoint on the DoWork call and when it hits use Debug > Windows > Disassembly to see the generated machine code.
I'll noodle a little bit about what you see. Your instinct is correct, only returning simple scalar values is efficient, they fit in a CPU register. Not this structure, the caller method must reserve space on its stack frame so the callee can store the structure there. It passes a pointer to that reserved space. In other words, there's no real difference if you had declared this method as Public Sub DoWork(ByRef retval As MyStructure)
.
Only other way this code can be optimized is by inlining the Function. The x86 jitter does not do that, it is picky about methods returning structs. The x64 jitter does in fact inline the method, but then does an absolutely horrible job with it. It still reserves the space for the return value and then generates lots and lots of needless initialization and value moving code. Quite atrocious. This jitter was rewritten for VS2015 (project name RyuJIT), I don't have it installed yet to see if it does a better job at it.
Basic conclusion to draw: this code isn't optimized. Structures work well when you pass them ByVal. Don't let this cramp your style, we're talking nanoseconds here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With