テンプレートエンジンの最適化をした際のノウハウ。
- 文字列操作がかなりのコストを占める。
- basestring.join, basestring.encode, basestring.decode は遅い。簡単なテンプレートでは実行時間の過半をこれらが占めることもある。
- str += str は list.append や list.extend より遅い。
- 引数を多く取れれば確実に list.extend の方が list.append より早い。ただし list.append -> cStringIO.StringIO.write のような単純な置換はできなくなる。
- 当然だが、__import__(‘xml.sax.saxutils’).sax.saxutils.escape と呼ぶよりも from xml.sax.saxutils import escape してキャッシュした方がずっと速い。名前空間が汚染されると捉えるか、速度を優先するかはデザイン次第。
- 関数呼び出しのコストは他の部分に比べればほぼ無視出来る。
テンプレートエンジンのベンチマークは上記のコストをいかに回避するかのテクニックを競っているようなもので、当てに出来るかは疑わしい。どんなに速くてもテンプレートの書き方次第で優位性は消えてしまう。PyPy とかの速い処理系に期待したほうがいい。
あと適当に文字列処理のコスト↓
import timeit N = 1000 def bench(name, setup, stmt): bench = timeit.Timer(stmt, setup) print('|*%s|%.10f|' % (name, bench.timeit(N))) import sys print(sys.version) bench('str +=', '_ = ""', '_ += " " * 1000') bench('[] +=', '_ = []', '_ += [" " * 1000]') bench('append', '_ = [].append', '_(" " * 1000)') bench('extend', '_ = [].extend', '_((" " * 1000, ))') bench('array.array("c").fromstring', 'import array; _ = array.array("c").fromstring', '_(" " * 1000)') bench('cStringIO.StringIO().write', 'from cStringIO import StringIO; _ = StringIO().write', '_(" " * 1000)') bench('"".join', '_ = [" " * 1000] * 1000', '"".join(_)') bench('array.array("c").tostring', 'import array; _ = array.array("c"); _.fromstring(" " * 1000 * 1000)', '_.tostring()') bench('cStringIO.StringIO().getvalue', 'from cStringIO import StringIO; _ = StringIO(); _.write(" " * 1000 * 1000)', '_.getvalue()')
>python bench.py
2.7.2 (default, Jun 12 2011, 15:08:59) [MSC v.1500 32 bit (Intel)]
str += | 0.0596243883 |
---|---|
[] += | 0.0008418275 |
append | 0.0006185779 |
extend | 0.0006430312 |
array.array(“c”).fromstring | 0.0503778714 |
cStringIO.StringIO().write | 0.0015428220 |
“”.join | 0.5401027310 |
array.array(“c”).tostring | 0.5463220201 |
cStringIO.StringIO().getvalue | 0.5506574994 |
>pypy bench.py
2.7.1 (930f0bc4125a, Nov 27 2011, 11:58:57)
[PyPy 1.7.0 with MSC v.1500 32 bit]str += | 0.8409847253 |
---|---|
[] += | 0.0022026083 |
append | 0.0011542863 |
extend | 0.0013141384 |
array.array(“c”).fromstring | 0.0230599138 |
cStringIO.StringIO().write | 0.0046778255 |
“”.join | 1.4231829048 |
array.array(“c”).tostring | 4.0303220907 |
cStringIO.StringIO().getvalue | 0.0004813677 |
>pypy –jit off bench.py
2.7.1 (930f0bc4125a, Nov 27 2011, 11:58:57)
[PyPy 1.7.0 with MSC v.1500 32 bit]str += | 0.5654540095 |
---|---|
[] += | 0.0015179159 |
append | 0.0012208536 |
extend | 0.0011954946 |
array.array(“c”).fromstring | 0.0187525104 |
cStringIO.StringIO().write | 0.0043712536 |
“”.join | 1.0814593729 |
array.array(“c”).tostring | 3.9519700981 |
cStringIO.StringIO().getvalue | 0.0004741223 |