Slide 1

Slide 1 text

いちユーザーが PHP に 新機能を追加するまで ~ Random Extension 5.x ~

Slide 2

Slide 2 text

氏名  : 部署  : 自己紹介 工藤 剛 / Go Kudo 2017 年新卒として入社し運用タイトルのサーバー エンジニアを経て SRE チームへ 運用タイトルの PHP のバージョンアップや静的解析 の導入・保守などを主に担当 2 サーバー基盤グループ SRE チーム

Slide 3

Slide 3 text

● Random Extension の概要 ● 提案までの経緯 ● PHP Internals Mailing List と議論の進め方 ● RFC の概要及び作成方法 ● PHP の開発・テストを行うための環境構築とテクニック ● マージされるまで、されたあとの作業 ● まとめ 3 Agenda

Slide 4

Slide 4 text

Random Extension の概要 4

Slide 5

Slide 5 text

Random Extension 乱数を用いた処理を提供する組み込みの拡張機能 特徴 ● オブジェクト指向な API ● 乱数生成器をユーザー定義可能 ● mt_srand(), mt_rand() とドロップインで交換可能 ● メルセンヌ・ツイスタよりモダンな RNG の提供 ● ステートがオブジェクト単位で保持されており安全 何が嬉しいのかについては後ほど 5

Slide 6

Slide 6 text

6 Random Extension

Slide 7

Slide 7 text

提案までの経緯 7

Slide 8

Slide 8 text

コロプラにおけるサーバーサイド構成図 8 引用: 大規模ゲームインフラとしての Kubernetes とノーメンテナンス運用 https://speakerdeck.com/toversus/da-gui-mo-kemuinhuratositefalse-kubernetes-tofalsementenansuyun-yong

Slide 9

Slide 9 text

9 PHP と API Server ● API Server ○ ステートレスに行える処理 ■ プレイ開始・終了処理 ■ データの取得・生成・受け渡し ○ リクエスト毎にステートレスな PHP を採用 ● Game Server ○ ステートフルな処理 ■ PvP, PvE におけるリレー通信 ■ Live Streaming における一対多・多対多通信 ○ Go や Node.js を採用 サーバー側ゲームロジックの大半を PHP で実装

Slide 10

Slide 10 text

ゲームの根幹を担うデータを生成・反映・保存する処理 たとえば... ● ダンジョンのマップ生成 ● 与えたり受けたりするダメージの計算 ● 獲得報酬の抽選 ゲームでは多くの場合にランダムな数 (乱数) が求められる 10 サーバー側ゲームロジックとは

Slide 11

Slide 11 text

ゲームに求められる乱数の要件 ● 可能な限りランダムな結果 ● 結果の再現性 ○ リプレイ機能やアプリケーションのチート検出のため ■ 抽選など結果の再現性よりもより真にランダムであることを求めることもある ● 実行速度 ランダムでありつつ再現性があるという相反する要素を 満たさなければならない 11 ゲームと乱数

Slide 12

Slide 12 text

無作為に生成された数値のこと (例: サイコロの出目) 現代のコンピュータは確定的な計算をしているため 完全に無作為な数を生成することは不可能 *1 *1 真なる乱数がどうしても必要な場合、ハードウェアベースの乱数生成器を搭載する場合もある。最近の CPU は熱ノイズなどを利用して乱数を生成する機能を備えており、拡張命令で利用可能だったりする (amd64 なら RDRAND, RDSEED 命令, arm64 (v8.5A 以降) なら RNDR 命令) 各 OS に搭載された CSPRNG (後述) もこれらの TRNG (後述) から生成された値を利用している事が多い 12 乱数とは?

Slide 13

Slide 13 text

乱数生成機は大まかに以下の形で分類できる 13 乱数生成器の分類 乱数生成器 (RNG: Random Number Generator) 真性乱数生成器 (TRNG: True Random Number Generator) 例: ● ダイス ● ノイズ ● コインの裏表 ● etc… 疑似乱数生成器 (PRNG: Pseudo Random Number Generator) 例: ● MT19937 ● Xorshift ● Xoshiro ● Xoroshiro ● PCG ● LCG ● etc… 暗号論的擬似乱数生成器 (CSPRNG: Cryptographically Secure PRNG) 例: ● Linux getrandom(2) ● BSD arc4random(3) ● etc…

Slide 14

Slide 14 text

PHP には再現性のある乱数生成器がすでに実装されている! しかし... 14 PHP と乱数生成器 (PRNG)

Slide 15

Slide 15 text

