Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delegating value arguments to functions that accept ref arguments

Tags:

c#

struct

ref

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.

like image 610
Koby Duck Avatar asked Aug 08 '14 08:08

Koby Duck


2 Answers

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.

like image 70
Patrick Hofman Avatar answered Nov 16 '22 06:11

Patrick Hofman


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 :)

like image 41
nvoigt Avatar answered Nov 16 '22 06:11

nvoigt