这里我有两个功能:
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()
。为什么?
答案 0 :(得分:0)
可能有很多原因。首先是CPU代码执行预测和编译器优化。对于我来说,如果您选择合适的算法,哪种形式的代码会更快对您来说并不重要。您需要使用一种适合您的需求并且看起来更好的产品,并将性能留给python编译器。通常,更快的选项会导致更多的内存/可读性/支持问题。同样,也不能保证仅在某些简单的周期内复杂的代码不会改变性能,只是因为可以进行一些优化。
-更新-
如果您想通过简单的操作来提高python的性能,我建议您看看PyPy,Cython,nim,它们在python类型包装器使用过多时会获得C的性能。
答案 1 :(得分:0)
这些总是很接近,但是出于一些有趣的原因:
如果乘积较小(例如,对于range(1,10)
而言),则该函数仅执行很少的“有用”工作,并且所有内容都将进入Python对象之间的编组机int
中,因此可以调用功能
如果乘积很大(例如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%的时间。
您的额外代码使数组遍历了,这似乎使问题变得更加复杂,因此我将其忽略了---这样的微基准测试很尴尬,无法正确执行!