【发布时间】:2014-02-18 20:51:23
【问题描述】:
我经常遇到这种情况。乍一看,我想,“那是糟糕的编码;我正在执行一个方法两次,必然会得到相同的结果。”但是想到这里,我不得不怀疑编译器是否和我一样聪明,并且可以得出相同的结论。
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 方法的内容?说它看起来像这样(有点类似于我现在的真实代码):
public OtherThing GetOtherThing() {
if (this.Category == null) return null;
return this.Category.OtherThings.FirstOrDefault(t => t.Text == this.Text);
}
除非对这些对象来自的任何存储进行非常糟糕的异步更改处理,否则如果连续运行两次,肯定会返回相同的内容。但是如果它看起来像这样(为了论证而荒谬的例子)怎么办:
public OtherThing GetOtherThing() {
return new OtherThing {
Id = new Random().Next(100)
};
}
连续运行两次将导致创建两个不同的对象,很可能具有不同的 ID。在这些情况下编译器会做什么?它是否像我在第一个列表中展示的那样低效?
自己做一些工作
我运行了与第一个代码清单非常相似的东西,并在GetOtherThing 实例方法中放置了一个断点。断点被击中一次。所以,看起来结果确实被缓存了。在第二种情况下会发生什么,该方法每次可能返回不同的东西?编译器会错误地优化吗?我发现的结果有什么警告吗?
编辑
那个结论是无效的。请参阅@usr 的答案下的 cmets。
【问题讨论】:
-
我相信方法结果存储在本地调用堆栈中并被引用,而不是在启用编译器优化时两次调用该方法。
-
@DavidHaney 你为什么这么相信?您对堆栈的讨论描述了如果执行优化,IL 可能会是什么样子,但不是这种优化是否有效和可能。
-
正如您所说,方法结果在调用之间可能会有所不同,因此编译器无法将其优化为仅一次调用。但是,当您实际运行代码时,很有可能会由 JIT 执行优化。
-
@DavidHaney 这听起来像是一个不太可能且影响行为的优化。你能提供一个引用吗?
-
@MarcinJuraszek 为什么 JIT 可能会执行此优化吗?虽然 JIT 可以轻松内联所涉及的方法,但我看不出有什么方法可以排除对
x.Category.OtherThings的并发修改,这会影响结果。
标签: c# .net optimization compiler-construction