Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Project Loom - 限定継続と軽量スレッド -

595 views

Published on

JJUG CCC 2019 Spring
Project Loom - 限定継続と軽量スレッド -

Published in: Software
  • Be the first to comment

  • Be the first to like this

Project Loom - 限定継続と軽量スレッド -

  1. 1. 櫻庭 祐一 Project Loom - 限定継続と軽量スレッド - 櫻庭 祐一 Project Loom - 限定継続と軽量スレッド -
  2. 2. Background Continuation Fiber Background Continuation Fiber
  3. 3. Background
  4. 4. Thread Fiber Loom 糸 繊維 織機
  5. 5. 並列 / 並行処理はむずかしい 同期 ロック モニタ 競合状態 再入 スレッドセーフ イミュータブル アトミック
  6. 6. 並列 / 並行処理はむずかしい 同期 ロック モニタ 競合状態 再入 スレッドセーフ イミュータブル アトミック スレッドセーフでも スケールしないこともある
  7. 7. なぜスケールしないのか CPU が遊んでいる 同時に複数からアクセスできないもの ロック ファイル 通信 DB ... 待ち状態になったら他の処理に譲る コンテキストスイッチ タスクスイッチ
  8. 8. 例 要素が 1 つのコンテナ public class MonoContainer<T> { private T value; public synchronized T take(); public synchronized void put(T v); private synchronized boolean isEmpty(); }
  9. 9. これは動かない ... public synchronized T take() { while (isEmpty()) {} T v = value; value = null; return v; } public synchronized void put(T v) { while (!isEmpty()) {} value = v; }
  10. 10. 動くけど ...... public T take() { while (true) { try { Thread.sleep(DURATION); } catch (InterruptedException ex) {} synchronized (this) { if (!isEmpty()) { T v = value; value = null; return v; } } } }
  11. 11. どうすればいいか? 要素がなければ他に処理を譲る take() 要素がセットされたら起こしてもらう 要素があるならば他に処理を譲る put() 要素がなくなったら起こしてもらう
  12. 12. どうすればいいか? 要素がなければ他に処理を譲る take() 要素がセットされたら起こしてもらう 要素があるならば他に処理を譲る put() 要素がなくなったら起こしてもらう wait - notifyAll
  13. 13. 正しく動作するコード public synchronized T take() { while (isEmpty()) { try { wait(); } catch (InterruptedException ex) {} } T v = value; value = null; notifyAll(); return v; } public synchronized void put(T v) { while (!isEmpty()) { try { wait(); } catch (InterruptedException ex) {} } value = v; notifyAll(); }
  14. 14. take wait() context switch putの途中 notifyAll() wake up waitの後
  15. 15. wait-notifyAll の問題点 Context SwitchOS による スイッチがいつ行われるか不明 スレッドの状態をすべて保存
  16. 16. Thread 1 Thread n Thread m … … JVM Stack Area
  17. 17. Thread 1 Thread n Thread m … … JVM Stack AreaPC Method X Method Y Method Z
  18. 18. Thread 1 Thread n Thread m … … JVM Stack AreaPC Method X Method Y Method Z Operand Stack Local Variable
  19. 19. wait-notifyAll の問題点 Context SwitchOS による スイッチがいつ行われるか不明 スレッドの状態をすべて保存 JVM スタック オペランドスタック ローカル変数 ネイティブスタック
  20. 20. OS によらずに JVM が管理するスレッドが必要 Fiber
  21. 21. ところで ... 処理の中断 / 再開のためのしくみ wait-notifyAll ( 限定 ) 継続を実現する技術要素の 1 つ (Delimited) Continuation
  22. 22. Fiber = Continuation Scheduling + ExecutorService Default: ForkJoinPool
  23. 23. Continuation
  24. 24. java.lang.Continuation タスク処理の中断 / 再開 run()処理の開始 / 再開 yield()処理の中断 Runnableタスク処理
  25. 25. var scope = new ContinuationScope("SCOPE"); var cont = new Continuation(scope, ()-> { for(int i = 0; i < 10; i++) { System.out.println("before: " + i); Continuation.yield(scope); System.out.println("after: " + i); } }); while (!cont.isDone()) { System.out.println("RUN"); cont.run(); }
  26. 26. > java ContinuationDemo RUN before: 0 RUN after: 0 before: 1 RUN after: 1 ...... before: 9 RUN after: 9 >
  27. 27. Fiber
  28. 28. java.lang.Fiber JVM が管理する軽量スレッド メモリ使用量 小 タスクスイッチコスト 低 Thread と同じように使える
  29. 29. java.lang.Fiber タスク Callable Runnable schedule() park() unpark() タスクの登録 タスクの中断 タスクの再開
  30. 30. var fiber = Fiber.schedule( () -> { System.out.println("Start"); // タスク処理 }); CompletableFuture<Void> future = fiber.toFuture();
  31. 31. park/unpark static void park() { Fiber<?> fiber = Thread.currentCarrierThread().getFiber(); …………… fiber.maybePark(); }
  32. 32. park/unpark static void park() { Fiber<?> fiber = Thread.currentCarrierThread().getFiber(); …………… fiber.maybePark(); } private void maybePark() { …………… // yield until continued on a carrier thread boolean yielded = false; boolean retry; do { if (Continuation.yield(FIBER_SCOPE)) { yielded = true; } ……………
  33. 33. park/unpark park/unpark は Package Private ライブラリがタスクスイッチ時に使用 例 j.u.c.l.LockSupport public static void park(Object blocker) { Object strand = Strands.currentStrand(); if (strand instanceof Fiber) { Fiber<?> fiber = (Fiber<?>) strand; setBlocker(fiber, blocker); Strands.parkFiber(); setBlocker(fiber, null); } else { Thread thread = (Thread) strand; setBlocker(thread, blocker); U.park(false, 0L);
  34. 34. park/unpark を行う API Thread sleep, join java.util.concurrent I/O Networking Pipe File は現状未対応
  35. 35. 現状の制限 ネイティブスタックからは yeild できない モニタからは yield できないモニタからは yield できない
  36. 36. MonoContainer を Fiber 対応に wait/notifyAll ReentrantLock ReentrantLock lock = new ReentrantLock(); Condition full= lock.newCondition(); Condition empty = lock.newCondition(); public T take() throws InterruptedException { lock.lock(); try { while (isEmpty()) empty.await(); T v = value; value = null; full.signal(); return v; } finally { lock.unlock(); }}
  37. 37. Oracle Code Keynote Demonstration Vector API 画像 処理Front 画像 処理 画像 処理 画像 処理 ... ... Fiber Front
  38. 38. Oracle Code Keynote Demonstration 400 200
  39. 39. 60 30 6s
  40. 40. import com.sun.net.httpserver.HttpServer; public void start(int port, Executor executor) throws IOException { this.port = port; HttpServer server = HttpServer.create(); ... server.setExecutor(cmd -> { executor.execute(() -> { try { command.run(); } finally { ... }}); }); httpServer.start(); }
  41. 41. public class Main { public static void main(String[] args) { switch (args[0].toLowerCase()) { case "fiber": new AppServer().start(20080, cmd -> Fiber.schedule(cmd)); break; case "thread": new AppServer().start(20081, Executors.newFixedThreadPool( Integer.parseInt(args[1]))); } } }
  42. 42. Fiber が Thread の代わりになる? かなり置き換えられる! 現状の問題点 ThreadLocal Thread.currentThread 現状 shadowThread で対応
  43. 43. Current Status サポートしている OS Linux x64 Mac OS X 試したい?
  44. 44. Conculusion Fiber: JVM が管理する軽量スレッド Continuation: 限定継続を実現 ロック、通信など Fiber 対応が進む いつリリースされるのか ...... ???
  45. 45. 櫻庭 祐一 Project Loom - 軽量スレッドと限定継続 - 櫻庭 祐一 Project Loom - 軽量スレッドと限定継続 -

×