Slide 1

Slide 1 text

作って分かる!非同期処理ランタイム Leverages TechFes 2025 Winter

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

今日話すこと ● 非同期処理とはなにか ● 非同期処理ランタイムについて ● 非同期処理ランタイムをつくってみ た

Slide 4

Slide 4 text

非同期処理とはなにか?

Slide 5

Slide 5 text

処理が終わるまで 待たずに他の処理を実 行すること

Slide 6

Slide 6 text

task_1 task_2 task_1 task_2 task_1 task_2 同期処理 task_2はtask_1が終わって から処理を開始する 非同期処理 (並行)

Slide 7

Slide 7 text

なぜ非同期処理が必要なのか?

Slide 8

Slide 8 text

時間がかかる処理を 待っている間を 有効活用するため

Slide 9

Slide 9 text

task_1 task_2 task_3 task_1 task_2 task_3 同期処理

Slide 10

Slide 10 text

task_1 task_2 task_3 task_1 task_2 task_3 非同期処理

Slide 11

Slide 11 text

task_1 task_2 task_3 task_1 task_2 task_3 task_1 task_2 task_3 task_1 task_2 task_3 同期処理 非同期処理

Slide 12

Slide 12 text

時間がかかる処理とは?

Slide 13

Slide 13 text

https://i.gzn.jp/img/2017/07/07/intel-core-i9-7900x/00.jpg,(参照2025-02-05)

Slide 14

Slide 14 text

CPU Intel Core i9 ● 動作周波数:2.3 ~ 6.0GHz https://i.gzn.jp/img/2017/07/07/intel-core-i9-7900x/00.jpg,(参照2025-02-05)

Slide 15

Slide 15 text

CPU Intel Core i9 ● 動作周波数:2.3 ~ 6.0GHz → 1秒間に23億回 ~ 60億回の計算が 可能 https://i.gzn.jp/img/2017/07/07/intel-core-i9-7900x/00.jpg,(参照2025-02-05)

Slide 16

Slide 16 text

CPU Intel Core i9 ● 動作周波数:2.3 ~ 6.0GHz → 1秒間に23億回 ~ 60億回の計算が 可能 → CPUが一回の計算にかかる時間は 0.2 ~ 0.5ナノ秒 https://i.gzn.jp/img/2017/07/07/intel-core-i9-7900x/00.jpg,(参照2025-02-05)

Slide 17

Slide 17 text

CPUとコンポーネント間の通信にかかる時間 レジスタ :0.2 ~ 0.5 ナノ秒 RAM :80 ~ ナノ秒 SSD (SATA3.0) :100 ~ マイクロ秒 HDD (SATA3.0) :10 ~ ミリ秒 ネットワーク :10 ~ 100 ミリ秒 図:Socket AM3+マザーボードの進化系、GIGABYTE「GA~990FXA-UD5 R5」徹底検証 https://www.gdm.or.jp/review/2015/0515/114349/2,(参照2025-02-05)

Slide 18

Slide 18 text

CPUとコンポーネント間の通信にかかるクロック数 レジスタ :1 クロック RAM :400~ クロック SSD (SATA3.0) :50,000~ クロック HDD (SATA3.0) :5,000,000~ クロック ネットワーク :5,000,000~ クロック 図:Socket AM3+マザーボードの進化系、GIGABYTE「GA~990FXA-UD5 R5」徹底検証 https://www.gdm.or.jp/review/2015/0515/114349/2,(参照2025-02-05)

Slide 19

Slide 19 text

CPUとコンポーネント間の通信にかかるクロック数 CPUがHDDにアクセスしてデータを得るまでに一 千万回計算ができる! レジスタ :1 クロック RAM :400~ クロック SSD (SATA3.0) :50,000~ クロック HDD (SATA3.0) :5,000,000~ クロック ネットワーク :5,000,000~ クロック 図:Socket AM3+マザーボードの進化系、GIGABYTE「GA~990FXA-UD5 R5」徹底検証 https://www.gdm.or.jp/review/2015/0515/114349/2,(参照2025-02-05)

Slide 20

Slide 20 text

I/O待ち中のプロセス I/O発生 I/O 結果返る

Slide 21

Slide 21 text

