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 [email protected].
For more options, visit https://groups.google.com/d/optout.