简体   繁体   中英

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. 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. Any write attempts are caught at compile time. Reading a final value does not use a memory barrier. You cannot write to a final variable, so a memory barrier is meaningless.

Using the hint, the compiler (or the JIT) may replace a memory reference to a final value with a constant. So in terms of performance, final does not introduce any additional overhead.

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.

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). 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. 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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