Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C#编译器会优化这段代码吗?_C#_.net_Optimization_Compiler Construction - Fatal编程技术网

C#编译器会优化这段代码吗?

C#编译器会优化这段代码吗?,c#,.net,optimization,compiler-construction,C#,.net,Optimization,Compiler Construction,我经常遇到这种情况。乍一看,我认为,“这是糟糕的编码;我执行了一个方法两次,必然会得到相同的结果。”但在考虑到这一点后,我不得不怀疑编译器是否和我一样聪明,是否能得出相同的结论 var newList = oldList.Select(x => new Thing { FullName = String.Format("{0} {1}", x.FirstName, x.LastName), OtherThingId = x.GetOtherThing() != null :

我经常遇到这种情况。乍一看,我认为,“这是糟糕的编码;我执行了一个方法两次,必然会得到相同的结果。”但在考虑到这一点后,我不得不怀疑编译器是否和我一样聪明,是否能得出相同的结论

var newList = oldList.Select(x => new Thing {
    FullName = String.Format("{0} {1}", x.FirstName, x.LastName),
    OtherThingId = x.GetOtherThing() != null : x.GetOtherThing().Id : 0 // Might call x.GetOtherThing() twice?
});
编译器的行为是否取决于
GetOtherThing
方法的内容?假设它看起来像这样(有点像我现在的真实代码):

除非对这些对象来自的任何存储进行处理非常糟糕的异步更改,否则如果连续运行两次,肯定会返回相同的结果。但是,如果它看起来像这样(为了论证,这是一个毫无意义的例子):

连续运行两次将导致创建两个不同的对象,很可能具有不同的ID。在这些情况下,编译器会做什么?它是否像我在第一个列表中显示的那样效率低下

我自己做一些工作 我运行了与第一个代码清单非常相似的东西,并在
GetOtherThing
实例方法中放置了一个断点。断点被击中一次。因此,看起来结果确实被缓存了。在第二种情况下会发生什么,方法每次可能返回不同的结果?编译器是否会错误地优化?我发现的结果有什么警告吗

编辑


这个结论是无效的。请参阅@usr的答案下的注释。

编译器只能在无法区分差异的情况下应用优化。在您的“随机”示例中,您可以清楚地分辨出差异。它不能以这种方式“优化”。这将违反C#规范。事实上,该规范并没有太多关于优化的内容。它只是说你应该观察程序做什么。在这种情况下,它指定应绘制两个随机数

在第一个示例中,可以应用此优化。这在实践中永远不会发生。以下是一些让它变得困难的事情:

  • 查询操作的数据可以通过您的虚拟函数调用进行更改,或者您的lambda(
    t=>t.Text==this.Text
    )可以更改列表。非常阴险
  • 它可以被另一个线程更改。我不确定.NET内存模型对此有何看法
  • 它可以通过反射来改变
  • 必须证明计算总是返回相同的值。你如何证明这一点?您需要分析所有可能运行的代码。包括虚拟调用和依赖于数据的控制流
所有这些都必须跨非内联方法和跨程序集工作

C#编译器无法执行此操作,因为它无法查看mscorlib。补丁发布可能会随时更改mscorlib


JIT是一种糟糕的JIT(唉),它针对编译速度(唉)进行了优化。它不能做到这一点。如果你怀疑当前JIT是否会做一些高级的优化,那么肯定不会。

< P>这里有两个编译器:C编译器将C语言转换成IL,IL编译器将IL转换成机器代码,称为抖动,因为它恰巧发生。

微软C#编译器当然不会进行这样的优化。方法调用作为方法调用生成,故事结束

如果无法检测到抖动,则允许抖动执行您描述的优化。例如,假设您有:

y = M() != 0 ? M() : N()

允许抖动将此程序转换为:

y = 1 != 0 ? 1 : N()
或者就这件事而言

y = 1;
抖动是否这样做是一个实现细节;如果您愿意,您必须询问抖动专家它是否真的执行了此优化

同样,如果你有

static int m;
static int M() { return m; }
然后抖动可以将其优化为

y = m != 0 ? m : N()
甚至进入:

int q = m;
y = q != 0 ? q : N();
因为允许抖动将一行中的两个字段读取(无中间写入)转换为单个字段读取(前提是字段不易失性)。同样,它是否这样做是一个实施细节;询问抖动开发人员

但是,在后一个示例中,抖动不能消除第二个调用,因为它有副作用

我运行了与第一个代码清单非常相似的东西,并在GetOtherThing实例方法中设置了一个断点。断点被击中一次


这是极不可能的。几乎所有的优化都在调试时关闭,这正是为了更容易调试。当你排除不可能的因素时,最有可能的解释是原来的海报错了

我相信,当启用编译器优化时,方法结果存储在本地调用堆栈中并被引用,而不是两次调用该方法。@DavidHaney你为什么相信这一点?您谈到的堆栈描述了如果执行了优化,IL可能会是什么样子,但没有描述此优化是否有效和可能。正如您所说,调用之间的方法结果可能不同,因为编译器无法将其优化为一次调用。然而,当您实际运行代码时,很可能会由JIT执行优化。@DavidHaney这听起来不太可能,而且会影响优化。你能提供一个引证吗?@MarcinJuraszek为什么JIT可能会执行这种优化?虽然JIT可以很容易地内联所涉及的方法,但我看不到一种方法可以排除并发修改
x.Category.OtherThings
,这会影响结果。这听起来是一个非常好的答案,那么您如何解释我的断点只被命中一次的事实呢?似乎优化确实发生了。可能是您转到了
:0
分支,或者您犯了一些其他错误。将一个
控制台.WriteLine
调用放入所有3个位置(
Write()?Write():Write()
对于某些位置
static int m;
static int M() { return m; }
y = m != 0 ? m : N()
int q = m;
y = q != 0 ? q : N();