PHP の乱数生成器実装はいろいろな問題を抱えていた... https://zenn.dev/zeriyoshi/articles/abd808d1c6d31b 15 PHP と乱数生成器 (PRNG) 注: 記事は私的なものであり、所属する企業としての見解ではありません

Slide 16

Slide 16 text

1. メルセンヌ・ツイスタ *1 としてそもそも実装が間違っていた ○ PHP 7.1 にて修正された (互換性のため間違った実装も残されている) *1: 疑似乱数生成アルゴリズム, 周期が 2^19937 - 1 であることから MT19937 とも 2. 初期シード *2 が PID と日時で行われている ○ PHP 8.1 で直した https://github.com/php/php-src/commit/53ee3f7f897f7ee33a4c45210014648043386e13 *2: コンテナ環境で利用すると有効なシード値が時刻のみになり、結果が偏る恐れがある 3. PHP ランタイムに対して状態がグローバル ○ 詳細は次ページ 他にもいろいろ... https://zenn.dev/zeriyoshi/articles/abd808d1c6d31b 16 PHP の乱数生成器が抱える問題

Slide 17

Slide 17 text

メルセンヌ・ツイスタの状態 (次に生成される数値の元)が PHP の実行時ランタイ ムに対しグローバル 途中で意図せず mt_rand() を呼び出すとそれ以降の結果がズレる 17 "状態がグローバル" とは

Slide 18

Slide 18 text

乱数を利用する他の関数でもメルセンヌ・ツイスタの状態を進めてしまう ● mt_rand() ● shuffle() ● str_shuffle() ● array_rand() 何らかの機能追加などでこれらの関数を利用してしまうと 意図せず再現性が失われる 18 "状態がグローバル" とは

Slide 19

Slide 19 text

PHP 標準のメルセンヌ・ツイスタを安全に利用することは困難 ● とはいえ、ゲームにおいて乱数を一切用いないというのは不可能 ● PHP Extension として実装する? ○ C 言語のコードを正しくメンテナンスしていけるか ○ メモリ・リソースリークを防ぎきれるか ○ 上流 (PHP 本体の実装) に適切に追従していけるか ○ etc… PHP で PRNG を実装できないか? 19 乱数問題と解決案

Slide 20

Slide 20 text

PHP による Xorshift128+ の実装 ¥ 特徴 ● オブジェクトスコープ ● デフォルトの初期シードを random_int() で生成 ● Chromium や Firefox で実績のある Xorshift128+ アルゴリズム ● (PHP 実装にしては) 比較的高速 再現性が求められる用途にはこのライブラリを使用することに 20 CRandom

Slide 21

Slide 21 text

使用していく中でいくつかの問題が見えてきた ● (わかってはいたが) ネイティブ実装に比べて圧倒的に遅い ○ 特にシード処理が遅い ○ PHP 8.0 から導入された JIT でかなり改善できるが JIT 自体がまだ不安定で見送り ● 実装を切り替えることができない ○ "特定の要件を満たすまで乱数を生成し続ける" ワークロードの負荷試験が困難 ○ 負荷試験環境ではシード値固定の PRNG を、本番環境では CSPRNG を利用したい やっぱりこれ...ネイティブ実装できないかなぁ.... 21 CRandom の課題

Slide 22

Slide 22 text

とりあえず作って PECL で公開 (PoC) https://github.com/zeriyoshi/php-ext-orng 22 orng (Object-scoped Random Number Generator) PHP 自体に同じようなも のがあったほうが良いの では....?

Slide 23

Slide 23 text

PHP Internals Mailing List (開発者メーリングリスト) に似たような話があった! ORNG の実装を整理し、 PHP 本体に機能追加の提案を出してみよう 23 過去ログを見てみる

Slide 24

Slide 24 text

● ほとんど反応をもらえなかった ○ 最初に来たのは "あなたをノックダウンします" という返信 ○ カタコトなせいで "やべーやつ来たし触らんとこ" みたいな流れになってるのか?と推測 ■ sorry を連呼しグダグダと弁明 ● 更なる "やべーやつ" へ... ● Nikita 氏がポジティブな返信をくれ、徐々に話が進み始めた ○ とはいえ英語力の低さからなかなかうまく進まない... ● 意見をまとめられず何度も更新される仕様と実装 ○ 1.0 から始まったバージョンはついに 5.x に到達... 24 PHP Internals Mailing List に突撃

Slide 25

Slide 25 text

