Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Emulating a memory barrier in Java to get rid of volatile reads

Assume I have a field that's accessed concurrently and it's read many times and seldom written to.

public Object myRef = new Object();

Let's say a Thread T1 will be setting myRef to another value, once a minute, while N other Threads will be reading myRef billions of times continuously and concurrently. I only need that myRef is eventually visible to all threads.

A simple solution would be to use an AtomicReference or simply volatile like this:

public volatile Object myRef = new Object();

However, afaik volatile reads do incur a performance cost. I know it's minuscule, this is more like something I wonder rather than something I actually need. So let's not be concerned with performance and assume this a purely theoretical question.

So the question boils down to: Is there way to safely bypass volatile reads for references that are only seldom written to, by doing something at the write site?

After some reading, it looks like memory barriers could be what I need. So if a construct like this existed, my problem would be solved:

  • Write
  • Invoke Barrier (sync)
  • Everything is synced and all threads will see the new value. (without a permanent cost at read sites, it can be stale or incur a one time cost as the caches are synced, but after that it's all back to regular field gets till next write).

Is there such a construct in Java, or in general? At this point I can't help but think if something like this existed, it would have been already incorporated into the atomic packages by the much smarter people maintaining those. (Disproportionately frequent read vs write might not have been a case to care for?) So maybe there is something wrong in my thinking and such a construct is not possible at all?

I have seen some code samples use 'volatile' for a similar purpose, exploiting it's happen-before contract. There is a separate sync field e.g.:

public Object myRef = new Object();
public volatile int sync = 0;

and at writing thread/site:

myRef = new Object();
sync += 1 //volatile write to emulate barrier

I am not sure this works, and some argue this works on x86 architecture only. After reading related sections in JMS, I think it's only guaranteed to work if that volatile write is coupled with a volatile read from the threads who need to see the new value of myRef. (So doesn't get rid of the volatile read).

Returning to my original question; is this possible at all? Is it possible in Java? Is it possible in one of the new APIs in Java 9 VarHandles?

like image 402
sydnal Avatar asked Oct 11 '19 07:10

sydnal


People also ask

What is memory barrier in Java?

Memory barriers, or fences, are a set of processor instructions used to apply ordering limitations on memory operations. This article explains the impact memory barriers have on the determinism of multi-threaded programs.

Is volatile a memory barrier?

The keyword volatile does not guarantee a memory barrier to enforce cache-consistency. Therefore, the use of volatile alone is not sufficient to use a variable for inter-thread communication on all systems and processors.

How does memory barrier work?

The memory barrier instructions halt execution of the application code until a memory write of an instruction has finished executing. They are used to ensure that a critical section of code has been completed before continuing execution of the application code.


1 Answers

So basically you want the semantics of a volatile without the runtime cost.

I don't think it is possible.

The problem is that the runtime cost of volatile is due the instructions that implement the memory barriers in the writer and the reader code. If you "optimize" the reader by getting rid of its memory barrier, then you are no longer guaranteed that the reader will see the "seldomly written" new value when it is actually written.

FWIW, some versions of the sun.misc.Unsafe class provide explicit loadFence, storeFence and fullFence methods, but I don't think that using them will give any performance benefit over using a volatile.


Hypothetically ...

what you want is for one processor in a multi-processor system to be able to tell all of the other processors:

"Hey! Whatever you are doing, invalidate your memory cache for address XYZ, and do it now."

Unfortunately, modern ISAs don't support this.

In practice, each processor controls its own cache.

like image 170
Stephen C Avatar answered Nov 14 '22 21:11

Stephen C