我有一个计算项目,大量使用日志函数(对于整数),数十亿次调用。我发现numpy的日志性能出乎意料地慢了。
以下代码需要15到17秒才能完成:
import numpy as np
import time
t1 = time.time()
for i in range(1,10000000):
np.log(i)
t2 = time.time()
print(t2 - t1)
但是,math.log函数从3到4秒的时间要少得多。
import math
import time
t1 = time.time()
for i in range(1,10000000):
math.log(i)
t2 = time.time()
print(t2 - t1)
我还测试了matlab和C#,它们分别需要大约2秒和0.3秒。
MATLAB
tic
for i = 1:10000000
log(i);
end
toc
C#
var t = DateTime.Now;
for (int i = 1; i < 10000000; ++i)
Math.Log(i);
Console.WriteLine((DateTime.Now - t).TotalSeconds);
在python中有什么方法可以提高日志功能的性能吗?
答案 0 :(得分:2)
NumPys函数适用于不适用于单值或标量的数组。它们具有相当高的开销,因为它们会进行多次检查和转换,这将为大型阵列提供速度优势,但这些对于标量来说代价很高。
如果您检查退货的类型,转换非常明显:
>>> import numpy as np
>>> import math
>>> type(np.log(2.))
numpy.float64
>>> type(math.log(2.))
float
另一方面,math
- 模块针对标量进行了优化。所以他们不需要那么多检查(我认为只有两个:转换为float
并检查它是<= 0
)。与math.log
相比,numpy.log
对于标量更快的原因。
但是如果你对数组进行操作并想要获取数组中所有元素的对数,NumPy可以更快。在我的计算机上,如果我在一个数组上执行np.log
与列表中每个项目的math.log
相比,则时间看起来不同:
arr = np.arange(1, 10000000)
%timeit np.log(arr)
201 ms ± 959 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
lst = arr.tolist()
%timeit [math.log(item) for item in lst]
8.77 s ± 63.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
所以np.log
在阵列上会快几个数量级(在这种情况下它会快40多倍)!而且你不需要自己编写任何循环。因为ufunc np.log
也可以正确地处理多维numpy数组,并且还允许在原地进行操作。
根据经验:如果你有一个包含数千个项目的数组,NumPy会更快,如果你有标量或只有几十个项math
+显式循环会更快。
也不要将time
用于计时代码。有专门的模块可以提供更准确的结果,更好的统计数据,并在计时期间禁用垃圾收集:
我通常使用%timeit
这是timeit
功能的方便包装,但它需要IPython。他们已经方便地显示结果均值和偏差,并做一些(大多数)有用的统计数据,如显示“最佳7”或“最佳3”结果。
我最近分析了another question的numpy函数的运行时行为,其中一些要点也适用于此。
答案 1 :(得分:1)
有趣的是,python标准库缓慢的问题并没有在我的机器上复制(Windows 10,运行Python 2.7.11和numpy 1.11.0)。
>>> t1 = time.time()
>>> for i in range(1,10000000):
>>> _ = np.log(i)
>>> t2 = time.time()
>>> print(t2 - t1)
9.86099982262
>>> t1 = time.time()
>>> for i in range(1,10000000):
>>> _ = math.log(i)
>>> t2 = time.time()
>>> print(t2 - t1)
2.48300004005
与您在Matlab中的表现类似。 @Nils提出了一个很好的观点,numpy旨在提高阵列效率。
>>> t1 = time.time()
>>> for i in range(1,1000):
>>> _ = np.log(np.arange(1,10000))
>>> t2 = time.time()
>>> print(t2 - t1)
0.146000146866
>>> t1 = time.time()
>>> for i in range(1,1000):
>>> _ = [math.log(i) for i in range(1,10000)]
>>> t2 = time.time()
>>> print(t2 - t1)
2.3220000267
如果您可以对输入进行矢量化,那么numpy将胜过标准数学库,甚至接近C#。