Release: | 1.1 |
---|---|
Date: | October 27, 2010 |
timeitモジュールで計測しました¶
単純に、リストで区切る場合
string = "I like spam."
list(string)
['I', ' ', 'l', 'i', 'k', 'e', ' ', 's', 'p', 'a', 'm', '.']
これを基準として速いか、遅いかを見比べてみてください。
■2系で計測
>>> from timeit import Timer
>>> print(Timer("list(string)", 'string = "I like spam."').repeat(number=100000))
[0.16765809059143066, 0.16036391258239746, 0.15968680381774902]
■3系で計測
3系はめちゃくちゃ速いですね。なぜだろう。
>>> from timeit import Timer
>>> print(Timer("list(string)", 'string = "I like spam."').repeat(number=100000))
[0.08800315856933594, 0.07892894744873047, 0.07909393310546875]
タブルで区切る場合
タブルは変更不可能なオブジェクトなので、中身を変更したいのであれば、リストを使います。
tuple(string)
('I', ' ', 'l', 'i', 'k', 'e', ' ', 's', 'p', 'a', 'm', '.')
■2系で計測
tuple() の方が速いことがわかります。
>>> from timeit import Timer
>>> print(Timer("tuple(string)", 'string = "I like spam."').repeat(number=100000))
[0.15124106407165527, 0.1477971076965332, 0.14786481857299805]
■3系で計測
とんでもなく速いです。
>>> from timeit import Timer
>>> print(Timer("tuple(string)", 'string = "I like spam."').repeat(number=100000))
[0.057578086853027344, 0.048284053802490234, 0.04918789863586426]
for文で回す
slist = []
for i in string:
slist.append(i)
print(slist)
['I', ' ', 'l', 'i', 'k', 'e', ' ', 's', 'p', 'a', 'm', '.']
■2系で計測
リスト内包表記よりも少し遅いです。
>>> from timeit import Timer
>>> print(Timer("for i in string: slist.append(i)", 'string = "I like spam."; slist = []').repeat(number=100000))
[0.29812097549438477, 0.28661012649536133, 0.28408908843994141]
■3系で計測
>>> from timeit import Timer
>>> print(Timer("for i in string: slist.append(i)", 'string = "I like spam."; slist = []').repeat(number=100000))
[0.1907179355621338, 0.17830896377563477, 0.1801919937133789]
リスト内包表記で
[string[i] for i in range(len(string))]
['I', ' ', 'l', 'i', 'k', 'e', ' ', 's', 'p', 'a', 'm', '.']
■2系で計測
list() よりちょっとだけ遅いです。
>>> from timeit import Timer
>>> print(Timer("[string[i] for i in range(len(string))]", 'string = "I like spam."').repeat(number=100000))
[0.25739216804504395, 0.25221920013427734, 0.25009298324584961]
■3系で計測
なぜ2系よりも遅くなるんだろう、、、
>>> from timeit import Timer
>>> print(Timer("[string[i] for i in range(len(string))]", 'string = "I like spam."').repeat(number=100000))
[0.2948720455169678, 0.2680661678314209, 0.2661139965057373]
もっとスマートに。
[x for x in string]
['I', ' ', 'l', 'i', 'k', 'e', ' ', 's', 'p', 'a', 'm', '.']
■2系で計測
さっきよりは速いですが、ちょっとだけ遅いです。
>>> from timeit import Timer
>>> print(Timer("[x for x in string]", 'string = "I like spam."').repeat(number=100000))
[0.21160793304443359, 0.20747208595275879, 0.20700287818908691]
■3系で計測
>>> from timeit import Timer
>>> print(Timer("[x for x in string]", 'string = "I like spam."').repeat(number=100000))
[0.15147709846496582, 0.13379907608032227, 0.13328313827514648]
イテレータを使う
>>> list(iter(string))
['I', ' ', 'l', 'i', 'k', 'e', ' ', 's', 'p', 'a', 'm', '.']
■2系で計測
イテレータなので、速いと思ったのですが、ちょっとだけ遅かったので残念です。
>>> from timeit import Timer
>>> print(Timer("list(iter(string))", 'string = "I like spam."').repeat(number=100000))
[0.25499391555786133, 0.24986600875854492, 0.25281620025634766]
■3系で計測
>>> from timeit import Timer
>>> print(Timer("list(iter(string))", 'string = "I like spam."').repeat(number=100000))
[0.17393803596496582, 0.15649890899658203, 0.15940403938293457]
イテレータのリスト内包表記では?
>>> [x for x in iter(string)]
['I', ' ', 'l', 'i', 'k', 'e', ' ', 's', 'p', 'a', 'm', '.']
■2系で計測
さっきより少しだけ早くなったのはなぜだろう?
>>> from timeit import Timer
>>> print(Timer("[x for x in iter(string)]", 'string = "I like spam."').repeat(number=100000))
[0.23363304138183594, 0.22565793991088867, 0.22397589683532715]
■3系で計測
>>> from timeit import Timer
>>> print(Timer("[x for x in iter(string)]", 'string = "I like spam."').repeat(number=100000))
[0.16548705101013184, 0.15951204299926758, 0.16102194786071777]
ジェネレータを使う
list(x for x in string)
['I', ' ', 'l', 'i', 'k', 'e', ' ', 's', 'p', 'a', 'm', '.']
■2系で計測
なぜかジェネレータ少し遅い…。どうしてだろう。
>>> from timeit import Timer
>>> print(Timer("list(x for x in string)", 'string = "I like spam."').repeat(number=100000))
[0.51497101783752441, 0.50594687461853027, 0.50299286842346191]
■3系で計測
>>> from timeit import Timer
>>> print(Timer("list(x for x in string)", 'string = "I like spam."').repeat(number=100000))
[0.30000805854797363, 0.2889258861541748, 0.2886178493499756]
Note
map()を使って
■Python2系のみ
map(None, string)
['I', ' ', 'l', 'i', 'k', 'e', ' ', 's', 'p', 'a', 'm', '.']
私の環境での結果ですので、なんとも言えませんが list() よりも速いです。すごい。
■2系で計測
>>> from timeit import Timer
>>> print(Timer("map(None, string)", 'string = "I like spam."').repeat(number=100000))
[0.15664911270141602, 0.14838504791259766, 0.15289902687072754]
■Python3系にも対応するならlambdaを使う
[x for x in map(lambda x: str(x), string)]
['I', ' ', 'l', 'i', 'k', 'e', ' ', 's', 'p', 'a', 'm', '.']
■3系で計測
遅いです。3系は list() がめちゃくちゃ速いので map() での工夫は必要無いでしょう。
>>> from timeit import Timer
>>> print(Timer("[x for x in map(lambda x: str(x), string)]", 'string = "I like spam."').repeat(number=100000))
[0.8320629596710205, 0.8203780651092529, 0.8253698348999023]
いろいろ使えそうなmap
map(lambda *x: ''.join(i for i in x if x), *(iter(string),))
■2系で計測
ものすごく遅いのは、いろいろな計算が入っているからでしょう。
>>> from timeit import Timer
>>> print(Timer("map(lambda *x: ''.join(i for i in x if x), *(iter(string),))", 'string = "I like spam."').repeat(number=100000))
[4.4103279113769531, 4.5268661975860596, 4.3819801807403564]
■3系で計測
>>> from timeit import Timer
>>> print(Timer("map(lambda *x: ''.join(i for i in x if x), *(iter(string),))", 'string = "I like spam."').repeat(number=100000))
[0.08291816711425781, 0.07854080200195312, 0.07890486717224121]
Note
正規表現で
import re
re.findall('.', string)
['I', ' ', 'l', 'i', 'k', 'e', ' ', 's', 'p', 'a', 'm', '.']
■2系で計測
>>> from timeit import Timer
>>> print(Timer("re.findall('.', string)", 'import re; string = "I like spam."').repeat(number=100000))
[0.54255795478820801, 0.53627490997314453, 0.53358101844787598]
■3系で計測
モジュールのimport時間も含まれているのでなんとも言えませんが、少し遅いです。
>>> from timeit import Timer
>>> print(Timer("re.findall('.', string)", 'import re; string = "I like spam."').repeat(number=100000))
[0.6249690055847168, 0.6120860576629639, 0.6094479560852051]
Note
もう何がしたいのかわからないとき
■2系のみ
map(lambda x: ''.join(tuple(x)), string)
['I', ' ', 'l', 'i', 'k', 'e', ' ', 's', 'p', 'a', 'm', '.']
遅いです。 map() が早かったぶん少し期待したのですが…。
■2系で計測
>>> from timeit import Timer
>>> print(Timer("map(lambda x: ''.join(tuple(x)), string)", 'string = "I like spam."').repeat(number=100000))
[2.1053709983825684, 2.0983140468597412, 2.1047899723052979]
■3系では
[x for x in map(lambda x: ''.join(tuple(x)), string)]
['I', ' ', 'l', 'i', 'k', 'e', ' ', 's', 'p', 'a', 'm', '.']
■3系で計測
めちゃくちゃ意外な健闘ぶりです。Python3.1.2で計測したからでしょうか。しかし遅いです。
>>> from timeit import Timer
>>> print(Timer("[x for x in map(lambda x: ''.join(tuple(x)), string)]", 'string = "I like spam."').repeat(number=100000))
[1.1109910011291504, 1.100836992263794, 1.0959341526031494]
もう何がしたいかわからないとき2
[x for x, y in zip(string, string)]
['I', ' ', 'l', 'i', 'k', 'e', ' ', 's', 'p', 'a', 'm', '.']
■2系で計測
ワケがわからない部門では見事1位です。yを捨てているぶん、無駄があるんでしょう。
>>> from timeit import Timer
>>> print(Timer("[x for x, y in zip(string, string)]", 'string = "I like spam."').repeat(number=100000))
[0.45125102996826172, 0.44776821136474609, 0.44718194007873535]
■3系で計測
>>> from timeit import Timer
>>> print(Timer("[x for x, y in zip(string, string)]", 'string = "I like spam."').repeat(number=100000))
[0.2564361095428467, 0.25066709518432617, 0.2534060478210449]
結論(仮)¶
しかし、
Note
によれば、map(None, x)は使えなくなるみたいなので、素直に、 list() を使ったほうがいいみたいでした。
イテレータやジェネレータを使ったら、メモリの消費量が抑えられる?? ので、できるだけ使いましょう。
2系と3系の差に驚かされた実験でした。
つづく¶
募集中です。
[2010-10-27] イテレータとジェネレータを追加。timeitモジュールの計測結果を追加。
0 件のコメント:
コメントを投稿