编译器指令重新排序的隐含限制?

时间:2016-02-15 09:44:42

标签: c#

最近了解锁机制和并发性时,我遇到了编译器指令重新排序。从那时起,即使没有对字段的并发访问,我对我编写的代码的正确性更加怀疑。我刚遇到一段这样的代码:

var now = DateTime.Now;
var newValue = CalculateCachedValue(); 
cachedValue = newValue;
lastUpdate = now;

在为lastUpdate = now分配新值之前,是否可能执行cachedValue?这意味着如果运行此代码的线程被取消,我将记录未保存的更新。据我所知,现在我必须假设情况就是如此,我需要一个记忆障碍。

但是第一个语句甚至可能在第二个语句之后执行吗?这意味着now是计算之后的时间,而不是之前的时间。我想这并非如此,因为涉及方法调用。但是,没有其他明确的依赖可以阻止重新排序。方法调用/属性访问是否隐含障碍?对于我应该注意的指令重新排序是否存在其他隐式约束?

1 个答案:

答案 0 :(得分:5)

.NET抖动可以重新排序指令,是的。不变代码运动和常见的子表达式消除是重要的优化,可以使代码更快。

但这不仅仅发生在威利。如果知道重新排序不会产生任何不良副作用,优化器将只考虑这样的优化。为了让它知道,它首先必须内联一个方法或属性getter调用。而DateTime.Now永远不会发生这种情况,它需要一个操作系统调用,而且永远不能内联。所以你很难保证在var now = DateTime.Now;

之前或之后没有任何陈述

它实际上必须使感觉移动代码并产生可衡量的好处。重新排序赋值语句没有意义,它不会使代码更快。不变代码运动是一种优化,它应用于循环内的语句,在循环之前移动这样的语句,因此它不会被反复执行得到回报。在这个片段中没有任何风险。消除子表达也在这里无处可见。

害怕优化者引发的错误有点像害怕走出去,因为你可能被闪电击中。那个会发生。赔率非常非常低。使用.NET抖动的一个很好的保证是它每天都会被测试数百万次。