【问题标题】:Does C# JIT compiler optimize null-check?C# JIT 编译器是否优化空值检查?
【发布时间】:2017-04-21 14:32:40
【问题描述】:

网上有很多文章列出了 C# JIT 在执行一段代码之前所做的优化。比如MSDN上的this post谈:

常量折叠、常量和复制传播、公共子表达式 消除,循环不变量的代码运动,死存储和死代码 消除、寄存器分配、方法内联、循环展开 (带有小主体的小环)。

我的问题是:JIT 编译器是否也处理无用的空检查?我找不到任何处理这个问题的来源。

在我读到的同一篇文章中:

因为 C# 语言规范确保对 null 的任何调用 对象引用抛出 NullReferenceException,每个调用站点都必须 确保实例不为空。这是通过取消引用 实例参考;如果它为空,它将产生一个错误,即 变成了这个异常。

那么,假设我这样写一段代码:

if (person != null)
{
    Console.WriteLine(person.Name);
}

person.Name 再次调用了第二个空检查,这显然是无用的,编译器可以将其删除。还是不行?

我读到在 Java 中这已经完成(许多 here、here 和 here 之间的一些来源)。

如果 C# 也这样做,您是否知道一些相关的资料或文档?

如果 C# 不这样做,你知道为什么吗?在 Java JIT 没有遇到的 .NET 环境中实现这样的功能是否存在内在困难?

【问题讨论】:

  • 仅作为正常优化的副作用,如死代码消除。这应该很少见。一般来说,抖动优化器没有执行空指针检查的代码的特殊知识,并将指针视为易失性。在任何带有垃圾收集器的 VM 中都非常重要,该垃圾收集器在压缩堆时会任意修改指针。值得注意的是,C# 编译器本身可以消除检查,当您使用 elvis 运算符 (?.) 时会这样做。

标签: c# .net optimization jit null-check


【解决方案1】:

Null 检查编译器完成的优化(Roslyn,不是 Jitter)in several cases,当它完全保存时。

例如,当您使用?(猫王运算符)时。

IL_0006: stloc.0              // Pop a value from stack into local variable 0
IL_0007: ldloc.0              // Load local variable 0 onto stack
IL_0008: brtrue.s IL_000c     // Branch to target if value is non-zero (true), short form
IL_000a: br.s IL_0013         // Branch to target, short form
IL_000c: ldloc.0              // Load local variable 0 onto stack
IL_000d: call instance void Foo::Bar() // Call method indicated on the stack with arguments

另一个例子是这样的代码:

new Bar().Foo();

编译器会为此 call 指令而不是 callvirt 生成(这意味着,不会对 this 进行空检查)

在其他情况下,您无法确定 this 不会为空。

不管怎样,null检查真的很快。

【讨论】:

    猜你喜欢
    • 2016-09-17
    • 2015-09-03
    • 2016-05-25
    • 2014-04-18
    • 2015-09-08
    • 2010-09-21
    • 2015-12-25
    • 1970-01-01
    • 2015-06-16
    相关资源
    最近更新 更多