Slide 1

Slide 1 text

乱数と向き合う 〜PHP によるゲームアプリケーションの実装と運用〜 株式会社コロプラ 技術基盤本部 第2バックエンドエンジニア部 サーバー基盤グループ SRE チーム 工藤 剛 1

Slide 2

Slide 2 text

自己紹介 工藤 剛 • 2017 年度新卒入社 (6 年目) • サーバー基盤グループ SRE チーム – タイトル横断での SRE 業務 – PHP のアップグレード補助 – 基盤技術開発 2

Slide 3

Slide 3 text

アジェンダ 1. 乱数について 2. PHP の PRNG の問題 3. コロプラ内製の乱数生成ライブラリ (CRandom) 4. CRandom の課題 5. PHP へのコントリビュート おまけ: 乱数と PHP のアップグレード 3

Slide 4

Slide 4 text

乱数とは • その名の通り "乱れた数" – 無作為に生成された数値 • ゲームアプリケーションとは切っても切り離せない存在 – こんなところで使います • 各種キャラクターの行動パターン選択 • マップやステージの生成 • ガチャやクリア報酬等などのアイテム抽選 • 乱数生成器 – PRNG (Pseudo Random Number Generator) – CSPRNG (Cryptographically Secure Random Number Generator) – TRNG (True Random Number Generator) 4

Slide 5

Slide 5 text

乱数生成器 コンピュータは確定的な計算を行うため、真にランダムな値を生成する ことはできない (!) 最近のコンピュータには熱ノイズなどを元に乱数を生成するためのハー ドウェアが実装されていることが多い • x86_64 CPU: RDRAND / RDSEED • ARMv8.5A CPU: RNDR 多くの OS ではこれらを利用しつつ乱数を生成する機能が備わっている (/dev/random※ , getrandom(2), CryptGenRandom / CNG) ※ /dev/random がハードウェアを利用しているかは実装に依存します 5

Slide 6

Slide 6 text

乱数生成器: PRNG Pseudo Random Number Generator (疑似乱数生成器) の略 • 任意の値で状態をシード (初期化) できる – 内部状態を持っており、それに基づき 次の値が生成される • シード値に対して生成される値が保証される – アルゴリズムと状態から次の値が推測可能 • 乱数としての品質はそこそこ 任意の値でシードできて出力結果が推測可能 (シードに対し同一) => ゲームのリプレイ・マップ生成機能などには必須! 6

Slide 7

Slide 7 text

乱数生成器: CSPRNG Cryptographically Secure Pseudo Random Number Generator の略 • 暗号論的に安全な PRNG – 暗号化などに使っても問題ない乱数品質が保証されている • シードにはとても大きい値が必要な場合が多い – 場合によっては指定できないこともある • 生成コストが高い (= 重い) – ゲームに使うには過剰 7

Slide 8

Slide 8 text

乱数生成器: TRNG True Random Number Generator の略 • 真の乱数生成器 • サイコロとか • 計算で求めていない 計算では導き出すことができない 前述した CPU などの乱数生成器もこれに分類されることがある 8

Slide 9

Slide 9 text

乱数生成器: ゲームでの使われ方 9 ゲームにおける乱数はサイコロのようなもの 例:

Slide 10

Slide 10 text

PHP の乱数実装 主に以下の表の通り 関数 種類 再現性 用途 srand() rand() 実装依存 (libc) PRNG あり マップ生成 リプレイなど mt_srand() mt_rand() メルセンヌ・ツイスタ PRNG あり マップ生成 リプレイなど openssl_random_pseudo_bytes() 実装依存 (OpenSSL) CSPRNG なし アイテム抽選 キャラ抽選など random_bytes() random_int() OS 依存 CSPRNG なし アイテム抽選 キャラ抽選など 10 : レガシーまたは拡張機能に依存した既に使われていない実装

Slide 11

Slide 11 text

PHP の乱数実装 主に以下の表の通り 関数 種類 再現性 用途 srand() rand() 実装依存 (libc) PRNG あり マップ生成 リプレイなど mt_srand() mt_rand() メルセンヌ・ツイスタ PRNG あり マップ生成 リプレイなど openssl_random_pseudo_bytes() 実装依存 (OpenSSL) CSPRNG なし アイテム抽選 キャラ抽選など random_bytes() random_int() OS 依存 CSPRNG なし アイテム抽選 キャラ抽選など 11 再現性が必要なものはこっち ● ダンジョン・マップ生成 ● 出題する問題や敵キャラの選択 ● リプレイ機能 再現性が必要ないものは基本こっち ● クリア報酬の抽選 ● ガチャ抽選 : レガシーまたは拡張機能に依存した既に使われていない実装

Slide 12

Slide 12 text

PHP の PRNG の問題 PHP の PRNG はスコープが言語ランタイムに対してグローバル => 乱数を使用する関数を実行すると一貫性が崩れてしまう... 12 必ず 7 だったものが... 処理を追加したら結果が変わってしまった! 後から追加

Slide 13

Slide 13 text

内製の PRNG ライブラリ CRandom というライブラリを作成 • XorShift128+ を採用 – シード値に対し生成結果の一貫性が保証されている – Chromium など既に幅広く使われている枯れたアルゴリズム • インスタンス単位で状態を保持 – 意図しない (一貫性のない) 結果にならず安全に リプレイなどの状況再現ができる 13

Slide 14

Slide 14 text

CRandom の問題 • PHP による実装のため速度が遅い – PHP が符号なし整数をそのまま扱えないので回避策が必要 • PHP のバージョンアップにより互換性が崩れるリスク – 最近だと演算子の挙動変更とかもあるので... • 生成される乱数の統計検定にかかるコストが高い – TestU01 など PHP 本体にクラスベースの乱数生成器の実装を提案してみよう 14

Slide 15

Slide 15 text

PHP へのコントリビュート C で拡張機能として実装し RFC を提案 https://github.com/php/php-src/pull/8094 PHP 8.2 での採用を目指し、現在は議論中のステータス 15

Slide 16

Slide 16 text

いろいろな課題が見えてきた • 英語でのコミュニケーション – DeepL のおかげで昔に比べてだいぶ楽になった • 意見のとりまとめ – どのような API が良いかについて長期の議論になった – 実装する PRNG アルゴリズムの選定 • 実装したはいいがボツにしたものも結構ある • さまざまな環境でのデバッグ – PHP がサポートする OS 、アーキテクチャでのデバッグ作業 • NTS / ZTS • Windows • IBM z/Architecture (s390x) コントリビュートを試みて 16

Slide 17

Slide 17 text

乱数と PHP のアップグレード PHP 7.1 から srand(), rand() が mt_srand(), mt_rand() の エイリアスに srand() でのシード結果に依存していたタイトルが PHP 7.1 以降に 更新できない状態に... 17

Slide 18

Slide 18 text

乱数と PHP のアップグレード srand(), rand() と完全互換な関数を提供する PHP 拡張を作成 無事サポートされているバージョンにアップグレードできた! 18

Slide 19

Slide 19 text

まとめ 乱数の世界は奥深い • ゲームと乱数は切っても切り離せない存在 • しっかりと理解し、適材適所を見極める必要がある • 必要であれば言語自体へのコントリビュートもしていく 乱数と PHP のアップグレードのより詳細な部分に関しては 弊社オウンドメディア「Be-ars」に記事を掲載しています。 https://be-ars.colopl.co.jp/team/en-blog_vol.2.html 19