57
Copyright 2014 FUJITSU LIMITED Java でででででででででででででででで 2014.5.18 でで でで JJUG CCC 2014 Spring

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

Embed Size (px)

DESCRIPTION

JJUG CCC 2014 Spring #ccc_r27のスライド @kkzr

Citation preview

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

Copyright 2014 FUJITSU LIMITED

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

2014.5.18数村 憲治

JJUG CCC 2014 Spring

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

目次

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

2 Copyright 2014 FUJITSU LIMITED

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

3 Copyright 2014 FUJITSU LIMITED

JavaVM ロック機構の進化

第一世代ヘビーロック

第二世代シンロック

第三世代バイアスロック

第四世代???

JDK1.0-1.2JDK1.3.1HotSpot VM

JDK5.0HotSpot VM

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

競合と共有(定義) ロック競合とは

複数のスレッドが同時に同じロックを取る

ロック共有とは複数のスレッドが同じ

ロックを使用する

4 Copyright 2014 FUJITSU LIMITED

スレッド A スレッド B

ロック X 獲得ロック X 待ち

ロック X の競合

ロック競合している例

共有しているが競合していない例スレッド A スレッド B

ロック X 獲得

ロック X 解放ロック X 獲得

CS

実行

CS

実行

CS

実行

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

競合と共有(定義) データ競合とは

複数のスレッドが同じデータにアクセスすること、あるいは、アクセスするためのロック競合

5 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

実行

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

ヘビーロック OS のロック関数 (pthread_mutex_lock/

unlock 等 ) を使用 実装が簡単なため、初期の JVM で使用 オブジェクト毎に mutex を作成・保持す

る必要がある

6 Copyright 2014 FUJITSU LIMITED

管理ヘッダ mutex

Java オブジェクト

mutex_tif(obj->mutex == null) { obj->mutex = malloc(…); …}pthread_mutex_lock(&obj->mutex);

本来のオブジェクトデータ

synchornize の実装例

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

シンロック ほとんどのロックは競合してない CAS(Compare and Swap) で十分 マルチコア・メニーコア CPU では、 CAS

のコストはバカにならない

7 Copyright 2014 FUJITSU LIMITED

ロックビット

Java オブジェクト

管理ヘッダ mov 0, %R2ld [ ロックビット ], %R1cas [ ロックビット ], %R1, %R2cmp %R1, %R2jne ヘビーロック通常処理

0: ロック1: アンロック

シンロック実装例

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

バイアスロック

ほとんどのロックは共有されていない特定のスレッド使うならロックする必要はない

最初(バイアスされていない時)だけ CAS そのあとは Load & Compare

8 Copyright 2014 FUJITSU LIMITED

ld [ バイアス情報 ], %R1cmp %R1, 自分自身のスレッドjne バイアス無効処理通常処理

バイアスされている場合の実装例 StringBuffer sb = new StringBuffer();

sb.append(“abc”);sb.append(“def”);String str = sb.toString();return str;

バイアスロックの有効例

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

java.util.concurrent パッケージ JDK5.0 から導入 synchronize より柔軟なロック

9 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)

便利なクラスReadWriteLockSemaphoreAtomicInteger

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

目次

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

10 Copyright 2014 FUJITSU LIMITED

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

トランザクショナルメモリとは 楽観的ロック とりあえず、ロックはしない 実行しているうちに、データ競合があった

ら、それまでの実行結果を破棄( アボート)して、最初の状態にもどる(ロールバック)

最後までデータ競合がなければ、結果を確定する(コミット)

11 Copyright 2014 FUJITSU LIMITED

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

悲観的ロックと楽観的ロック

12 Copyright 2014 FUJITSU LIMITED

楽観的ロック

非観的ロックスレッド1

スレッド 2

スレッド 3

スレッド 4

ロック

ロック

ロック

ロック

クリティカルセクションの実行はシリアライズ

スレッド1

スレッド 2

スレッド 3

スレッド 4

   

   

   

   

ロックなし

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

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

HTM と STM

トランザクショナルメモリ( TM )をハードで実現するのを HTM 、ソフトで実現するのを STM と呼ぶ。

HTM の実装例 IBM System z (zEC12) Intel Haswell Microarchitecture

STM の実装例ScalaHaskell

13 Copyright 2014 FUJITSU LIMITED

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

目次

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

14 Copyright 2014 FUJITSU LIMITED

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

TSX

Intel® Transactional Synchronization Extension Intel アーキテクチャでの HTM 実装Haswell で利用可能HLE と RTM の2種類

Hardware Lock Elision従来 (mutex) 方式と互換インタフェース

