![](/img/trans.png)
[英]Are list-comprehensions and functional functions faster than "for loops"?
[英]Comparing list comprehensions and explicit loops (3 array generators faster than 1 for loop)
我做了功課,我意外地發現了算法的速度奇怪的不一致。 這是相同功能bur的2個版本的代碼bur與1差異:在第一個版本中我使用3倍數組生成器來過濾一些數組,在第二個版本中我使用1 for循環與3 if語句進行相同的過濾工作。
所以,這是第一版的代碼:
def kth_order_statistic(array, k):
pivot = (array[0] + array[len(array) - 1]) // 2
l = [x for x in array if x < pivot]
m = [x for x in array if x == pivot]
r = [x for x in array if x > pivot]
if k <= len(l):
return kth_order_statistic(l, k)
elif k > len(l) + len(m):
return kth_order_statistic(r, k - len(l) - len(m))
else:
return m[0]
這里是第二版的代碼:
def kth_order_statistic2(array, k):
pivot = (array[0] + array[len(array) - 1]) // 2
l = []
m = []
r = []
for x in array:
if x < pivot:
l.append(x)
elif x > pivot:
r.append(x)
else:
m.append(x)
if k <= len(l):
return kth_order_statistic2(l, k)
elif k > len(l) + len(m):
return kth_order_statistic2(r, k - len(l) - len(m))
else:
return m[0]
第一版的IPython輸出:
In [4]: %%timeit
...: A = range(100000)
...: shuffle(A)
...: k = randint(1, len(A)-1)
...: order_statisctic(A, k)
...:
10 loops, best of 3: 120 ms per loop
對於第二版:
In [5]: %%timeit
...: A = range(100000)
...: shuffle(A)
...: k = randint(1, len(A)-1)
...: kth_order_statistic2(A, k)
...:
10 loops, best of 3: 169 ms per loop
那么為什么第一版比第二版快呢? 我還使用filter()函數而不是數組生成器制作了第三個版本,它比第二個版本慢(它每個循環得到218毫秒)
使用simple for
比list comprehesion
更快。 它快了近2倍。 檢查以下結果:
使用list comprehension
: 58 usec
moin@moin-pc:~$ python -m timeit "[i for i in range(1000)]"
10000 loops, best of 3: 58 usec per loop
使用for
循環: 37.1 usec
moin@moin-pc:~$ python -m timeit "for i in range(1000): i"
10000 loops, best of 3: 37.1 usec per loop
但是,在你的情況, for
正在花費更多的時間比列表理解,不是因為你的循環很慢。 但是因為.append()
你在代碼中使用。
使用append()
in for
循環`: 114 usec
moin@moin-pc:~$ python -m timeit "my_list = []" "for i in range(1000): my_list.append(i)"
10000 loops, best of 3: 114 usec per loop
這清楚地表明它是.append()
,它占用了for
循環所用時間的兩倍 。
但是, storing the "list.append" in different variable
: 69.3 usec
moin@moin-pc:~$ python -m timeit "my_list = []; append = my_list.append" "for i in range(1000): append(i)"
10000 loops, best of 3: 69.3 usec per loop
與上面比較中的最后一個案例相比,性能有了很大的提高,結果與list comprehension
結果相當。 這意味着,不是每次調用my_list.append()
,而是通過將函數的引用存儲在另一個變量(即append_func = my_list.append
並使用該變量append_func(i)
進行調用來提高性能。
這也證明, 與直接使用類的對象進行函數調用相比,調用存儲在變量中的類函數更快 。
感謝Stefan提出最后一個案例。
讓我們定義回答問題所需的函數並將它們計時:
In [18]: def iter():
l = [x for x in range(100) if x > 10]
....:
In [19]: %timeit iter()
100000 loops, best of 3: 7.92 µs per loop
In [20]: def loop():
l = []
for x in range(100):
if x > 10:
l.append(x)
....:
In [21]: %timeit loop()
10000 loops, best of 3: 20 µs per loop
In [22]: def loop_fast():
l = []
for x in range(100):
if x > 10:
pass
....:
In [23]: %timeit loop_fast()
100000 loops, best of 3: 4.69 µs per loop
我們可以看到沒有append命令的for循環和列表理解一樣快。 實際上,如果我們看一下字節碼,我們可以看到,在列表解析的情況下,python能夠使用一個名為LIST_APPEND的內置字節碼命令,而不是:
正如您從下面的輸出中看到的那樣,列表理解和“loop_fast”函數缺少前一個字節碼。 比較三個函數的時間顯然是那些負責三種方法的不同時間。
In [27]: dis.dis(iter)
2 0 BUILD_LIST 0
3 LOAD_GLOBAL 0 (range)
6 LOAD_CONST 1 (1)
9 LOAD_CONST 2 (100)
12 CALL_FUNCTION 2
15 GET_ITER
>> 16 FOR_ITER 24 (to 43)
19 STORE_FAST 0 (x)
22 LOAD_FAST 0 (x)
25 LOAD_CONST 2 (100)
28 COMPARE_OP 4 (>)
31 POP_JUMP_IF_FALSE 16
34 LOAD_FAST 0 (x)
37 LIST_APPEND 2
40 JUMP_ABSOLUTE 16
>> 43 STORE_FAST 1 (l)
46 LOAD_CONST 0 (None)
49 RETURN_VALUE
In [28]: dis.dis(loop)
2 0 BUILD_LIST 0
3 STORE_FAST 0 (1)
3 6 SETUP_LOOP 51 (to 60)
9 LOAD_GLOBAL 0 (range)
12 LOAD_CONST 1 (1)
15 LOAD_CONST 2 (100)
18 CALL_FUNCTION 2
21 GET_ITER
>> 22 FOR_ITER 34 (to 59)
25 STORE_FAST 1 (x)
4 28 LOAD_FAST 1 (x)
31 LOAD_CONST 3 (10)
34 COMPARE_OP 4 (>)
37 POP_JUMP_IF_FALSE 22
5 40 LOAD_FAST 0 (l)
43 LOAD_ATTR 1 (append)
46 LOAD_FAST 1 (x)
49 CALL_FUNCTION 1
52 POP_TOP
53 JUMP_ABSOLUTE 22
56 JUMP_ABSOLUTE 22
>> 59 POP_BLOCK
>> 60 LOAD_CONST 0 (None)
63 RETURN_VALUE
In [29]: dis.dis(loop_fast)
2 0 BUILD_LIST 0
3 STORE_FAST 0 (1)
3 6 SETUP_LOOP 38 (to 47)
9 LOAD_GLOBAL 0 (range)
12 LOAD_CONST 1 (1)
15 LOAD_CONST 2 (100)
18 CALL_FUNCTION 2
21 GET_ITER
>> 22 FOR_ITER 21 (to 46)
25 STORE_FAST 1 (x)
4 28 LOAD_FAST 1 (x)
31 LOAD_CONST 3 (10)
34 COMPARE_OP 4 (>)
37 POP_JUMP_IF_FALSE 22
5 40 JUMP_ABSOLUTE 22
43 JUMP_ABSOLUTE 22
>> 46 POP_BLOCK
>> 47 LOAD_CONST 0 (None)
50 RETURN_VALUE
讓我們消除這個疑問: 第二個版本稍快一些: 列表理解更快 ,但是在一次迭代中丟棄了兩個數組循環和多個條件。
def kth_order_statistic1(array,k):
pivot = (array[0] + array[len(array) - 1]) // 2
l = [x for x in array if x < pivot]
m = [x for x in array if x == pivot]
r = [x for x in array if x > pivot]
if k <= len(l):
return kth_order_statistic1(l, k)
elif k > len(l) + len(m):
return kth_order_statistic1(r, k - len(l) - len(m))
else:
return m[0]
def kth_order_statistic2(array,k):
pivot = (array[0] + array[len(array) - 1]) // 2
l = []
m = []
r = []
for x in array:
if x < pivot:
l.append(x)
elif x > pivot:
r.append(x)
else:
m.append(x)
if k <= len(l):
return kth_order_statistic2(l, k)
elif k > len(l) + len(m):
return kth_order_statistic2(r, k - len(l) - len(m))
else:
return m[0]
def kth_order_statistic3(array,k):
pivot = (array[0] + array[len(array) - 1]) // 2
l = []
m = []
r = []
for x in array:
if x < pivot: l.append(x)
for x in array:
if x== pivot: m.append(x)
for x in array:
if x > pivot: r.append(x)
if k <= len(l):
return kth_order_statistic3(l, k)
elif k > len(l) + len(m):
return kth_order_statistic3(r, k - len(l) - len(m))
else:
return m[0]
import time
import random
if __name__ == '__main__':
A = range(100000)
random.shuffle(A)
k = random.randint(1, len(A)-1)
start_time = time.time()
for x in range(1000) :
kth_order_statistic1(A,k)
print("--- %s seconds ---" % (time.time() - start_time))
start_time = time.time()
for x in range(1000) :
kth_order_statistic2(A,k)
print("--- %s seconds ---" % (time.time() - start_time))
start_time = time.time()
for x in range(1000) :
kth_order_statistic3(A,k)
print("--- %s seconds ---" % (time.time() - start_time))
python :
--- 25.8894710541 seconds ---
--- 24.073086977 seconds ---
--- 32.9823839664 seconds ---
ipython
--- 25.7450709343 seconds ---
--- 22.7140650749 seconds ---
--- 35.2958850861 seconds ---
時間可能會根據隨機抽取而有所不同,但三者之間的差異幾乎相同。
算法結構不同,條件結構也是有罪的。 可以通過之前的測試丟棄附加到r和m中的測試。 關於具有append
的for循環和列表理解的更嚴格的比較將反對非最優跟隨
for x in array:
if x < pivot:
l.append(x)
for x in array:
if x== pivot:
m.append(x)
for x in array:
if x > pivot:
r.append(x)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.