● プロセスがディスクやネットワーク に対してI/Oを行うとそのプロセス を実行待機状態にして他の実行 可能なプロセスを動かす とはいえOSも馬鹿じゃ ない I/O 発生 I/O 結果返る 現代のOSは こっち

Slide 22

Slide 22 text

● プロセスがディスクやネットワーク に対してI/Oを行うとそのプロセス を実行待機状態にして他の実行 可能なプロセスを動かす ● プロセスを切り替えるときに切り替 えるためのオーバーヘッド(スイッ チングコスト )が生じるのでシング ルプロセスと比べて時間がかかる とはいえOSも馬鹿じゃ ない ただし… I/O 発生 I/O 結果返る スイッチングコスト

Slide 23

Slide 23 text

● I/Oは発生しないが時間のかかる 処理が存在する ● 時間計算量の大きい計算が該当 する CPU bound…?

Slide 24

Slide 24 text

CPU bound…? 別スレッドで 非同期に処理をする ● I/Oは発生しないが時間のかかる 処理が存在する ● 時間計算量の大きい計算が該当 する

Slide 25

Slide 25 text

時間がかかる処理は CPU boundな処理と I/O boundな処理の 2つがある

Slide 26

Slide 26 text

CPU bound ● CPUの処理速度によって律速され る ● プロセスレベルで最適化するために は並列処理が有効 ● 特定のワークロードに最適化された 専用ハードウェアを使うことで最適 化が可能(ネットワーク、グラフィッ ク、暗号化) I/O bound ● ディスクのIOPS、レイテンシによっ て律速される ● プロセスレベルで最適化するために は並行処理が有効 ● HDD→SSDにするなどしてスルー プットやレイテンシを向上させること で最適化が可能

Slide 27

Slide 27 text

CPU bound ● 文字列処理(正規表現など) ● 全文検索 ● 暗号化 ● ハッシュ化 ● 圧縮・解凍処理 ● 画像処理 ● 動画エンコード I/O bound ● ファイルの読み取り・書き出し ● APIへのリクエスト ● メモリスワップ 基本的なWebアプリケーションはほとんど I/Oが問 題になる(テーブルフルスキャン , N + 1問題, 外部 API連携, …etc)

Slide 28

Slide 28 text

これらの処理で CPUを効率よく扱うた めに生まれたのが非同 期処理

Slide 29

Slide 29 text

非同期処理ランタイムについて

Slide 30

Slide 30 text

非同期処理を ユーザー空間レベルで 実現するための機構が 非同期処理ランタイム

Slide 31

Slide 31 text

言語機能として組み込み ● JavaScript(Promise) ● Java(CompletableFuture) ● Kotlin(Coroutines) ● Scala(Future) ● Go(Goルーチン) ● C#(Task) ● Erlang ライブラリとして提供 ● Rust(tokio) ● C言語 ● PHP(ReactPHP) ● Python(asyncio)

Slide 32

Slide 32 text

非同期処理ランタイムを 構成しているもの

Slide 33

Slide 33 text

非同期処理ランタイムを構成しているもの ● Executor ● Scheduler ● (Reactor) ● Task

Slide 34

Slide 34 text

非同期処理ランタイムを構成しているもの ● Executor ● Scheduler ● (Reactor) ● Task TaskをSchedulerから受け 取って実際に処理を行う 実装によっては他の ExecutorからTaskを奪うこと もある(stealing)

Slide 35

Slide 35 text

非同期処理ランタイムを構成しているもの ● Executor ● Scheduler ● (Reactor) ● Task Taskを受け取っていつ、どれ くらい実行するのかを決めて Executorに実行させる

Slide 36

Slide 36 text

非同期処理ランタイムを構成しているもの ● Executor ● Scheduler ● (Reactor) ● Task 非同期なIOにおいて結果が 返ったときにTaskを実行可能 状態に変える

Slide 37

Slide 37 text

非同期処理ランタイムを構成しているもの ● Executor ● Scheduler ● (Reactor) ● Task 処理の実行状態を管理する オブジェクト 一般にコルーチンを用いて実 装される

Slide 38

Slide 38 text

非同期処理ランタイムの構成例 Executor Executor Executor Scheduler 実行キュー Task Task Task Task Task ランタイム I/Oキュー Task Reactor

Slide 39

Slide 39 text

