PyYAMLが非常に遅かった
うすうす感じてはいたが測ってみたらPyYAMLがめちゃめちゃ遅い。しかもこの調査をするまでlibyamlを使えていなかったのでなおさら。
ということでテキストならujson、バイナリならmsgpackがおすすめ。どうしても可読性が必要な場合は容量を抑えるか速度の必要ないところでYAMLに変換する方が良さそうです。
import sys import gzip import timeit import json import yaml import bson import pymongo import msgpack import ujson #======================================================================================================================= def json_loads(dump): return json.loads(dump) #======================================================================================================================= def ujson_loads(dump): return ujson.loads(dump) #======================================================================================================================= def yaml_loads(dump): return yaml.load(dump) #======================================================================================================================= def cyaml_loads(dump): return yaml.load(dump, Loader=yaml.CLoader) #======================================================================================================================= def bson_loads(dump): return bson.BSON(dump).decode() #======================================================================================================================= def msgpack_loads(dump): return msgpack.loads(dump) #======================================================================================================================= def test_load(): """load時間計測""" print '[load]' dump = gzip.open('data.json.gz', 'rb').read() print 'json.loads: %.5f sec' % (timeit.timeit(stmt=lambda : json_loads(dump), number=40) / 40) dump = gzip.open('data.yaml.gz', 'rb').read() print 'yaml.load: %.5f sec' % (timeit.timeit(stmt=lambda : yaml_loads(dump), number=1) / 1) if yaml.__with_libyaml__: dump = gzip.open('data.yaml.gz', 'rb').read() print 'yaml.load(libyaml): %.5f sec' % (timeit.timeit(stmt=lambda : cyaml_loads(dump), number=1) / 1) else: print 'yaml.load(libyaml):', '-' dump = gzip.open('data.bson.gz', 'rb').read() print 'bson.BSON.decode: %.5f sec' % (timeit.timeit(stmt=lambda : bson_loads(dump), number=110) / 110) dump = gzip.open('data.json.gz', 'rb').read() print 'ujson.loads: %.5f sec' % (timeit.timeit(stmt=lambda : ujson_loads(dump), number=100) / 100) dump = gzip.open('data.mpac.gz', 'rb').read() print 'msgpack.loads: %.5f sec' % (timeit.timeit(stmt=lambda : msgpack_loads(dump), number=250) / 250) print #======================================================================================================================= def json_dumps(data): return json.dumps(data) #======================================================================================================================= def ujson_dumps(data): return ujson.dumps(data) #======================================================================================================================= def yaml_dumps(data): return yaml.dump(data) #======================================================================================================================= def cyaml_dumps(data): return yaml.dump(data, Dumper=yaml.CDumper) #======================================================================================================================= def bson_dumps(data): return bson.BSON.encode(data) #======================================================================================================================= def msgpack_dumps(data): return msgpack.dumps(data) #======================================================================================================================= def test_dump(): """dump時間計測""" dump = gzip.open('data.mpac.gz', 'rb').read() data = msgpack.loads(dump) print '[dump]' print 'json.dumps: %.5f sec' % (timeit.timeit(stmt=lambda : json_dumps(data), number=75) / 75) print 'yaml.dump: %.5f sec' % (timeit.timeit(stmt=lambda : yaml_dumps(data), number=1) / 1) if yaml.__with_libyaml__: print 'yaml.dump(libyaml): %.5f sec' % (timeit.timeit(stmt=lambda : cyaml_dumps(data), number=1) / 1) else: print 'yaml.dump(libyaml):', '-' print 'bson.BSON.encode: %.5f sec' % (timeit.timeit(stmt=lambda : bson_dumps(data), number=18) / 18) print 'ujson.dumps: %.5f sec' % (timeit.timeit(stmt=lambda : ujson_dumps(data), number=116) / 116) print 'msgpack.dumps: %.5f sec' % (timeit.timeit(stmt=lambda : msgpack.dumps(data), number=166) / 166) print #======================================================================================================================= def validationcheck(): """dump/loadがちゃんと可逆になっているかの動作確認""" print '[data]' dump = gzip.open('data.json.gz', 'rb').read() dump = json_dumps(json_loads(dump)) dump_len = len(dump) print 'data.json:', dump_len, 'bytes' dump = json_dumps(json_loads(dump)) assert len(dump) == dump_len, len(dump) dump = ujson_dumps(ujson_loads(dump)) ### jsonとujsonとで出力の仕方が微妙に違うので再dumpしてからチェック dump_len = len(dump) print 'data.json:', dump_len, 'bytes', '(ujson)' dump = ujson_dumps(ujson_loads(dump)) assert len(dump) == dump_len, len(dump) dump = gzip.open('data.yaml.gz', 'rb').read() dump = yaml_dumps(yaml_loads(dump)) dump_len = len(dump) print 'data.yaml:', dump_len, 'bytes' dump = yaml_dumps(yaml_loads(dump)) assert len(dump) == dump_len if yaml.__with_libyaml__: dump = cyaml_dumps(cyaml_loads(dump)) dump_len = len(dump) print 'data.yaml:', dump_len, 'bytes', '(cyaml)' dump = cyaml_dumps(cyaml_loads(dump)) assert len(dump) == dump_len dump = gzip.open('data.bson.gz', 'rb').read() dump_len = len(dump) print 'data.bson:', dump_len, 'bytes' dump = bson_dumps(bson_loads(dump)) assert len(dump) == dump_len dump = gzip.open('data.mpac.gz', 'rb').read() dump_len = len(dump) print 'data.mpac:', dump_len, 'bytes' dump = msgpack_dumps(msgpack_loads(dump)) assert len(dump) == dump_len print #======================================================================================================================= def version(): print '[version]' print 'sys.version:', sys.version print 'json.__version__:', json.__version__ print 'yaml.__version__:', yaml.__version__, 'libyaml=' + str(yaml.__with_libyaml__) print 'pymongo.version(bson):', pymongo.version print 'ujson.__version__:', ujson.__version__ print 'msgpack.version:', msgpack.version print #======================================================================================================================= def main(): version() validationcheck() test_load() test_dump() return 0 #======================================================================================================================= if __name__ == '__main__': sys.exit(main() or 0)
$ python check.py [version] sys.version: 2.7.2 (default, Jun 12 2011, 15:08:59) [MSC v.1500 32 bit (Intel)] json.__version__: 2.0.9 yaml.__version__: 3.10 libyaml=True pymongo.version(bson): 2.3 ujson.__version__: 1.23 msgpack.version: (0, 2, 4) [data] data.json: 5999414 bytes data.json: 5642600 bytes (ujson) data.yaml: 5876895 bytes data.yaml: 5876803 bytes (cyaml) data.bson: 6915449 bytes data.mpac: 4610724 bytes [load] json.loads: 0.10192 sec yaml.load: 41.04686 sec yaml.load(libyaml): 5.33758 sec bson.BSON.decode: 0.04291 sec ujson.loads: 0.04812 sec msgpack.loads: 0.01930 sec [dump] json.dumps: 0.06862 sec yaml.dump: 23.78141 sec yaml.dump(libyaml): 6.38722 sec bson.BSON.encode: 0.33344 sec ujson.dumps: 0.04413 sec msgpack.dumps: 0.03087 sec