【发布时间】:2020-11-12 21:37:34
【问题描述】:
解决方案
事实证明,np.sum 是一个调用np.add.reduce 的python 函数。后者ufunc call is 由cProfile 报告,我推测是因为这仍然是一个python 对象。 np.maximum 和 np.subtract 是对纯 C 函数的调用,cProfile 认为这些是原子表达式。
问题
我正在尝试优化一小段代码,这会花费我预期的更多时间。但是,在运行 cProfile 时,它没有指定哪个 numpy 函数调用消耗的时间最多,只是将我的函数 _H 列为消耗几乎所有时间。代码如下:
def _H(X, granularity, knots=None, out=None, buf=None, bias=0):
'''This is the version that I am profiling'''
np.subtract(knots, granularity*X[..., np.newaxis], out=buf)
np.maximum(0, buf, out=buf)
np.sum(buf, axis=1, out=out)
np.subtract(1-bias, out, out=out)
np.maximum(-bias, out, out=out)
我正在使用 ufuncs 来减少临时分配的数量,这节省了相当长的时间。一个更加pythonic的版本只是为了可读性:
def _H(X, granularity, knots=None, out=None, buf=None, bias=0):
'''slow but more readable version'''
return np.maximum(
0, 1 - np.maximum(
0, knots - granularity*X[..., np.newaxis]
).sum(1),
) - bias
从 for 循环调用此函数(因为 buf 数组不适合大步长的内存):
def H(X, knots, granularity, step=1_000):
t, d = X.shape
_, k = knots.shape
buf = np.empty((step, d, k))
out = np.empty((t, k))
for i in range(0, t, step):
_H(X[i:i + step], granularity, knots=knots,
out=out[i:i + step], buf=buf[:min(step, t-i)], bias=0)
return out
分析
这是我的分析 sn-p:
t = 1_000_000
d = 3
p = 2
X = np.random.uniform(size=(t, d))
cProfile.run('H(X, p, 10_000)', 'profiler')
pstats.Stats('profiler').strip_dirs().sort_stats('tottime').print_stats()
输出:
1178 function calls in 0.689 seconds
Ordered by: internal time
ncalls tottime percall cumtime percall filename:lineno(function)
100 0.371 0.004 0.680 0.007 grid.py:333(_H)
100 0.306 0.003 0.306 0.003 {method 'reduce' of 'numpy.ufunc' objects}
1 0.008 0.008 0.689 0.689 <string>:1(<module>)
1 0.001 0.001 0.681 0.681 grid.py:308(H)
100 0.001 0.000 0.307 0.003 fromnumeric.py:70(_wrapreduction)
100 0.001 0.000 0.308 0.003 fromnumeric.py:2105(sum)
...
据我所知,几乎一半(0.306 秒)的时间都花在了 numpy ufunc 中,即np.subtract、np.maximum 和 np.sum。但是,_H 中超过一半的时间(0.371 秒)用于“其他事情”。但究竟是什么? cProfile 没有进一步说明哪段代码,为什么?
【问题讨论】:
标签: python numpy array-broadcasting