拳击会导致性能问题吗?我怎样才能防止拳击?
Does boxing cause performance issues? How can I prevent 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[=11=]00
IL_0015: br.s IL_0017
IL_0017: ldloc.0 // CS[=11=]00
IL_0018: ret
有时会,通常不会。
您无法通过这种方式真正预测性能问题。您是否有一个太慢的特定程序并且您怀疑问题出在拳击上?
请参阅此 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
中提取了部分代码
Does Boxing cause performance issues in my code?
只有你能回答。这是你的代码,这是你的代码的性能。对我来说,这不是问题,对你来说,可能是。
How can I prevent 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)
。
计算机为执行任务所做的任何额外操作都会降低性能,性能下降是一回事,性能问题是另一回事。如果你试图实现一个包含数百万数据的高速数据结构,它可能会导致性能问题,但如果你只是编写一个在数据库和网络上执行许多查询的信息系统,那么我认为装箱不会导致性能问题给你。
您应该始终对您的应用程序进行概要分析,看看哪些部分会影响性能。
因此,如果您尝试比较两个整数,则操作是 (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);
}
结果是
True
254379
True
35514
装箱会导致我的代码出现性能问题吗?如何防止拳击?
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[=11=]00
IL_0015: br.s IL_0017
IL_0017: ldloc.0 // CS[=11=]00
IL_0018: ret
有时会,通常不会。
您无法通过这种方式真正预测性能问题。您是否有一个太慢的特定程序并且您怀疑问题出在拳击上?
请参阅此 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
中提取了部分代码Does Boxing cause performance issues in my code?
只有你能回答。这是你的代码,这是你的代码的性能。对我来说,这不是问题,对你来说,可能是。
How can I prevent 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)
。
计算机为执行任务所做的任何额外操作都会降低性能,性能下降是一回事,性能问题是另一回事。如果你试图实现一个包含数百万数据的高速数据结构,它可能会导致性能问题,但如果你只是编写一个在数据库和网络上执行许多查询的信息系统,那么我认为装箱不会导致性能问题给你。
您应该始终对您的应用程序进行概要分析,看看哪些部分会影响性能。
因此,如果您尝试比较两个整数,则操作是 (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);
}
结果是
True
254379
True
35514