非同期処理ランタイムの構成例 Executor Executor Executor Scheduler 実行キュー Task Task Task Task Task ランタイム I/Oキュー Task Reactor Executorは処理をある程度 進めると実行キューに戻す

Slide 40

Slide 40 text

非同期処理ランタイムの構成例 Executor Executor Executor Scheduler 実行キュー Task Task Task Task Task ランタイム I/Oキュー Task Reactor Executorは処理をある程度 進めると実行キューに戻す 処理は実行・中断・再開が 可能でないといけない

Slide 41

Slide 41 text

こんな非同期処理があったとき...

Slide 42

Slide 42 text

こんな非同期処理があったとき... 時間のかかる処理 (ネットワーク IO)

Slide 43

Slide 43 text

こんな非同期処理があったとき... 時間のかかる処理 (ネットワーク IO) ここまで到達したら一 時中断する

Slide 44

Slide 44 text

こんな非同期処理があったとき... 時間のかかる処理 (ネットワーク IO) ここまで到達したら一 時中断する レスポンスが 届いたら再開する

Slide 45

Slide 45 text

こんな非同期処理があったとき... 時間のかかる処理 (ネットワーク IO) ここまで到達したら一 時中断する レスポンスが 届いたら再開する こっちも同じ

Slide 46

Slide 46 text

タスクA,B,Cを A 1 →B 1 →C 1 →A 2 →…と進 めることで非同期処理が 可能になる

Slide 47

Slide 47 text

JavaScriptだとawaitは プログラマがランタイムに 中断ポイントを 明示する構文

Slide 48

Slide 48 text

Goはawaitがなくランタイムが 強制的に割り込んで処理の中断・再 開を行う →プログラマに非同期を極力意識さ せない言語設計になっている

Slide 49

Slide 49 text

No content

Slide 50

Slide 50 text

No content

Slide 51

Slide 51 text

実際にJavaScriptで実装してみよう!

Slide 52

Slide 52 text

今回作るもの ● Executor ● Scheduler ● (Reactor) ● Task シングルスレッドで実行する のでExecutorは必要ない 非同期I/Oはサポートしない 非同期処理の状態を管理す るTaskとTaskを実行する Schedulerのみを実装する

Slide 53

Slide 53 text

Scheduler + Executor 実行キュー 実装する非同期処理ランタイムの構成 Task Task ランタイム

Slide 54

Slide 54 text

https://github.com/nonaka-shu-lvgs/async-runtime

Slide 55

Slide 55 text

Runtime = Executor + Scheduler spawnされるとタスクを 実行キューに積む

Slide 56

Slide 56 text

JSで中断可能な関数(コルーチン)→ ジェネレータ Runtimeから処理を進めるように 指示がされるとコルーチンを進める コルーチンを進めた結果、 それが終了したら完了状態にする

Slide 57

Slide 57 text

No content

Slide 58

Slide 58 text

No content

Slide 59

Slide 59 text

No content

Slide 60

Slide 60 text

No content

Slide 61

Slide 61 text

出力結果が同じに なった!

Slide 62

Slide 62 text

さらなる高みへ ● 非同期処理ランタイムを実現するために今回はコルーチンを用いたがこの方式を 協調的マルチタスキングという ○ 協調的マルチタスキングは非協調的マルチタスキングと比べて実装が容易というメリットがある反 面、あるタスクが無限ループに陥ると処理系全体が機能不全に陥るというデメリットがある ○ 協調的マルチタスク方式を採用している言語は JavaScript, Python, Ruby, Rustなどがある ● 非同期タスクが自発的に他のタスクに譲らず、処理系から割り込む方式を非協調 的マルチタスキングという ○ 実装が複雑になる反面、無限ループに陥ったタスクを処理系が強制終了させることができる ○ OSはこっち

Slide 63

Slide 63 text

さらなる高みへ ー 最適化への道 ● V8エンジンの制約によりNode.jsでは不可能だったが、複数Executorによる並列処 理を実装することで更にスループットが向上する ○ Rust(tokio)はイベント駆動 × マルチ軽量スレッド ○ Goは同期IO × マルチ軽量スレッド ○ Node.jsはイベント駆動 × シングルスレッド ● 今回は非同期I/Oを実装しなかったが、I/Oをポーリングするのではなくイベント駆動 にすることで計算負荷が低減する

Slide 64

Slide 64 text

Thank you for listening!