PHP7.4でjson_encodeとserializeの速度比較
1年以上前に「PHPでjson_encode/json_decodeする例」という記事でPHPによるシリアライズについて触れました。
当時はjson_encode関数とserialize関数の両方を試し、json_encodeのほうがサイズが小さかったので、json_encodeを採用したんですね。
JavaScriptのXMLHttpRequestでブラウザとPHPの間で非同期通信することもあったのでどのみちjsonが便利だろう、という考えもありました。(JavaScriptならJSON.Parse関数で一発で変換可能なので)
でも今回はローカルにあるPCであれこれデータベースの解析処理をした後、その解析結果だけをWebサーバーに送る、という仕組みを作っています。それなら、JavaScriptとのやりとりは発生しないので、あらためてjson_encodeとserializeのサイズと速度について比較してみようと思いました。
先に結論を書いてしまいますが、サイズはjson_encode関数のほうが小さく、処理速度はserialize関数のほうが早いです。但し、速度については環境によっても違うと思います。
json_encodeとserializeのサイズ比較
PHPのサンプルコード
<?php
$row['id'] = 1;
$row['mode'] = 'B4MW-012JP';
$row['name'] = 'Ryzen 5 4500U搭載14.1インチノートパソコン';
$row['category'] = 'ノートパソコン';
$row['weight'] = 1.29;
$row['price'] = 106000;
$json = json_encode($row, JSON_UNESCAPED_UNICODE);
$serial = serialize($row);
echo '<textarea>'.$json.'</textarea>';
echo '<textarea>'.$serial.'</textarea>';
?>
連想配列$rowはデータベースから取得したデータをイメージしています。実際には複数行を取得してWebサーバーへ送信するので、
$rows[] = $row;
みたいに連想配列の中に連想配列をセットしてまとめて送るのですが、解説がややこしくなるので1行データで話を進めますね。
尚、json_encodeの2つ目のパラメーターJSON_UNESCAPED_UNICODEはマルチバイトのUTF-8をエスケープ処理しないように指定するオプションです。エスケープ処理してしまうとマルチバイトの1文字が \uXXXX というように長ったらしくなり、サイズ的にとても不利になるため指定しています。
PHPサンプルの実行結果
■json_encode
■serialize
json_encodeでは$row配列が155バイトにエンコードされ、serializeでは205バイトでした。
これはもう当たり前というか、上のテキストエリアを見て頂ければ一目瞭然ですが、jsonだと項目名と値が羅列されているだけなのに対して、serializeでは「a:6」とあるように配列が6個だよー、という指定や、「s:2:"id"」では「文字列が2文字で"id"だよ」というようにきめ細やかな指定がされています。そりゃサイズも大きくなるわ。
この特性はどれだけデータが増えても変わらないので、どうやってもjson_encodeより、serializeのほうがデータは大きくなります。
個人的にはserializeのようなエンコードのほうがデータ型等も厳密に指定されているので安心できるんですけどね。
json_encodeとserializeの速度比較
PHPのサンプルコード
<?php
$t1 = microtime(true);
for ($i = 0; $i < 100000;$i++) {
$json = json_encode($row, JSON_UNESCAPED_UNICODE);
}
echo 'json:'.(microtime(true)-$t1).' sec.<br>';
$t1 = microtime(true);
for ($i = 0; $i < 100000;$i++) {
$serial = serialize($row);
}
echo 'serial:'.(microtime(true)-$t1).' sec.<br>';
?>
先程のエンコード処理を10万回繰り返すだけの単純なコードです。
単純なだけにキャッシュが効いてしまって実行するたびに早くなってしまうのですがw まぁjson_encodeとserializeの相対的な速度比較にはなるかな、と。
PHPサンプルの実行結果
json:0.20221996307373 sec. serial:0.14348101615906 sec.
これにはちょっと驚きました。
「json_encode serialize 速度比較」でググると出てくるページではjson_encodeのほうが早いという結論になっていましたが、今回のテストでは逆ですね。serialize関数のほうがだいぶ早いです。
ぼくが見たのは10年くらい前の記事なのでPHPのバージョンもかなり違うのでしょう。特にPHP5からPHP7では劇的なパフォーマンスの向上がありましたし、PHP7.3から7.4ですら10%もの速度向上が見られました。(うちのWebサイトでは)
今回のテストはローカルのWebサーバー上で動かしましたが、その環境はnginx 1.18.0上でFastCGIとして動作しているPHP7.4.7です。
別の環境では結果が異なる可能性があります。
json_decodeとunserializeの速度比較
エンコードとデコードで速度差があり、エンコードではjson_encodeのほうが早いものの、デコードではunserializeのほうが早い、といった情報も見かけたのでデコードのテストもしてみました。
PHPのサンプルコード
$t1 = microtime(true);
for ($i = 0; $i < 100000;$i++) {
$row = json_decode($json, true);
}
echo 'json_decode:'.(microtime(true)-$t1).' sec.<br>';
$t1 = microtime(true);
for ($i = 0; $i < 100000;$i++) {
$row = unserialize($serial);
}
echo 'unserialize:'.(microtime(true)-$t1).' sec.<br>';
json_encode/serializeのときと同様、10万回デコードするだけの単純なテストです。
PHPサンプルの実行結果
json_decode:0.34030795097351 sec. unserialize:0.11675095558167 sec.
…こちらもまた想像以上にunserializeのほうが早くて驚きました。
テストする前は、json_encodeのほうがデータサイズが小さい上に早いならserializeの立場ないなぁ~なんて思っていましたが、エンコードもデコードもserialize/unserializeのほうが早いし、データ型等も厳密なので、サイズを気にしないならserializeのほうがええやん。
まとめ
- json_encodeとserializeのデータ量を比較したらjson_encodeのほうが32%くらい小さかった。
- json_encodeとserializeのエンコード速度を比較したらserializeのほうが41%くらい早かった。
- json_decodeとunserializeのデコード速度を比較したらunserializeのほうが191%くらい早かった。
但し、環境によって変換速度はかなり変わるため、あくまで参考程度にしてください。
そしてこの結果により、うちの環境ではjson_encodeをやめてserializeに変更するか、真剣に検討をはじめました。
というのも、最初に書いたとおり、今回は重い処理をレンタルサーバー上で実行するのは迷惑になるため、ローカルで集計処理をして結果だけをWebサーバーに送る、という仕組みで使おうと考えています。
ローカルにあるWebサーバーのCPUはどうせ暇を持て余しているので、エンコード処理が遅いのはどうでも良いです。また、レンタルサーバーへの負荷を考えるなら送信するデータ量は小さければ小さいほど良い。
ならjson_encodeが良いじゃないか、とも思うのですが、デコード速度が違いすぎる。デコードするのはレンタルサーバー側なのでなるべく負荷を下げたいところ。
そして送信するデータは何も生で送る必要はないのです。serializeで連想配列をテキスト化した後、zip圧縮して送るという手もあります。
これならデータサイズの削減をした上に、レンタルサーバー上でのデコード処理も迅速にできるのではないか…。いやいや解凍処理でCPUパワー使ったら本末転倒ではないか、とますます悩み始めてしまいましたw
業務で使うシステムなら悩んでいる時間がもったいないのでjson_encodeのまま運用するところですが、今回は趣味のプログラミングなので変なところでこだわっています。そしてそれがまた楽しいんですよね。