6

我必须迭代二维整数数组中的所有项目并更改值(根据某些规则,不重要)。

我很惊讶 python 运行时和 C# 或 java 运行时之间的性能差异有多大。我是否写了完全错误的python代码(v2.7.2)?

import numpy
a = numpy.ndarray((5000,5000), dtype = numpy.int32)
for x in numpy.nditer(a.T):
    x = 123
>python -m timeit -n 2 -r 2 -s "import numpy; a = numpy.ndarray((5000,5000), dtype=numpy.int32)" "for x in numpy.nditer(a.T):" "  x = 123"
2 loops, best of 2: 4.34 sec per loop

例如,C# 代码只执行 50 毫秒,即 python 几乎慢了 100 倍!(假设matrix变量已经初始化)

for (y = 0; y < 5000; y++)
for (x = 0; x < 5000; x++)
    matrix[y][x] = 123;
4

6 回答 6

13

是的!在 python 中遍历 numpy 数组很慢。(也比遍历 python 列表要慢。)

通常,您会避免直接遍历它们。

如果你能给我们一个例子来说明你改变事物所基于的规则,那么它很可能很容易矢量化。

作为一个玩具示例:

import numpy as np

x = np.linspace(0, 8*np.pi, 100)
y = np.cos(x)

x[y > 0] = 100

然而,在许多情况下,您必须进行迭代,要么是由于算法(例如有限差分方法),要么是为了减少临时数组的内存成本。

在这种情况下,请查看CythonWeave或类似的东西。

于 2012-04-11T19:45:07.840 回答
10

您给出的示例可能是要将二维 NumPy 数组的所有项设置为 123。这可以像这样有效地完成:

a.fill(123)

或者

a[:] = 123
于 2012-04-11T19:50:31.560 回答
5

Python 是一种比 C 或 C# 更具动态性的语言。循环如此缓慢的主要原因是,在每次通过时,CPython 解释器都在做一些浪费时间的额外工作:具体来说,它将名称x与迭代器中的下一个对象绑定,然后在评估它的赋值时再次查找名称x

正如@Sven Marnach 指出的那样,您可以调用方法函数numpy.fill()并且速度很快。该函数是用 C 或 Fortran 编译的,它会简单地遍历numpy.array数据结构的地址并填写值。比 Python 动态性要低得多,这对于这种简单的情况很有用。

但现在考虑 PyPy。在 PyPy 下运行程序后,JIT 会分析代码实际在做什么。在此示例中,它注意到名称x仅用于分配,它可以优化绑定名称。这个例子应该是 PyPy 加速的一个例子;PyPy 可能会比普通的 Python 快十倍(所以只有 C 的十分之一,而不是 1/100)。

http://pypy.org

据我了解,PyPy 暂时不会使用 Numpy,所以你不能只在 PyPy 下运行现有的 Numpy 代码。但这一天即将到来。

我对 PyPy 很兴奋。它为我们提供了一种希望,即我们可以用一种非常高级的语言(Python)编写代码,同时获得几乎可以用“可移植汇编语言”(C)编写东西的性能。对于像这样的示例,Numpy 甚至可能通过使用来自 CPU(SSE2、NEON 或其他)的 SIMD 指令来击败幼稚 C 代码的性能。对于此示例,使用 SIMD,您可以在每个循环中将四个整数设置为 123,这将比普通 C 循环更快。(除非 C 编译器也使用 SIMD 优化!想一想,这很可能适用于这种情况。所以我们回到“接近 C 的速度”而不是在这个例子中更快。但我们可以想象更棘手的情况C 编译器不够聪明,无法优化,未来的 PyPy 可能会。)

但暂时不要介意 PyPy。如果您将使用 Numpy,最好学习所有类似的函数numpy.fill()来加速您的代码。

于 2012-04-11T21:37:39.277 回答
4

C++ 强调机器时间而不是程序员时间。

Python 强调程序员时间而不是机器时间。

Pypy是用python写的python,它们有numpy的开头;你可以试试。Pypy 有一个很好的 JIT,它使事情变得非常快。

您也可以尝试 cython,它允许您将 Python 方言翻译成 C,并将 C 编译为 Python C 扩展模块;这允许您继续使用 CPython 处理您的大部分代码,同时仍然获得一点加速。但是,在我尝试比较 Pypy 和 Cython 的一个微基准测试中,Pypy 比 Cython 快很多。

Cython 使用高度 Python 的语法,但它允许您非常自由地将 Python 数据类型与 C 数据类型混合。如果你用 C 数据类型重做你的热点,它应该很快。Cython 也加快了继续使用 Python 数据类型的速度,但速度没有那么快。

于 2012-04-11T23:00:54.290 回答
0

代码没有为的nditer元素赋值a。这不会影响时间问题,但我提到它是因为它不应该被视为对nditer.

正确的版本是:

for i in np.nditer(a, op_flags=[["readwrite"]]):
    i[...] = 123

[...]需要保留对循环值的引用,它是一个 shape数组()

使用 没有意义A.T,因为它的基数A会发生变化。

我同意完成这项任务的正确方法是a[:]=123.

于 2014-08-03T19:49:35.527 回答
0

如果您需要对依赖于数组值但不依赖于数组内部位置的多维数组进行操作,那么.itemset它比我快 5 倍nditer

所以而不是做类似的事情

image = np.random.random_sample((200, 200,3));
with np.nditer(image, op_flags=['readwrite']) as it:
    for x in it:
        x[...] = x*4.5 if x<0.2 else x

你可以这样做

image2 = np.random.random_sample((200, 200,3));
for i in range(0,image2.size):
    x = image2.item(i)
    image2.itemset(i, x*4.5 if x<0.2 else x);
于 2019-01-24T14:55:17.517 回答