2016-12-29 36 views
10

這個問題是不是他們之間的區別 - 我知道虛假的失敗是什麼,爲什麼會發生在LL/SC。我的問題是如果我在intel x86上並使用java-9(build 149),爲什麼它們的彙編代碼有區別?weakCompareAndSwap VS比較並交換

public class WeakVsNonWeak { 

    static jdk.internal.misc.Unsafe UNSAFE = jdk.internal.misc.Unsafe.getUnsafe(); 

    public static void main(String[] args) throws NoSuchFieldException, SecurityException { 

     Holder h = new Holder(); 
     h.setValue(33); 
     Class<?> holderClass = Holder.class; 
     long valueOffset = UNSAFE.objectFieldOffset(holderClass.getDeclaredField("value")); 

     int result = 0; 
     for (int i = 0; i < 30_000; ++i) { 
      result = strong(h, valueOffset); 
     } 
     System.out.println(result); 

    } 

    private static int strong(Holder h, long offset) { 
     int sum = 0; 
     for (int i = 33; i < 11_000; ++i) { 
      boolean result = UNSAFE.weakCompareAndSwapInt(h, offset, i, i + 1); 
      if (!result) { 
       sum++; 
      } 
     } 
     return sum; 

    } 

    public static class Holder { 

     private int value; 

     public int getValue() { 
      return value; 
     } 

     public void setValue(int value) { 
      this.value = value; 
     } 
    } 
} 

與運行:

輸出compareAndSwapInt的
java -XX:-TieredCompilation 
     -XX:CICompilerCount=1 
     -XX:+UnlockDiagnosticVMOptions 
     -XX:+PrintIntrinsics 
     -XX:+PrintAssembly 
     --add-opens java.base/jdk.internal.misc=ALL-UNNAMED 
     WeakVsNonWeak 

(相關部分):

輸出的weakCompareAndSwapInt
 0x0000000109f0f4b8: movabs $0x111927c18,%rsi ; {metadata({method} {0x0000000111927c18} 'compareAndSwapInt' '(Ljava/lang/Object;JII)Z' in 'jdk/internal/misc/Unsafe')} 
    0x0000000109f0f4c2: mov %r15,%rdi 
    0x0000000109f0f4c5: test $0xf,%esp 
    0x0000000109f0f4cb: je  0x0000000109f0f4e3 
    0x0000000109f0f4d1: sub $0x8,%rsp 
    0x0000000109f0f4d5: callq 0x00000001098569d2 ; {runtime_call SharedRuntime::dtrace_method_entry(JavaThread*, Method*)} 
    0x0000000109f0f4da: add $0x8,%rsp 
    0x0000000109f0f4de: jmpq 0x0000000109f0f4e8 
    0x0000000109f0f4e3: callq 0x00000001098569d2 ; {runtime_call SharedRuntime::dtrace_method_entry(JavaThread*, Method*)} 
    0x0000000109f0f4e8: pop %r9 
    0x0000000109f0f4ea: pop %r8 
    0x0000000109f0f4ec: pop %rcx 
    0x0000000109f0f4ed: pop %rdx 
    0x0000000109f0f4ee: pop %rsi 
    0x0000000109f0f4ef: lea 0x210(%r15),%rdi 
    0x0000000109f0f4f6: movl $0x4,0x288(%r15) 
    0x0000000109f0f501: callq 0x00000001098fee40 ; {runtime_call Unsafe_CompareAndSwapInt(JNIEnv_*, _jobject*, _jobject*, long, int, int)} 
    0x0000000109f0f506: vzeroupper 
    0x0000000109f0f509: and $0xff,%eax 
    0x0000000109f0f50f: setne %al 
    0x0000000109f0f512: movl $0x5,0x288(%r15) 
    0x0000000109f0f51d: lock addl $0x0,-0x40(%rsp) 
    0x0000000109f0f523: cmpl $0x0,-0x3f04dd(%rip)  # 0x0000000109b1f050 

0x000000010b698840: sub $0x18,%rsp 
    0x0000010b698847: mov %rbp,0x10(%rsp) 
    0x000000010b69884c: mov %r8d,%eax 
    0x000000010b69884f: lock cmpxchg %r9d,(%rdx,%rcx,1) 
    0x000000010b698855: sete %r11b 
    0x000000010b698859: movzbl %r11b,%r11d  ;*invokevirtual compareAndSwapInt {reexecute=0 rethrow=0 return_oop=0} 
               ; - jdk.internal.misc.Unsafe::[email protected] (line 1369) 

我遠遠不夠的多才多藝,瞭解整個輸出,但絕對可以看到鎖ADDL和鎖CMPXCHG之間的差異。

編輯 彼得的回答讓我想到了。讓我們來看看比較並交換將是一個內在的呼叫:

-XX:+ PrintIntrinsics -XX:-PrintAssembly

@ 7 jdk.internal.misc.Unsafe::compareAndSwapInt (0 bytes) (intrinsic) 
@ 20  jdk.internal.misc.Unsafe::weakCompareAndSwapInt (11 bytes) (intrinsic). 

然後用/運行兩次的例子,而不:

-XX:DisableIntrinsic = _compareAndSwapInt

This is sor奇怪的T,輸出是完全一樣的(完全相同的指令),唯一的變化是與使內在我得到這樣的電話:

0x000000010c23e355: callq 0x00000001016569d2 ; {runtime_call SharedRuntime::dtrace_method_entry(JavaThread*, Method*)} 
    0x000000010c23e381: callq 0x00000001016fee40 ; {runtime_call Unsafe_CompareAndSwapInt(JNIEnv_*, _jobject*, _jobject*, long, int, int)} 

和殘疾人:

