Pythonのシリアライズ/デシリアライズ
こんにちは。gumiの木村です。
すっかり秋めいて寒い位になりましたが皆さんいかがお過ごしでしょうか。
さて、今回はPythonのシリアライズを題材に書いてみたいと思います。
シリアライズとは、簡単にいうと数値・リスト等のオブジェクトをファイルやDB等に保存できる形式に変換することです。シリアライズされたものに対し逆変換を行いデータに戻すことをデシリアライズといいます。
Pythonでシリアライズ/デシリアライズを行なう方法を以下にいくつか挙げてみました。
pickle, cPickle モジュール
Pythonに標準で付属するpickleモジュールを使用する方法。シリアライズされたデータは独自形式になります。
更に内部処理をCで記述し高速化されているcPickleが用意されています。
simplejson, json モジュール
オブジェクトをjavascriptのデータ表現形式であるjsonに変換する方法。
Python 2.6未満ではサードパーティ製のsimplejsonモジュール、2.6以降では標準付属のjsonモジュールが使用できます。
この中で一番使用が簡単なのはpickleです。ほとんどのオブジェクトを何も考えずにシリアライズできます。
ただし保存形式が独自なのでシリアライズされたデータを外部から加工する用途等には向きません。
シリアライズされたデータ形式の汎用性が高いのはpickle以外の方法ですが、速度的にどのシリアライズ/デシリアライズ方法が早いのか気になりますね。
そこで、それぞれの方法で整数データ、要素が整数10個のリスト、要素が整数10000個のリストを対象に計測を行ってみました。
ベンチマークプログラム ソース
import pickle import cPickle try: import json except ImportError: import simplejson as json import time def bench(title, data, counts, func): start = time.time() for i in xrange(counts): outdata = func(data) end = time.time() e = int((end - start) * 1000) print '%-10s %-5s count:%5d %8dmsec' % (title, func.__name__, counts, e) return outdata def benchmain(title, data, counts, funcs): print '========== %s' % title for title, seriarizer, deseriarizer in funcs: outdata = bench(title, data, counts, seriarizer) bench(title, outdata, counts, deseriarizer) FUNCTIONS = ( ('', str, eval), ('pickle', pickle.dumps, pickle.loads), ('cPickle', cPickle.dumps, cPickle.loads), ('json', json.dumps, json.loads), ) if __name__ == '__main__': data = 1 benchmain('Integer', data, 10000, FUNCTIONS) data = range(10) benchmain('Integer x10 list', data, 10000, FUNCTIONS) data = range(10000) benchmain('Integer x10000 list', data, 100, FUNCTIONS)
ベンチマークプログラム 実行結果
MacBook (Intel Core2Duo 2.4GHz, 4GBメモリ) 上で実行。
$ python -V Python 2.5.4 $ python ./sbench.py ========== Integer str count:10000 5msec eval count:10000 134msec pickle dumps count:10000 103msec pickle loads count:10000 153msec cPickle dumps count:10000 9msec cPickle loads count:10000 9msec json dumps count:10000 112msec json loads count:10000 62msec ========== Integer x10 list str count:10000 54msec eval count:10000 415msec pickle dumps count:10000 619msec pickle loads count:10000 585msec cPickle dumps count:10000 49msec cPickle loads count:10000 74msec json dumps count:10000 179msec json loads count:10000 85msec ========== Integer x10000 list str count: 100 357msec eval count: 100 3381msec pickle dumps count: 100 4566msec pickle loads count: 100 3847msec cPickle dumps count: 100 351msec cPickle loads count: 100 504msec json dumps count: 100 476msec json loads count: 100 180msec $ python -V Python 2.6.1 $ python ./sbench.py ========== Integer str count:10000 2msec eval count:10000 126msec pickle dumps count:10000 76msec pickle loads count:10000 144msec cPickle dumps count:10000 8msec cPickle loads count:10000 6msec json dumps count:10000 64msec json loads count:10000 189msec ========== Integer x10 list str count:10000 25msec eval count:10000 354msec pickle dumps count:10000 411msec pickle loads count:10000 496msec cPickle dumps count:10000 38msec cPickle loads count:10000 53msec json dumps count:10000 354msec json loads count:10000 1728msec ========== Integer x10000 list str count: 100 178msec eval count: 100 3530msec pickle dumps count: 100 2957msec pickle loads count: 100 3020msec cPickle dumps count: 100 280msec cPickle loads count: 100 368msec json dumps count: 100 2345msec json loads count: 100 15805msec
興味深い結果が出ましたね。
- strは早いがevalはかなり遅い
- pickleは全般的に遅い
- cPickleとsimplejsonを比較すると対象オブジェクトが小さい場合はcPickle, 大きい場合はsimplejsonが早い
- cPickleとjsonを比較すると全般的にcPickleの方が早い
さらに、他のオブジェクトを対象にした場合や、他の方法によるシリアライズ/デシリアライズの場合を
計ってみると面白いかもしれません。