whether it’s faster to convert an 8-bit string to Unicode by appending an empty Unicode string to it or by using the unicode function

ヘンなタイトルですんまそん。

チマチマとチビチビと Python の What’s New 翻訳続けてる。その中にこんなのがあった:

 1 import timeit
 2 
 3 timer1 = timeit.Timer('unicode("abc")')
 4 timer2 = timeit.Timer('"abc" + u""')
 5 
 6 # Run three trials
 7 print(timer1.repeat(repeat=3, number=100000))
 8 print(timer2.repeat(repeat=3, number=100000))
 9 
10 # On my laptop this outputs:
11 # [0.36831796169281006, 0.37441694736480713, 0.35304892063140869]
12 # [0.17574405670166016, 0.18193507194519043, 0.17565798759460449]

これは timeit モジュール初登場時(2.3)の timeit の例として挙げられていたもの。倍にもなるのか、と。

「特定の Python 実装に依存した最適化に拠るべからず」というのがスタイル標準の教え (PEP8 など) と思うし、これに関係したトピックもまさに見た記憶はあるけれど、実際これを自分の環境でも試してみて驚いてしまった。

Windows 7 の Python 2.7.9 (公式 CPython) でこんなことになった:

 1 me@host: ~$ cat z.py
 2 import timeit
 3 
 4 timer0 = timeit.Timer('s = u"abc"')
 5 timer1 = timeit.Timer('unicode("abc")')
 6 timer2 = timeit.Timer('"abc" + u""')
 7 
 8 # Run three trials
 9 print(timer0.repeat(repeat=3, number=100000))
10 print(timer1.repeat(repeat=3, number=100000))
11 print(timer2.repeat(repeat=3, number=100000))
12 me@host: ~$ python z.py
13 [0.0018384687723313655, 0.001671685143299211, 0.0017058971697673452]
14 [0.02718402093091779, 0.025255317938776722, 0.025425095120124842]
15 [0.0025043203374674333, 0.0017563599088078335, 0.0017572152094695453]
16 me@host: ~$ 

なんで 10 倍の開きにもなるんだろう?

これを知ったからといって何か得するだろうか、というのは疑問。Python 3 系では通用しないし。けどここまで差があるのはなんでなんだろうか、というのは気にはなる。

個人的には unicode コンストラクタ(関数)をダイレクトに使うことがほとんどないので余計困りはしない。とりわけ Python 3 系で「も」動作するコードを書こうとすると、「移植性のために」unicode を明示的に使うのを避けるからさ。だから無意識に「速度で問題が出る」コードを避けていたことにはなる。けど「unicode 型にするために」という明確な意図を持って「空 unicode リテラルを足し算で」なんてコードを書く人、いるけ? おらんでしょう。

CPython 以外でどうなのかが気になるのと、あと、なんで What’s New ではこれをあえて例として選んだのかの著者の真意を知りたい。なんかしら有名なの?