【问题标题】:Which numpy function calls is cProfile hiding from me?cProfile 对我隐藏了哪个 numpy 函数调用?
【发布时间】:2020-11-12 21:37:34
【问题描述】:

解决方案

事实证明,np.sum 是一个调用np.add.reduce 的python 函数。后者ufunc call is 由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

分析

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

【问题讨论】:

    标签: python numpy array-broadcasting


    【解决方案1】:

    据我所知,几乎一半(0.306 秒)的时间都花在了 numpy ufunc 中,即 np.subtract、np.maximum 和 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 秒数字。

    【讨论】:

    • 感谢您的回复。为什么 cProfile 指定 reduce 作为消耗 0.306 秒的源,但没有指定 maximumsubtract(和匿名 broadcast)?分析的调用堆栈中根本没有提到后面的这些函数。
    • @Sebastiaan: numpy.minimumnumpy.subtract 是 NumPy ufunc 对象,而不是分析器理解的任何可调用类型,因此它不知道如何报告它们。
    • 它所能做的最好的可能是将它们的运行时间归因于ufunc.__call__,但显然它没有这样做。
    • 有趣的是,我预计 minimum 的行为与 sum 的行为相似。我将在函数中逐行拆分 _H 以查看是否可以澄清问题,然后报告。
    • numpy.maximum 不是归约操作。它不使用ufunc.reducenumpy.max 是归约,但 numpy.maximum 是二元运算。 numpy.maximumnumpy.max 就像 numpy.addnumpy.sum
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-15
    • 1970-01-01
    • 2011-03-21
    相关资源
    最近更新 更多