I'm trying to write optimized code in C# .NET and Mono that will be used for games. (Yes I have valid reasons for using C# and not C++).
I notice C# does not seem to optimize its operators correctly. Operators run twice as slow vs manually inlining the code with Vector4 math. Here are some simple benchmark tests I did in .NET 4.5 running 9999999 times:
// Test 1
for (uint i = 0; i != 9999999; ++i)// 230 MS
{
vector += vector2;
vector2 -= vector;
}
// Test 2
for (uint i = 0; i != 9999999; ++i)// 185 MS
{
vector = vector.Add(ref vector2);
vector2 = vector2.Sub(ref vector);
}
// Test 3
for (uint i = 0; i != 9999999; ++i)// 116 MS
{
vector.X += vector2.X;
vector.Y += vector2.Y;
vector.Z += vector2.Z;
vector.W += vector2.W;
vector2.X -= vector1.X;
vector2.Y -= vector1.Y;
vector2.Z -= vector1.Z;
vector2.W -= vector1.W;
}
// EDIT Test 1 SOLVED ----------------------------------
// When the Operators are created like so, they actually perform the BEST!
// Sry MS for complaining :(... Although SIMD support would be nice :)
struct Vector4
{
public static Vector4 operator +(Vector4 p1, Vector4 p2)
{
p1.X += p2.X;
p1.Y += p2.Y;
p1.Z += p2.Z;
p1.W += p2.W;
return p1;
}
public static Vector4 operator -(Vector4 p1, Vector4 p2)
{
p1.X -= p2.X;
p1.Y -= p2.Y;
p1.Z -= p2.Z;
p1.W -= p2.W;
return p1;
}
}
for (uint i = 0; i != 9999999; ++i)// 75 MS
{
vector += vector2;
vector2 -= vector;
}
I was wondering if there were any .NET IL optimizer tools? I've looked but having found anything really yet. Or to be MORE clear, anyway to optimize my C# code or IL code for performance.
I really would like to see the operators perform at LEAST 185ms. It only makes sense for it too.
Here is a link to the app im using for testing: Download
Your own answer and the comments all give a strong hint to why calling .Add is faster than using the addition operator.
The semantics of +
is that the operands are left untuched. Doing 1 +2 you wouldn't expect that 1 had the value of 3 afterwards would you? SO to follow the rule of least surprise the addition operators in various implementations follow this semantic.
This also means that the addition operator for vector4 needs to create a new Vector4 object. The memory for this new object might already be allocated (e.g. the stack) but that does not help much because we will have to copy that value when it is assigned to whatever the return type is assigned to.
The semantics of the Add instance method is different from the addition operator. It mutates one of the instances and therefor does not have to create a new object.
Your semantics of the addition operator in your posted answer are equivalent to that of the add
I found this solution to my problem... Although I would still like any information on .NET IL optimizers. Also It would be interesting to know why using operators are actually faster then manually inclining the code yourself?
To get the best performance with Vector4 operators on .NET do the following:
public static Vector4 operator +(Vector4 p1, Vector4 p2)
{
p1.X += p2.X;
p1.Y += p2.Y;
p1.Z += p2.Z;
p1.W += p2.W;
return p1;
}
public static Vector4 operator -(Vector4 p1, Vector4 p2)
{
p1.X -= p2.X;
p1.Y -= p2.Y;
p1.Z -= p2.Z;
p1.W -= p2.W;
return p1;
}
DONT DO:
public static Vector4 operator +(Vector4 p1, Vector4 p2)
{
return new Vector4(p1.X+p2.X, p1.Y+p2.Y, p1.Z+p2.Z, p1.W+p2.W);
}
//ect...
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