● PHP Extension を保守していた経験を元に実装を行った ○ php-src における作法を理解しきれておらず最初はいびつな実装に... ● Windows 環境でのデバッグがつらい (経験がない) 25 RFC の内容を実装 PHP 謹製の Autotools っぽい何か (php-sdk) と notepad.exe, cmd.exe, windbg.exe との たのしいひととき

Slide 26

Slide 26 text

いろいろありつつ (後述) も RFC は可決され、 master ブランチにマージされた しかし... PHP 8.2.0beta1 にて Windows 向けのビルドが SEGV する問題が発生 26 無事マージされるも...

Slide 27

Slide 27 text

ZTS ビルドにて特定のケースで未初期化領域にアクセスしていた... 27 SEGV 問題

Slide 28

Slide 28 text

28 謝り癖よくない

Slide 29

Slide 29 text

Internals ML での議論を積極的に進めてくれた Tim さんがとても協力してくれて いる 29 バグ修正やリファクタリング

Slide 30

Slide 30 text

Random Extension のメンテナになった 30 Random Extension Maintainer

Slide 31

Slide 31 text

Docker で手軽に試せるようになった! 31 PHP 8.2.0 RC1, 2 がリリース

Slide 32

Slide 32 text

PHP 8.2.0 ● ドキュメントの整備 (英, 日) ● PHP 8.2.0 ランディングページの整備 PHP 8.3.0 ● 検討中 ○ ランタイムグローバルな RNG 関数 (mt_srand(), mt_rand()) の非推奨化 ○ 暗黙的にランタイムグローバルな RNG 実装を利用する関数の実装変更 ■ shuffle() ■ str_shuffle() ■ array_rand() ○ MT_RAND_PHP の非推奨化 32 残タスク

Slide 33

Slide 33 text

タイムライン 33 2018 スコープの問題が社内で広く 認知され始める 対策チームが立ち上がる 超優秀な後輩が CRandom を 開発 2019 2020 Internals ML に最初の提案 メールを送信 当時の実装ターゲットは PHP 8.1 だった orng を開発 2021 2021 議論が進展しないため投票へ踏 み切るも API と実装のいびつさ から Declined に 投票開始と共に大量に寄せられ るツッコミの嵐に泣きそうになる コンセプトには理解が得られてい たので、投票フェーズになってか ら指摘された問題を修正した RFC を作ることに 議論を続けるも落とし所が見つ からず、モチベが低下 2022 8.1 Feature Freeze を目前に するも Windows 環境でのデ バッグで行き詰まりモチベが終 了 手元に Windows マシンが無い 問題を解決するため会社として 取り組めないかを相談し、 OK をもらう 致命的だった英語面もチームメ ンバーに協力してもらえることに じわじわとだが議論も進み RFC も改善されてくる 8.2 Feature Freeze 目前に投票を開始、無 事 Accepted Supplemental RFC master merged

Slide 34

Slide 34 text

PHP Internals Mailing List と 議論の進め方 34

Slide 35

Slide 35 text

PHP Internals Mailing List (以下: Internals ML) とは PHP の "内部" にまつわる話題のためのメーリングリスト https://www.php.net/mailing-lists.php ● 実装の変更・新機能追加の議論 ● 変更や新機能追加に伴う RFC 作成権限の付与依頼・議論と各種アナウンス ● PHP Group 自体の運用に関わる議論 PHP-ML を見やすくまとめてくれる externals.io が便利 https://externals.io/ 35

Slide 36

Slide 36 text

1. Internals ML に参加する 2. 提案メールを出してみる (いわゆるジャブ) 3. RFC を書く (この時点でひとまずの実装・ GitHub への PR を出すと良) 4. 最低 2 週間ほど議論を行う 5. RFC の状態を Vote にして ML に告知メールを出す 6. 2 週間投票を行う 7. 投票結果に応じて状態を Accepted または Declined に更新し、メールを出す 参照: https://wiki.php.net/rfc/howto 36 Internals ML での大まかな流れ

Slide 37

Slide 37 text

● Under Discussion の時に反応がないことが多いがめげない ○ Vote フェーズになって議論が突然加速することが多い ○ 反応がなさすぎても "もしかして空気読めてない?ごめん" とか言わない ○ ML では反応がなくても stackoverflow の Room 11 では触れられてたりも ■ あんまり公な場所じゃなさそうなのでチラ見するくらいで https://chat.stackoverflow.com/rooms/11/php ● 英語は機械翻訳でだいたいなんとかなる ○ とはいえスラングとかも結構使われたりする ○ 英語がダメダメならある程度は覚悟しておいたほうが良い ■ 覚悟しておけばなんとかなるレベル ● なんでもかんでも sorry と言わない ○ 日本人的な感覚で sorry を使いすぎるのはダメ ○ 情緒的ではなく建設的に話をする 37 Internals ML での議論のポイント

