Project Loom
櫻庭 祐一
Project Panama
Alan Bateman Richard Bäckman
Project Loom
Background
How to Use
Performance
Thread
Fiber
Loom
糸
繊維
織機
Thread の問題点
Footprint
スレッドの状態をすべて保存
Context Switch Cost
JVM スタック
オペランドスタック
ローカル変数
ネイティブスタック
Thread 1 Thread n Thread m
… …
JVM Stack Area
Thread 1 Thread n Thread m
… …
JVM Stack AreaPC
Method X
Method Y
Method Z
Thread 1 Thread n Thread m
… …
JVM Stack AreaPC
Method X
Method Y
Method Z
Operand Stack Local Variable
public int calc(int x) {
return 10 * x * Math.abs(x);
}
Runnable task = () -> {
...
int result = calc(2);
...
}
public int calc(int);
0: bipush 10
2: iload_1
3: imul
4: iload_1
5: invokestatic #1 // abs:(I)I
8: imul
9: ireturn
public int calc(int x) {
return 10 * x * Math.abs(x);
}
Runnable task = () -> {
...
int result = calc(2);
...
}
public int calc(int);
0: bipush 10
2: iload_1
3: imul
4: iload_1
5: invokestatic #1 // abs:(I)I
8: imul
9: ireturn
run ※ 実際にはラムダ式なので、run がコールされるわけではないです
calc
public int calc(int x) {
return 10 * x * Math.abs(x);
}
Runnable task = () -> {
...
int result = calc(2);
...
}
public int calc(int);
0: bipush 10
2: iload_1
3: imul
4: iload_1
5: invokestatic #1 // abs:(I)I
8: imul
9: ireturn
run ※ 実際にはラムダ式なので、run がコールされるわけではないです
calc
Operand Stack Local Variable
2
public int calc(int x) {
return 10 * x * Math.abs(x);
}
Runnable task = () -> {
...
int result = calc(2);
...
}
public int calc(int);
0: bipush 10
2: iload_1
3: imul
4: iload_1
5: invokestatic #1 // abs:(I)I
8: imul
9: ireturn
run ※ 実際にはラムダ式なので、run がコールされるわけではないです
calc
Operand Stack Local Variable
210
public int calc(int x) {
return 10 * x * Math.abs(x);
}
Runnable task = () -> {
...
int result = calc(2);
...
}
public int calc(int);
0: bipush 10
2: iload_1
3: imul
4: iload_1
5: invokestatic #1 // abs:(I)I
8: imul
9: ireturn
run ※ 実際にはラムダ式なので、run がコールされるわけではないです
calc
Operand Stack Local Variable
210
2
public int calc(int x) {
return 10 * x * Math.abs(x);
}
Runnable task = () -> {
...
int result = calc(2);
...
}
public int calc(int);
0: bipush 10
2: iload_1
3: imul
4: iload_1
5: invokestatic #1 // abs:(I)I
8: imul
9: ireturn
run ※ 実際にはラムダ式なので、run がコールされるわけではないです
calc
Operand Stack Local Variable
210
2
スタックから 2 つの数字をポップして掛け算
結果をスタックにプッシュ
public int calc(int x) {
return 10 * x * Math.abs(x);
}
Runnable task = () -> {
...
int result = calc(2);
...
}
public int calc(int);
0: bipush 10
2: iload_1
3: imul
4: iload_1
5: invokestatic #1 // abs:(I)I
8: imul
9: ireturn
run ※ 実際にはラムダ式なので、run がコールされるわけではないです
calc
Operand Stack Local Variable
220
public int calc(int x) {
return 10 * x * Math.abs(x);
}
Runnable task = () -> {
...
int result = calc(2);
...
}
public int calc(int);
0: bipush 10
2: iload_1
3: imul
4: iload_1
5: invokestatic #1 // abs:(I)I
8: imul
9: ireturn
run ※ 実際にはラムダ式なので、run がコールされるわけではないです
calc
Operand Stack Local Variable
220
2
public int calc(int x) {
return 10 * x * Math.abs(x);
}
Runnable task = () -> {
...
int result = calc(2);
...
}
public int calc(int);
0: bipush 10
2: iload_1
3: imul
4: iload_1
5: invokestatic #1 // abs:(I)I
8: imul
9: ireturn
run ※ 実際にはラムダ式なので、run がコールされるわけではないです
calc
Operand Stack Local Variable
220
2
スタックからポップした数値を引数にして
static メソッドをコール
public int calc(int x) {
return 10 * x * Math.abs(x);
}
Runnable task = () -> {
...
int result = calc(2);
...
}
public int calc(int);
0: bipush 10
2: iload_1
3: imul
4: iload_1
5: invokestatic #1 // abs:(I)I
8: imul
9: ireturn
run ※ 実際にはラムダ式なので、run がコールされるわけではないです
calc
Operand Stack Local Variable
2
abs
20
public int calc(int x) {
return 10 * x * Math.abs(x);
}
Runnable task = () -> {
...
int result = calc(2);
...
}
public int calc(int);
0: bipush 10
2: iload_1
3: imul
4: iload_1
5: invokestatic #1 // abs:(I)I
8: imul
9: ireturn
run ※ 実際にはラムダ式なので、run がコールされるわけではないです
calc
Operand Stack Local Variable
2
abs
20
この状態を保存
java.lang.Exception: Stack trace
at java.base/java.lang.Thread.dumpStack(Thread.java:1537)
at java.base/java.lang.Continuation.yield(Continuation.java:396)
at java.base/java.lang.Fiber.maybePark(Fiber.java:597)
at java.base/java.lang.Fiber.parkNanos(Fiber.java:559)
at java.base/java.lang.Fiber.sleepNanos(Fiber.java:1039)
at java.base/java.lang.Thread.sleep(Thread.java:380)
at Test.lambda$new$0(Test.java:14)
at java.base/java.lang.Fiber.lambda$new$0(Fiber.java:198)
at java.base/java.lang.Continuation.enter(Continuation.java:372) Fibers
at java.base/java.lang.Continuation.run(Continuation.java:328)
at java.base/java.lang.Fiber.runContinuation(Fiber.java:366)
at java.base/java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1425)
at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1017)
at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1666)
at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1599)
at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:189)
Task
Thread
OS によらずに
JVM が管理するスレッド
Fiber
How to Use Fiber
try (FiberScope scope = FiberScope.open()) {
Runnable task1 = () -> { /* Task */ };
Fiber fiber1 = scope.schedule(task1);
Runnable task2 = () -> { /* Task */ };
Fiber fiber2 = scope.schedule(task2);
}
処理の中断 / 再開 : park/unpark
ただし package private
park/unpark を行う API
java.util.concurrent.locks
Socket/SocketChannel
Thread.sleep
File は現状未対応
現状の制限
ネイティブスタックからは yeild できない
モニタ取得状態で yield できない
キャリアスレッドを pin してしまう
Fiber = Continuation
Scheduling
+
ExecutorService
Default: ForkJoinPool
Continuation Performance
Lazy Copy
Walking the Frames
Freezing oops
Keeping nmethods Alive
Lazy Copy
foo() bar()
baz()
qux()
yeild
Lazy Copy
Stack Continuation Object
タスク中断時
スタックの状態を保存
foo()
bar()
baz()
qux()
Lazy Copy
Stack Continuation Object
タスク中断時
スタックの状態を保存
foo()
bar()
baz()
qux()
Lazy Copy
Stack Continuation Object
タスク再開時
スタックに RB(Read Barrier) を設定し
保存した状態の一部をスタックに戻す
foo()
bar()
baz()
qux()
RB
Lazy Copy
Stack Continuation Object
baz,qux を抜けてスタックが RB まで達したら
残ったスタックの状態を戻す
foo()
bar()
RB
Lazy Copy
Stack Continuation Object
baz,qux を抜けてスタックが RB まで達したら
残ったスタックの状態を戻す
foo()
bar()
Project Panama
Maurizio Cimadamore
Project Panama
Background
Off-Heap Memory Access
Panama
Canal
Native
Java
NativeJava
JNI
NativeJava
Panama
Binding Tool: jextract
Using Panama is Easier,
Safer
and Faster!!
NativeJava
Panama
Binding Tool: jextract
Using Panama is Easier,
Safer
and Faster!!
Missing Link: Memory Access
Off-Heap Memory Access
sun.misc.Unsafe
ByteBuffer
Perfomance Safe Ease of Use
Memory Access API
Key Abstraction
MemorySegment
MemoryAddress
MemoryLayout
メモリ領域
領域のアドレス
領域のメモリレイアウト
struct Point {
int x;
int y;
} pts[5];
x0 y0 x1 y1 x2 y2 x3 y3 x4 y4
Segment
Layout
Address
SequenceLayout seq = MemoryLayout.ofSequent(5,
MemoryLayout.ofStruct(
MemoryLayout.ofValueBits(32).withName(” x” ),
MemoryLayout.ofValueBits(32).withName(” y” ),
)
);
var xHandle = seq.varHandle(int.class,
PathElement.sequenceElement(),
PathElement.groupElement(” x” ));
var yHandle = seq.varHandle(int.class,
PathElement.sequenceElement(),
PathElement.groupElement(” y” ));
var xHandle = seq.varHandle(int.class,
PathElement.sequenceElement(),
PathElement.groupElement(” x” ));
var yHandle = seq.varHandle(int.class,
PathElement.sequenceElement(),
PathElement.groupElement(” y” ));
try (MemorySegment points
= MemorySegment.ofNative(seq)) {
MemoryAddress base = points.baseAddress();
for (long i = 0; i < seq.elementsCount(); i++) {
xHandle.set(base, i, (int) i);
yHandle.set(base, i, (int) i);
}
}
Conclusion
Project Panama
Project Loom
Fiber 軽量スレッド
Continuation 限定継続 機能は検討中
検討すべき項目はまだ多い
jextract 速い 簡単 便利
Memory Access API まだ提案レベル?
Project Loom
櫻庭 祐一
Project Panama

