2016-07-28 64 views
10

我正在研究Python生成器並決定運行一個小實驗。爲什麼通過產量比由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)  

內存使用(使用psutil獲得進程RSS存儲器)和所用的時間(使用了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生成一個生成器一直(稍微)比使用yield更慢。爲什麼?

+3

'xrange'是一個序列對象而不是生成器,因此它們的內部並不完全相同。此外,你給出的時間在xrange和發生器之間並沒有真正的顯着差異。事實上,這種差異可以忽略不計 – smac89

+0

有一點需要解決的是在使用'xrange'時實際創建列表。在'my_xrange'函數中,您只返回xrange _generator_(它實際上不是一個生成器)。但尚未完成清單。所以它可能會比上面的數字更慢。 – aneroid

+2

您只需計算構建生成器和構建「xrange」對象需要多少時間 - 您並未測量實際迭代這些對象所花費的時間量...... – mgilson

回答

9

我要在這個答案前面說這個規模的時間可能很難準確測量(可能最好使用timeit),並且這些優化幾乎不會影響您的實際情況程序運行時間...

好了,現在該聲明後...

,你需要注意的第一件事是,你只定時發生器/ x範圍對象的構造 - 你是不是計時實際迭代值需要多長時間 。有幾個原因產生的發電機可能在某些情況下比創建的xrange對象更快...

  1. 爲發電機的情況下,你只創建一個發電機 - 發電機無碼實際上得到跑。這相當於大致1個函數調用。
  2. 對於xrange情況下,你調用該函數然後你要查找的全局名稱xrange,全球TOTAL,然後你需要調用內置的 - 所以有在執行更多的東西這個案例。

至於內存 - 在這兩種懶惰的方法中,使用的內存將由python運行時控制 - 不是由生成器對象的大小決定的。內存使用受腳本影響的唯一情況就是您構建1億個項目的列表。

另外請注意,我不能真正確認您的成績一直在我的系統...使用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') 

而且我的結果是(爲CPython在OS-X埃爾 - 匹):

0.227491140366 
0.356791973114 

然而,pypy似乎青睞發電機的結構(我首先嚐試了my_xrangemy_sequence,並且得到了相當一致的結果,儘管第一個運行似乎有點不利 - 也許是由於JIT預熱時間mething):

0.00285911560059 
0.00137305259705 

在這裏,我會期望xrange很有優勢 - 但是,沒有什麼是真的,直到你timeit,然後如果定時差異是唯一真正的重要的,只有在你做計時的電腦上纔是真實的。
:-P

+0

此外,前兩種情況中列出的「總內存」實際上是Python運行時使用的內存。 – jsbueno

+1

@jsbueno - 噢,我完全無視這一點。這與'my_list'情況下的所有情況無關:-) – mgilson

3

正如我在評論中提及上面,你發生器功能,並與x範圍,你不實際創建序列,只創建對象見開幕的免責聲明。 @ 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倍。

  3. list(my_list)是多餘的,因爲my_list已經返回由range產生的名單,所以我做了一個更多的時間,而不調用list()

  4. rangexrange幾乎相同,但那是因爲我減少了TOTAL。最大的區別是range會消耗更多的內存,因爲它會首先創建整個列表,因此只會在的那個部分更長。有效地從xrange = range創建一個列表。所以最終使用的內存將是相同的,因爲我只是創建了一個列表的xrange,很難看到這個微不足道的情況的差異。