※資料内の参照リンクを選択し閲覧する場合は、ダウンロードをお願いいたします
\積極的に技術発信を行なっております/ ▽ Twitter/COLOPL_Tech https://twitter.com/colopl_tech
▽ connpassページ http://colopl.connpass.com
▽ COLOPL Tech Blog http://blog.colopl.dev
いちユーザーが PHP に新機能を追加するまで~ Random Extension 5.x ~
View Slide
氏名 :部署 :自己紹介工藤 剛 / Go Kudo2017 年新卒として入社し運用タイトルのサーバーエンジニアを経て SRE チームへ運用タイトルの PHP のバージョンアップや静的解析の導入・保守などを主に担当2サーバー基盤グループ SRE チーム
● Random Extension の概要● 提案までの経緯● PHP Internals Mailing List と議論の進め方● RFC の概要及び作成方法● PHP の開発・テストを行うための環境構築とテクニック● マージされるまで、されたあとの作業● まとめ3Agenda
Random Extensionの概要4
Random Extension乱数を用いた処理を提供する組み込みの拡張機能特徴● オブジェクト指向な API● 乱数生成器をユーザー定義可能● mt_srand(), mt_rand() とドロップインで交換可能● メルセンヌ・ツイスタよりモダンな RNG の提供● ステートがオブジェクト単位で保持されており安全何が嬉しいのかについては後ほど5
6Random Extension
提案までの経緯7
コロプラにおけるサーバーサイド構成図8引用: 大規模ゲームインフラとしての Kubernetes とノーメンテナンス運用https://speakerdeck.com/toversus/da-gui-mo-kemuinhuratositefalse-kubernetes-tofalsementenansuyun-yong
9PHP と API Server● API Server○ ステートレスに行える処理■ プレイ開始・終了処理■ データの取得・生成・受け渡し○ リクエスト毎にステートレスな PHP を採用● Game Server○ ステートフルな処理■ PvP, PvE におけるリレー通信■ Live Streaming における一対多・多対多通信○ Go や Node.js を採用サーバー側ゲームロジックの大半を PHP で実装
ゲームの根幹を担うデータを生成・反映・保存する処理たとえば...● ダンジョンのマップ生成● 与えたり受けたりするダメージの計算● 獲得報酬の抽選ゲームでは多くの場合にランダムな数 (乱数) が求められる10サーバー側ゲームロジックとは
ゲームに求められる乱数の要件● 可能な限りランダムな結果● 結果の再現性○ リプレイ機能やアプリケーションのチート検出のため■ 抽選など結果の再現性よりもより真にランダムであることを求めることもある● 実行速度ランダムでありつつ再現性があるという相反する要素を満たさなければならない11ゲームと乱数
無作為に生成された数値のこと (例: サイコロの出目)現代のコンピュータは確定的な計算をしているため完全に無作為な数を生成することは不可能 *1*1真なる乱数がどうしても必要な場合、ハードウェアベースの乱数生成器を搭載する場合もある。最近のCPU は熱ノイズなどを利用して乱数を生成する機能を備えており、拡張命令で利用可能だったりする(amd64 なら RDRAND, RDSEED 命令, arm64 (v8.5A 以降) なら RNDR 命令)各 OS に搭載された CSPRNG (後述) もこれらの TRNG (後述) から生成された値を利用している事が多い12乱数とは?
乱数生成機は大まかに以下の形で分類できる13乱数生成器の分類乱数生成器 (RNG: Random Number Generator)真性乱数生成器(TRNG: TrueRandom NumberGenerator)例:● ダイス● ノイズ● コインの裏表● etc…疑似乱数生成器(PRNG: Pseudo Random Number Generator)例:● MT19937● Xorshift● Xoshiro● Xoroshiro● PCG● LCG● etc…暗号論的擬似乱数生成器(CSPRNG:Cryptographically SecurePRNG)例:● Linux getrandom(2)● BSD arc4random(3)● etc…
PHP には再現性のある乱数生成器がすでに実装されている!しかし...14PHP と乱数生成器 (PRNG)
PHP の乱数生成器実装はいろいろな問題を抱えていた...https://zenn.dev/zeriyoshi/articles/abd808d1c6d31b15PHP と乱数生成器 (PRNG)注: 記事は私的なものであり、所属する企業としての見解ではありません
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/abd808d1c6d31b16PHP の乱数生成器が抱える問題
メルセンヌ・ツイスタの状態 (次に生成される数値の元)が PHP の実行時ランタイムに対しグローバル途中で意図せず mt_rand() を呼び出すとそれ以降の結果がズレる17"状態がグローバル" とは
乱数を利用する他の関数でもメルセンヌ・ツイスタの状態を進めてしまう● mt_rand()● shuffle()● str_shuffle()● array_rand()何らかの機能追加などでこれらの関数を利用してしまうと意図せず再現性が失われる18"状態がグローバル" とは
PHP 標準のメルセンヌ・ツイスタを安全に利用することは困難● とはいえ、ゲームにおいて乱数を一切用いないというのは不可能● PHP Extension として実装する?○ C 言語のコードを正しくメンテナンスしていけるか○ メモリ・リソースリークを防ぎきれるか○ 上流 (PHP 本体の実装) に適切に追従していけるか○ etc…PHP で PRNG を実装できないか?19乱数問題と解決案
PHP による Xorshift128+ の実装¥特徴● オブジェクトスコープ● デフォルトの初期シードを random_int() で生成● Chromium や Firefox で実績のある Xorshift128+ アルゴリズム● (PHP 実装にしては) 比較的高速再現性が求められる用途にはこのライブラリを使用することに20CRandom
使用していく中でいくつかの問題が見えてきた● (わかってはいたが) ネイティブ実装に比べて圧倒的に遅い○ 特にシード処理が遅い○ PHP 8.0 から導入された JIT でかなり改善できるが JIT 自体がまだ不安定で見送り● 実装を切り替えることができない○ "特定の要件を満たすまで乱数を生成し続ける" ワークロードの負荷試験が困難○ 負荷試験環境ではシード値固定の PRNG を、本番環境では CSPRNG を利用したいやっぱりこれ...ネイティブ実装できないかなぁ....21CRandom の課題
とりあえず作って PECL で公開 (PoC)https://github.com/zeriyoshi/php-ext-orng22orng (Object-scoped Random Number Generator)PHP 自体に同じようなものがあったほうが良いのでは....?
PHP Internals Mailing List (開発者メーリングリスト) に似たような話があった!ORNG の実装を整理し、 PHP 本体に機能追加の提案を出してみよう23過去ログを見てみる
● ほとんど反応をもらえなかった○ 最初に来たのは "あなたをノックダウンします" という返信○ カタコトなせいで "やべーやつ来たし触らんとこ" みたいな流れになってるのか?と推測■ sorry を連呼しグダグダと弁明● 更なる "やべーやつ" へ...● Nikita 氏がポジティブな返信をくれ、徐々に話が進み始めた○ とはいえ英語力の低さからなかなかうまく進まない...● 意見をまとめられず何度も更新される仕様と実装○ 1.0 から始まったバージョンはついに 5.x に到達...24PHP Internals Mailing List に突撃
● PHP Extension を保守していた経験を元に実装を行った○ php-src における作法を理解しきれておらず最初はいびつな実装に...● Windows 環境でのデバッグがつらい (経験がない)25RFC の内容を実装PHP 謹製の Autotools っぽい何か (php-sdk) とnotepad.exe, cmd.exe, windbg.exe とのたのしいひととき
いろいろありつつ (後述) も RFC は可決され、 master ブランチにマージされたしかし...PHP 8.2.0beta1 にて Windows 向けのビルドが SEGV する問題が発生26無事マージされるも...
ZTS ビルドにて特定のケースで未初期化領域にアクセスしていた...27SEGV 問題
28謝り癖よくない
Internals ML での議論を積極的に進めてくれた Tim さんがとても協力してくれている29バグ修正やリファクタリング
Random Extension のメンテナになった30Random Extension Maintainer
Docker で手軽に試せるようになった!31PHP 8.2.0 RC1, 2 がリリース
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残タスク
タイムライン332018スコープの問題が社内で広く認知され始める対策チームが立ち上がる超優秀な後輩が CRandom を開発2019 2020Internals ML に最初の提案メールを送信当時の実装ターゲットは PHP8.1 だったorng を開発20212021議論が進展しないため投票へ踏み切るも API と実装のいびつさから Declined に投票開始と共に大量に寄せられるツッコミの嵐に泣きそうになるコンセプトには理解が得られていたので、投票フェーズになってから指摘された問題を修正したRFC を作ることに議論を続けるも落とし所が見つからず、モチベが低下20228.1 Feature Freeze を目前にするも Windows 環境でのデバッグで行き詰まりモチベが終了手元に Windows マシンが無い問題を解決するため会社として取り組めないかを相談し、 OKをもらう致命的だった英語面もチームメンバーに協力してもらえることにじわじわとだが議論も進みRFC も改善されてくる8.2 Feature Freeze目前に投票を開始、無事 AcceptedSupplemental RFCmaster merged
PHP InternalsMailing List と議論の進め方34
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
1. Internals ML に参加する2. 提案メールを出してみる (いわゆるジャブ)3. RFC を書く (この時点でひとまずの実装・ GitHub への PR を出すと良)4. 最低 2 週間ほど議論を行う5. RFC の状態を Vote にして ML に告知メールを出す6. 2 週間投票を行う7. 投票結果に応じて状態を Accepted または Declined に更新し、メールを出す参照: https://wiki.php.net/rfc/howto36Internals ML での大まかな流れ
● Under Discussion の時に反応がないことが多いがめげない○ Vote フェーズになって議論が突然加速することが多い○ 反応がなさすぎても "もしかして空気読めてない?ごめん" とか言わない○ ML では反応がなくても stackoverflow の Room 11 では触れられてたりも■ あんまり公な場所じゃなさそうなのでチラ見するくらいでhttps://chat.stackoverflow.com/rooms/11/php● 英語は機械翻訳でだいたいなんとかなる○ とはいえスラングとかも結構使われたりする○ 英語がダメダメならある程度は覚悟しておいたほうが良い■ 覚悟しておけばなんとかなるレベル● なんでもかんでも sorry と言わない○ 日本人的な感覚で sorry を使いすぎるのはダメ○ 情緒的ではなく建設的に話をする37Internals ML での議論のポイント
RFC の概要と作成方法38
RFC とはRequest for Comment の略● 現状どういう課題があるのか● どういう解決策があるのか● することによるメリット・デメリットを簡潔にまとめたもの下位互換性に影響する変更や新機能の追加にはRFC を作成し、有権者による 2/3 以上の賛成を得る必要がある39
まずは Internals ML や Room 11 でジャブを打ってみる既存の RFC をなんとなく読んでおき、提案内容の RFC の大まかな流れが思いついたらwiki.php.net のアカウントを作成して Internals ML に RFC karma のリクエストを送る今見るとだいぶ恥ずかしい...40RFC 作成までの流れ
具体的には● 提案の概要● より具体的な提案の内容● メリットとデメリット● (必要なら) 将来的な展望● 下位互換性への影響を書いていくhttps://wiki.php.net/rfc/rng_extension41RFC の書き方
● 過去の RFC を参考にしよう○ 文章のフォーマットみたいなのは過去に即しておけばだいたいなんとかなる○ RFC karma が得られたら他の人のページも見られるので参考に● 人に頼ろう○ ネイティブもしくはそれに近い人がいれば事前に読んでもらえないか頼む○ Twitter にいる日本の PHP 界隈の人たちも見てくれるかも● 正直、めっちゃ難しい○ 秩序立てが困難■ ですます調がだである調になるみたいなのが頻発■ ツッコミの嵐をくらって勝手にメンタルが削れる● 相手に悪意があるわけではないので考えすぎないのが吉○ 英語で論文とか書いたことがあれば苦でもない? (書いたこと無い)42RFC の書き方のポイント
環境構築とテクニック43
PHP の技術スタック● 言語は基本的に C (C99), m4 (Autotools)● 対応するプラットフォームは Unix-like OS (含 macOS), Windows● 複数のスレッドモデル○ NTS: Non-Thread Safe (Unix-like OS での標準)○ ZTS: Zend Thread Safe (スレッドセーフ, Windows IIS での標準)● 複数の SAPIcli: Command Line Interface, fpm: FastCGI Process Managerapache2handler: Apache, cgi: CGI, etc...44
● 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 あたりに書きたい...45PHP 開発 Tips 1
● 基本的なビルドからテストまでの道のり○ $ ./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 標準のメモリ確保関数を使う)46PHP 開発 Tips 2
● 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-info47PHP 開発 Tips 2
あとは既存の実装を見つつ真似すればわりとなんとかなる!48
わからないことがあったら Twitter とかで聞いてください多分私もわからないので一緒に悩みます49相談所のご案内
マージされるまで・されたあとの作業について50
マージされるまで実装が完了したら https://github.com/php/php-src に PR を作成https://github.com/php/php-src/pull/8094GitHub を見ていない人もいるので RFC についての議論は GitHub ではなくInternals ML で行うことその他は特に変わった部分はない51
不備や不具合などが見つかると Internals ML で指摘をもらったり GitHub のIssue が立つので定期的に確認しておく (両方見ておくこと)必要に応じて修正し、 Pull Requestを作成する52マージされたあと
マージされた後に RFC を伴うレベルの変更が必要になった場合、追加の RFC を作成する場合がある (所謂 Supplemental RFC)過去の例● Shorter Attribute Syntaxhttps://wiki.php.net/rfc/shorter_attribute_syntax○ Shorter Attribute Syntax Changehttps://wiki.php.net/rfc/shorter_attribute_syntax_change● Attributes (v2)https://wiki.php.net/rfc/attributes_v2○ Attribute Amendmentshttps://wiki.php.net/rfc/attribute_amendments53補足的な変更が必要になった場合
● 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 を作成するのは危険54Supplemental RFC の注意点
まとめ55
タスクリスト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
振り返り● 機能実装・整理に関してはコツさえ掴めばかなり直感的○ そこまでゴリゴリに C 言語っぽい感じではない■ 確かに Zephir みたいなアプローチをしてみたくなる気持ちもわかる○ ある程度の慣れは必要そう● コミュニティの課題も見えてきた○ Random Extension に限らずどの RFC も投票フェーズになって初めて議論が進む傾向■ stackoverflow Room 11 はカジュアルに話ができて良いが、通常のルートからはたどり着けない...● 現状半公式みたいになってるけどいっそ公式にしてほしい気持ち● コミュニケーションの難しさ○ ニホンジン エイゴ ニガテ○ 今からでも本気で英会話勉強しようかなという気持ちが芽生えた57
コロプラではエンターテインメントで日常をより楽しくする仲間を募集しています● Google Cloud Spanner や Kubernetes (GKE) を用いた開発● PHP, Go を用いたゲームアプリケーション開発● OSS 活動も積極的に進めていますhttps://github.com/coloplhttps://be-ars.colopl.co.jp/recruit/career/技術ブログもよろしくお願いします!https://blog.colopl.dev/58We are Hiring!
質疑応答59
60ご清聴ありがとうございました