thanks :)

W dniu poniedziałek, 16 października 2017 22:47:07 UTC+2 użytkownik Gil 
Tene napisał:
>
> The bytecode doesn't matter. It's not the javac compiler that will be 
> doing the optimizations you should be worried about. It's the JIT compilers 
> in the JVM. The javac-generated bytecode is only executed by the 
> interpreter. The bytecode is eventually transformed to machine code by the 
> JIT compiler, during which it will undergo aggressive optimization.
>
> The "CPU" you should worry about and model in your mind is not x86, SPARC, 
> or ARM. It's the JVM's execution engine and the JIT-generated machine code 
> that does most of the actual execution. And that "CPU" will reorder the 
> code more aggressively than any HW CPU ever would. The JIT's optimizing 
> transformations include arbitrary and massive re-ordereing, reshaping, 
> folding-together, and completely eliminating big parts off your apparent 
> bytecode instructions. And the JIT will do all those as long as it can 
> prove that the transformations are allowed.
>
> On Monday, October 16, 2017 at 3:30:13 PM UTC+1, John Hening wrote:
>>
>> Thanks Gil Tene! 
>>
>> You obviously right. The read is not volatile so the compiler is allowed 
>> to reorder it. Moreover, the read is not volatile, so the compiler assumes 
>> that noone changes the source of getInt(). So, it can hoist out curr.
>>
>>
>> The last question (sorry for being inquisitive)  is:
>>
>> Let's assume that the compiler generated bytecode equivalent to 
>>
>> 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;
>> }
>>
>>
>>
>> so it wasn't optimized. 
>>
>> Now, it seems to work correctly. But, note that CPU can make some 
>> reordering. We are lucky because the CPU cannot reorder here: there is a 
>> data dependency: (1) -> (2). So, on every sensible (with data dependency) 
>> CPU it works, yes?
>>
>> W dniu poniedziałek, 16 października 2017 12:13:07 UTC+2 użytkownik Gil 
>> Tene napisał:
>>>
>>> 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