$30 off During Our Annual Pro Sale. View Details »

Javaでトランザクショナルメモリを使う

 Javaでトランザクショナルメモリを使う

JJUG CCC 2014 Spring

Kenji Kazumura

May 25, 2022
Tweet

More Decks by Kenji Kazumura

Other Decks in Programming

Transcript

  1. Copyright 2014 FUJITSU LIMITED
    Javaでトランザクショナルメモ
    リを使う
    2014.5.18
    数村 憲治
    JJUG CCC 2014 Spring

    View Slide

  2. 目次
     JavaVMにおけるロックの進化
     トランザクショナルメモリ(TM)とは
     Haswellでのトランザクショナルメモリ
     Javaでトランザクショナルメモリを使う
     JDK8/9の動向
     まとめ
    1 Copyright 2014 FUJITSU LIMITED

    View Slide

  3. 2 Copyright 2014 FUJITSU LIMITED
    JavaVMロック機構の進化
    第一世代
    ヘビーロック
    第二世代
    シンロック
    第三世代
    バイアスロック
    第四世代
    ???
    JDK1.0-1.2
    JDK1.3.1
    HotSpot VM
    JDK5.0
    HotSpot VM

    View Slide

  4. 競合と共有(定義)
     ロック競合とは
    複数のスレッドが同時
    に同じロックを取る
     ロック共有とは
    複数のスレッドが同じ
    ロックを使用する
    3 Copyright 2014 FUJITSU LIMITED
    スレッドA スレッドB
    ロックX獲得
    ロックX待ち
    ロックXの競合
    ロック競合している例
    共有しているが競合していない例
    スレッドA スレッドB
    ロックX獲得
    ロックX解放
    ロックX獲得
    CS


    CS


    CS


    View Slide

  5. 競合と共有(定義)
     データ競合とは
    複数のスレッドが同じ
    データにアクセスする
    こと、あるいは、アク
    セスするためのロック
    競合
    4 Copyright 2014 FUJITSU LIMITED
    synchronized(array) {
    array[1] = 2;
    }
    synchronized(array) {
    array[2] = 4;
    }
    スレッドA スレッドB
    ロック競合しているが
    データ競合していないコード例
    ロック競合しているが
    データ競合していない例
    スレッドA スレッドB
    ロックX獲得 ロックX待ち
    ロックの競合
    アクセスY
    アクセスZ
    ロックX解放 ロックX獲得
    データ競合なし
    CS


    CS


    View Slide

  6. ヘビーロック
     OSのロック関数(pthread_mutex_lock/unlock
    等)を使用
     実装が簡単なため、初期のJVMで使用
     オブジェクト毎にmutexを作成・保持する必
    要がある
    5 Copyright 2014 FUJITSU LIMITED
    管理ヘッダ
    mutex
    Javaオブジェクト
    mutex_t
    if(obj->mutex == null) {
    obj->mutex = malloc(…);

    }
    pthread_mutex_lock(&obj->mutex);
    本来の
    オブジェクト
    データ
    synchornizeの実装例

    View Slide

  7. シンロック
     ほとんどのロックは競合してない
     CAS(Compare and Swap)で十分
     マルチコア・メニーコアCPUでは、CASのコ
    ストはバカにならない
    6 Copyright 2014 FUJITSU LIMITED
    ロック
    ビット
    Javaオブジェクト
    管理ヘッダ mov 0, %R2
    ld [ロックビット], %R1
    cas [ロックビット], %R1, %R2
    cmp %R1, %R2
    jne ヘビーロック
    通常処理
    0:ロック
    1:アンロック
    シンロック実装例

    View Slide

  8. バイアスロック
     ほとんどのロックは共有されていない
    特定のスレッド使うならロックする必要はない
     最初(バイアスされていない時)だけCAS
     そのあとはLoad&Compare
    7 Copyright 2014 FUJITSU LIMITED
    ld [バイアス情報], %R1
    cmp %R1, 自分自身のスレッド
    jne バイアス無効処理
    通常処理
    バイアスされている場合の
    実装例 StringBuffer sb = new StringBuffer();
    sb.append(“abc”);
    sb.append(“def”);
    String str = sb.toString();
    return str;
    バイアスロックの有効例

    View Slide

  9. java.util.concurrentパッケージ
     JDK5.0から導入
     synchronizeより柔軟なロック
    8 Copyright 2014 FUJITSU LIMITED
    import java.util.concurrent.locks.ReentrantLock;
    ReentrantLock lock = new ReentrantLock();

    public void m() {
    lock.lock();
    try {
    //クリティカルセクションの実行
    } finally {
    lock.unlock();
    }
    tryLock()
    isLocked()
    getOwner()
    使用例 便利なAPI
    (ReentrantLock)
    便利なクラス
    ReadWriteLock
    Semaphore
    AtomicInteger

    View Slide

  10. 目次
     JavaVMにおけるロックの進化
     トランザクショナルメモリ(TM)とは
     Haswellでのトランザクショナルメモリ
     Javaでトランザクショナルメモリを使う
     JDK8/9の動向
     まとめ
    9 Copyright 2014 FUJITSU LIMITED

    View Slide

  11. トランザクショナルメモリとは
     楽観的ロック
     とりあえず、ロックはしない
     実行しているうちに、データ競合があった
    ら、それまでの実行結果を破棄( アボート
    )して、最初の状態にもどる(ロールバッ
    ク)
     最後までデータ競合がなければ、結果を確
    定する(コミット)
    10 Copyright 2014 FUJITSU LIMITED

    View Slide

  12. 悲観的ロックと楽観的ロック
    11 Copyright 2014 FUJITSU LIMITED
    楽観的ロック
    非観的ロック
    スレッド1
    スレッド2
    スレッド3
    スレッド4
    ロック
    ロック
    ロック
    ロック
    クリティカルセクションの実行はシリアライズ
    スレッド1
    スレッド2
    スレッド3
    スレッド4





    クリティカルセクションの実行は並行

    View Slide

  13. HTMとSTM
     トランザクショナルメモリ(TM)をハード
    で実現するのをHTM、ソフトで実現するの
    をSTMと呼ぶ。
     HTMの実装例
    IBM System z (zEC12)
    Intel Haswell Microarchitecture
     STMの実装例
    Scala
    Haskell
    12 Copyright 2014 FUJITSU LIMITED

    View Slide

  14. 目次
     JavaVMにおけるロックの進化
     トランザクショナルメモリ(TM)とは
     Haswellでのトランザクショナルメモリ
     Javaでトランザクショナルメモリを使う
     JDK8/9の動向
     まとめ
    13 Copyright 2014 FUJITSU LIMITED

    View Slide

  15. TSX
     Intel® Transactional Synchronization
    Extension
    IntelアーキテクチャでのHTM実装
    Haswellで利用可能
    HLEとRTMの2種類
     Hardware Lock Elision
    従来(mutex)方式と互換インタフェース
     Restricted Transaction Memory
    新しいインタフェース
    14 Copyright 2014 FUJITSU LIMITED

    View Slide

  16. HLE
     従来のmutex_lock/mutex_unlockの形式でそ
    のまま置き換えられる
     アボートしたら、自動的にリトライ
    15 Copyright 2014 FUJITSU LIMITED
    mov $1, %eax
    RETRY:
    xacquire xchg mutex, %eax
    test %eax, %eax
    jne RETRY
    xrelease
    mov $0, mutex
    mutex_lock(&mutex);
    mutex_unlock(&mutex);
    クリティカルセクションの実行
    クリティカルセクションの実行
    従来(mutex) HLE

    View Slide

  17. GCC4.8からHLEサポート
    16 Copyright 2014 FUJITSU LIMITED
    #include
    void lock_hle_gcc(int *mutex) {
    while (__atomic_exchange_n(mutex, 1,
    __ATOMIC_ACQUIRE|__ATOMIC_HLE_ACQUIRE))
    _mm_pause();
    }
    void unlock_hle_gcc(int *mutex) {
    __atomic_clear(mutex,
    __ATOMIC_RELEASE|__ATOMIC_HLE_RELEASE);
    }
    以下のような関数を自分で用意する(fallbackなし)
    int mutex = 0;
    void func() {
    lock_hle_gcc(&mutex);
    // クリティカルセクションの実行
    unlock_hle_gcc(&mutex);
    クリティカルセクションを
    上記2関数で挟む

    View Slide

  18. GCC4.8からHLEをサポート
    17 Copyright 2014 FUJITSU LIMITED
    0000000000000000 :
    0: ba 01 00 00 00 mov $0x1,%edx
    5: eb 0b jmp 12
    7: 66 0f 1f 84 00 00 00 nopw 0x0(%rax,%rax,1)
    10: f3 90 pause
    12: 89 d0 mov %edx,%eax
    14: f2 87 07 xacquire xchg %eax,(%rdi)
    17: 85 c0 test %eax,%eax
    19: 75 f5 jne 10
    1b: f3 c3 repz retq
    1d: 0f 1f 00 nopl (%rax)
    0000000000000020 :
    20: f3 c6 07 00 xrelease movb $0x0,(%rdi)
    24: c3 retq
    ディスアセンブルするには、新しいobjdump(binutils)が必要

    View Slide

  19. RTM
     アボートした時のハンドラ―を自分で書く
    必要あり
    18 Copyright 2014 FUJITSU LIMITED
    RETRY:
    xbegin FAIL
    cmp mutex, $0
    jz OK
    FAIL:
    # goto RETRY or SLOWPATH
    SLOWPATH:
    # original code
    OK:
    ret
    cmp mutex, $0
    jne SLOWPATH
    xend
    ret
    SLOWPATH:
    # original code
    ロックコード アンロックコード

    View Slide

  20. いつアボートするか
     トランザクションが長い
     I/Oが入る
     OSによるスレッドコンテキストスイッチ
     特殊な命令を実行(CPUID/XABORT)
     ネストが深すぎる
     L1キャッシュラインの競合
    19 Copyright 2014 FUJITSU LIMITED

    View Slide

  21. Haswellのキャッシュ
     L1 Instruction Cache 32KB 8-way
     L1 Data Cache 32KB 8-way
    コアごと(スレッドで共有)
     L2 Unified Cache 256KB 8-way
    コアごと(スレッドで共有)
     キャッシュライン 64B
    20 Copyright 2014 FUJITSU LIMITED
    64バイト 64バイト
    64バイト 64バイト
    L1 Data Cache 64個
    8-way

    View Slide

  22. キャッシュラインの競合
     TSXでは、L1キャッシュラインでデータ競合
    を検出
    21 Copyright 2014 FUJITSU LIMITED
    xbegin FAIL
    mov (0x1000), %eax
    xend
    スレッドA
    xbegin FAIL
    mov %eax, (0x1010)
    xend
    スレッドB
    0x1000
    0x1010
    0x1004
    0x1008
    0x1040
    アドレス










    競合

    View Slide

  23. 目次
     JavaVMにおけるロックの進化
     トランザクショナルメモリ(TM)とは
     Haswellでのトランザクショナルメモリ
     Javaでトランザクショナルメモリを使う
     JDK8/9の動向
     まとめ
    22 Copyright 2014 FUJITSU LIMITED

    View Slide

  24. Javaでの実装案
     synchronizeの実装として使う
     APIとして提供
     特定ライブラリでの実装
    23 Copyright 2014 FUJITSU LIMITED

    View Slide

  25. synchronizeの実装
     アプリの修正が不要
     個別に指定できない
    競合あるところはTMは向いていないので、従来
    実装を使いたい
    競合のないところは、TM実装を使いたい
     実装するのがたいへん
    JVM(HotSpot)の中でかなりの部分に影響
    24 Copyright 2014 FUJITSU LIMITED

    View Slide

  26. APIとして提供
     アプリ修正が必要
     選択的に使用可能
     使う側がどういう場合にTMが向いているか
    理解している必要あり
     使用方法はReentrantLockと同じ
     実装はsyhchronizeほどではないが、比較的
    たいへん。
    25 Copyright 2014 FUJITSU LIMITED

    View Slide

  27. 特定ライブラリでの実装
     アプリ修正は不要
     使いたいところで使えない
     例 java.util.HashMap/ArrayList/LinkedList
    26 Copyright 2014 FUJITSU LIMITED
    table
    HashMap
    Entryテーブル Key Value
    Key Value

    View Slide

  28. API方式での実装
    27 Copyright 2014 FUJITSU LIMITED
    public final class HTM {
    private ByteBuffer data;
    private long mutex_addr;
    public HTM() {
    data = ByteBuffer.allocateDirect(64);
    mutex_addr = nativeAddress(data);
    }
    public void lock() {
    lockRTM(mutex_addr);
    }
    public void unlock() {
    unlockRTM(mutex_addr);
    }
    static private native void lockRTM(long addr);
    static private native void unlockRTM(long addr);
    使用方法
    HTM htm = new HTM();
    htm.lock();
    // クリティカル
    //セクションの実行
    htm.unlock();

    View Slide

  29. HTMクラスの構造
    28 Copyright 2014 FUJITSU LIMITED
    ByteBuffer data
    HTM
    64バイト
    long mutex_addr
    Javaヒープ Cヒープ
    DirectByteBuffer
    ・・・
    long address
    allocateDirect(64)
    mutex

    View Slide

  30. ネイティブメソッド(C)
    29 Copyright 2014 FUJITSU LIMITED
    #include
    void java_htm_lock_rtm(jlong);
    void java_htm_unlock_rtm(jlong);
    JNIEXPORT void JNICALL
    Java_HTM_lockRTM(JNIEnv *env, jclass kl, jlong add)
    {
    java_htm_lock_rtm(add);
    }
    JNIEXPORT void JNICALL
    Java_HTM_unlockRTM(JNIEnv *env, jclass kl, jlong add)
    {
    java_htm_unlock_rtm(add);
    }

    View Slide

  31. ネイティブメソッド(asm)
    30 Copyright 2014 FUJITSU LIMITED
    # void java_htm_lock_rtm(jlong)
    java_htm_lock_rtm:
    movl $10, %ecx #retry count
    .RETRY:
    xbegin .FAIL
    cmpl $0, (%rdi)
    jz .OK
    xabort $0xfe
    .FAIL:
    pause
    dec %ecx
    cmpl $0, %ecx
    jz .SLOWPATH
    jmp .RETRY
    .SLOWPATH:
    xor %edx, %edx
    inc %edx
    xor %eax, %eax
    lock cmpxchgl %edx, (%rdi)
    jne .SLOWPATH
    .OK:
    ret
    # void java_htm_unlock_rtm(jlong)
    java_htm_unlock_rtm:
    cmpl $0, (%rdi)
    jz .END
    movl $0, (%rdi)
    ret
    .END:
    xend
    ret

    View Slide

  32. データ競合なしプログラム
     本来ロックを取る必要のないプログラムで
    のベンチマーク
     一定量(500,000,000回)スレッドローカルな
    変数をインクリメントする
     スレッド数を1から8まで増やす
     各スレッドに均等な処理量を分割する
     各スレッドの処理時間の平均を求める
     「排他なし」、「synchronized」、
    「ReentrantLock」「HTM」の4パターン
    31 Copyright 2014 FUJITSU LIMITED

    View Slide

  33. データ競合なしプログラム
    32 Copyright 2014 FUJITSU LIMITED
    class LockNone extends Test {
    LockNone(long amount) {
    this.amount = amount;
    }
    void doIt() {
    count++;
    }
    }
    abstract class Test implements Runnable {
    long count;
    long time;
    long amount;
    public void run() {
    count = 0;
    long start = System.currentTimeMillis();
    while (count < amount) {
    doIt();
    }
    long end = System.currentTimeMillis();
    time = (end-start);
    }
    }
    排他なし

    View Slide

  34. データ競合なしプログラム
    33 Copyright 2014 FUJITSU LIMITED
    class LockSync extends Test {
    Object lock;
    LockSync(long amount, Object lock) {
    this.amount = amount;
    this.lock = lock;
    }
    void doIt() {
    synchronized (lock) {
    count++;
    }
    }
    }
    synchronized使用

    View Slide

  35. データ競合なしプログラム
    34 Copyright 2014 FUJITSU LIMITED
    class LockConc extends Test {
    ReentrantLock lock;
    LockConc(long amount, ReentrantLock lock) {
    this.amount = amount;
    this.lock = lock;
    }
    void doIt() {
    lock.lock();
    count++;
    lock.unlock();
    }
    }
    ReentrantLock使用

    View Slide

  36. データ競合なしプログラム
    35 Copyright 2014 FUJITSU LIMITED
    class LockRTM extends Test {
    HTM htm;
    LockRTM(long amount, HTM htm) {
    this.amount = amount;
    this.htm = htm;
    }
    void doIt() {
    htm.lock();
    count++;
    htm.unlock();
    }
    }
    HTM使用

    View Slide

  37. データ競合なし(結果)
    36 Copyright 2014 FUJITSU LIMITED
    1スレッドあたりの平均処理時間
    JDK1.7.0_51
    Intel Xeon E3-1270 v3 3.5GHz
    Red Hat Enterprise Linux Server release 6.5 (Santiago)

    View Slide

  38. ネイティブメソッド呼出し
     ネイティブメソッドの呼び出しはコストが
    高い
     性能高速化のために、Javaでかけるコード
    をCにしても、速くなるとは限らない
    37 Copyright 2014 FUJITSU LIMITED
    Java
    メソッド
    ネイティブ
    メソッド
    スタブ
    コード
    直接呼べない

    View Slide

  39. スタブコード(unlockRTM)
    38 Copyright 2014 FUJITSU LIMITED
    Decoding compiled method 0x00007f12dd05ebd0:
    Code:
    [Disassembling for mach='i386:x86-64']
    [Entry Point]
    # {method} 'unlockRTM' '(J)V' in 'HTM'
    # parm0: rsi:rsi = long
    # [sp+0x50] (sp of caller)
    0x00007f12dd05ed60: mov 0x8(%rsi),%r10d
    0x00007f12dd05ed64: cmp %r10,%rax
    0x00007f12dd05ed67: je 0x00007f12dd05ed78
    0x00007f12dd05ed6d: jmpq 0x00007f12dd037960 ; {runtime_call}
    0x00007f12dd05ed72: nopw 0x0(%rax,%rax,1)
    [Verified Entry Point]
    0x00007f12dd05ed78: mov %eax,-0x14000(%rsp)
    0x00007f12dd05ed7f: push %rbp
    0x00007f12dd05ed80: mov %rsp,%rbp
    0x00007f12dd05ed83: sub $0x40,%rsp
    0x00007f12dd05ed87: mov %rsi,%rdx
    0x00007f12dd05ed8a: movabs $0xdff84370,%r14 ; {oop(a 'java/lang/Class' = 'HTM')}
    0x00007f12dd05ed94: mov %r14,0x30(%rsp)
    0x00007f12dd05ed99: lea 0x30(%rsp),%r14
    0x00007f12dd05ed9e: mov %r14,%rsi ; OopMap{[48]=Oop off=65}
    0x00007f12dd05eda1: movabs $0x7f12dd05eda1,%r10 ; {section_word}
    0x00007f12dd05edab: mov %r10,0x1c0(%r15)
    0x00007f12dd05edb2: mov %rsp,0x1b8(%r15)
    0x00007f12dd05edb9: cmpb $0x0,0x8ef495a(%rip) # 0x00007f12e5f5371a
    ; {external_word}
    0x00007f12dd05edc0: je 0x00007f12dd05edfa
    0x00007f12dd05edc6: push %rsi
    0x00007f12dd05edc7: push %rdx
    0x00007f12dd05edc8: movabs $0xdafd0040,%rsi ; {oop({method} 'unlockRTM' '(J)V' in 'HTM')}
    0x00007f12dd05edd2: mov %r15,%rdi
    0x00007f12dd05edd5: test $0xf,%esp
    0x00007f12dd05eddb: je 0x00007f12dd05edf3
    0x00007f12dd05ede1: sub $0x8,%rsp
    0x00007f12dd05ede5: callq 0x00007f12e59c4230 ; {runtime_call}
    0x00007f12dd05edea: add $0x8,%rsp
    0x00007f12dd05edee: jmpq 0x00007f12dd05edf8
    0x00007f12dd05edf3: callq 0x00007f12e59c4230 ; {runtime_call}
    0x00007f12dd05edf8: pop %rdx
    0x00007f12dd05edf9: pop %rsi
    0x00007f12dd05edfa: lea 0x1d8(%r15),%rdi
    0x00007f12dd05ee01: movl $0x4,0x250(%r15)
    0x00007f12dd05ee0c: callq 0x00007f12da7ef660 ; {runtime_call}
    0x00007f12dd05ee11: vzeroupper
    0x00007f12dd05ee14: movl $0x5,0x250(%r15)
    0x00007f12dd05ee1f: mov %r15d,%ecx
    0x00007f12dd05ee22: shr $0x4,%ecx
    0x00007f12dd05ee25: and $0xffc,%ecx
    0x00007f12dd05ee2b: movabs $0x7f12e61ac000,%r10 ; {external_word}
    0x00007f12dd05ee35: mov %ecx,(%r10,%rcx,1)
    0x00007f12dd05ee39: cmpl $0x0,0x8efd83d(%rip) # 0x00007f12e5f5c680
    ; {external_word}
    0x00007f12dd05ee43: jne 0x00007f12dd05ee57
    0x00007f12dd05ee49: cmpl $0x0,0x30(%r15)
    0x00007f12dd05ee51: je 0x00007f12dd05ee74
    0x00007f12dd05ee57: mov %r15,%rdi
    0x00007f12dd05ee5a: mov %rsp,%r12
    0x00007f12dd05ee5d: sub $0x0,%rsp
    0x00007f12dd05ee61: and $0xfffffffffffffff0,%rsp
    0x00007f12dd05ee65: callq 0x00007f12e5a655a0 ; {runtime_call}
    0x00007f12dd05ee6a: mov %r12,%rsp
    0x00007f12dd05ee6d: mov 0x8ed8f3c(%rip),%r12 # 0x00007f12e5f37db0
    ; {external_word}
    0x00007f12dd05ee74: movl $0x8,0x250(%r15)
    0x00007f12dd05ee7f: cmpl $0x1,0x27c(%r15)
    0x00007f12dd05ee8a: je 0x00007f12dd05ef13
    0x00007f12dd05ee90: cmpb $0x0,0x8ef4883(%rip) # 0x00007f12e5f5371a
    ; {external_word}
    0x00007f12dd05ee97: je 0x00007f12dd05eecd
    0x00007f12dd05ee9d: movabs $0xdafd0040,%rsi ; {oop({method} 'unlockRTM' '(J)V' in 'HTM')}
    0x00007f12dd05eea7: mov %r15,%rdi
    0x00007f12dd05eeaa: test $0xf,%esp
    0x00007f12dd05eeb0: je 0x00007f12dd05eec8
    0x00007f12dd05eeb6: sub $0x8,%rsp
    0x00007f12dd05eeba: callq 0x00007f12e59c4380 ; {runtime_call}
    0x00007f12dd05eebf: add $0x8,%rsp
    0x00007f12dd05eec3: jmpq 0x00007f12dd05eecd
    0x00007f12dd05eec8: callq 0x00007f12e59c4380 ; {runtime_call}
    0x00007f12dd05eecd: movabs $0x0,%r10
    0x00007f12dd05eed7: mov %r10,0x1b8(%r15)
    0x00007f12dd05eede: movabs $0x0,%r10
    0x00007f12dd05eee8: mov %r10,0x1c0(%r15)
    0x00007f12dd05eeef: mov 0x38(%r15),%rcx
    0x00007f12dd05eef3: movq $0x0,0x100(%rcx)
    0x00007f12dd05eefe: leaveq
    0x00007f12dd05eeff: cmpq $0x0,0x8(%r15)
    0x00007f12dd05ef07: jne 0x00007f12dd05ef0e
    0x00007f12dd05ef0d: retq
    0x00007f12dd05ef0e: jmpq Stub::forward exception ; {runtime_call}
    0x00007f12dd05ef13: mov %rsp,%r12
    0x00007f12dd05ef16: sub $0x0,%rsp
    0x00007f12dd05ef1a: and $0xfffffffffffffff0,%rsp
    0x00007f12dd05ef1e: callq 0x00007f12e59c3600 ; {runtime_call}
    0x00007f12dd05ef23: mov %r12,%rsp
    0x00007f12dd05ef26: mov 0x8ed8e83(%rip),%r12 # 0x00007f12e5f37db0
    ; {external_word}
    0x00007f12dd05ef2d: jmpq 0x00007f12dd05ee90
    0x00007f12dd05ef32: hlt

    View Slide

  40. JITコードをdisassemble
     hsdis-amd64.soの場所をLD_LIBRARY_PATHに
     -XX:+UnlockDiagnosticVMOptions
    -XX:+PrintAssembly
    39 Copyright 2014 FUJITSU LIMITED

    View Slide

  41. native intrinsic
     ネイティブメソッドをJITが翻訳したコード
    のように扱う
     インライン展開することで、スタブが不要
    40 Copyright 2014 FUJITSU LIMITED
    Java
    ソース
    Cソース
    .class
    .so/.dll
    翻訳
    コード
    javac
    Cコンパイラ
    jit
    スタブ経由の
    呼出し
    同等コード
    アセンブラ
    翻訳
    コード
    取込み

    View Slide

  42. intrinsicコード(lock)
    41 Copyright 2014 FUJITSU LIMITED
    instruct htm_lock_rtm(
    memory adr, rax_RegI result,
    rcx_RegI tmp)
    %{
    match(HtmRtmLock adr);
    ins_encode %{
    Label retry, fail, ok, slowpath;
    __ movl($tmp$$Register, 10);
    __ bind(retry);
    __ xbegin(fail);
    __ cmpl($adr$$Address, 0);
    __ jccb(Assembler::equal, ok);
    __ xabort(0xfe);
    __ bind(fail);
    __ pause();
    __ decrementl($tmp$$Register);
    __ cmpl($tmp$$Register, 0);
    __ jccb(Assembler::equal, slowpath);
    __ jmp(retry);
    __ bind(slowpath);
    __ movl($tmp$$Register, 1);
    __ movl($result$$Register, 0);
    __ lock();
    __ cmpxchgl($tmp$$Register,
    $adr$$Address);
    __ jccb(Assembler::notEqual, slowpath);
    __ bind(ok);
    hotspot/src/cpu/x86/vm/x86_64.ad

    View Slide

  43. intrinsicコード(unlock)
    42 Copyright 2014 FUJITSU LIMITED
    instruct htm_unlock_rtm(memory adr, rax_RegI result)
    %{
    match(HtmRtmUnlock adr);
    ins_encode %{
    Label end, done;
    __ cmpl($adr$$Address, 0);
    __ jccb(Assembler::equal, end);
    __ movl($adr$$Address, 0);
    __ jmp(done);
    __ bind(end);
    __ xend();
    __ bind(done);
    hotspot/src/cpu/x86/vm/x86_64.ad

    View Slide

  44. データ競合なし(結果)
    43 Copyright 2014 FUJITSU LIMITED
    1スレッドあたりの平均処理時間
    JDK1.7.0_51
    Intel Xeon E3-1270 v3 3.5GHz
    Red Hat Enterprise Linux Server release 6.5 (Santiago)

    View Slide

  45. データ競合あるかもプログラム
     ArrayListアクセス時にロックを取るプログ
    ラムでのベンチマーク
     全部で一定回数(100,000,000回)、ArrayListの
    連続した2要素を入れ替える
     入れ替える場所はランダムに決める
     配列要素は十分大きい
     スレッド数を1から8まで増やす
     各スレッドに均等な処理量を分割する
     各スレッドの処理時間の平均を求める
    44 Copyright 2014 FUJITSU LIMITED

    View Slide

  46. データ競合あるかもプログラム
    45 Copyright 2014 FUJITSU LIMITED
    static long AMOUNT = 100_000_000L;
    static int ARRAY_SIZE = 131_072;
    static ArrayList alist =
    new ArrayList<>(ARRAY_SIZE);
    abstract class Test implements Runnable {
    long count;
    long time;
    long amount;
    long seed;
    public void run() {
    count = 0;
    long start = System.currentTimeMillis();
    while (count < amount) {
    int n1 = nextInt(ARRAY_SIZE);
    int n2 = n1+1;
    if (n2 == ARRAY_SIZE)
    n2 -= 2;
    doIt(n1, n2);
    count++;
    }
    long end = System.currentTimeMillis();
    time = (end-start);
    }
    void swap(int n1, int n2) {
    Object o1 = alist.get(n1);
    Object o2 = alist.get(n2);
    alist.set(n1, o2);
    alist.set(n2, o1);
    }
    int nextInt(int n) {
    long nextseed = (seed * 0x5deece66dL
    + 0xbL) & ((1L << 48) - 1);
    long rnd31 = nextseed >>> (48-31);
    seed = nextseed;
    return (int) ((n * rnd31) >> 31);
    }

    View Slide

  47. データ競合あるかもプログラム
    46 Copyright 2014 FUJITSU LIMITED
    void doIt(int n1, int n2) {
    synchronized (lock) {
    swap(n1, n2);
    }
    }
    void doIt(int n1, int n2) {
    lock.lock();
    try {
    swap(n1, n2);
    } finally {
    lock.unlock();
    }
    }
    void doIt(int n1, int n2) {
    swap(n1, n2);
    }
    void doIt(int n1, int n2) {
    htm.lock();
    try {
    swap(n1, n2);
    } finally {
    htm.unlock();
    }
    }
    排他なし
    HTM
    synchronized
    ReentrantLock

    View Slide

  48. データ競合あるかも(結果)
    47 Copyright 2014 FUJITSU LIMITED
    1スレッドあたりの平均処理時間
    JDK1.7.0_51
    Intel Xeon E3-1270 v3 3.5GHz
    Red Hat Enterprise Linux Server release 6.5 (Santiago)

    View Slide

  49. 目次
     JavaVMにおけるロックの進化
     トランザクショナルメモリ(TM)とは
     Haswellでのトランザクショナルメモリ
     Javaでトランザクショナルメモリを使う
     JDK8/9の動向
     まとめ
    48 Copyright 2014 FUJITSU LIMITED

    View Slide

  50. JDK8/9の動向
     -XX:+UseRTMLocking
    https://bugs.openjdk.net/browse/JDK-8031320
     synchronizeの実装として使用
    メソッド毎に有効・無効を指定する仕組みはあ
    りそう
     バイアスロックとは排他関係
     性能は?
    49 Copyright 2014 FUJITSU LIMITED

    View Slide

  51. データ競合なし(JDK9)
    50 Copyright 2014 FUJITSU LIMITED
    1スレッドあたりの平均処理時間

    View Slide

  52. 目次
     JavaVMにおけるロックの進化
     トランザクショナルメモリ(TM)とは
     Haswellでのトランザクショナルメモリ
     Javaでトランザクショナルメモリを使う
     JDK8/9の動向
     まとめ
    51 Copyright 2014 FUJITSU LIMITED

    View Slide

  53. JavaでTMが有効なケース
     ロック共有しているが、ロック競合がない
    バイアスロックが不向きなパターン
     ロック競合はあるが、データ競合がない
    本来プログラム的にはロックの必要がなく、安
    心のためだけにロックを入れている
     ロック競合はあるが、ほとんどデータ競合
    せず、たまに競合する場合がある
    ArrayListのパターン
    52 Copyright 2014 FUJITSU LIMITED

    View Slide

  54. JavaでTMの効果ないケース
     ロック競合がほとんど起きていない
     ロック期間が長い
    バッファ枯渇やコンテキストスイッチ
     ロック中にIOあり
    デバッグは難しい
     ロック中にGCあり
     共有しないロックを何度も使う
    バイアスロックがよい
    53 Copyright 2014 FUJITSU LIMITED

    View Slide

  55. Q&A
    54 Copyright 2014 FUJITSU LIMITED

    View Slide

  56. 55 Copyright 2014 FUJITSU LIMITED
    Javaは、Oracle Corporationおよびその子会社、関連会社の米国および
    その他の国おける登録商標です。
    本ドキュメントに記載されている、社名、商品名等は各社の商標または
    登録商標である場合があります。
    その他の記載されている、商標および登録商標については、
    一般に各社の商標または登録商標です。

    View Slide

  57. 56 Copyright 2010 FUJITSU LIMITED

    View Slide