Share
Embed
「いちユーザーがPHPに新機能を追加するまで/PHPConference 2022」
※資料内の参照リンクを選択し閲覧する場合は、ダウンロードをお願いいたします
\積極的に技術発信を行なっております/
▽ Twitter/COLOPL_Tech
https://twitter.com/colopl_tech
▽ connpassページ
http://colopl.connpass.com
▽ COLOPL Tech Blog
http://blog.colopl.dev
COLOPL Inc.
September 22, 2022
Transcript
60 slides
-
氏名 : 部署 : 自己紹介 工藤 剛 / Go Kudo
2017 年新卒として入社し運用タイトルのサーバー エンジニアを経て SRE チームへ 運用タイトルの PHP のバージョンアップや静的解析 の導入・保守などを主に担当 2 サーバー基盤グループ SRE チーム -
• Random Extension の概要 • 提案までの経緯 • PHP Internals Mailing
List と議論の進め方 • RFC の概要及び作成方法 • PHP の開発・テストを行うための環境構築とテクニック • マージされるまで、されたあとの作業 • まとめ 3 Agenda -
Random Extension 乱数を用いた処理を提供する組み込みの拡張機能 特徴 • オブジェクト指向な API • 乱数生成器をユーザー定義可能 •
mt_srand(), mt_rand() とドロップインで交換可能 • メルセンヌ・ツイスタよりモダンな RNG の提供 • ステートがオブジェクト単位で保持されており安全 何が嬉しいのかについては後ほど 5 -
9 PHP と 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:
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… -
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 の乱数生成器が抱える問題 -
乱数を利用する他の関数でもメルセンヌ・ツイスタの状態を進めてしまう • 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 実装にしては) 比較的高速 再現性が求められる用途にはこのライブラリを使用することに 20 CRandom -
使用していく中でいくつかの問題が見えてきた • (わかってはいたが) ネイティブ実装に比べて圧倒的に遅い ◦ 特にシード処理が遅い ◦ PHP 8.0 から導入された
JIT でかなり改善できるが JIT 自体がまだ不安定で見送り • 実装を切り替えることができない ◦ "特定の要件を満たすまで乱数を生成し続ける" ワークロードの負荷試験が困難 ◦ 負荷試験環境ではシード値固定の PRNG を、本番環境では CSPRNG を利用したい やっぱりこれ...ネイティブ実装できないかなぁ.... 21 CRandom の課題 -
とりあえず作って PECL で公開 (PoC) https://github.com/zeriyoshi/php-ext-orng 22 orng (Object-scoped Random Number
Generator) PHP 自体に同じようなも のがあったほうが良いの では....? -
• ほとんど反応をもらえなかった ◦ 最初に来たのは "あなたをノックダウンします" という返信 ◦ カタコトなせいで "やべーやつ来たし触らんとこ" みたいな流れになってるのか?と推測
▪ sorry を連呼しグダグダと弁明 • 更なる "やべーやつ" へ... • Nikita 氏がポジティブな返信をくれ、徐々に話が進み始めた ◦ とはいえ英語力の低さからなかなかうまく進まない... • 意見をまとめられず何度も更新される仕様と実装 ◦ 1.0 から始まったバージョンはついに 5.x に到達... 24 PHP Internals Mailing List に突撃 -
• PHP Extension を保守していた経験を元に実装を行った ◦ php-src における作法を理解しきれておらず最初はいびつな実装に... • Windows 環境でのデバッグがつらい
(経験がない) 25 RFC の内容を実装 PHP 謹製の Autotools っぽい何か (php-sdk) と notepad.exe, cmd.exe, windbg.exe との たのしいひととき -
いろいろありつつ (後述) も RFC は可決され、 master ブランチにマージされた しかし... PHP 8.2.0beta1
にて Windows 向けのビルドが SEGV する問題が発生 26 無事マージされるも... -
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 残タスク -
タイムライン 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 -
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/howto 36 Internals ML での大まかな流れ -
• Under Discussion の時に反応がないことが多いがめげない ◦ Vote フェーズになって議論が突然加速することが多い ◦ 反応がなさすぎても "もしかして空気読めてない?ごめん"
とか言わない ◦ ML では反応がなくても stackoverflow の Room 11 では触れられてたりも ▪ あんまり公な場所じゃなさそうなのでチラ見するくらいで https://chat.stackoverflow.com/rooms/11/php • 英語は機械翻訳でだいたいなんとかなる ◦ とはいえスラングとかも結構使われたりする ◦ 英語がダメダメならある程度は覚悟しておいたほうが良い ▪ 覚悟しておけばなんとかなるレベル • なんでもかんでも sorry と言わない ◦ 日本人的な感覚で sorry を使いすぎるのはダメ ◦ 情緒的ではなく建設的に話をする 37 Internals ML での議論のポイント -
RFC とは Request for Comment の略 • 現状どういう課題があるのか • どういう解決策があるのか
• することによるメリット・デメリット を簡潔にまとめたもの 下位互換性に影響する変更や新機能の追加には RFC を作成し、有権者による 2/3 以上の賛成を 得る必要がある 39 -
まずは Internals ML や Room 11 でジャブを打ってみる 既存の RFC をなんとなく読んでおき、提案内容の
RFC の大まかな流れが思いついたら wiki.php.net のアカウントを作成して Internals ML に RFC karma のリクエストを送る 今見るとだいぶ恥ずかしい... 40 RFC 作成までの流れ -
具体的には • 提案の概要 • より具体的な提案の内容 • メリットとデメリット • (必要なら) 将来的な展望
• 下位互換性への影響 を書いていく https://wiki.php.net/rfc/rng_extension 41 RFC の書き方 -
• 過去の RFC を参考にしよう ◦ 文章のフォーマットみたいなのは過去に即しておけばだいたいなんとかなる ◦ RFC karma が得られたら他の人のページも見られるので参考に
• 人に頼ろう ◦ ネイティブもしくはそれに近い人がいれば事前に読んでもらえないか頼む ◦ Twitter にいる日本の PHP 界隈の人たちも見てくれるかも • 正直、めっちゃ難しい ◦ 秩序立てが困難 ▪ ですます調がだである調になるみたいなのが頻発 ▪ ツッコミの嵐をくらって勝手にメンタルが削れる • 相手に悪意があるわけではないので考えすぎないのが吉 ◦ 英語で論文とか書いたことがあれば苦でもない? (書いたこと無い) 42 RFC の書き方のポイント -
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 -
• 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 -
• 基本的なビルドからテストまでの道のり ◦ $ ./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 -
• 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 -
マージされるまで 実装が完了したら https://github.com/php/php-src に PR を作成 https://github.com/php/php-src/pull/8094 GitHub を見ていない人もいるので RFC
についての議論は GitHub ではなく Internals ML で行うこと その他は特に変わった部分はない 51 -
不備や不具合などが見つかると Internals ML で指摘をもらったり GitHub の Issue が立つので定期的に確認しておく (両方見ておくこと) 必要に応じて修正し、
Pull Requestを作成する 52 マージされたあと -
マージされた後に 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 補足的な変更が必要になった場合 -
• 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 の注意点 -
タスクリスト 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/colopl https://be-ars.colopl.co.jp/recruit/career/ 技術ブログもよろしくお願いします! https://blog.colopl.dev/ 58 We are Hiring!