To reduce maintenance in a library I am developing, I am trying to delegate similar functionality to single functions. As an example, say one has a two component vector with Add functions accepting by-ref args and others accepting by-value args. The idea is to simply call the by-ref function within the by-value functions, making it so that only the by-ref function would have to be maintained.
i.e.
struct Vector2
{
public float X;
public float Y;
public Vector2(float x, float y)
{
this.X = x;
this.Y = y;
}
public static void Add(ref Vector2 a, ref Vector2 b, out Vector2 result)
{
result.X = a.X + b.X;
result.Y = a.Y + b.Y;
}
public static Vector2 Add1(Vector2 a, Vector2 b)
{
Add(ref a, ref b, out a);
return a;
}
public static Vector2 Add2(Vector2 a, Vector2 b)
{
a.X += b.X;
a.Y += b.Y;
return a;
}
}
The problem is the by-ref overload function isn't inlined, causing what I believe to be slower code(excluding the nops).
Release output with JIT optimizations enabled:
Add1:
Add(ref a, ref b, out a);
0000002b lea eax,[ebp+10h]
0000002e push eax
0000002f lea ecx,[ebp+10h]
00000032 lea edx,[ebp+8]
00000035 call FFEDA508
0000003a nop
return a;
0000003b lea edi,[ebp-44h]
0000003e lea esi,[ebp+10h]
00000041 movq xmm0,mmword ptr [esi]
00000045 movq mmword ptr [edi],xmm0
00000049 nop
0000004a jmp 0000004C
Add2:
a.X += b.X;
0000002b fld dword ptr [ebp+8]
0000002e fadd dword ptr [ebp+10h]
00000031 fstp dword ptr [ebp+10h]
a.Y += b.Y;
00000034 lea eax,[ebp+8]
00000037 fld dword ptr [eax+4]
0000003a lea eax,[ebp+10h]
0000003d fadd dword ptr [eax+4]
00000040 fstp dword ptr [eax+4]
return a;
00000043 lea edi,[ebp-44h]
00000046 lea esi,[ebp+10h]
00000049 movq xmm0,mmword ptr [esi]
0000004d movq mmword ptr [edi],xmm0
00000051 nop
00000052 jmp 00000054
Is there a way to get the call to Add to be inlined?
Note that the library requires .NET 4.0, meaning that aggressive inlining isn't available.
Try enabling aggressive inlining:
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Add(ref Vector2 a, ref Vector2 b, out Vector2 result)
{
result.X = a.X + b.X;
result.Y = a.Y + b.Y;
}
It hints the compiler (or JIT'er) to inline that function.
Note: AggressiveInlining
is new in .NET 4.5.
I'll take a wild guess and say you are probably a C++ programmer at heart. The ref
and out
keywords you use here are completely superfluent. You can delete them and your code will run just fine.
In fact, I don't see any functionality, that a simple version would not offer:
public Vector2 Add(Vector2 other)
{
return new Vector2() { X = this.X + other.X, Y = this.Y + other.Y };
}
Even if you want to keep your API static, remove all ref
and out
keywords, they are not necessary for your code.
Edit:
I just noticed you are using structs (value types) so what I wrote before is incorrect and I deleted my post. Then I thought about it some more and I wonder: either you want this to be a value type, because it's so small that copying is fast enough, or you don't. You made it a value type and now you are trying to work around your own decision by somewhat misusing the ref
and out
keywords.
public static void Add(ref Vector2 a, ref Vector2 b, out Vector2 result)
This could easily be
public static void Add(Vector2 a, Vector2 b, ref Vector2 result)
If you want by-reference passing, why don't you make it a reference type in the first place? If you do, then everything I wrote above would still be valid :)
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