preg_replaceでまとめて置換するのとpreg_replace後にstr_replaceするのどっちがいいのか
これまでやってきたことを整理してもう一度検証を行う。(そもそも検証コードおかしかったしね)
検証コード
<?php
declare(strict_types=1);
class Str
{
// もともとのコード
public static function first(string $str): string
{
return strtolower(str_replace('-', '_', ltrim(preg_replace('/([A-Z])/', '_\0', $str), '_')));
}
// ltrimしなくていいように正規表現を変えたパターン
public static function second(string $str): string
{
return strtolower(str_replace('-', '_', preg_replace('/(?<=.)([A-Z])/', '_\1', $str)));
}
// 最終版
public static function third(string $str): string
{
return strtolower(preg_replace('/(?<=.)([A-Z])|-/', '_\1', $str));
}
}
function run(callable $callback, string $str, int $times = 1): float
{
$input = str_repeat($str, $times);
$start = microtime(true);
for ($i = 0; $i < 1000000; $i++) {
$callback($input);
}
$end = microtime(true);
return $end - $start;
}
$array = [
'snakeCase' => 'snake_hoge_fuga_piyo',
'kebabCase' => 'kebab-hoge-fuga-piyo',
'camelCase' => 'camelHogeFugaPiyo',
'pascalCase' => 'PascalHogeFugaPiyo',
];
foreach ($array as $key => $str) {
echo $key, PHP_EOL;
echo 'first', PHP_EOL;
echo '20文字程度: ', run([Str::class, 'first'], $str, 1), ' 秒', PHP_EOL;
echo '100文字程度: ', run([Str::class, 'first'], $str, 5), ' 秒', PHP_EOL;
echo '200文字程度: ', run([Str::class, 'first'], $str, 10), ' 秒', PHP_EOL;
echo PHP_EOL;
echo 'second', PHP_EOL;
echo '20文字程度: ', run([Str::class, 'second'], $str, 1), ' 秒', PHP_EOL;
echo '100文字程度: ', run([Str::class, 'second'], $str, 5), ' 秒', PHP_EOL;
echo '200文字程度: ', run([Str::class, 'second'], $str, 10), ' 秒', PHP_EOL;
echo PHP_EOL;
echo 'third', PHP_EOL;
echo '20文字程度: ', run([Str::class, 'third'], $str, 1), ' 秒', PHP_EOL;
echo '100文字程度: ', run([Str::class, 'third'], $str, 5), ' 秒', PHP_EOL;
echo '200文字程度: ', run([Str::class, 'third'], $str, 10), ' 秒', PHP_EOL;
echo str_repeat('=', 40), PHP_EOL;
}
first
メソッドはもともと書いていたコード。
second
メソッドは ltrim
を実行しなくてもいいように正規表現だけを変えたパターン。
third
メソッドは正規表現だけで処理を済ませたパターン。
検証パターン
それぞれのメソッドに、20/100/200字程度の文字列を与えて、処理速度を計る。
与える文字列はスネークケース、ケバブケース、キャメルケース、パスカルケースの4種類。
結果
単位: 秒(小数点以下第三位を切り捨て)
スネークケース
20字程度 | 100字程度 | 200字程度 | |
---|---|---|---|
first | 0.95 | 1.06 | 1.09 |
second | 0.84 | 0.86 | 0.93 |
third | 0.64 | 0.67 | 0.71 |
ケバブケース
20字程度 | 100字程度 | 200字程度 | |
---|---|---|---|
first | 1.02 | 1.17 | 1.42 |
second | 0.88 | 1.04 | 1.28 |
third | 0.75 | 1.16 | 1.71 |
キャメルケース
20字程度 | 100字程度 | 200字程度 | |
---|---|---|---|
first | 1.18 | 1.57 | 2.14 |
second | 0.96 | 1.42 | 2.04 |
third | 0.75 | 1.22 | 1.82 |
パスカルケース
20字程度 | 100字程度 | 200字程度 | |
---|---|---|---|
first | 1.16 | 1.76 | 2.51 |
second | 0.95 | 1.60 | 2.37 |
third | 0.75 | 1.40 | 2.18 |
基本的には third
> second
> first
の順で時間がかかっている。(ケバブケースを変換したときだけ second
が一番速い)
下手な正規表現を書かなければ、まとめて記述してもいいのかもしれない。
以前作った、入力文字列をスネークケースにするメソッドを書き直してみたところ、利用する関数を少なくすることができた。
変更前
変更後
public static function toSnake(string $str): string
{
return strtolower(preg_replace('/(?<=.)([A-Z])|-/', '_\1', $str));
}
短く書けたのはよいが、正規表現ってあんまりパフォーマンス良い印象がないので、どっちが速いのか検証してみる。
検証バージョン
PHP: 8.1.10
検証コード
<?php
declare(strict_types=1);
class Str
{
public static function toSnake(string $str): string
{
return strtolower(str_replace('-', '_', ltrim(preg_replace('/([A-Z])/', '_\0', $str), '_')));
}
public static function toSnakeImprovement(string $str): string
{
return strtolower(preg_replace('/(?<=.)([A-Z])|-/', '_\1', $str));
}
}
$snakeCase = 'snake_hoge_fuga_piyo';
$kebabCase = 'kebab-hoge-fuga-piyo';
$camelCase = 'camelHogeFugaPiyo';
$pascalCase = 'PascalHogeFugaPiyo';
// 改修前
$start = microtime(true);
for ($i = 0; $i < 1000000; $i++) {
Str::toSnake($snakeCase);
Str::toSnake($kebabCase);
Str::toSnake($camelCase);
Str::toSnake($pascalCase);
}
$end = microtime(true);
echo '改修前: ', $end - $start , ' 秒', PHP_EOL;
// 改修後
$start = microtime(true);
for ($i = 0; $i < 1000000; $i++) {
Str::toSnakeImprovement($snakeCase);
Str::toSnakeImprovement($kebabCase);
Str::toSnakeImprovement($camelCase);
Str::toSnakeImprovement($pascalCase);
}
$end = microtime(true);
echo '改修後: ', $end - $start , ' 秒', PHP_EOL;
結果
$ php Str.php
改修前: 3.8098320960999 秒
改修後: 2.482449054718 秒
正規表現でまとめて置換してあげたほうが1秒以上速い。
これは何度やっても同じような結果だったので、20字程度の文字列だったら正規表現のほうが速いみたいで。
もっと長かったりするともしかしたら速度変わるのかも?
=> 100文字前後の場合、結果はしたのようになった。
php Str.php
改修前: 5.6295590400696 秒
改修後: 4.0872659683228 秒
相変わらず正規表現使ったほうが速い。
改修前のコードは利用している関数の数が多いので、正規表現のコストより関数実行のコストのほうが高いみたい。
改修前のコードが関数利用しすぎていたので、ちょこっと変えてもう一度検証してみる。
public static function toSnake(string $str): string
{
return strtolower(str_replace('-', '_', preg_replace('/(?<=.)([A-Z])/', '_\1', $str)));
}
上記のコードと改修後のコードで速度比較する。
結果
# 20字前後の文字列
$ php Str.php
改修前: 3.2798988819122 秒
改修後: 2.5775918960571 秒
# 100字前後の文字列
$ php Str.php
改修前: 4.7906939983368 秒
改修後: 4.2071549892426 秒
やっぱり改修後のコードの方がちょっとだけ速かった。