易失性写入的成本

问题描述 投票:2回答:1

我已经研究了x86硬件中Java易失性写入的成本。我打算在共享内存位置上使用Unsafe的putLongVolatile方法。查看实现,将putLongVolatile get转换为Link中的Unsafe_SetLongVolatile,然后转换为AtomicWrite,然后转换为围栅Link

简而言之,每个易失性写入都将转换为原子写入,然后再加上完整的篱笆(x86中的mfence或锁定的add指令)。

问题:

1)为什么x86需要fence()?由于商店-商店的排序,简单的编译器障碍还不够吗?完整的围栏似乎非常昂贵。

2)是不安全的putLong而不是putLongVolatile更好的选择吗?在多线程情况下会很好吗?

java multithreading volatile
1个回答
2
投票

问题1:

没有完整的屏障,您将没有JMM所需的顺序一致性。

因此X86提供了TSO。因此,您可以免费获得以下障碍[LoadLoad] [LoadStore] [StoreStore]。唯一缺少的是[StoreLoad]。

负载具有获取语义

r1=X
[LoadLoad]
[LoadStore]

商店具有发布语义

[LoadStore]
[StoreStore]
Y=r2

如果要先存储然后进行加载,则最终会这样:

[LoadStore]
[StoreStore]
Y=r2
r1=X
[LoadLoad]
[LoadStore]

问题在于,加载和存储仍然可以重新排序,因此顺序不一致。这对于Java内存模型是必需的。防止这种情况的唯一方法是使用[StoreLoad]。而且最合乎逻辑的地方是将其添加到写入中,因为通常读取比写入更频繁。

[这可以通过MFENCElock addl %(RSP),0]完成>

问题2的答案:

putLong的问题在于,不仅CPU可以对指令进行重新排序,而且编译器还可以以导致导致指令重新排序的方式更改代码。

示例:如果要在循环中执行putLong,则编译器可以决定将写操作从循环中拉出,并且该值对于其他线程将不可见。如果您希望具有低开销的单一编写器性能计数器,则可能需要查看putLongRelease / putLongOrdered(oldname)。这将阻止编译器执行上述操作。您可以免费获得X86上的发布语义。

但是很难为您的第二个问题提供适合的解决方案,因为这取决于您的目标是什么。

© www.soinside.com 2019 - 2024. All rights reserved.