元ネタ
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)
実証スクリプト
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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がいいとは言えない).