Python FOR循环与内置的reduce()和mul()函数?

时间:2019-05-03 10:37:55

标签: python python-3.x

这里我有两个功能:

from functools import reduce
from operator import mul

def fn1(arr):
    product = 1
    for number in arr:
        product *= number
    return [product//x for x in arr]

def fn2(arr):
   product = reduce(mul, arr)
   return [product//x for x in arr]

基准:

In [2]: arr = list(range(1,11))

In [3]: %timeit fn1(arr)
1.62 µs ± 23.5 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [4]: %timeit fn2(arr)
1.88 µs ± 28.3 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [5]: arr = list(range(1,101))

In [6]: %timeit fn1(arr)
38.5 µs ± 190 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

In [7]: %timeit fn2(arr)
41 µs ± 463 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

In [8]: arr = list(range(1,1001))

In [9]: %timeit fn1(arr)
4.23 ms ± 25.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [10]: %timeit fn2(arr)
4.24 ms ± 36.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [11]: arr = list(range(1,10001))

In [12]: %timeit fn1(arr)
605 ms ± 4.97 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [13]: %timeit fn2(arr)
594 ms ± 4.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

fn2()中,小列表的速度稍慢。我的理解是reduce()mul()函数都是内置函数,因此它们以C的速度运行,并且应该比for循环快。可能是因为fn2中有更多的函数调用(这也需要花费一些时间),这有助于最终性能吗?但随后的趋势表明,列表较大时fn2()优于fn1()。为什么?

2 个答案:

答案 0 :(得分:0)

可能有很多原因。首先是CPU代码执行预测和编译器优化。对于我来说,如果您选择合适的算法,哪种形式的代码会更快对您来说并不重要。您需要使用一种适合您的需求并且看起来更好的产品,并将性能留给python编译器。通常,更快的选项会导致更多的内存/可读性/支持问题。同样,也不能保证仅在某些简单的周期内复杂的代码不会改变性能,只是因为可以进行一些优化。

-更新-

如果您想通过简单的操作来提高python的性能,我建议您看看PyPy,Cython,nim,它们在python类型包装器使用过多时会获得C的性能。

答案 1 :(得分:0)

这些总是很接近,但是出于一些有趣的原因:

  1. 如果乘积较小(例如,对于range(1,10)而言),则该函数仅执行很少的“有用”工作,并且所有内容都将进入Python对象之间的编组机int中,因此可以调用功能

  2. 如果乘积很大(例如range(1,10001)),则数字变得巨大(即数千个十进制数字),并且大部分时间都花在乘以非常大的数字上

例如,对于Python 3.7.3(在Linux 5.0.10中):

from functools import reduce
from operator import mul

prod = reduce(mul, range(1, 10001))

prod的位数约为36k,消耗的数据约为16KiB,即用math.log10(prod)sys.getsizeof(prod)进行检查。

使用小型(即非bignum)产品,例如:

reduce(mul, [1] * 10001)
在我的计算机上,

的速度比我们如上所述需要使用bignums快50倍。还请注意,与整数(例如)相比,使用浮点数时的速度基本相同。

reduce(mul, [1.] * 10001)

只需要大约10%的时间。

您的额外代码使数组遍历了,这似乎使问题变得更加复杂,因此我将其忽略了---这样的微基准测试很尴尬,无法正确执行!