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

本文关键字:排序 指令 新排序 编译器 | 更新日期: 2023-09-27 18:34:40

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

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

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

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

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

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

但这并非一蹴而就。 优化程序只有在知道重新排序不会产生任何不良副作用时才会考虑这样的优化。 为了让它知道,它首先必须内联一个方法或属性 getter 调用。 这永远不会发生在DateTime.Now,它需要操作系统调用,而这些调用永远无法内联。 因此,您很难保证在var now = DateTime.Now;之前或之后不会移动任何语句

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

害怕

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