2016-07-28 6 views
10

私はPythonのジェネレータを調べていて、少し実験をすることにしました。xrangeで生成されたジェネレータは、xrangeで生成されたジェネレータよりも速く歩留まりで生産されるのはなぜですか?

TOTAL = 100000000 
def my_sequence(): 
    i = 0 
    while i < TOTAL: 
     yield i 
     i += 1 

def my_list(): 
    return range(TOTAL) 

def my_xrange(): 
    return xrange(TOTAL)  

メモリ使用量(プロセスRSSメモリを取得するためにpsutilを使用して)、時間撮影した(time.time()を使用して)各メソッドを複数回実行して平均値を取った後、以下の通りであるが:

sequence_of_values = my_sequence() # Memory usage: 6782976B Time taken: 9.53674e-07 s 

sequence_of_values2 = my_xrange() # Memory usage: 6774784B Time taken: 2.14576e-06 s 

list_of_values = my_list() # Memory usage: 3266207744B Time taken: 1.80253s 

私は、xrangeを使用して発電機を生産することは、歩留まりを使用して発電機を生産することよりも(わずかに)遅くなることに気づいた。どうしてこんなことに?

+3

'xrange'はジェネレータではないシーケンスオブジェクトであるため、内部構造はまったく同じではありません。また、あなたが与えたタイミングは、xrangeとジェネレーターとの間に大きな違いはありません。実際には違いはごくわずかです – smac89

+0

修正するには、 'xrange'を使うときに実際にリストを作成することです。 'my_xrange'関数では、xrange_generator_(実際にはジェネレータではありません)を返すだけです。しかし、それはまだ完全なリストに処理されていません。だから、上記の数よりもさらに遅くなるかもしれません。 – aneroid

+2

ジェネレータを構築し、 'xrange'オブジェクトを構築するのに必要な時間を計測するだけです。これらのオブジェクトを実際に反復するのにかかる時間は計測していません。 – mgilson

答えて

9

この尺度のタイミングは、正確に測定するのが難しい(おそらくtimeitを使用するのが最適です)と、これらの種類の最適化は実際にはほとんど変わりませんプログラムの実行時...

[OK]を、今免責事項は

あなたが気づくする必要がある最初の事はあなたが唯一の発電機/ xrangeのオブジェクトの建設をタイミングしているということです...行われている - あなたがいますNOTタイミング値を実際に反復するのにかかる時間 。発電機ケースのためにジェネレータを作成すると、より高速ないくつかのケースではxrangeオブジェクトを作成するよりもあるかもしれない理由のカップルの理由...

  1. がありますが、あなただけのジェネレータを作成している - 発電におけるNOのコードは、実際に得ません走るこれはおおよそ1つの関数呼び出しになります。
  2. xrangeケースでは、あなたが機能を呼び出している、あなたは、グローバル名xrangeをルックアップする必要がグローバルTOTALと、あなたはその組み込みを呼び出す必要があります - だから、がで実行されているより多くの事がありますこの場合。

メモリについて - 両方の怠け者のアプローチでは、使用されるメモリはPythonランタイムによって支配されます。ジェネレータオブジェクトのサイズではありません。あなたのスクリプトによってメモリ使用量がかなり影響を受ける唯一のケースは、100万件のリストを作成する場合です。

はまた、私は実際に私のシステムで一貫して結果を確認することができない... timeitを使用して、私は実際にmy_xrangeは時々(〜30%)を構築する 速いであることを取得します注意してください。スクリプトの最後に次の追加

from timeit import timeit 
print timeit('my_xrange()', setup='from __main__ import my_xrange') 
print timeit('my_sequence()', setup='from __main__ import my_sequence') 

そして、私の結果は(OS-Xエル・キャピタンのCPython用)です。

0.227491140366 
0.356791973114 

しかし、pypyが好むようですジェネレータの構築(まず最初にmy_xrangemy_sequenceを試してみましたが、最初に実行するのは少し不利なようですが、かなり安定した結果が得られました.JITのウォームアップ時間などが原因ですmething): - あなたがして、タイミングの差がある場合にのみ、本当だtimeitまでは、再び、何も真実ではありません

0.00285911560059 
0.00137305259705 
ここ

、私はxrangeは、エッジを持っていることを期待します重要なことであり、タイミングを実行したコンピュータでのみ当てはまります。
は、私はあなたのジェネレータ関数でとはxrangeと、上記の私のコメントで述べたように免責事項:-P

+0

また、最初の2つのケースに挙げられている「合計メモリ」は、実際にはPythonランタイムで使用されるメモリです。 – jsbueno

+1

@jsbueno - ああ、私は完全にそのビットを無視しています。 'my_list'の場合はすべての場合に無関係です:-) – mgilson

3

を開くを参照してください、あなたは実際に単にオブジェクトを作成し、シーケンスを作成していません。 @ mgilsonの答えはに関連するコールをカバーし、を作成します。実際に彼らと何かを行うためとして

>>> TOTAL = 100000 
>>> # your functions here 
... 
>>> import timeit 
>>> timeit.timeit("list(my_seq())", setup="from __main__ import my_seq", number=1000) 
9.783777457339898 
>>> timeit.timeit("list(my_xrange())", setup="from __main__ import my_xrange", number=1000) 
1.2652621698083024 
>>> timeit.timeit("list(my_list())", setup="from __main__ import my_list", number=1000) 
2.666709824464867 
>>> timeit.timeit("my_list()", setup="from __main__ import my_list", number=1000) 
1.2324339537661615 
  1. あなたは、私が列を処理していますので、私はそれぞれのうちlistを作成していていることがわかります。

  2. ジェネレータの機能は、xrangeの約10倍です。 my_listはすでにrangeによって生成されるリストを返しますので、

  3. list(my_list)は冗長であるので、私はもう一回list()への呼び出しなしでそれをやりました。

  4. rangeは、xrangeとほぼ同じですが、これは合計を減らしたためです。最大の違いは、rangeはリスト全体を最初に作成するので、その部分ではだけ長く続くので、より多くのメモリを消費することになります。 xrange = rangeからリストを効果的に作成する。使用される最終的なメモリは同じであり、単にxrangeからリストを作成するだけなので、この単純な場合の違いを見るのは難しいです。

関連する問題