先日投稿した「pythonにおけるlist、numpy.arrayの複製、初期化(0埋め)」という記事でlist、ndarrayの幾つかの複製方法について触れましたが、今回の記事では、各複製方法の速度を測定しました。
結果
1次元list・ndarrayのそれぞれの場合で、同じ長さの配列を用意し、それの複製速度を測定しました。前回の記事ではcopyモジュールのdeepcopyは使いませんでしたが、今回は方法(6)として、deepcopyによる複製速度も測定しました。
1次元listの複製
長さ1000の1次元list aを用意して、方法(2)〜(6)のそれぞれの方法でaを複製する関数を定義し、処理速度を測定した結果が次の表です。
番号 | 方法 | 処理速度の平均[秒] | 標準偏差[秒] |
---|---|---|---|
(2) | copy_a = a[:] | 0.00352 | 0.000101 |
(3) | copy_a = a.copy() | 0.00353 | 0.000115 |
(4) | copy_a = list(a) | 0.00344 | 0.000156 |
(5) | copy_a = [elem for elem in a] | 0.017 | 0.000632 |
(6) | copy_a = deepcopy(a) | 0.405 | 0.0259 |
list aが高次元配列の場合、方法(6)のdeepcopyは複製に重宝しますが、実行速度は最も遅い結果となりました。
1次元ndarrayの複製
長さ1000の1次元ndarray aを用意して、方法(3)〜(6)のそれぞれの方法でaを複製する関数を定義し、処理速度を測定した結果が次の表です。
番号 | 方法 | 処理速度の平均[秒] | 標準偏差[秒] |
---|---|---|---|
(3) | copy_a = a.copy() | 0.00053 | 6.17e-05 |
(4) | copy_a = np.array(a) | 0.000568 | 8.08e-05 |
(5) | copy_a = np.array([elem for elem in a]) | 0.093 | 0.00314 |
(6) | copy_a = deepcopy(a) | 0.00172 | 0.000179 |
aがlistのときと比べると、ndarrayの方が全体的に複製速度が速いですね。方法(5)は一度listを作ってndarrayに変換しているため1次元listの複製のときよりも遅い結果となっています。
測定方法
次のスクリプトを実行して処理速度の測定を行いました。
from mytools import measure_time
import numpy as np
from copy import deepcopy
def do_nothing():
pass
def copy_list2(_list):
new_list = _list[:]
del new_list
def copy_list3(_list):
new_list = _list.copy()
del new_list
def copy_list4(_list):
new_list = list(_list)
del new_list
def copy_list5(_list):
new_list = [elem for elem in _list]
del new_list
def copy_list6(_list):
new_list = deepcopy(_list)
del new_list
def copy_array3(_array):
new_array = _array.copy()
del new_array
def copy_array4(_array):
new_array = np.array(_array)
del new_array
def copy_array5(_array):
new_array = np.array([elem for elem in _array])
del new_array
def copy_array6(_array):
new_array = deepcopy(_array)
del new_array
if __name__ == '__main__':
N = 1000
_list = list([0 for _ in range(N)])
_array = np.array([0 for _ in range(N)])
measure_time(do_nothing)
measure_time(copy_list2, _list)
measure_time(copy_list3, _list)
measure_time(copy_list4, _list)
measure_time(copy_list5, _list)
measure_time(copy_list6, _list)
measure_time(copy_array3, _array)
measure_time(copy_array4, _array)
measure_time(copy_array5, _array)
measure_time(copy_array6, _array)
次が、上記スクリプト中のmeasure_time
関数の定義です。
import sys
import time
import numpy as np
def measure_time(func, *args, iteration=1000, samples=1000):
_tuple = (func.__name__, iteration, samples)
print('Function: {}\nIteration: {}\nSamples: {}'.format(*_tuple))
series = []
for i in range(samples):
sys.stdout.write('\rProgress: [{}/{}]'.format(i, samples))
start = time.time()
for j in range(iteration):
func(*args)
series.append(time.time()-start)
print('\rMean: {}, SD: {}\n'.format(np.average(series), np.std(series)))
measure_time
関数は、引数として渡された関数がiteration回実行されるのに掛かる時間をsamples回記録して、処理時間の平均と標準偏差を標準出力します。以下がmeasure_time
の実行例です。
Function: do_nothing
Iteration: 1000
Samples: 1000
Mean: 9.026002883911132e-05, SD: 9.253773932953281e-06
Function: copy_list2
Iteration: 1000
Samples: 1000
Mean: 0.0034766535758972168, SD: 6.660195155484076e-05
備考
上記スクリプト中の関数copy_array4
では、ndarrayを受け取ってnp.array()
により新たにndarrayオブジェクトを作っていますが、copy_array4
に上記スクリプト中の_listを渡した場合の処理速度を測定してみました。
Function: copy_array4
Iteration: 1000
Samples: 1000
Mean: 0.04266641592979431, SD: 0.00023159095884393903
ndarrayを渡されたときよりも100倍程遅いですね。加えて、関数copy_list4
に同じ_listを渡したときよりも10倍程遅いです。
逆に、関数copy_list4
にスクリプト中の_arrayを渡した場合の処理速度を測定してみましょう。
Function: copy_list4
Iteration: 1000
Samples: 1000
Mean: 0.029821698427200317, SD: 0.004067780773085223
copy_array4
に_listを渡した場合の平均処理速度と同じオーダーになりました。
listオブジェクトを引数にnp.array()
でndarrayオブジェクトを作ったり、ndarrayオブジェクトを引数にlist()
でlistオブジェクトを作るのって時間掛かるんですね。
追記(代入速度の測定)
興味本位で、次のスクリプトにより1次元list・ndarrayの各要素を書き換える操作の処理速度を測定してみました。
from mytools import measure_time
import numpy as np
def for_only(N):
for n in range(N):
pass
def substitute_list(_list, N):
for n in range(N):
_list[n] = 0
del _list
def substitute_array(_array, N):
for n in range(N):
_array[n] = 0
del _array
if __name__ == '__main__':
N = 1000
_list = list([0 for _ in range(N)])
_array = np.array([0 for _ in range(N)])
measure_time(dummy, _list, N)
measure_time(substitute_list, _list, N)
measure_time(substitute_array, _array, N)
以下がこのスクリプトの実行結果です。
Function: for_only
Iteration: 1000
Samples: 1000
Mean: 0.01462605857849121, SD: 0.0008302230687152986
Function: substitute_list
Iteration: 1000
Samples: 1000
Mean: 0.02975408983230591, SD: 0.0016898342277376784
Function: substitute_array
Iteration: 1000
Samples: 1000
Mean: 0.0648690721988678, SD: 0.00520460908502261
関数for_only
はfor文をただ回すだけの関数なので、for文の実行時間文を差し引くと、listオブジェクトの要素を書き換える方がndarrayオブジェクトの場合よりも3倍程速い結果となります。