易失性变量“读取”和正常读取一样快吗?

时间:2009-07-07 04:08:11

标签: java performance multithreading volatile

我知道写入volatile变量会将其从所有cpus的内存中刷新,但我想知道对volatile变量的读取是否与正常读取一样快?

volatile变量是否可以放在cpu缓存中,还是总是从主内存中提取?

5 个答案:

答案 0 :(得分:17)

你应该看看这篇文章:http://brooker.co.za/blog/2012/09/10/volatile.html。博客文章认为,与x86上的非易失性读取相比,volatile读取(也适用于x86)要慢得多。

  • 测试1是对非易失性变量的并行读写。那里 没有可见性机制,读取的结果是 可能陈旧。
  • 测试2是对volatile变量的并行读写。这并没有具体解决OP的问题。但值得注意的是,争用的挥发性可能非常缓慢。
  • 测试3是对紧密循环中的易失性的读取。证明了它表示易失性的语义表明该值可以随着每次循环迭代而改变。因此,JVM无法优化读取并将其提升出循环。在测试1中,很可能读取并存储了一次值,因此没有实际的“读取”发生。

Marc Booker's tests

感谢Marc Booker运行这些测试。

答案 1 :(得分:13)

答案有点依赖于架构。在x86上,没有专门针对易失性读取的额外开销,尽管会对其他优化产生影响。

JMM cookbook from Doug Lea, see architecture table near the bottom.

澄清:读取本身没有任何额外的开销。内存屏障用于确保正确的排序。 JSR-133对“LoadLoad,LoadStore,StoreLoad和StoreStore”四个障碍进行了分类。根据架构,这些障碍中的一些对应于“无操作”,意味着不采取任何行动,其他障碍需要围栏。没有与Load本身相关的隐式成本,但是如果有栅栏就可能会产生一个成本。对于x86,只有StoreLoad屏障会产生栅栏。

正如博客文章中所指出的那样,变量是易失性的这一事实意味着对变量的性质有一些假设,这些变量无法再进行,并且某些编译器优化不会应用于volatile。

挥发性不是应该被明智地使用的东西,但也不应该担心。在很多情况下,挥发性就足以代替更多的重型锁定。

答案 2 :(得分:1)

它取决于架构。 volatile的作用是告诉编译器不要优化该变量。它迫使大多数操作将变量的状态视为未知状态。因为它是易失性的,所以可以通过另一个线程或其他一些硬件操作来改变它。因此,读取将需要重新读取变量,操作将是读取 - 修改 - 写入类型。

此类变量用于设备驱动程序,也用于与内存互斥锁/信号量同步。

答案 3 :(得分:0)

易失性读取速度不是很快,特别是在多核CPU上(但也只是单核)。 执行内核必须从实际内存地址中获取,以确保它获得当前值 - 该变量确实无法缓存。

与此处的另一个答案相反,volatile变量不仅仅用于设备驱动程序!它们有时对编写高性能多线程代码至关重要!

答案 4 :(得分:-3)

volatile表示编译器无法通过将其值放入CPU寄存器来优化变量。必须从主存储器访问它。但是,它可以放在CPU缓存中。缓存将保证系统中任何其他CPU /核心之间的一致性。如果内存映射到IO,那么事情就会复杂一些。如果它是这样设计的,硬件将阻止缓存该地址空间,并且对该存储器的所有访问都将进入硬件。如果没有这样的设计,硬件设计人员可能需要额外的CPU指令以确保读/写通过缓存等。

通常,'volatile'关键字仅用于操作系统中的设备驱动程序。