Restricted Transaction Memory新しいインタフェース

15 Copyright 2014 FUJITSU LIMITED

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

HLE 従来の mutex_lock/mutex_unlock の形式

でそのまま置き換えられる アボートしたら、自動的にリトライ

16 Copyright 2014 FUJITSU LIMITED

mov $1, %eaxRETRY: xacquire xchg mutex, %eax test %eax, %eax jne RETRY

xrelease mov $0, mutex

mutex_lock(&mutex);

mutex_unlock(&mutex);

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

従来 (mutex) HLE

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

GCC4.8 から HLE サポート

17 Copyright 2014 FUJITSU LIMITED

#include <immintrin.h>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 関数で挟む

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

GCC4.8 から HLE をサポート

18 Copyright 2014 FUJITSU LIMITED

0000000000000000 <lock_hle_gcc>: 0: ba 01 00 00 00 mov $0x1,%edx 5: eb 0b jmp 12 <lock_hle_gcc+0x12> 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 <lock_hle_gcc+0x10> 1b: f3 c3 repz retq 1d: 0f 1f 00 nopl (%rax)

0000000000000020 <unlock_hle_gcc>: 20: f3 c6 07 00 xrelease movb $0x0,(%rdi) 24: c3 retq

ディスアセンブルするには、新しい objdump(binutils) が必要

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

RTM

アボートした時のハンドラ―を自分で書く必要あり

19 Copyright 2014 FUJITSU LIMITED

RETRY: xbegin FAIL cmp mutex, $0 jz OKFAIL: # goto RETRY or SLOWPATHSLOWPATH: # original codeOK: ret

cmp mutex, $0 jne SLOWPATH xend ret

SLOWPATH: # original code

ロックコード アンロックコード

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

いつアボートするか

トランザクションが長い I/O が入る OS によるスレッドコンテキストスイッチ 特殊な命令を実行 (CPUID/XABORT) ネストが深すぎる L1 キャッシュラインの競合

20 Copyright 2014 FUJITSU LIMITED

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

Haswell のキャッシュ L1 Instruction Cache 32KB 8-way L1 Data Cache 32KB 8-way

コアごと ( スレッドで共有 )

L2 Unified Cache 256KB 8-wayコアごと ( スレッドで共有 )

キャッシュライン 64B

21 Copyright 2014 FUJITSU LIMITED

64 バイト 64 バイト

64 バイト 64 バイト

L1 Data Cache 64 個

8-way

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

キャッシュラインの競合 TSX では、 L1 キャッシュラインでデータ

競合を検出

22 Copyright 2014 FUJITSU LIMITED

xbegin FAILmov (0x1000), %eaxxend

スレッド A

xbegin FAILmov %eax, (0x1010)xend

スレッド B

0x1000

0x1010

0x1004

0x1008

0x1040

アドレス

同一キャッシュ

ライン

競合

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

目次

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

23 Copyright 2014 FUJITSU LIMITED

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

Java での実装案 synchronize の実装として使う API として提供 特定ライブラリでの実装

24 Copyright 2014 FUJITSU LIMITED

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

synchronize の実装 アプリの修正が不要 個別に指定できない

競合あるところは TM は向いていないので、従来実装を使いたい

競合のないところは、 TM 実装を使いたい

実装するのがたいへん JVM ( HotSpot )の中でかなりの部分に影響

25 Copyright 2014 FUJITSU LIMITED

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

API として提供 アプリ修正が必要 選択的に使用可能 使う側がどういう場合に TM が向いている

か理解している必要あり 使用方法は ReentrantLock と同じ

実装は syhchronize ほどではないが、比較的たいへん。

26 Copyright 2014 FUJITSU LIMITED

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

特定ライブラリでの実装 アプリ修正は不要 使いたいところで使えない 例

java.util.HashMap/ArrayList/LinkedList

27 Copyright 2014 FUJITSU LIMITED

table

HashMapEntry テーブル Key Value

Key Value

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

API 方式での実装

28 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();

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

HTM クラスの構造

29 Copyright 2014 FUJITSU LIMITED

ByteBuffer dataHTM

64 バイト

long mutex_addr

Javaヒープ C ヒープ

DirectByteBuffer

・・・

long address

allocateDirect(64)

mutex

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

ネイティブメソッド (C)

30 Copyright 2014 FUJITSU LIMITED

#include <jni.h>

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);}

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

ネイティブメソッド (asm)

31 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

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

データ競合なしプログラム 本来ロックを取る必要のないプログラムでのベンチマーク

一定量 (500,000,000回 ) スレッドローカルな変数をインクリメントする

