拳击是否会导致性能问题?我怎么能防止拳击?

时间:2015-06-18 08:27:31

标签: c# .net performance boxing

拳击会导致我的代码出现性能问题吗?我怎样才能防止拳击?

void Main()
{
    AreEqual<int>(12, 13);
}

public static bool AreEqual<T>(T a, T b)
{
    return a.Equals(b);
}

IL:

IL_0000:  nop         
IL_0001:  ldc.i4.s    0C 
IL_0003:  ldc.i4.s    0D 
IL_0005:  call        UserQuery.AreEqual
IL_000A:  pop         
IL_000B:  ret         

AreEqual:
IL_0000:  nop         
IL_0001:  ldarga.s    00 
IL_0003:  ldarg.1     
IL_0004:  box         01 00 00 1B 
IL_0009:  constrained. 01 00 00 1B 
IL_000F:  callvirt    System.Object.Equals
IL_0014:  stloc.0     // CS$1$0000
IL_0015:  br.s        IL_0017
IL_0017:  ldloc.0     // CS$1$0000
IL_0018:  ret       

4 个答案:

答案 0 :(得分:4)

  

Boxing会导致我的代码出现性能问题吗?

只有你能回答这个问题。它是您的代码,是您的代码的性能。对我来说,这不是问题,对你来说,可能是。

  

我该如何预防拳击?

强制编译器选择正确的重载。您的初始代码调用object.Equals(object)(在Int32.Equals(object)中重写),因为这是唯一适用于每个无约束T的方法,这需要装箱int参数你过关了。

Int32.Equals(Int32) implements IEquatable<T>.Equals(T),所以把它作为约束:

private static bool AreEqualEquatable<T>(T a, T b)
    where T : IEquatable<T>
{
    return a.Equals(b);
}

编译到这个IL:

IL_0000: nop
IL_0001: ldarga.s a
IL_0003: ldarg.1
IL_0004: constrained. !!T
IL_000a: callvirt instance bool class [mscorlib]System.IEquatable`1<!!T>::Equals(!0)
IL_000f: stloc.0
IL_0010: br.s IL_0012

IL_0012: ldloc.0
IL_0013: ret

因为编译器会尝试找到最专业的重载,在本例中为IEquatable<T>.Equals(T)

答案 1 :(得分:1)

有时确实如此,通常情况并非如此。

您无法以这种方式真正预测性能问题。你有一个特定的程序太慢,你怀疑问题是拳击?

答案 2 :(得分:1)

请参阅此hidden feature,可以将其用作解决方法。但是 - 一如既往 - 确保这不是过早的优化。

示例代码:

   static void foo<T>(ref T value) {
        //This is the ONLY way to treat value as bool, without boxing/unboxing objects
        if(value is bool) {
            TypedReference reference = __makeref(value); //get reference
            bool boolVal = __refvalue(reference,bool);   //get primitive value
            __refvalue(reference, bool) = !boolVal;      //set primitive value
        } else {
            value = default(T);
        }
    }

我从this thread

部分获取了代码

答案 3 :(得分:1)

计算机执行任务的任何其他操作都会降低性能,降低性能是一回事,性能问题是另一回事。如果您正在尝试实现其中包含数百万个数据的高速数据结构,则可能会导致性能问题,但如果您只是编写一个在数据库和网络上执行许多查询的信息系统,那么我认为拳击不会导致性能问题对你而言。

您应始终对应用程序进行概要分析,并了解确切损害性能的部分在哪里。

因此,如果您尝试比较两个整数,则操作为(a == b)。如果为此编写函数,则需要进行额外的函数调用。如果你添加其他通用方法,匿名类型,装箱,拆箱......那么所有这些额外的操作都会降低性能。

如上所述here你可以防止整数装箱。

这是基准

private static void Main(string[] args)
{
    var sw1 = new Stopwatch();
    bool b1 = true;
    sw1.Start();
    for (int i = 0; i < 10 * 1000 * 1000; i++)
    {
        b1 = b1 ^ AreEqual(i, i + 1);
    }
    sw1.Stop();
    Console.WriteLine(b1);
    Console.WriteLine(sw1.ElapsedTicks);


    var sw2 = new Stopwatch();
    bool b2 = true;
    sw2.Start();
    for (int i = 0; i < 10 * 1000 * 1000; i++)
    {
        b2 = b2 ^ AreEqualEx(i, i + 1);
    }
    sw2.Stop();
    Console.WriteLine(b2);
    Console.WriteLine(sw2.ElapsedTicks);
}

public static bool AreEqual<T>(T a, T b)
{
    return a.Equals(b);
}

public static bool AreEqualEx<T>(T a, T b) where T:IEquatable<T>
{
    return a.Equals(b);
}

,结果是

  


  254379个
  真
  35514