元ネタ
1はできるlistが違うから比較として不適切では? そしてpypyを使うのがいろいろ解決してしまうこともある | あなたのPythonを爆速にする7つの方法 http://t.co/0lWErBIzU4
— Yuichiro Fukubayashi (@fukubaya) 2015, 5月 8
あと時間計測ならば1回だけじゃなくて,何回か実行した平均を取った方がいい.
条件を揃えてやってみた
全要素が0のリストの生成
- 空の配列に1つずつ0をappend (list_0_append)
- リスト内包表記 (list_0_comp)
- 乗算 (list_0_multi)
- array (list_0_array)
- rangeで作ったリストに0を代入 (list_0_range)
要素が[0, 1, ..., n-1]のリストの生成
- 空の配列に1つずつ数値をappend (list_i_append)
- リスト内包表記 (list_i_comp)
- 全0のリストを作ったあとに代入 (list_i_multi)
- array (list_i_array)
- range (list_i_range)
実証スクリプト
#!/usr/bin/python | |
# -*- coding: utf-8 -*- | |
# | |
# bench.py | |
# | |
# Created by FUKUBAYASHI Yuichiro on 2015/05/09 | |
# Copyright (c) 2015, FUKUBAYASHI Yuichiro | |
# | |
# last update: <2015/05/09 02:42:22> | |
# | |
import sys | |
from optparse import OptionParser | |
import time | |
from array import array | |
# list all 0 | |
def list_0_append(n): | |
xs = [] | |
for i in xrange(n): | |
xs.append(0) | |
return xs | |
def list_0_comp(n): | |
return [0 for i in xrange(n)] | |
def list_0_multi(n): | |
return [0] * n | |
def list_0_array(n): | |
return array('I', [0 for i in xrange(n)]) | |
def list_0_range(n): | |
xs = range(n) | |
for i in xrange(n): | |
xs[i] = 0 | |
return xs | |
# list i | |
def list_i_append(n): | |
xs = [] | |
for i in xrange(n): | |
xs.append(i) | |
return xs | |
def list_i_comp(n): | |
return [i for i in xrange(n)] | |
def list_i_multi(n): | |
xs = [0] * n | |
for i in xrange(n): | |
xs[i] = i | |
return xs | |
def list_i_array(n): | |
return array('I', [i for i in xrange(n)]) | |
def list_i_range(n): | |
return range(n) | |
# measure exec time | |
def exec_time(n, n_sample, func): | |
t = 0.0 | |
for i in range(n_sample): | |
start = time.clock() | |
func(n) | |
end = time.clock() | |
t += (end - start) | |
return t / n_sample | |
def main(options, args): | |
n = options.n | |
n_sample = 5 | |
print "--Python version--" | |
print sys.version | |
print "--list_0--" | |
for f in [list_0_append, | |
list_0_comp, | |
list_0_multi, | |
list_0_array, | |
list_0_range]: | |
print "%s(%d) in %f(s)" % (f.__name__, | |
n, | |
exec_time(n, n_sample, f)) | |
print "--list_i--" | |
for f in [list_i_append, | |
list_i_comp, | |
list_i_multi, | |
list_i_array, | |
list_i_range]: | |
print "%s(%d) in %f(s)" % (f.__name__, | |
n, | |
exec_time(n, n_sample, f)) | |
if __name__ == '__main__': | |
parser = OptionParser() | |
parser.add_option('-n', action='store', type='int', default=10000) | |
options, args = parser.parse_args() | |
main(options, args) |
結果
% python bench.py -n 1000000 --Python version-- 2.7.9 (v2.7.9:648dcafa7e5f, Dec 10 2014, 10:10:46) [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] --list_0-- list_0_append(1000000) in 0.229992(s) list_0_comp(1000000) in 0.087843(s) list_0_multi(1000000) in 0.016838(s) list_0_array(1000000) in 0.212526(s) list_0_range(1000000) in 0.175999(s) --list_i-- list_i_append(1000000) in 0.241931(s) list_i_comp(1000000) in 0.112636(s) list_i_multi(1000000) in 0.155012(s) list_i_array(1000000) in 0.237255(s) list_i_range(1000000) in 0.050963(s)
全要素0であっても乗算が一番速かった. [0, 1,...,n-1]の場合は,乗算よりリスト内包表記の方が速い.が,range(n)の方がもっと速かった.
pypyでもやってみた
pypy bench.py -n 1000000 --Python version-- 2.7.9 (9c4588d731b7, Mar 23 2015, 16:20:40) [PyPy 2.5.1 with GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.57)] --list_0-- list_0_append(1000000) in 0.152410(s) list_0_comp(1000000) in 0.014103(s) list_0_multi(1000000) in 0.010896(s) list_0_array(1000000) in 0.022531(s) list_0_range(1000000) in 0.022315(s) --list_i-- list_i_append(1000000) in 0.143897(s) list_i_comp(1000000) in 0.014448(s) list_i_multi(1000000) in 0.016995(s) list_i_array(1000000) in 0.023090(s) list_i_range(1000000) in 0.000005(s)
CPythonよりだいぶ速い.list_i_rangeは異常 (笑).
結論
全要素同じの場合は乗算,[0, 1,...,n-1]ならばrangeが速い. そしてpypy超速い (もちろんメモリやCPUの使い方まで比較してないので一概にpypyがいいとは言えない).