拳击会导致性能问题吗?如何防止拳击

本文关键字:拳击 何防止 性能 问题 | 更新日期: 2023-09-27 17:56:49

装箱会导致我的代码出现性能问题吗?如何防止拳击?

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       

拳击会导致性能问题吗?如何防止拳击

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

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

如何防止拳击?

通过强制编译器选择正确的重载。您的初始代码调用 object.Equals(object)(在 Int32.Equals(object) 中被覆盖),因为这是唯一适用于每个不受约束T的方法,这需要对您传递的int参数进行装箱。

Int32.Equals(Int32)实现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) .

有时有,通常不会。

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

请参阅此隐藏功能,该功能可用作解决方法。但是 - 一如既往 - 确保这不是过早的优化。

示例代码:

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

我从这个线程中获取了部分代码

计算机为

执行任务所做的任何其他操作都会降低性能,性能下降是一回事,性能问题是另一回事。如果您尝试实现包含数百万数据的高速数据结构,可能会导致性能问题,但如果您只是编写一个在数据库和网络上执行许多查询的信息系统,那么我认为装箱不会给您带来性能问题。

您应该始终分析您的应用程序,并查看究竟在哪里损害性能。

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

如此处所述,您可以防止整数装箱。

这是一个基准

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