0x00000001109322d5: callq 0x0000000105c569d2 ; {runtime_call _ZN13SharedRuntime19dtrace_method_entryEP10JavaThreadP6Method} 
    0x00000001109322e3: callq 0x0000000105c569d2 ; {runtime_call _ZN13SharedRuntime19dtrace_method_entryEP10JavaThreadP6Method} 

這是相當有趣,不應該內在的代碼是不同的?

EDIT-2 the8472也有意義。

鎖ADDLMFENCE一個替代品刷新StoreBuffer在x86,據我所知,它有知名度,而不是原子確實做。此條目之前對,就是:

0x00000001133db6f6: movl $0x4,0x288(%r15) 
0x00000001133db701: callq 0x00000001060fee40 ; {runtime_call Unsafe_CompareAndSwapInt(JNIEnv_*, _jobject*, _jobject*, long, int, int)} 
0x00000001133db706: vzeroupper 
0x00000001133db709: and $0xff,%eax 
0x00000001133db70f: setne %al 
0x00000001133db712: movl $0x5,0x288(%r15) 
0x00000001133db71d: lock addl $0x0,-0x40(%rsp) 
0x00000001133db723: cmpl $0x0,-0xd0bc6dd(%rip)  #  0x000000010631f050 
              ; {external_word} 

如果你看看here是將委託給另一本地call to Atomic:: cmpxchg這似乎是原子做掉。

爲什麼這不是直接替代鎖cmpxchg是我的一個謎。

+0

與您的編輯和來自不同優化級別的衆多彙編樣本不太清楚你實際要求什麼。 – the8472

+0

因此,'sun.misc.Unsafe'仍然沒有消失,但是移動到另一個包jdk.internal.misc中,證明它實際上不是一個兼容性問題,它使這個類保持活着? – Holger

+0

@Holger它沒有移動,現在有兩個版本。正如Shipilev所說,sun.misc.Unsafe將被刪除 - 這次肯定。在sun.misc.Unsafe過去很有用的* other *地方有多個增強功能,現在已經過時(比如AtomicFieldUpdater)。他們甚至將釋放/獲取語義直接添加到不安全! – Eugene

回答

4

TL; DR您正在查看組件輸出中的錯誤位置。

兩個compareAndSwapIntweakCompareAndSwapInt電話被編譯到X86-64 完全相同的 ASM序列。然而,方法本身編譯不同(但它通常不重要)。

  1. source code的的compareAndSwapIntweakCompareAndSwapInt定義是不同的。前者是本地方法,而後者是Java方法。

    @HotSpotIntrinsicCandidate 
    public final native boolean compareAndSwapInt(Object o, long offset, 
                   int expected, 
                   int x); 
    
    @HotSpotIntrinsicCandidate 
    public final boolean weakCompareAndSwapInt(Object o, long offset, 
                    int expected, 
                    int x) { 
        return compareAndSwapInt(o, offset, expected, x); 
    } 
    
  2. 什麼,你所看到的是這些獨立方法如何編譯。本地方法編譯爲調用相應C函數的存根。但這不是在快速道路上運行的。

  3. 固有方法是那些調用替換爲特定於HotSpot的內聯實現的方法。 注:調用被替換,但不是方法本身。

  4. 如果你看看你的WeakVsNonWeak.strong方法的彙編輸出,你會看到它包含lock cmpxchg指令,是否調用UNSAFE.compareAndSwapIntUNSAFE.weakCompareAndSwapInt

    0x000001bd76170c44: lock cmpxchg %ecx,(%r11) 
    0x000001bd76170c49: sete %r10b 
    0x000001bd76170c4d: movzbl %r10b,%r10d  ;*invokevirtual compareAndSwapInt 
                   ; - WeakVsNonWeak::[email protected] (line 23) 
                   ; - WeakVsNonWeak::[email protected] (line 14) 
    
    0x0000024f56af1097: lock cmpxchg %r11d,(%r8) 
    0x0000024f56af109c: sete %r10b 
    0x0000024f56af10a0: movzbl %r10b,%r10d  ;*invokevirtual weakCompareAndSwapInt 
                   ; - WeakVsNonWeak::[email protected] (line 23) 
                   ; - WeakVsNonWeak::[email protected] (line 14) 
    

    一旦主要方法是JIT編譯的,不安全的獨立版本。*方法不會直接調用。

+2

你是對的:如果沒有一些適當的經驗(比如我),很難在整個榮耀中看到輸出。你的解釋太棒了!我在代碼中看到並顯示的是來自c2編譯的* individual *方法輸出,其中!=內部代碼;一旦'strong'方法被編譯,使用'UNSAFE.compareAndSwapInt'或'UNSAFE.weakCompareAndSwapInt'產生相同的輸出意味着它們是內在代碼是相同的。 – Eugene

4

在第一種情況下,正在使用本機方法。代碼沒有被優化,或者它不是內在的。

在第二種情況下,已經使用內在函數來內聯所需的程序集,而不是調用JNI方法。我會雖然這兩種情況下會這樣做,但我猜不是。

+2

確實你*很可能*是正確的,但我不知道爲什麼。查看編輯 – Eugene

+1

@Eugene我同意它出現倒退。內在應該有mov,非內在應該有callq –

+2

那不是重點。 * compareAndSwap內在*和* compareAndSwap非內在函數*僅在callq函數中有** **。我期待更多 – Eugene

4

我相信lock addl不是原子操作本身,而是store-load barrier implementation。原子發生在callq

由於您已經使用PrintIntrinsics進行了日誌記錄,因此您應該檢查它是否真正具有內在的含義。

+0

的確你也是對的(見編輯-2),但它不回答主要問題。儘管如此,感謝您的意見。 – Eugene