PHP で大量のランダムなデータを生成したくなる時があります(主にベンチマークを兼ねたテストデータとして)。こういった処理をする時、10万、100万どころでない回数ランダムデータ生成関数(例えばランダムな名前の生成)を実行することになり、その実行時間はランダムデータ生成関数の実行時間に比例します。これはランダムデータ生成関数の実行時間 10ms をから 1ms に短縮できれば、全体で10分かかっていたランダムデータの生成関数の処理を1分にできるということです。この記事ではどうすると実行時間の短い関数でランダムなデータを生成できるか紹介します。
※テストデータ生成の場合、処理全体のネックはテストデータ保存先のI/O関連になりやすいです。これ以上ランダムデータ生成関数よりネックな部分を詰められなくなってからランダムデータ生成関数を最適化することをお勧めします
主役となる PHP の関数は rand です。なるべくこの rand 関数でランダムな部分を決定できると高速です。似た関数に random_int がありますが random_int は暗号論的に安全な乱数を扱うために計算処理が rand より重くなっています。
PHP: rand – Manual
PHP: random_int – Manual
簡単な実行時間のスクリーンショットが次図です。
図の様に rand 関数の方が明らかに速いです。このため暗号論的に安全か否かが重要でないランダムデータ生成処理の様な場合は rand 関数の方が向いています。
rand 関数はランダムな整数を取得する関数です。これを使って様々なランダムデータを作ることで処理を高速化できます。
例えば日時のランダムデータを作る場合は次の様にできます。
// 現在時刻のUnixタイムスタンプ(整数値)を取得します // @see https://www.php.net/manual/ja/function.time.php $max = time(); // 現在時刻から3年前のUnixタイムスタンプ(整数値)を取得します // @see https://www.php.net/manual/ja/function.strtotime.php $min = strtotime('-3 years') // 3年前から現在時刻までの間のランダムなタイムスタンプを取得します $randTimestamp = rand($min, $max); // タイムスタンプを 2020-11-24 17:30:36 の様な Y-m-d H:i:s 形式文字列に変換します $randomDateTimeStr = date('Y-m-d H:i:s', $randTimestamp); // ↑をまとめて一行にすると $randomDateTimeStr = date('Y-m-d H:i:s', rand(time(), strtotime('-3 years')));
よく Carbon, Faker 等の DateTime クラスと深くかかわっているライブラリが日時操作の便利なライブラリと挙げられ、実際そうなのですがインスタンス化等のランダムデータ生成には無用な重い処理が多々含まれています(時にはメモリを大量に占有したりもします)。上記コードの様に rand 関数と他の PHP 組み込み関数とで処理を作ると高速な上、メモリ空間も節約できます。
配列内の要素をランダムで選択する場合は次の様にできます。
$arr = range(0, 99); // 何か適当な連番添え字の配列を想定 $maxIndex = count($arr) - 1;// ループの外で確定できる値は確定させておくと処理を都度せずに済みます foreach(range(0, 99) as $i){ $randomItem = $arr[rand(0, $maxIndex)]; } // もし配列の添え字が連番でないならば array_rand 関数を使いましょう // @see https://www.php.net/manual/ja/function.array-rand.php $arr[array_rand($arr)];
array_rand でキーを見つけるよりも rand で何かしらの整数を引っ張ってくる方が高速です。とはいえ PHP の配列は連想配列であるためいつも rand でキーを作れるわけではありません。そういった時は array_rand 関数で配列中のランダムなキーを取得し、それを使います。
真偽値は次です。MySQL 等のデータベースに保存する、三項演算子の分岐条件にする、といった前提ならば boolean 型への変換をなくして更に速くできます。
// true or false !!rand(0, 1); // 75% の true と 25% の false !!rand(0, 3); // 論理削除の有無や nullable な値になどに便利です rand(0, 1) ? "これはテストデータID {$i} のコメントです。" : '';
PHP には Faker をはじめとしたランダムデータ生成ライブラリがあり、案件に適したテストデータを生成しやすいですが PHP 組み込み関数より速度面で不利です。適材適所で使っていきましょう。
fzaninotto/Faker: Faker is a PHP library that generates fake data for you
余談ですが Faker が今月の半ばに差し掛かったあたりで archived 送りになりました。メインコントリビュータが PHP から長く離れていること(React Adminにかかりっきり?)、多言語で著作権的にアウトかセーフか不明なデータが含まれるプルリクがひっきりなしに送られてきて疲れた、などが理由の様です。
Sunsetting PHP Faker