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

pde*_*eva 39 java performance multithreading volatile

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

volatile变量永远被放置在CPU缓存或者它总是从主存储器中获取?

Ale*_*øld 17

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

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

Marc Booker的测试

感谢Marc Booker运行这些测试.

  • @pdeva,不,图表清楚地显示无竞争的易失性读取比非易失性读取慢.它的眼球,看起来慢了2-3倍. (6认同)
  • 似乎只有在竞争 volatile 时才会出现这种情况。 (2认同)

Tim*_*der 13

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

来自Doug Lea的JMM食谱,见底部附近的建筑表.

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

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

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

  • 没有什么是"无操作",除非它可以从你的程序中完全消除.易失性读取是便宜的,但不是免费的,并且以普通读取不具有的方式抑制优化(例如,易失性读取不能从循环中提升,或从寄存器读取).在这种情况下,"no-op"指的是缺少相应的fence指令,但是read本身具有语义并且具有成本. (7认同)
  • 怎么会这样?那么多核处理器呢?(没有阅读你发布的整个链接) (2认同)
  • 正确.然而,那些是优化语义,它们位于volatile明确强加的范围之外.这个问题的主要答案是"它依赖于架构".目前评论是真的.很多人都害怕动荡,这有些愚蠢.虽然我也不会建议在所有事情上使用volatile. (2认同)

dru*_*dru -2

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

通常,“易失性”关键字仅用于操作系统中的设备驱动程序。

  • 这可能是 volatile 在 C 中的含义,但不是 Java 中的含义。在 Java 中,波动性是指一个执行读取的线程是否会“看到”另一个线程所做的更改。这不仅仅是该值是否可以位于 CPU 寄存器中的问题。volatile 关键字还可以防止 JVM 对使用该变量的代码执行何种类型的重新排序。 (6认同)