はじめに
グレストリでサーバサイドの開発をしていますgrnishiです。
突然ですが皆さん、設定ファイルってどうやって書いていますか?データベースの接続情報とかアプリケーションのログレベルの設定とか、色々項目はあると思うのですが、それをどうやって管理するべきか?というのは中々デファクトスタンダードなやり方がわかっておりません。
言語を問わず色々調べてみると
JSON, YAML, ini, TOML ざっくり比較 · GitHub
いまいちこれといったものが無いように見えます。
そこで今回はパフォーマンスの観点から設定ファイルについて考えていきたいと思います。
環境
- さくらのVPS 1G (HDD 100GBプラン)
- PHP 7.0.21
対象フォーマット
- serialize
- unserializeを使います
- ini
- parse_ini_string を使います
- xml
- simplexml_load_stringを使います
- json
- json_decodeを使います
- yaml
- peclのyamlを使います
- toml
- Yosymfony/Tomlを使います
hoconとか試したかったんだけど自分で実装するのが面倒なので使えそうなライブラリが無かったので見送りました。
明らかにtomlは分が悪いのですがご愛嬌ということで。
計測項目
- ファイルサイズ
- PHPの配列形式に変換100、1000、10000、100000回の速度
- データ量を3件と220件で
- ファイル読み込み速度は含まれません。
- 使用メモリ量(memory_get_peak_usageで)
テストデータ
本来はデータベースの接続情報やログの出力設定などなのでそんなに大きなファイルにはならないと思いますが、ベンチマークを測るのであればある程度データが無いと差が出ないと思いますので、下記サイトにあるデータを借りました。
キン肉マンのキャラクター(超人)238人を紹介|キン肉マンDB
以下各フォーマットのデータサンプル。ここに書くためにデータ減らしていますが、実際は220人分の超人データが入っています。
一部アレな感じのデータになっていますが、各フォーマットの特性を吸収するためです。
serialize_data.txt a:3:{s:5:"data1";a:6:{s:4:"name";s:15:"キン肉マン";s:4:"from";s:12:"キン肉星";s:5:"power";s:14:"95万パワー";s:6:"height";s:5:"185cm";s:6:"weight";s:4:"90kg";s:8:"finisher";a:3:{i:0;s:21:"キン肉バスター";i:1;s:24:"キン肉ドライバー";i:2;s:27:"マッスル・スパーク";}}s:5:"data2";a:6:{s:4:"name";s:15:"テリーマン";s:4:"from";s:12:"アメリカ";s:5:"power";s:14:"95万パワー";s:6:"height";s:5:"190cm";s:6:"weight";s:4:"95kg";s:8:"finisher";a:3:{i:0;s:36:"スピニング・トーホールド";i:1;s:45:"テキサス・クローバー・ホールド";i:2;s:33:"カーフ・ブランディング";}}s:5:"data3";a:6:{s:4:"name";s:18:"ラーメンマン";s:4:"from";s:6:"中国";s:5:"power";s:14:"97万パワー";s:6:"height";s:5:"208cm";s:6:"weight";s:5:"130kg";s:8:"finisher";a:3:{i:0;s:27:"レッグ・ラリアート";i:1;s:27:"キャメル・クラッチ";i:2;s:51:"九龍城落地(ガウロンセンドロップ)";}}}
data.ini [data1] name = キン肉マン from = キン肉星 power = 95万パワー height = 185cm weight = 90kg finisher[] = キン肉バスター finisher[] = キン肉ドライバー finisher[] = マッスル・スパーク [data2] name = テリーマン from = アメリカ power = 95万パワー height = 190cm weight = 95kg finisher[] = スピニング・トーホールド finisher[] = テキサス・クローバー・ホールド finisher[] = カーフ・ブランディング [data3] name = ラーメンマン from = 中国 power = 97万パワー height = 208cm weight = 130kg finisher[] = レッグ・ラリアート finisher[] = キャメル・クラッチ finisher[] = 九龍城落地(ガウロンセンドロップ)
data.xml <root> <data1> <name>キン肉マン</name> <from>キン肉星</from> <power>95万パワー</power> <height>185cm</height> <weight>90kg</weight> <finisher> <finisher1>キン肉バスター</finisher1> <finisher2>キン肉ドライバー</finisher2> <finisher3>マッスル・スパーク</finisher3> </finisher> </data1> <data2> <name>テリーマン</name> <from>アメリカ</from> <power>95万パワー</power> <height>190cm</height> <weight>95kg</weight> <finisher> <finisher1>スピニング・トーホールド</finisher1> <finisher2>テキサス・クローバー・ホールド</finisher2> <finisher3>カーフ・ブランディング</finisher3> </finisher> </data2> <data3> <name>ラーメンマン</name> <from>中国</from> <power>97万パワー</power> <height>208cm</height> <weight>130kg</weight> <finisher> <finisher1>レッグ・ラリアート</finisher1> <finisher2>キャメル・クラッチ</finisher2> <finisher3>九龍城落地(ガウロンセンドロップ)</finisher3> </finisher> </data3> </root>
data.json { "data1": { "finisher": { "finisher1": "キン肉バスター", "finisher2": "キン肉ドライバー", "finisher3": "マッスル・スパーク" }, "from": "キン肉星", "height": "185cm", "name": "キン肉マン", "power": "95万パワー", "weight": "90kg" }, "data2": { "finisher": { "finisher1": "スピニング・トーホールド", "finisher2": "テキサス・クローバー・ホールド", "finisher3": "カーフ・ブランディング" }, "from": "アメリカ", "height": "190cm", "name": "テリーマン", "power": "95万パワー", "weight": "95kg" }, "data3": { "finisher": { "finisher1": "レッグ・ラリアート", "finisher2": "キャメル・クラッチ", "finisher3": "九龍城落地(ガウロンセンドロップ)" }, "from": "中国", "height": "208cm", "name": "ラーメンマン", "power": "97万パワー", "weight": "130kg" } }
data.yaml --- data1: name: キン肉マン from: キン肉星 power: 95万パワー height: 185cm weight: 90kg finisher: finisher1: キン肉バスター finisher2: キン肉ドライバー finisher3: マッスル・スパーク data2: name: テリーマン from: アメリカ power: 95万パワー height: 190cm weight: 95kg finisher: finisher1: スピニング・トーホールド finisher2: テキサス・クローバー・ホールド finisher3: カーフ・ブランディング data3: name: ラーメンマン from: 中国 power: 97万パワー height: 208cm weight: 130kg finisher: finisher1: レッグ・ラリアート finisher2: キャメル・クラッチ finisher3: 九龍城落地(ガウロンセンドロップ) ...
data.toml #Toml file [data1] name = "キン肉マン" from = "キン肉星" power = "95万パワー" height = "185cm" weight = "90kg" finisher = ["キン肉バスター", "キン肉ドライバー", "マッスル・スパーク"] [data2] name = "テリーマン" from = "アメリカ" power = "95万パワー" height = "190cm" weight = "95kg" finisher = ["スピニング・トーホールド", "テキサス・クローバー・ホールド", "カーフ・ブランディング"] [data3] name = "ラーメンマン" from = "中国" power = "97万パワー" height = "208cm" weight = "130kg" finisher = ["レッグ・ラリアート", "キャメル・クラッチ", "九龍城落地(ガウロンセンドロップ)"]
計測結果
ファイルサイズ
単位:byte
serialize | ini | xml | json | yaml | toml |
---|---|---|---|---|---|
54,746 | 37,502 | 53,708 | 43,152 | 41,586 | 38,663 |
どれも似たようなものですが、iniやtomlがやや優位でしょうか。
実行速度(データ量3件)
単位:sec
format | 100回 | 1000回 | 10000回 | 100000回 |
---|---|---|---|---|
serialize | 0.00065 | 0.00480 | 0.0482 | 0.5020 |
ini | 0.00171 | 0.01500 | 0.1487 | 1.4844 |
xml | 0.00295 | 0.02722 | 0.2731 | 2.7566 |
json | 0.00103 | 0.01155 | 0.1063 | 1.0793 |
yaml | 0.00711 | 0.07225 | 0.7400 | 7.3343 |
toml | 0.16975 | 1.70521 | 16.9795 | 172.9021 |
実行速度(データ量220件)
単位:sec
format | 100回 | 1000回 | 10000回 | 100000回 |
---|---|---|---|---|
serialize | 0.03427 | 0.34198 | 3.5682 | 35.4121 |
ini | 0.08679 | 0.89662 | 9.1200 | 90.6430 |
xml | 0.14188 | 1.37301 | 13.8983 | 140.0371 |
json | 0.07225 | 0.71918 | 6.8512 | 68.5011 |
yaml | 0.45630 | 4.47941 | 43.9969 | - |
toml | 10.02795 | 99.03344 | - | - |
※時間がかかるので途中で止めました。
メモリ使用量
単位:MB
serialize | ini | xml | json | yaml | toml |
---|---|---|---|---|---|
0.7278 | 0.6883 | 0.4748 | 0.6672 | 0.6677 | 0.7500 |
どれも大差ありません。
考察
serializeはまぁ反則ですよね。速いには速いですが、別言語で使いまわせませんしね。あくまで参考値として。 ただ、どうしてもパフォーマンスが求められる場面ではほんの僅かに高速化の余地として知っておいた方が良いかもです。
yamlの結果がこんなに悪いのは予想していませんでした。jsonと比較すると約7倍時間がかかるみたいです。
予想通りtomlは爆死してます。仕方が無い。PECLなりがあれば良かったんですが。
というわけで、設定ファイルの見やすさとパフォーマンスの観点からjsonでいいんじゃないですかね?
ちなみにPHP特化で良ければ配列に書いて別ファイルで持っておけばrequireするだけだから一番速いです。
告知
弊社からゲームアプリがリリースされました
簡単操作で楽しめるジャンプアクションゲームなっています。
かわいいニワトリが沢山登場しますよ。
apps.apple.com