【Python】結局、リスト内包表記は速いのか?
Pythonでは一般的に単純なリスト追加のループを回す場合、forで囲うよりもリスト内包表記というのを使った方が最大で5倍程度高速であるという俗説があります。
例えば単純なforループで回す場合、
l = []
for i in range(100):
l.append(i)
とかやりますが、これと同じものをリスト内包表記で書くと、
l = [i for i in range(100)]
こうなります。
可読性はさておいてこの俗説、実際リスト内包表記を多用していた割にはあまり実証したことがなく、然らば1次ソースを辿るべきなのでしょうけれどそれも億劫な気がしたのでならいっそ作って試しちゃえ。そう思って作りました。
実験1. 1000万個の整数
それぞれ上がforループ、下がリスト内包表記です。
--------------------------------------------------
List int with iteration
time: 1296.9629764556885 ms
--------------------------------------------------
--------------------------------------------------
List int with comprehension
time: 529.3018817901611 ms
--------------------------------------------------
5倍とまではいかなかったですがリスト内包表記の方が2倍以上速いです。
実験2. 1000万個のランダムな小文字英字
--------------------------------------------------
List str with iteration
time: 14827.327489852905 ms
--------------------------------------------------
--------------------------------------------------
List str with comprehension
time: 13321.378707885742 ms
--------------------------------------------------
こちらはさほど差が見受けられませんでした。
というよりは random.choice()
のオーバーヘッドが大きかったのかも。
実験3. 100列1万行のランダムな整数のDataFrame
--------------------------------------------------
df with iteration
time: 40496.30618095398 ms
--------------------------------------------------
--------------------------------------------------
df with comprehension
time: 1698.488473892212 ms
--------------------------------------------------
forの方は Series
にしたり append
したりするための時間がありますが入れ子のループの場合は20倍以上速くなりました。
まとめ
- イテレーションの内部でやっている処理のオーバーヘッドによって速度改善の効果は変わる
- とはいえある程度のケースで「使えるならリスト内包表記」が正解と言えるのかも
yield
を使いこなせていないばかりに同じ処理を何度も書く残念コードになってしまったことだなぁ!
以上です。