简体   繁体   English

volatile 与最终字段性能

[英]volatile vs final fields performance

What is preferable for performance?什么更适合性能? Assume no to little contention假设没有或几乎没有争用

  1. mutable class with volatile fields and updating them one by one具有可变字段并一一更新的可变类
  2. immutable class with final fields, an update cycle avoids the multi-field update and instead re-creates the class once具有最终字段的不可变类,更新周期避免了多字段更新,而是重新创建类一次

Volatiles require memory barriers on every write, I suppose the same is true for final fields?波动性在每次写入时都需要内存屏障,我想最终字段也是如此? Meaning a single memory barrier upon object construction?意思是对象构造时的单个内存屏障?

Update for clarification:更新澄清:

I feel the question is valuable on its own and answerable generically, taking into account the java memory model and current gen hardware.考虑到 java 内存模型和当前的硬件,我觉得这个问题本身很有价值,并且一般可以回答。 If you must assume specifics:如果您必须假设具体情况:

  • the object is of course accessed from multiple threads, otherwise this exercise would be pointless该对象当然是从多个线程访问的,否则这个练习将毫无意义
  • a single object is long-lived, as in multiple hours单个对象是长期存在的,就像在几个小时内一样
  • there are hundreds to thousands of those objects, with hundreds to thousands of update events per second这些对象有成百上千个,每秒有成百上千个更新事件

final is a hint to the compiler that the field value cannot change. final提示编译器字段值不能更改。 Any write attempts are caught at compile time.在编译时捕获任何写入尝试。 Reading a final value does not use a memory barrier.读取final值不使用内存屏障。 You cannot write to a final variable, so a memory barrier is meaningless.您不能写入final变量,因此内存屏障是没有意义的。

Using the hint, the compiler (or the JIT) may replace a memory reference to a final value with a constant.使用提示,编译器(或 JIT)可以用常量替换对最终值的内存引用。 So in terms of performance, final does not introduce any additional overhead.所以在性能方面, final不会引入任何额外的开销。

For volatile fields, keeping the volatile field's updated value consistent across all threads that access it requires coordination at runtime. 对于易失性字段,要使易失性字段的更新值在所有访问它的线程之间保持一致,需要在运行时进行协调。 Using volatile introduces an invariant that has to be enforced. 使用volatile引入了必须执行的不变式。

For immutable objects the cost is making copies of the objects to reflect changes. 对于不可变的对象,成本是复制对象以反映更改。 There are ways to limit the amount of copying, such as use of persistent data structures . 存在限制复制量的方法,例如使用持久性数据结构 Also when you have immutable objects you can have value-based comparisons, and techniques like memoization that return already-cached instances. 同样,当您拥有不可变的对象时,您可以进行基于值的比较,并使用诸如备忘录之类的技术来返回已缓存的实例。

So there are possible detriments to performance with each approach, depending on what you're doing. 因此,每种方法都可能会损害性能,具体取决于您正在执行的操作。

The main thing that is better about the immutable approach is it is cleaner and easier to reason about. 关于不变方法更好的主要事情是它更干净,更容易推理。 With mutable objects the whole idea of identity gets murky : 对于可变对象,整个身份概念变得晦暗

An imperative program manipulates its world (eg memory) directly. 命令式程序直接操纵其世界(例如内存)。 It is founded on a now-unsustainable single-threaded premise - that the world is stopped while you look at or change it. 它建立在一个现在不可持续的单线程前提下-当您查看或更改世界时,世界就停止了。 You say "do this" and it happens, "change that" and it changes. 您说“做到这一点”,它就会发生,“改变这一点”,它就会发生变化。 Imperative programming languages are oriented around saying do this/do that, and changing memory locations. 命令式编程语言主要围绕执行/执行操作以及更改内存位置。

This was never a great idea, even before multithreading. 即使在多线程之前,这也不是一个好主意。 Add concurrency and you have a real problem, because "the world is stopped" premise is simply no longer true, and restoring that illusion is extremely difficult and error-prone. 添加并发,您就会遇到一个真正的问题,因为“世界已停止”的前提已经不再成立,并且恢复幻觉非常困难且容易出错。 Multiple participants, each of which acts as though they were omnipotent, must somehow avoid destroying the presumptions and effects of the others. 多个参与者(每个参与者的行为似乎无所不能)必须以某种方式避免破坏其他参与者的假设和影响。 This requires mutexes and locks, to cordon off areas for each participant to manipulate, and a lot of overhead to propagate changes to shared memory so they are seen by other cores. 这需要互斥锁和锁,以封锁每个参与者要操纵的区域,并需要大量开销才能将更改传播到共享内存,以便其他内核可以看到它们。 It doesn't work very well. 效果不是很好。

The mutable state approach makes your code vulnerable to race conditions and deadlocks. 可变状态方法使您的代码容易受到竞争条件和死锁的影响。 Reducing the amount of mutable state reduces your exposure to these bugs. 减少可变状态的数量可以减少您遭受这些错误的可能性。

If the garbage-collector flushes every thread's cache between the last time an old object is accessed and the space being made available for a new object, and if no cache line contains data from multiple objects, then it would on most platforms be naturally impossible for a newly-constructed object to get loaded into any thread's cache before a reference to that object is stored in a location which is accessible to that thread, even in the absence of any read barriers (beyond the aforementioned once-per-GC-cycle system-wide barrier).如果垃圾收集器在上次访问旧对象和为新对象提供可用空间之间刷新每个线程的缓存,并且如果没有缓存行包含来自多个对象的数据,那么在大多数平台上自然不可能一个新构造的对象在对该对象的引用存储在该线程可访问的位置之前加载到任何线程的缓存中,即使没有任何读取障碍(超出前面提到的每 GC 循环一次系统) -宽障碍)。 Further, if a compiler can tell that writes occur to multiple fields of an object will occur without any intervening writes to any other object whose reference may have been exposed, it could omit write barriers for all but the last.此外,如果编译器可以判断一个对象的多个字段将发生写入,而不会对任何其他可能已公开其引用的对象进行任何干预写入,则它可以省略除最后一个之外的所有对象的写入屏障。

The only time using final fields would be more expensive than volatile would be if it necessitated the creation of more objects to handle changes which could have been done "in place" with volatile fields.唯一一次使用final字段会比volatile更昂贵,如果它需要创建更多对象来处理可以使用volatile字段“就地”完成的更改。 Since many factors can affect object-creation cost, the only reliable way to judge which approach is more efficient under a particular set of circumstances on a particular system would often benchmark both.由于许多因素会影响对象创建成本,因此在特定系统的特定环境下判断哪种方法更有效的唯一可靠方法通常是对两者进行基准测试。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM