Ok. So the question below (ignoring other optimizations in the JVM that are 
specific to this method) is "If I were doing this myself in some other 
method, would this logic be valid if Unsafe.getIntVolatile() could be be 
replaced with Unsafe.getInt()?"

The answer IMO is "no".

The issue here is that unlike e.g. AtomicInteger.compareAndSet(), which is 
explicitly specified to include the behavior of a volatile read on the 
field involved, Unsafe.compareAndSwapInt() does not make any claims about 
exhibiting volatile read semantics. As a result, if you replace 
Unsafe.getIntVolatile() with Unsafe.getInt(), the resulting code:

public final int getAndAddInt(Object ptr, long offset, int value) {
 int curr;
 do {
    curr = this.getInt(ptr, offset); (1)
 } while(!this.compareAndSwapInt(ptr, offset, curr, curr + value)); (2)
 return curr;
}

Can be validly transformed by the optimizer to:

public final int getAndAddInt(Object ptr, long offset, int value) {
 int curr = this.getInt(ptr, offset); (1)
 do {  
 } while(!this.compareAndSwapInt(ptr, offset, curr, curr + value)); (2)
 return curr;
}

Because:

(a) The optimizer can prove that if the compareAndSwapInt ever actually 
wrote to the field, the method would return and curr wouldn't be read again.
(b) Since the read of curr is not volatile, and the read in 
Unsafe.compareAndSwapInt() is not required to act like a volatile read, all 
the reads of curr can be reordered with the all the reads in the 
compareAndSwapInt() calls, which means that they can be folded together and 
hoisted out of the loop.

If this valid optimization happened, the resulting code would get stuck in 
an infinite loop if another thread modified the field between the read of 
curr and the compareAndSwapInt call, and that is obviously not the intended 
behavior of getAndAddInt()...

On Sunday, October 15, 2017 at 2:12:30 AM UTC-7, John Hening wrote:
>
> Gil Tene, thanks you very much. 
>
> Ok, so does it mean that Unsafe.getIntVolatile() could be be replaced with 
> Unsafe.getInt()?
>
> W dniu niedziela, 15 października 2017 01:34:34 UTC+2 użytkownik Gil Tene 
> napisał:
>>
>> A simple answer would be that the field is treated by the method as a 
>> volatile, and the code is simply staying consistent with that notion. Is an 
>> optimization possible here? Possibly. Probably. But does it matter? No. The 
>> source code involved is not performance critical, and is not worth 
>> optimizing. The interpreter may be running this logic, but no hot path 
>> would be executing the actual logic in this code... 
>>
>> Why?  Because the java code you see there is NOT what the hot code would 
>> be doing on (most) JVMs. Specifically, optimizing JITs can and will 
>> identify and intrinsify the method, replacing it's body with code that does 
>> whatever they want it to do. They don't have to perform any of the actual 
>> the logic in the method, as long as they make sure the method's performs 
>> it's intended (contracted) function. and that contracted functionality is 
>> to perform a getAndAddInt on a field, treating it logically as a volatile.
>>
>> For example, on x86 there is support for atomic add via the XADD 
>> instruction. Using XADD for this method's functionality has multiple 
>> advantages over doing the as-coded CAS loop. And most optimizing JITs will 
>> [transparently] use an XADD in place of a CAS in this case and get rid of 
>> the loop altogether.
>>
>> On Saturday, October 14, 2017 at 6:58:17 AM UTC-7, John Hening wrote:
>>>
>>> Hello
>>>
>>>
>>> it is an implementation from sun.misc.Unsafe.
>>>
>>>
>>>
>>> public final int getAndAddInt(Object ptr, long offset, int value) {
>>>  int curr;
>>>  do {
>>>     curr = this.getIntVolatile(ptr, offset); (1)
>>>  } while(!this.compareAndSwapInt(ptr, offset, curr, curr + value)); (2)
>>>
>>>  return curr;}
>>>
>>>
>>>
>>>
>>>
>>> Why there is 
>>> Unsafe.getIntVolatile()
>>>
>>>  called instead of 
>>> Unsafe.getInt()
>>>
>>>  here?
>>>
>>>
>>> I am basically familiar with memory models, memory barriers etc., but, 
>>> perhaps I don't see any something important. 
>>>
>>>
>>> *getIntVolatile* means here: ensure the order of execution: (1) -> (2)
>>>
>>>
>>> It looks something like:
>>>
>>>
>>> curr = read(); 
>>> acquire();
>>> CAS operation
>>>
>>>
>>>
>>> Obviously, acquire() depends on CPU, for example on x86 it is empty, on ARM 
>>> it is a memory barrier, etc. 
>>>
>>>
>>> My question/misunderstanding:
>>>
>>>
>>> For my eye the order is ensured by data dependency between read of *(ptr + 
>>> offset)* and *CAS* operation on it. So, I don't see a reason to worry about 
>>> memory (re)ordering. 
>>>
>>>
>>>
>>>

-- 
You received this message because you are subscribed to the Google Groups 
"mechanical-sympathy" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to mechanical-sympathy+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to