スレッド数を1から8まで増やす各スレッドに均等な処理量を分割する各スレッドの処理時間の平均を求める「排他なし」、「 synchronized」、   「 ReentrantLock」「 HTM」の4パターン

32 Copyright 2014 FUJITSU LIMITED

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

データ競合なしプログラム

33 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); }}

排他なし

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

データ競合なしプログラム

34 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 使用

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

データ競合なしプログラム

35 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 使用

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

データ競合なしプログラム

36 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 使用

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

データ競合なし(結果)

37 Copyright 2014 FUJITSU LIMITED

1スレッドあたりの平均処理時間

JDK1.7.0_51Intel Xeon E3-1270 v3 3.5GHzRed Hat Enterprise Linux Server release 6.5 (Santiago)

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

ネイティブメソッド呼出し

ネイティブメソッドの呼び出しはコストが高い

性能高速化のために、 Java でかけるコードを C にしても、速くなるとは限らない

38 Copyright 2014 FUJITSU LIMITED

Javaメソッド

ネイティブメソッドスタブ

コード

直接呼べない

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

スタブコード (unlockRTM)

39 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

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

JIT コードを disassemble hsdis-amd64.so の場所を LD_LIBRARY_PATH に -XX:+UnlockDiagnosticVMOptions -

XX:+PrintAssembly

40 Copyright 2014 FUJITSU LIMITED

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

native intrinsic

ネイティブメソッドを JIT が翻訳したコードのように扱う

インライン展開することで、スタブが不要

41 Copyright 2014 FUJITSU LIMITED

Javaソース

C ソース

.class

.so/.dll

翻訳コード

javac

C コンパイラ

jit

スタブ経由の呼出し

同等コード

アセンブラ

翻訳コード

取込み

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

intrinsic コード (lock)

42 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

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

intrinsic コード (unlock)

43 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

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

データ競合なし(結果)

44 Copyright 2014 FUJITSU LIMITED

1スレッドあたりの平均処理時間

JDK1.7.0_51Intel Xeon E3-1270 v3 3.5GHzRed Hat Enterprise Linux Server release 6.5 (Santiago)

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

データ競合あるかもプログラム ArrayList アクセス時にロックを取るプログラムでのベンチマーク

全部で一定回数 (100,000,000回 ) 、 ArrayList の連続した2要素を入れ替える

入れ替える場所はランダムに決める配列要素は十分大きい スレッド数を1から8まで増やす各スレッドに均等な処理量を分割する各スレッドの処理時間の平均を求める

45 Copyright 2014 FUJITSU LIMITED

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

データ競合あるかもプログラム

46 Copyright 2014 FUJITSU LIMITED

static long AMOUNT = 100_000_000L;static int ARRAY_SIZE = 131_072;static ArrayList<Object> 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); }

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

データ競合あるかもプログラム

47 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

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

データ競合あるかも(結果)

48 Copyright 2014 FUJITSU LIMITED

1スレッドあたりの平均処理時間

JDK1.7.0_51Intel Xeon E3-1270 v3 3.5GHzRed Hat Enterprise Linux Server release 6.5 (Santiago)

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

目次

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

49 Copyright 2014 FUJITSU LIMITED

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

JDK8/9 の動向

-XX:+UseRTMLockinghttps://bugs.openjdk.net/browse/JDK-

8031320

synchronize の実装として使用メソッド毎に有効・無効を指定する仕組みはあ

りそう バイアスロックとは排他関係性能は?

50 Copyright 2014 FUJITSU LIMITED

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

データ競合なし (JDK9)

51 Copyright 2014 FUJITSU LIMITED

1スレッドあたりの平均処理時間

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

目次

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

52 Copyright 2014 FUJITSU LIMITED

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

Java で TM が有効なケース

ロック共有しているが、ロック競合がないバイアスロックが不向きなパターン

ロック競合はあるが、データ競合がない本来プログラム的にはロックの必要がなく、安心のためだけにロックを入れている

ロック競合はあるが、ほとんどデータ競合せず、たまに競合する場合があるArrayList のパターン

53 Copyright 2014 FUJITSU LIMITED

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

Java で TM の効果ないケース ロック競合がほとんど起きていない ロック期間が長い

バッファ枯渇やコンテキストスイッチ ロック中に IO あり

デバッグは難しい ロック中に GC あり 共有しないロックを何度も使う

バイアスロックがよい

54 Copyright 2014 FUJITSU LIMITED

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

Q&A

55 Copyright 2014 FUJITSU LIMITED

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

56 Copyright 2014 FUJITSU LIMITED

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

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

57 Copyright 2010 FUJITSU LIMITED