簡體   English   中英

使用for循環比使用reduce更快地求和?

[英]Summing with a for loop faster than with reduce?

我想看看比使用for循環進行簡單的數值運算要快多少。 這是我發現的(使用標准timeit庫):

In [54]: print(setup)
from operator import add, iadd
r = range(100)

In [55]: print(stmt1)    
c = 0
for i in r:
    c+=i        

In [56]: timeit(stmt1, setup)
Out[56]: 8.948904991149902
In [58]: print(stmt3)    
reduce(add, r)    

In [59]: timeit(stmt3, setup)
Out[59]: 13.316915035247803

再看一點:

In [68]: timeit("1+2", setup)
Out[68]: 0.04145693778991699

In [69]: timeit("add(1,2)", setup)
Out[69]: 0.22807812690734863

這里發生了什么? 顯然,reduce會比for循環更快,但函數調用似乎占主導地位。 減少版本不應該幾乎完全在C中運行嗎? 在for循環版本中使用iadd(c,i)使其在~24秒內運行。 為什么使用operator.add比+慢得多? 我的印象是+和operator.add運行相同的C代碼(我檢查以確保operator.add不只是在python中調用+或任何東西)。

順便說一句,只需在~2.3秒內使用總和運行。

In [70]: print(sys.version)
2.7.1 (r271:86882M, Nov 30 2010, 09:39:13) 
[GCC 4.0.1 (Apple Inc. build 5494)]

reduce(add, r)必須調用add()函數100次,因此函數調用的開銷加起來 - reduce使用PyEval_CallObject在每次迭代時調用add

for (;;) {
    ...
    if (result == NULL)
        result = op2;
    else {
        # here it is creating a tuple to pass the previous result and the next
        # value from range(100) into func add():
        PyTuple_SetItem(args, 0, result);
        PyTuple_SetItem(args, 1, op2);
        if ((result = PyEval_CallObject(func, args)) == NULL)
            goto Fail;
    }

更新 :回復評論中的問題。

當您在Python源代碼中鍵入1 + 2時,字節碼編譯器會執行添加並將該表達式替換為3

f1 = lambda: 1 + 2
c1 = byteplay.Code.from_code(f1.func_code)
print c1.code

1           1 LOAD_CONST           3
            2 RETURN_VALUE         

如果添加兩個變量a + b ,編譯器將生成字節碼,該字節碼加載兩個變量並執行BINARY_ADD,這比調用函數執行添加要快得多:

f2 = lambda a, b: a + b
c2 = byteplay.Code.from_code(f2.func_code)
print c2.code

1           1 LOAD_FAST            a
            2 LOAD_FAST            b
            3 BINARY_ADD           
            4 RETURN_VALUE         

編輯 :切換零而不是數組乘以縮短差距的大時間。

from functools import reduce
from numpy import array, arange, zeros
from time import time

def add(x, y):
    return x + y

def sum_columns(x):
    if x.any():
        width = len(x[0])
        total = zeros(width)
    for row in x:
        total += array(row)
    return total

l = arange(3000000)
l = array([l, l, l])

start = time()
print(reduce(add, l))
print('Reduce took {}'.format(time() - start))

start = time()
print(sum_columns(l))
print('For loop took took {}'.format(time() - start))

讓你失望幾乎沒有差別。

Reduce took 0.03230619430541992 For loop took took 0.058577775955200195

old :如果使用reduce來按索引將NumPy數組加在一起,它可能比for循環更快。

from functools import reduce
from numpy import array, arange
from time import time

def add(x, y):
    return x + y

def sum_columns(x):
    if x.any():
        width = len(x[0])
        total = array([0] * width)
    for row in x:
        total += array(row)
    return total

l = arange(3000000)
l = array([l, l, l])

start = time()
print(reduce(add, l))
print('Reduce took {}'.format(time() - start))

start = time()
print(sum_columns(l))
print('For loop took took {}'.format(time() - start))

隨着結果

[      0       3       6 ..., 8999991 8999994 8999997]
Reduce took 0.024930953979492188
[      0       3       6 ..., 8999991 8999994 8999997]
For loop took took 0.3731539249420166

它可能是復制args和返回值的開銷(即“add(1,2)”),而不是簡單地操作數字文字

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM