Upgrade to Pro — share decks privately, control downloads, hide ads and more …

同期的なプログラミング言語の視点から非同期処理を理解する/understand async from sync

同期的なプログラミング言語の視点から非同期処理を理解する/understand async from sync

PHPerKaigi 2022 の登壇資料です。
原題は「PHPでEventLoopを書いて非同期処理を完全に理解する」です。

Ryo Tomidokoro

April 10, 2022
Tweet

More Decks by Ryo Tomidokoro

Other Decks in Technology

Transcript

  1. @hanhan1978
    PHPでEventLoopを書いて非同期処理を
    完全に理解する
    PHPerKaigi 2022

    View Slide

  2. @hanhan1978
    同期的なプログラミング言語の目線から非
    同期処理を理解する
    PHPerKaigi 2022
    とりあえず改題

    View Slide

  3. イベントループは関係なかった
    3
    すまんな!

    View Slide

  4. @hanhan1978
    ● 富所 亮
    ● 所属
    株式会社カオナビ Expert
    ● 職業
    Webアプリケーションエンジニア
    ● ブログ
    https://blog.hanhans.net
    ● Yokohama North AM
    https://anchor.fm/yokohama-north-am
    4

    View Slide

  5. 前回までのあらすじ
    5

    View Slide

  6. 2021-11-25 PHP 8.1 Release
    Fibers が導入される
    6

    View Slide

  7. 7
    「非同期処理が簡単にかけるようになるらしいぜ?」

    View Slide

  8. ここにビジネスチャンスを見出したが....
    非同期沼に突入
    分かりにくいプレゼンを行ってしまう
    8

    View Slide

  9. 9
    https://speakerdeck.com/hanhan1978/php-async-programming

    View Slide

  10. 3行でまとめると...
    - 非同期処理はわからん
    - Fibers もよくわからん
    - とりあえず、PHPでPHPを動かせ
    10
    すまんかった

    View Slide

  11. 11
    リベンジ
    https://speakerdeck.com/hanhan1978/fiber-and-async-request

    View Slide

  12. 3行でまとめると...
    - なぜ Fibers
    - イベントループってなに?
    - なぜ 非同期の仕組みがいまさらPHPに入ってくる?
    12
    自信作だったが、スライドが荒かった

    View Slide

  13. 本日は合体整理版
    13

    View Slide

  14. ● 非同期処理とは
    ● 2つの視点
    ● イベントループ
    ● まとめ
    目次
    14

    View Slide

  15. ● 非同期処理とは
    ● 2つの視点
    ● イベントループ
    ● まとめ
    目次
    15

    View Slide

  16. 同期的なプログラミング
    記述したとおりの順番で動作するようなプログラミングモデル
    非同期プログラミング
    独立して発生するイベントに対する処理を記述するための並行プログラミン
    グ手法の総称
    16
    高野祐輝. (2021). 並行プログラミング入門 5章 (初版.). オライリー・ジャパン.

    View Slide

  17. 17
    PHPアプリケーションは
    同期的なBlocking IOが基本

    View Slide

  18. 18
    よくある MVC フレームワークのコード例

    View Slide

  19. 19
    プログラムは書いた順番に順次実行され
    ていく

    View Slide

  20. 20
    入出力処理が行われると
    プログラムは結果を待つ

    View Slide

  21. 21
    DB検索の処理シーケンス
    クエリ結果が返ってくるまで
    メインの処理は待つ
    ブロッキングIOと言う

    View Slide

  22. 22
    非同期プログラミングの例

    View Slide

  23. 23
    単純なAPIコールの例

    View Slide

  24. 24
    ここで処理が待たない

    View Slide

  25. 25
    IO処理の結果を待たない
    メインスレッドは、待ち時間の間
    に他の処理を行える
    ノンブロッキングIO という

    View Slide

  26. 26
    実行順序は直感と反する



    慣れれば分かるんだが ......

    View Slide

  27. 入出力処理の結果が、いつ返ってくるか分からないの
    で、結果が戻ってきたときに実行する処理をコール
    バックで渡しておく
    27

    View Slide

  28. 28
    非同期プログラミングの利点

    View Slide

  29. 29
    ブロッキング ノンブロッキング
    CPUを効率的に利用できる

    View Slide

  30. マルチスレッドは難しすぎたが
    非同期プログラミングであれば、普通の人間でもギリギリ許
    容できそう。
    Promise, Async/Await などを使えば、非同期処理も扱いや
    すくなる。
    30

    View Slide

  31. 世の中の潮流としては、マルチコア
    -> コアの利用効率を上げることで、プログラムの処理効率
    を上げる
    スループットの向上
    31

    View Slide

  32. ● 非同期処理とは
    ● 2つの視点
    ● イベントループ
    ● まとめ
    目次
    32

    View Slide

  33. 33
    非同期処理を考える時の二つの視点

    View Slide

  34. 34
    1リクエスト
    複数リクエスト(プロセス)

    View Slide

  35. 35
    1リクエスト単位の視点

    View Slide

  36. 36

    View Slide

  37. 37
    ToDoリストを検索するバックエンドの処理(例)
    非同期処理チャンス

    View Slide

  38. 38
    ToDoリストを検索するバックエンドの処理(例)
    2つめのクエリが1つ目のクエリに依存して
    いる
    無邪気に非同期処理を書くと、整合性のある
    データを取得できない

    View Slide

  39. 39
    コールバックPHPの仮想コード

    View Slide

  40. 40
    コールバックPHPの仮想コード
    結果出力時には、検索が終わってなければいけ
    ない。
    非同期処理をしてしまうと、データ取得前に結果
    出力するはめになる

    View Slide

  41. しょうがないので、全部コールバックにする....
    こうして、コールバック地獄が生まれる
    41

    View Slide

  42. 42
    コールバックPHPの仮想コード

    View Slide

  43. 43
    コールバックPHPの仮想コード
    同期的プログラミングのコードのほうがはるかに
    見通しが良い。
    処理時間も変わらない......

    View Slide

  44. コールバックは、直列に実行され、データが戻ってくるまで待
    つことになるので、非同期処理の書き方をしたところで、ス
    ループットが上がっていない。
    ※相互に依存した処理の場合、1リクエスト単位で見ると同
    期でも非同期でも処理時間は変わらない
    44

    View Slide

  45. Fibers が いわゆる PHP アプリケーション制作において役
    に立つ機能ではないと言われるのは、この辺。
    45

    View Slide

  46. とはいえ
    1リクエスト単位の視点でも非同期処理が役に立ちそうな
    ケースはある
    46

    View Slide

  47. 47
    例えば、APIリクエストを3回行
    う必要がある場合
    かつ、それぞれのAPIコールは
    相互に依存していない。

    View Slide

  48. 48
    こんな風に処理をまとめ
    られたら、効率的

    View Slide

  49. そういった用途には古来より curl_multi というものがある
    Fibers は別に使わなくても問題ない...
    49

    View Slide

  50. しかし、1リクエスト内でIO処理を束ねるようなケースは、そん
    なに多くない。
    レアケースに対して、本気に殴りに行くような改善よりも、もっと
    一般的なケースの改善をするほうが意味がある。
    50

    View Slide

  51. 1リクエスト単位で考えた場合
    PHPで非同期プログラミングを行うのは、あまり効果的である
    とは思えない。
    51

    View Slide

  52. 52
    余談

    View Slide

  53. JavaScript が基本的にノンブロッキングな処理を行う理由は、
    ブラウザを操作しているユーザーを待たせないため
    53

    View Slide

  54. 54
    JSのローディングイメージ例
    https://webdevtrick.com/lazy-load-images/

    View Slide

  55. ノンブロッキングを徹底することで、ブラウザ利用者の体験に
    寄与している。このへんはプログラミング言語の根本の思想に
    関わっている
    55

    View Slide

  56. 56
    複数リクエストの視点

    View Slide

  57. 話を単純化するために、CPUが1コアでウェブサーバーは1リ
    クエストずつ処理していくものとする。
    57

    View Slide

  58. 58
    1リクエストを以下のように図示する
    ※斜線部分は IO 以外の処理を行っているものとする

    View Slide

  59. 59
    通常の PHP ウェブアプリケーションでは
    リクエストはそれぞれ独立に直列で処理される

    View Slide

  60. 60
    いわゆる PHP ウェブサーバーの構成

    View Slide

  61. 今までの普通のPHPウェブアプリケーションでは、IO処理が
    もったいないと感じても、その間の資源を他に割り当てられな
    かった。
    61

    View Slide

  62. 62
    いわゆる PHP ウェブサーバーの構成
    Nginx も PHP-FPM も
    個別の PHP プロセスの処理状況を細かく
    監視できない

    View Slide

  63. もし、リクエストをまたいで非同期処理が行えたら、IO待ちの時
    に、他のリクエストを処理することで効率的にリクエストをさばく
    ことができる。
    63

    View Slide

  64. 64

    View Slide

  65. 実際に Fibers が利用が想定されているのは、リクエストをま
    たいだパターンが多そう
    リクエストをまたいで、処理の停止・実行を行うためには、全て
    のリクエスト処理が、PHPのメイン処理から実行されている必
    要がある
    65

    View Slide

  66. 66
    私が、PHP が PHP を実行する必要があると
    言っていたのはコレ。

    View Slide

  67. この世界観については、すでに Swoole 、 ReactPHP、
    AmPHP などで実現されている
    このように、PHPの主処理が停止せずにリクエストを実行して
    いくという処理パターンの文脈で
    イベントループがでてくる
    67

    View Slide

  68. 68
    余談

    View Slide

  69. 69
    Mastering Swoole PHP - Bruce Dou
    Swoole 作者による解説著作
    全体の2/3がPHPの処理モデル
    やIOモデルの説明となっていて、
    非同期を取り巻く状況を雑観でき
    る。

    View Slide

  70. ● 非同期処理とは
    ● 2つの視点
    ● イベントループ
    ● まとめ
    目次
    70

    View Slide

  71. 71
    イベントループとは?

    View Slide

  72. 72
    https://en.wikipedia.org/wiki/Event_loop
    イベントループの構成要素
    Wiki から想像して作図

    View Slide

  73. 73
    イベントループの構成要素
    リクエストはQueueで直列化される (DeMultiplex)

    View Slide

  74. 74
    イベントループの構成要素
    Event Loop 側はシングルスレッドで処理できる
    (Reactor Pattern)

    View Slide

  75. 75
    実装はループ処理
    例えば、配列にイベントが追加され
    ループで逐次処理されるのも、立派なイベントループ

    View Slide

  76. イベントループで実行されるEvent が Fiber オブジェクトになっ
    ていれば
    Suspend
    Restart
    をメインスレッドから抽象的に扱うことができる。
    76

    View Slide

  77. 77
    一番のポイント

    View Slide

  78. フレームワークの利用者側の視点で考えると
    同期的プログラミングを継続できる
    同期的プログラミングを行ったまま、非同期プログラミングの恩
    恵を受けることができる
    そんな非同期フレームワークを、Fibers を使うことで抽象的に
    理解しやすく作れる
    78

    View Slide

  79. 今後、Promise や Async/Await が PHP にもたらされる可能
    性はあるが、そもそも同期的であることに意味がある場合が多
    そうなので、利用シーンは限られそう
    79

    View Slide

  80. ● 非同期処理とは
    ● 2つの視点
    ● イベントループ
    ● まとめ
    目次
    80

    View Slide

  81. 非同期処理、Fibers、イベントループ
    この3つのキーワードがセットになって語られることが多いが、
    その理由はリクエストをまたいだ非同期処理を行うことが、
    PHPウェブアプリケーションにとって、もっとも現実的で徳が多
    そうだから
    81

    View Slide

  82. 82
    Fibers を使った Event Loop の例
    実装方法はたくさんある

    View Slide

  83. 実際は、ev拡張などを利用すれば、現状でもイベントループを
    簡単に書くことは出来る。
    Fibers はイベントループから実行された処理スレッドの停止・
    再開判断を抽象的に便利に行える機能と考えると分かりやす

    83

    View Slide

  84. 84
    とはいえ、書いてみないと実感しづらい

    View Slide

  85. 85
    雑なスライドだが実装サンプルを載せた
    https://speakerdeck.com/hanhan1978/fiber-and-async-request

    View Slide

  86. APIリクエストの非同期実行を
    生PHP -> Generator -> Fiber と抽象化していくサンプル
    Fibers は、抽象化の道具なんだね!が実感できる
    86

    View Slide

  87. @hanhan1978
    相談・指摘・その他 
    下記のTwitterアカウントにどうぞ
    87

    View Slide