将值参数委托给接受ref参数的函数

时间:2014-08-08 08:44:30

标签: c# struct ref

为了减少我正在开发的库中的维护,我试图将类似的功能委托给单个函数。例如,假设一个具有双组件向量,其中Add函数接受by-ref args,而其他函数接受by-value args。我们的想法是简单地在by-value函数中调用by-ref函数,使其只需要维护by-ref函数。

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;
    }
}

问题是by-ref重载函数没有内联,导致我认为代码更慢(不包括nops)。

在启用JIT优化的情况下释放输出:

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

有没有方法可以调用Add来内联?

请注意,该库需要.NET 4.0,这意味着无法使用积极的内联。

3 个答案:

答案 0 :(得分:1)

尝试启用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;
}

它提示编译器(或JIT'er)内联该函数。

注意:AggressiveInlining是.NET 4.5中的新功能。

答案 1 :(得分:1)

我会猜测并说你可能是C ++程序员。您在此处使用的refout关键字完全是流畅的。你可以删除它们,你的代码就可以正常运行了。

事实上,我没有看到任何功能,简单版本不会提供:

public Vector2 Add(Vector2 other)
{
    return new Vector2() { X = this.X + other.X, Y = this.Y + other.Y };
}

即使您希望保持API静态,也请删除所有refout个关键字,但这些关键字对您的代码不是必需的。

编辑:

我刚刚注意到你正在使用结构(值类型),所以我之前写的不正确,我删除了我的帖子。然后我又考虑了一下,我想知道:要么你想让它成为一种价值类型,因为它太小,以至于复制速度不够快,或者你没有。您将其设为值类型,现在您尝试通过稍微滥用refout关键字来解决您自己的决定。

public static void Add(ref Vector2 a, ref Vector2 b, out Vector2 result)

这可能很容易

public static void Add(Vector2 a, Vector2 b, ref Vector2 result)

如果您希望按参考传递,为什么不首先将其作为参考类型?如果你这样做,那么我上面写的所有内容仍然有效:)

答案 2 :(得分:0)

我只是咬紧牙关并手动内联代码,其中内联不会自动完成,并且调用指令会很重要,例如在像Add1这样的函数中。在这种情况下,自动化单元测试可以处理问题检测,因此维护噩梦不是一个大问题。

感谢SO的帮助。