为什么对于小尺寸数据多次调用 numpy.linalg.norm 会很慢?

Lad*_*dih 4 python performance numpy

import numpy as np
from datetime import datetime
import math

def norm(l):
    s = 0
    for i in l:
        s += i**2
    return math.sqrt(s)

def foo(a, b, f):
    l = range(a)
    s = datetime.now()
    for i in range(b):
        f(l)
    e = datetime.now()
    return e-s

foo(10**4, 10**5, norm)
foo(10**4, 10**5, np.linalg.norm)
foo(10**2, 10**7, norm)
foo(10**2, 10**7, np.linalg.norm)
Run Code Online (Sandbox Code Playgroud)

我得到以下输出:

0:00:43.156278
0:00:23.923239
0:00:44.184835
0:01:00.343875
Run Code Online (Sandbox Code Playgroud)

似乎当np.linalg.norm多次调用小尺寸数据时,它的运行速度比我的标准函数慢。

其原因何在?

MSe*_*ert 5

首先:datetime.now()不适合衡量性能,它包括挂机时间,当高优先级进程运行或 Python GC 启动时,您可能会选择一个不好的时间(对于您的计算机),...

\n\n

Python 中有专用的计时函数/模块:内置timeit模块或%timeitIPython/Jupyter 以及其他几个外部模块(例如perf,...)

\n\n

让我们看看如果我在您的数据上使用这些会发生什么:

\n\n
import numpy as np\nimport math\n\ndef norm(l):\n    s = 0\n    for i in l:\n        s += i**2\n    return math.sqrt(s)\n\nr1 = range(10**4)\nr2 = range(10**2)\n\n%timeit norm(r1)\n3.34 ms \xc2\xb1 150 \xc2\xb5s per loop (mean \xc2\xb1 std. dev. of 7 runs, 100 loops each)\n%timeit np.linalg.norm(r1)\n1.05 ms \xc2\xb1 3.92 \xc2\xb5s per loop (mean \xc2\xb1 std. dev. of 7 runs, 1000 loops each)\n\n%timeit norm(r2)\n30.8 \xc2\xb5s \xc2\xb1 1.53 \xc2\xb5s per loop (mean \xc2\xb1 std. dev. of 7 runs, 10000 loops each)\n%timeit np.linalg.norm(r2)\n14.2 \xc2\xb5s \xc2\xb1 313 ns per loop (mean \xc2\xb1 std. dev. of 7 runs, 100000 loops each)\n
Run Code Online (Sandbox Code Playgroud)\n\n

对于短迭代来说它并不慢,它仍然更快。但请注意,如果您已经拥有 NumPy 数组,那么 NumPy 函数的真正优势就会显现:

\n\n
a1 = np.arange(10**4)\na2 = np.arange(10**2)\n\n%timeit np.linalg.norm(a1)\n18.7 \xc2\xb5s \xc2\xb1 539 ns per loop (mean \xc2\xb1 std. dev. of 7 runs, 100000 loops each)\n%timeit np.linalg.norm(a2)\n4.03 \xc2\xb5s \xc2\xb1 157 ns per loop (mean \xc2\xb1 std. dev. of 7 runs, 100000 loops each)\n
Run Code Online (Sandbox Code Playgroud)\n\n

是的,现在速度快多了。18.7us 与 1ms - 对于 10000 个元素几乎快了 100 倍。这意味着您的示例中的大部分时间np.linalg.norm都花在将 转换rangenp.array.

\n