python の zip関数は for文と組み合わせて使うと、2つのシーケンスに対して同時にループ処理を行うことができるので、とても便利。
しかし、実際に使ってみると、zipするシーケンスのサイズが大きくなると、パフォーマンスがガタ落ちすることがわかった。
実際に動かしてみたスクリプトが以下。
#! /usr/bin/env python # -*- coding: utf-8 -*- from progressbar import * import random widgets_i = ['index: ', Percentage(), ' ', Bar(), ' ', ETA()] widgets_z = ['zip: ', Percentage(), ' ', Bar(), ' ', ETA()] for n in [10**5, 10**6, 2*10**6, 3*10**6, 5*10**6]: print '*** data size = %d ***' %(n) data1 = tuple([random.random() for i in range(n)]) data2 = tuple([random.random() for i in range(n)]) data3 = tuple([random.random() for i in range(n)]) #print '--- index ---' val_i = 0 pbar = ProgressBar(widgets=widgets_i, maxval=n).start() for i in range(n): val_i += (data1[i]+data2[i]+data3[i])/3 pbar.update(i+1) pbar.finish() #print '--- zip ---' val_z = 0 pbar = ProgressBar(widgets=widgets_z, maxval=n).start() for (i,j,k) in zip(data1,data2,data3): val_z += (i+j+k)/3 pbar.update(i+1) pbar.finish() print 'index res = %f, zip res = %f, diff = %f'%(val_i/n, val_z/n, (val_i-val_z)/n)
実行結果は以下の通り。
$ python ./zip_progress.py *** data size = 100000 *** index: 100% |##########################################| Time: 0:00:00 zip: 100% |############################################| Time: 0:00:00 index res = 0.499342, zip res = 0.499342, diff = 0.000000 *** data size = 1000000 *** index: 100% |##########################################| Time: 0:00:03 zip: 100% |############################################| Time: 0:00:05 index res = 0.500120, zip res = 0.500120, diff = 0.000000 *** data size = 2000000 *** index: 100% |##########################################| Time: 0:00:06 zip: 100% |############################################| Time: 0:00:17 index res = 0.500008, zip res = 0.500008, diff = 0.000000 *** data size = 3000000 *** index: 100% |##########################################| Time: 0:00:09 zip: 100% |############################################| Time: 0:00:33 index res = 0.500110, zip res = 0.500110, diff = 0.000000 *** data size = 5000000 *** index: 100% |##########################################| Time: 0:00:16 zip: 100% |############################################| Time: 0:01:18 index res = 0.500025, zip res = 0.500025, diff = 0.000000
- 計算結果は、indexを使ったループもzip関数でのループも同じ。
- n=10^5 では差がない。
- n=10^6 を超えても、indexでのループは要素数に比例した実行時間となる。
- 一方、n=10^6 を超えたところから、zip関数のパフォーマンスが落ちる。
- 実行環境のメモリサイズなどで、ボトルネックとなるシーケンスサイズは異なる可能性はあるが、予め大きなデータを扱うことがわかっている場合は、注意する必要がありそう。
言語仕様上のお話なのかは、ググってみたけれどわからず、、、