Slide 38

Slide 38 text

RFC の概要と 作成方法 38

Slide 39

Slide 39 text

RFC とは Request for Comment の略 ● 現状どういう課題があるのか ● どういう解決策があるのか ● することによるメリット・デメリット を簡潔にまとめたもの 下位互換性に影響する変更や新機能の追加には RFC を作成し、有権者による 2/3 以上の賛成を 得る必要がある 39

Slide 40

Slide 40 text

まずは Internals ML や Room 11 でジャブを打ってみる 既存の RFC をなんとなく読んでおき、提案内容の RFC の大まかな流れが思いついたら wiki.php.net のアカウントを作成して Internals ML に RFC karma のリクエストを送る 今見るとだいぶ恥ずかしい... 40 RFC 作成までの流れ

Slide 41

Slide 41 text

具体的には ● 提案の概要 ● より具体的な提案の内容 ● メリットとデメリット ● (必要なら) 将来的な展望 ● 下位互換性への影響 を書いていく https://wiki.php.net/rfc/rng_extension 41 RFC の書き方

Slide 42

Slide 42 text

● 過去の RFC を参考にしよう ○ 文章のフォーマットみたいなのは過去に即しておけばだいたいなんとかなる ○ RFC karma が得られたら他の人のページも見られるので参考に ● 人に頼ろう ○ ネイティブもしくはそれに近い人がいれば事前に読んでもらえないか頼む ○ Twitter にいる日本の PHP 界隈の人たちも見てくれるかも ● 正直、めっちゃ難しい ○ 秩序立てが困難 ■ ですます調がだである調になるみたいなのが頻発 ■ ツッコミの嵐をくらって勝手にメンタルが削れる ● 相手に悪意があるわけではないので考えすぎないのが吉 ○ 英語で論文とか書いたことがあれば苦でもない? (書いたこと無い) 42 RFC の書き方のポイント

Slide 43

Slide 43 text

環境構築と テクニック 43

Slide 44

Slide 44 text

PHP の技術スタック ● 言語は基本的に C (C99), m4 (Autotools) ● 対応するプラットフォームは Unix-like OS (含 macOS), Windows ● 複数のスレッドモデル ○ NTS: Non-Thread Safe (Unix-like OS での標準) ○ ZTS: Zend Thread Safe (スレッドセーフ, Windows IIS での標準) ● 複数の SAPI cli: Command Line Interface, fpm: FastCGI Process Manager apache2handler: Apache, cgi: CGI, etc... 44

Slide 45

Slide 45 text

● Ubuntu ベースで環境を構築すると楽 ○ PHP 本体の CI でも使われているのでパイプラインを参考にできる https://github.com/php/php-src/blob/master/azure/job.yml ○ ってかだいたい既存の CI パイプラインを参考にすればなんとかなる ● Docker を活用する ○ static QEMU + binfmt_misc で他のアーキテクチャを利用可能にしておくと楽 ○ Docker Desktop for Mac なら標準で QEMU が組み込まれているので何もしなくていい ○ ビッグエンディアン (IBM s390x など) な環境も簡単に作れる ● 開発マシンには amd64 (非 Apple Silicon マシン) を使うと快適 ○ 32-bit (i386) 環境でのテストが速い このあたりをしっかり説明すると発表に収まらなくなるので 後日 COLOPL Tech Blog あたりに書きたい... 45 PHP 開発 Tips 1

Slide 46

Slide 46 text

● 基本的なビルドからテストまでの道のり ○ $ ./buildconf —force ○ $ ./configure —enable-debug ○ $ make -j$(nproc) ○ $ make test ● 開発に使えるツールを把握しておく ○ Valgrind ■ https://valgrind.org/ ■ ./configure で —with-valgrind の指定が必要 ○ LLVM Sanitizer (ASan, MSan, UBSan) ■ https://github.com/google/sanitizers ■ Clang でオプションを指定してビルドする必要がある ○ Zend Memory Manager によるメモリ管理が行われるとデバッグが困難になるので 環境変数に USE_ZEND_ALLOC=0 を指定すること (OS 標準のメモリ確保関数を使う) 46 PHP 開発 Tips 2

Slide 47

Slide 47 text

