if (x == null) x = new X();
与
x = x ?? new X();
这两个中哪一个实际上更高效?一旦编译完成它们就会有效地结束(结果会x = x;
成为NO-OP)?
答案 0 :(得分:30)
查看中间语言代码会有所不同:
.method private hidebysig instance void Method1() cil managed
{
.maxstack 8
L_0000: ldarg.0
L_0001: ldfld class X Program::x
L_0006: brtrue.s L_0013
L_0008: ldarg.0
L_0009: newobj instance void X::.ctor()
L_000e: stfld class X Program::x
L_0013: ret
}
.method private hidebysig instance void Method2() cil managed
{
.maxstack 8
L_0000: ldarg.0
L_0001: ldarg.0
L_0002: ldfld class X Program::x
L_0007: dup
L_0008: brtrue.s L_0010
L_000a: pop
L_000b: newobj instance void X::.ctor()
L_0010: stfld class X Program::x
L_0015: ret
}
这是我编译的代码:
void Method1()
{
if (x == null) x = new X();
}
void Method2()
{
x = x ?? new X();
}
为了确保哪个更快,你应该两个时间。
Method Initial condition Iterations per second --------------------------------------------------- NullCheck x is null 33 million Coalesce x is null 33 million NullCheck x is not null 40 million Coalesce x is not null 33 million
结论:
x不为null时的差异看起来可能是由于空合并运算符将x的值分配回x(stfld
在IL中),而空检查跳过{{1}当x不为空时指令。
两者都非常快,你必须有一个非常紧密的循环来注意差异。如果您已使用数据分析代码,则应该只进行这些性能优化。不同的情况,不同版本的.NET,不同的编译器等可能会产生不同的结果。
如果有人想知道我如何得到这些结果或重现它们,这里是我使用的代码:
stfld
免责声明:没有基准是完美的,而且这个bechmark远远不是完美的,主要是为了保持简单。我做了很多不同的测试,例如使用不同顺序的方法,有或没有“预热”,在不同的时间长度等,我每次得到大致相同的结果。我没有任何证据可以证明这种或那种方式,所以任何有利于一种方法或另一种方法的偏见都是偶然的。
答案 1 :(得分:16)
我不担心这种过早的优化。我确信C#编译器的设计人员足够聪明,可以为你做这件事。
答案 2 :(得分:6)
没有 interresting 性能差异 。最重要的是if (x == null) x = new X();
更具可读性。
回答您的原始问题:智能优化编译器将以与x = x ?? new X()
相同的方式编译if (x == null) x = new X();
。
因此,在编译器上留下微优化,并专注于代码的可读性和清晰度。
更新:要详细了解优化做法,请阅读this article on wikipedia,然后就问题的性质,Google “premature optimization is the root of all evil”。
答案 3 :(得分:4)
正如其他人所提到的,两者之间的表现差异并没有那么不同,但两者之间存在重要差异,尽管从你的例子中并不那么明显。
if (x == null) x = new X();
创建了一个潜在的竞争条件,其中x可以在检查null和创建之间由另一个线程新建,从而丢失一个对象。
x = x ?? new X();
首先创建变量的副本,因此没有潜在的竞争条件。
在引用对象的情况下,这是一个更大的问题。例如:
if (x != null) x.DoSomething(); // this can cause a null reference exception
// if another thread nulls x between the check
// and the call to DoSomething()
(x = x ?? new X()).DoSomething() // this should be fine.