Numpy 数组计算速度比等效的 Java 代码慢



我正在尝试在Python中使用大型2D数组,但它非常慢。例如:

start = time.time()
result = numpy.empty([5000, 5000])
for i in range(5000):
for j in range(5000):
result[i, j] = (i * j) % 10
end = time.time()
print(end - start) # 8.8 s

Java中的相同程序要快得多:

long start = System.currentTimeMillis();
int[][] result = new int[5000][5000];
for (int i = 0; i < 5000; i++) {
for (int j = 0; j < 5000; j++) {
result[i][j] = (i * j) % 10;
}
}
long end = System.currentTimeMillis();
System.out.println(end - start); // 121 ms

这是因为Python是解释语言?有什么方法可以改进它吗?或者为什么Python在处理矩阵,人工智能等方面如此受欢迎?

阅读到最后,看看 NumPy 如何比你的 Java 代码高出 5 倍。

numpy的优势在于矢量化计算。 你的 Python 代码依赖于解释循环,迭代循环往往很慢。

我把你的 Python 代码重写为矢量化计算,这立即将其速度提高了 ~16 倍:

In [41]: v = np.arange(5000)
In [42]: %timeit np.outer(v, v) % 10
1 loop, best of 3: 544 ms per loop

就地计算% 10而不是创建新阵列可将工作再加快 20%:

In [37]: def f(n):
...:     v = np.arange(n)
...:     a = np.outer(v, v)
...:     a %= 10
...:     return a
...:
In [39]: %timeit f(5000)
1 loop, best of 3: 437 ms per loop

编辑 1:以 32 位而不是 64 位(以匹配您的 Java 代码(进行计算基本上与 Java 的性能相匹配 — h/t 到 @user2357112 指出这一点:

In [50]: def f(n):
...:  v = np.arange(n, dtype=np.int32)
...:  a = np.outer(v, v)
...:  a %= 10
...:  return a
...:
In [51]: %timeit f(5000)
10 loops, best of 3: 126 ms per loop

编辑 2:通过一些工作,我们可以使这段代码比你的 Java 实现快 5 倍(这里ne指的是numexpr模块(:

In [69]: v = np.arange(5000, dtype=np.int32)
In [70]: vt = v[np.newaxis].T
In [71]: %timeit ne.evaluate('v * vt % 10')
10 loops, best of 3: 25.3 ms per loop

编辑3:请务必也看看@max9111给出的答案。

你实际上并没有使用NumPy的强大功能 - 你在Python级别手动执行循环。这大致类似于想知道为什么每个人都使用汽车,当你拖着车在你身后时,步行到商店需要更长的时间。

使用本机 NumPy 操作将您的工作推送到 C 级循环中。例如

temp = numpy.arange(5000)
result = numpy.outer(temp, temp) % 10
# or result = temp * temp[:, None] % 10

这将进行得更快。

有什么办法可以改进它吗?

查看时间性能差异:

In [13]: arr = np.empty([5000, 5000])                                                                          
In [14]: %timeit np.multiply(*np.indices(arr.shape)) % 10                                                      
482 ms ± 2.73 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

其中np.inidices表示网格的索引


为什么 Python 在处理矩阵、人工智能等方面如此受欢迎......

Numpy 例程是用 C 实现的(它仍然是最快的语言之一,如果不是最快的语言之一(,并使用密集的数组。相关主题: https://stackoverflow.com/a/8385658/3185459

您可能还意味着Pandas,一个流行且功能强大的数据分析/数据科学库。它因其灵活的数据表示、简洁的语法、广泛的功能集和高效处理大型数据集而受到众多专家的青睐和选择。

@user2357112和@NPE已经显示的示例的另一个选择是使用 Numba(Jit 编译器(。纯解释型 Python 循环非常慢,在性能很重要的地方应避免使用。

import numpy as np
import numba as nb
import numexpr as ne
@nb.njit(parallel=True)
def func_1(num):
result = np.empty((num, num),dtype=np.int32)
for i in nb.prange(result.shape[0]):
for j in range(result.shape[1]):
result[i, j] = (i * j) % 10
return result

计时

#The first call has a higher overhead due to compilation
#parallel: @nb.njit(parallel=True)
%timeit res=func_1(5000)
#20.7 ms ± 1.11 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
#single threaded: @nb.njit(parallel=True)
%timeit res=func_1(5000)
#71.9 ms ± 521 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
#NPE
%%timeit
v = np.arange(5000, dtype=np.int32)
vt = v[np.newaxis].T
ne.evaluate('v * vt % 10')
#35.5 ms ± 863 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

在用二维数组替换 Numpy 时掉了一半

start = time.time()
#result = numpy.empty([5000, 5000])
w, h = 5000, 5000;
result = [[0 for x in range(w)] for y in range(h)]
for i in range(5000):
for j in range(5000):
result[i][j] = (i * j) % 10
end = time.time()
print(end - start) # 4.4 s

Python 在 AI 中非常流行,原因有很多: -易于原型制作 -很多ML库/大通信 -例如,使用 GPU 对带有 CUDA 的张量进行大规模并行计算

对于我们的问题,尝试在python上使用本机列表(您使用的是numpy,它可能更重

最新更新