Project Loom + Project Panama

  • 1.
  • 2.
    Alan Bateman RichardBäckman Project Loom Background How to Use Performance
  • 3.
  • 4.
    Thread の問題点 Footprint スレッドの状態をすべて保存 Context SwitchCost JVM スタック オペランドスタック ローカル変数 ネイティブスタック
  • 5.
    Thread 1 Threadn Thread m … … JVM Stack Area
  • 6.
    Thread 1 Threadn Thread m … … JVM Stack AreaPC Method X Method Y Method Z
  • 7.
    Thread 1 Threadn Thread m … … JVM Stack AreaPC Method X Method Y Method Z Operand Stack Local Variable
  • 8.
    public int calc(intx) { return 10 * x * Math.abs(x); } Runnable task = () -> { ... int result = calc(2); ... } public int calc(int); 0: bipush 10 2: iload_1 3: imul 4: iload_1 5: invokestatic #1 // abs:(I)I 8: imul 9: ireturn
  • 9.
    public int calc(intx) { return 10 * x * Math.abs(x); } Runnable task = () -> { ... int result = calc(2); ... } public int calc(int); 0: bipush 10 2: iload_1 3: imul 4: iload_1 5: invokestatic #1 // abs:(I)I 8: imul 9: ireturn run ※ 実際にはラムダ式なので、run がコールされるわけではないです calc
  • 10.
    public int calc(intx) { return 10 * x * Math.abs(x); } Runnable task = () -> { ... int result = calc(2); ... } public int calc(int); 0: bipush 10 2: iload_1 3: imul 4: iload_1 5: invokestatic #1 // abs:(I)I 8: imul 9: ireturn run ※ 実際にはラムダ式なので、run がコールされるわけではないです calc Operand Stack Local Variable 2
  • 11.
    public int calc(intx) { return 10 * x * Math.abs(x); } Runnable task = () -> { ... int result = calc(2); ... } public int calc(int); 0: bipush 10 2: iload_1 3: imul 4: iload_1 5: invokestatic #1 // abs:(I)I 8: imul 9: ireturn run ※ 実際にはラムダ式なので、run がコールされるわけではないです calc Operand Stack Local Variable 210
  • 12.
    public int calc(intx) { return 10 * x * Math.abs(x); } Runnable task = () -> { ... int result = calc(2); ... } public int calc(int); 0: bipush 10 2: iload_1 3: imul 4: iload_1 5: invokestatic #1 // abs:(I)I 8: imul 9: ireturn run ※ 実際にはラムダ式なので、run がコールされるわけではないです calc Operand Stack Local Variable 210 2
  • 13.
    public int calc(intx) { return 10 * x * Math.abs(x); } Runnable task = () -> { ... int result = calc(2); ... } public int calc(int); 0: bipush 10 2: iload_1 3: imul 4: iload_1 5: invokestatic #1 // abs:(I)I 8: imul 9: ireturn run ※ 実際にはラムダ式なので、run がコールされるわけではないです calc Operand Stack Local Variable 210 2 スタックから 2 つの数字をポップして掛け算 結果をスタックにプッシュ
  • 14.
    public int calc(intx) { return 10 * x * Math.abs(x); } Runnable task = () -> { ... int result = calc(2); ... } public int calc(int); 0: bipush 10 2: iload_1 3: imul 4: iload_1 5: invokestatic #1 // abs:(I)I 8: imul 9: ireturn run ※ 実際にはラムダ式なので、run がコールされるわけではないです calc Operand Stack Local Variable 220
  • 15.
    public int calc(intx) { return 10 * x * Math.abs(x); } Runnable task = () -> { ... int result = calc(2); ... } public int calc(int); 0: bipush 10 2: iload_1 3: imul 4: iload_1 5: invokestatic #1 // abs:(I)I 8: imul 9: ireturn run ※ 実際にはラムダ式なので、run がコールされるわけではないです calc Operand Stack Local Variable 220 2
  • 16.
    public int calc(intx) { return 10 * x * Math.abs(x); } Runnable task = () -> { ... int result = calc(2); ... } public int calc(int); 0: bipush 10 2: iload_1 3: imul 4: iload_1 5: invokestatic #1 // abs:(I)I 8: imul 9: ireturn run ※ 実際にはラムダ式なので、run がコールされるわけではないです calc Operand Stack Local Variable 220 2 スタックからポップした数値を引数にして static メソッドをコール
  • 17.
    public int calc(intx) { return 10 * x * Math.abs(x); } Runnable task = () -> { ... int result = calc(2); ... } public int calc(int); 0: bipush 10 2: iload_1 3: imul 4: iload_1 5: invokestatic #1 // abs:(I)I 8: imul 9: ireturn run ※ 実際にはラムダ式なので、run がコールされるわけではないです calc Operand Stack Local Variable 2 abs 20
  • 18.
    public int calc(intx) { return 10 * x * Math.abs(x); } Runnable task = () -> { ... int result = calc(2); ... } public int calc(int); 0: bipush 10 2: iload_1 3: imul 4: iload_1 5: invokestatic #1 // abs:(I)I 8: imul 9: ireturn run ※ 実際にはラムダ式なので、run がコールされるわけではないです calc Operand Stack Local Variable 2 abs 20 この状態を保存
  • 19.
    java.lang.Exception: Stack trace atjava.base/java.lang.Thread.dumpStack(Thread.java:1537) at java.base/java.lang.Continuation.yield(Continuation.java:396) at java.base/java.lang.Fiber.maybePark(Fiber.java:597) at java.base/java.lang.Fiber.parkNanos(Fiber.java:559) at java.base/java.lang.Fiber.sleepNanos(Fiber.java:1039) at java.base/java.lang.Thread.sleep(Thread.java:380) at Test.lambda$new$0(Test.java:14) at java.base/java.lang.Fiber.lambda$new$0(Fiber.java:198) at java.base/java.lang.Continuation.enter(Continuation.java:372) Fibers at java.base/java.lang.Continuation.run(Continuation.java:328) at java.base/java.lang.Fiber.runContinuation(Fiber.java:366) at java.base/java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1425) at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290) at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1017) at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1666) at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1599) at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:189) Task Thread
  • 20.
  • 21.
    How to UseFiber try (FiberScope scope = FiberScope.open()) { Runnable task1 = () -> { /* Task */ }; Fiber fiber1 = scope.schedule(task1); Runnable task2 = () -> { /* Task */ }; Fiber fiber2 = scope.schedule(task2); } 処理の中断 / 再開 : park/unpark ただし package private
  • 22.
  • 23.
    現状の制限 ネイティブスタックからは yeild できない モニタ取得状態でyield できない キャリアスレッドを pin してしまう
  • 24.
  • 25.
    Continuation Performance Lazy Copy Walkingthe Frames Freezing oops Keeping nmethods Alive
  • 26.
  • 27.
    Lazy Copy Stack ContinuationObject タスク中断時 スタックの状態を保存 foo() bar() baz() qux()
  • 28.
    Lazy Copy Stack ContinuationObject タスク中断時 スタックの状態を保存 foo() bar() baz() qux()
  • 29.
    Lazy Copy Stack ContinuationObject タスク再開時 スタックに RB(Read Barrier) を設定し 保存した状態の一部をスタックに戻す foo() bar() baz() qux() RB
  • 30.
    Lazy Copy Stack ContinuationObject baz,qux を抜けてスタックが RB まで達したら 残ったスタックの状態を戻す foo() bar() RB
  • 31.
    Lazy Copy Stack ContinuationObject baz,qux を抜けてスタックが RB まで達したら 残ったスタックの状態を戻す foo() bar()
  • 32.
    Project Panama Maurizio Cimadamore ProjectPanama Background Off-Heap Memory Access
  • 33.
  • 34.
  • 35.
    NativeJava Panama Binding Tool: jextract UsingPanama is Easier, Safer and Faster!!
  • 36.
    NativeJava Panama Binding Tool: jextract UsingPanama is Easier, Safer and Faster!! Missing Link: Memory Access
  • 37.
  • 38.
    Memory Access API KeyAbstraction MemorySegment MemoryAddress MemoryLayout メモリ領域 領域のアドレス 領域のメモリレイアウト
  • 39.
    struct Point { intx; int y; } pts[5]; x0 y0 x1 y1 x2 y2 x3 y3 x4 y4 Segment Layout Address
  • 40.
    SequenceLayout seq =MemoryLayout.ofSequent(5, MemoryLayout.ofStruct( MemoryLayout.ofValueBits(32).withName(” x” ), MemoryLayout.ofValueBits(32).withName(” y” ), ) ); var xHandle = seq.varHandle(int.class, PathElement.sequenceElement(), PathElement.groupElement(” x” )); var yHandle = seq.varHandle(int.class, PathElement.sequenceElement(), PathElement.groupElement(” y” ));
  • 41.
    var xHandle =seq.varHandle(int.class, PathElement.sequenceElement(), PathElement.groupElement(” x” )); var yHandle = seq.varHandle(int.class, PathElement.sequenceElement(), PathElement.groupElement(” y” )); try (MemorySegment points = MemorySegment.ofNative(seq)) { MemoryAddress base = points.baseAddress(); for (long i = 0; i < seq.elementsCount(); i++) { xHandle.set(base, i, (int) i); yHandle.set(base, i, (int) i); } }
  • 42.
    Conclusion Project Panama Project Loom Fiber軽量スレッド Continuation 限定継続 機能は検討中 検討すべき項目はまだ多い jextract 速い 簡単 便利 Memory Access API まだ提案レベル?
  • 43.