views:

392

answers:

6

I have a function that I use to add vectors, like this:

public static Vector AddVector(Vector v1, Vector v2)
{
    return new Vector(
      v1.X + v2.X,
      v1.Y + v2.Y,
      v1.Z + v2.Z);
}

Not very interesting. However, I overload the '+' operator for vectors and in the overload I call the AddVector function to avoid code duplication. I was curious whether this would result in two method calls or if it would be optimized at compile or JIT time. I found out that it did result in two method calls because I managed to gain 10% in total performance by duplicating the code of the AddVector as well as the dot product method in the '+' and '*' operator overload methods. Of course, this is a niche case because they get called tens of thousands of times per second, but I didn't expect this. I guess I expected the method to be inlined in the other, or something. And I suppose it's not just the overhead of the method call, but also the copying of the method arguments into the other method (they're structs).

It's no big deal, I can just duplicate the code (or perhaps just remove the AddVector method since I never call it directly) but it will nag me a lot in the future when I decide to create a method for something, like splitting up a large method into several smaller ones.

+3  A: 

"And I suppose it's not just the overhead of the method call, but also the copying of the method arguments into the other method (they're structs)."

Why don't you test this out? Write a version of AddVector that takes a reference to two vector structs, instead of the structs themselves.

siz
That would be slower than passing by value (depending on the struct size, but I am talking about the recommended size).
Joan Venge
+4  A: 

If you compile into debug mode or begin the process with a debugger attatched (though you can add one later) then a large class of JIT optimisations, including inlining, won't happen.

Try re-running your tests by compiling it in Release mode and then running it without a debugger attatched (Ctrl+F5 in VS) and see if you see the optimisations you expected.

ICR
A: 

You say Vector is a struct. According to a blog post from 2004, value types are a reason for not inlining a method. I don't know whether the rules have changed about that in the meantime.

Rob Kennedy
Value types can be inlined on x86 from .NET 3.5 SP1 (see: http://blogs.msdn.com/vancem/archive/2008/05/12/what-s-coming-in-net-runtime-performance-in-version-v3-5-sp1.aspx); x64 supported inlining in an earlier version than this but I'm not sure which one exactly...
Greg Beech
A: 

Don't assume that struct is the right choice for performance. The copying cost can be significant in some scenarios. Until you measure you don't know. Furthermore, structs have spooky behaviors, especially if they're mutable, but even if they're not.

In addition, what others have said is correct:

  • Running under a debugger will disable JIT optimizations, making your performance measurements invalid.
  • Compiling in Debug mode also makes performance measurements invalid.
Jay Bazuzi
Definitely true. Changing a custom vector type to a class in a ray tracer from codeproject.com (can't find the url) resulted in a significant performance boost.
SealedSun
Thanks for the helpful insights :)I just changed my structs to classes, and behold, significant performance gains! I really didn't expect that either, but I'll create a new question for the questions I have with that.
JulianR
That's strange, I got 10x better performance when I switched to structs from classes.
Joan Venge
+1  A: 

I had VS in Release mode and I ran without debugging so that can't be to blame. Running the .exe in the Release folder yields the same result. I have .NET 3.5 SP1 installed.

And whether or not I use structs depends on how many I create of something and how large it is when copying versus referencing.

JulianR
A: 

Theres only one optimization I can think of, maybe you want to have a vOut parameter, so you avoid the call to new() and hence reduce garbage collection - Of course, this depends entirely on what you are doing with the returned vector and if you need to persist it or not, and if you're running into garbage collection problems.

JSmyth
Since it is a struct, GC isn't an issue. The only thing that changes is some minor stack usage points - but since it is the return value even this is negligible.
Marc Gravell
Oh, I didn't realise that - Cheers.
JSmyth