cProfile 对我隐藏了哪些 numpy 函数调用?

Which numpy function calls is cProfile hiding from me?

解决方案

事实证明,np.sum 是一个 python 函数调用 np.add.reduce。后一个 ufunc 调用 是由 cProfile 报告的 ,我想是因为这仍然是一个 python 对象。 np.maximumnp.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

剖析

这是我的分析片段:

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.subtractnp.maximumnp.sum。然而,在 _H 中,超过一半的时间(0.371 秒)花在了“其他事情”上。但究竟是什么? cProfile 没有进一步说明哪一段代码,为什么?

From what I can see, almost half (0.306 seconds) of the time is spent in numpy ufuncs, i.e. np.subtract, np.maximum, and np.sum

你读错了。 ufunc.reduce 方法花费了 0.306 秒,该方法用于执行缩减操作,例如 numpy.sum - numpy.sum 委托给 numpy.add.reduce.

numpy.maximum(...)numpy.subtract(...)granularity*X[..., np.newaxis] 等操作不使用 ufunc.reduce,因此它们不属于 0.306 秒数字的一部分。