编译器如何处理 IF 语句的优化

how does compiler handle optimizations for IF statement

提问人:user22155685 提问时间:10/25/2023 最后编辑:user22155685 更新时间:10/25/2023 访问量:69

问:

我正在读一本书,描述了为什么需要它Volatile

class ThreadsSharingData {
   private Int32 m_flag = 0;
   private Int32 m_value = 0;

   // This method is executed by one thread
   public void Thread1() {
      // Note: These could execute in reverse order
      m_value = 5;
      m_flag = 1;
   }
   
   // This method is executed by another thread
   public void Thread2() {
      // Note: m_value could be read before m_flag
      if (m_flag == 1)
         Console.WriteLine(m_value);
   }
}

我明白使用会禁用编译器优化,例如重新排序,书中说:Volatile

但是,即使 Thread1 方法按程序顺序(编写方式)执行,Thread2 方法仍可能显示 0。在 Thread2 方法中编译代码时,编译器必须生成代码以将 RAM 中的 m_flag 和m_value读取到 CPU 寄存器中。RAM 可能会首先提供 m_value 的值,其中包含 0。然后可以执行 Thread1 方法,将 m_value 更改为 5,将 m_flag 更改为 1。但是 Thread2 的 CPU 寄存器没有看到 m_value 已被另一个线程更改为 5,然后可以将 m_flag 中的值从 RAM 读取到 CPU 寄存器中,并且 m_flag 的值现在变为 1,导致 Thread2 再次显示 0。

我不明白为什么“RAM可以首先提供m_value的价值”。如果不是 1,那么就不需要将值加载到 ,所以应该先由 RAM 传递,这是正确的逻辑,不是吗?m_flagm_valuem_flag

而且描述听起来像是,无论值是多少,都必须读取以防万一是'1,这听起来效率不高,因为从RAM读取值并将值保存到寄存器中可能很昂贵,那么为什么编译器会生成汇编代码,这可能会产生不必要的读取呢?或者提前进行不必要的读取有什么好处?m_flagm_valuem_flag

C# 程序集 CLR 编译器优化

评论

1赞 Jeremy Lakeman 10/25/2023
根据 learn.microsoft.com/en-us/archive/msdn-magazine/2012/december/,编译器或 cpu 可以对读取和写入进行重新排序。这就是记忆模型理论的定义方式。在实践中,在当前 CPU 上使用当前编译器/JIT,可能不会发生这些重新排序。
0赞 Jeremy Lakeman 10/25/2023
你认为你知道的关于程序如何执行的大多数都是谎言。操作系统和 CPU 假装单线程程序的内存和 CPU 行为在过去 60 年中没有改变。当 CPU 发出读取请求和收到响应时,将内存交换到磁盘、CPU 内存缓存、CPU 推测执行等可能会有很大差异。至少与古代计算机设计相比,在古代计算机设计中,CPU 会暂停直到每次读取或写入完成。
0赞 Hans Passant 10/25/2023
developer.arm.com/documentation/102376/0100/......
1赞 Peter Cordes 10/25/2023
如果它们位于不同的缓存行中,则很容易发生这种情况。分支预测预测主体应该运行,因此它在第一次加载完成之前(推测性地)运行。如果负载在缓存中未命中,但负载在缓存中命中,则读取核心可以在加载最终完成之前首先读取该值,从而允许执行分支指令以确认对该推测的预测。另请参阅 preshing.com/20120710/...ifm_flagm_valuem_flag
0赞 Peter Cordes 10/25/2023
还相关: C11 独立内存屏障 LoadLoad StoreStore LoadStore StoreLoad / 加载或存储是否可以在条件之前重新排序? / 嵌套分支和推测执行会发生什么?

答: 暂无答案