● PHP 本体のテストフレームワーク (PHPT) を覚えよう ○ Random Extension の例: https://github.com/php/php-src/tree/master/ext/random/tests ○ run-tests.php で実行する ■ make コマンドで実行したほうが楽かも $ TEST_PHP_ARGS="./ext/random/tests" make test ● クラスや関数の定義は PHP ファイルで行う (ようになった) ○ Random Extension の例: https://github.com/php/php-src/blob/master/ext/random/random.stub.php ○ 編集したら以下を実行して C ヘッダファイルを生成 (要 PHP) $ ./build/gen-stub.php —force-regeneration —generate-optimizer-info 47 PHP 開発 Tips 2

Slide 48

Slide 48 text

あとは 既存の実装を見つつ真似すれば わりとなんとかなる! 48

Slide 49

Slide 49 text

わからないことがあったら Twitter とかで聞いてください 多分私もわからないので一緒に悩みます 49 相談所のご案内

Slide 50

Slide 50 text

マージされるまで・ されたあとの 作業について 50

Slide 51

Slide 51 text

マージされるまで 実装が完了したら https://github.com/php/php-src に PR を作成 https://github.com/php/php-src/pull/8094 GitHub を見ていない人もいるので RFC についての議論は GitHub ではなく Internals ML で行うこと その他は特に変わった部分はない 51

Slide 52

Slide 52 text

不備や不具合などが見つかると Internals ML で指摘をもらったり GitHub の Issue が立つので定期的に確認しておく (両方見ておくこと) 必要に応じて修正し、 Pull Requestを作成する 52 マージされたあと

Slide 53

Slide 53 text

マージされた後に RFC を伴うレベルの変更が必要になった場合、追加の RFC を 作成する場合がある (所謂 Supplemental RFC) 過去の例 ● Shorter Attribute Syntax https://wiki.php.net/rfc/shorter_attribute_syntax ○ Shorter Attribute Syntax Change https://wiki.php.net/rfc/shorter_attribute_syntax_change ● Attributes (v2) https://wiki.php.net/rfc/attributes_v2 ○ Attribute Amendments https://wiki.php.net/rfc/attribute_amendments 53 補足的な変更が必要になった場合

Slide 54

Slide 54 text

● Feature Freeze に間に合う場合 ○ RFC を新規に作成し、通常通り投票を行う ○ Random Extension でも発生した ■ https://wiki.php.net/rfc/random_extension_improvement ● Feature Freeze に間に合わない場合 ○ RFC を介さない (対象バージョンの Release Manager 及び Internals ML での議論ベー ス) の変更を行う必要が生じる ■ これ自体そもそも良いことではない ○ こちらも Random Extension で発生してしまった ■ https://externals.io/message/118290 期限ギリギリで RFC を作成するのは危険 54 Supplemental RFC の注意点

Slide 55

Slide 55 text

まとめ 55

Slide 56

Slide 56 text

タスクリスト 1. 言語改善のためのアイディア考案 2. 実現可能か調査・検討 3. アイディアの共有・ブラッシュアップ ○ Internals ML, Twitter, stackoverflow Room 11… 4. Pull Request 作成 5. (なければ) RFC karma のリクエスト, RFC 作成 6. Internals ML での議論・投票 7. Pull Request のレビュー対応 8. ドキュメントの整備 9. リリース! 56

Slide 57

Slide 57 text

振り返り ● 機能実装・整理に関してはコツさえ掴めばかなり直感的 ○ そこまでゴリゴリに C 言語っぽい感じではない ■ 確かに Zephir みたいなアプローチをしてみたくなる気持ちもわかる ○ ある程度の慣れは必要そう ● コミュニティの課題も見えてきた ○ Random Extension に限らずどの RFC も投票フェーズになって初めて議論が進む傾向 ■ stackoverflow Room 11 はカジュアルに話ができて良いが、通常のルートからは たどり着けない... ● 現状半公式みたいになってるけどいっそ公式にしてほしい気持ち ● コミュニケーションの難しさ ○ ニホンジン エイゴ ニガテ ○ 今からでも本気で英会話勉強しようかなという気持ちが芽生えた 57

Slide 58

Slide 58 text

コロプラではエンターテインメントで日常をより楽しくする仲間を募集しています ● Google Cloud Spanner や Kubernetes (GKE) を用いた開発 ● PHP, Go を用いたゲームアプリケーション開発 ● OSS 活動も積極的に進めています https://github.com/colopl https://be-ars.colopl.co.jp/recruit/career/ 技術ブログもよろしくお願いします! https://blog.colopl.dev/ 58 We are Hiring!

Slide 59

Slide 59 text

質疑応答 59

Slide 60

Slide 60 text

60 ご清聴 ありがとう ございました