下面,我在处理C连续和Fortran连续数组(C vs FORTRAN memory order)之间的求和运算时比较了性能。我设置axis=0
以确保数字按列添加。我很惊讶Fortran连续数组实际上比它的C对应慢。 Fortran连续数组是否在列中具有连续的内存分配,因此在列式操作方面更好?
import numpy as np
a = np.random.standard_normal((10000, 10000))
c = np.array(a, order='C')
f = np.array(a, order='F')
在Jupyter笔记本中,运行
%timeit c.sum(axis=0)
10 loops, best of 3: 84.6 ms per loop
%timeit f.sum(axis=0)
10 loops, best of 3: 137 ms per loop
答案 0 :(得分:2)
我认为这是在np.sum()的实现中。例如:
import numpy as np
A = np.random.standard_normal((10000,10000))
C = np.array(A, order='C')
F = np.array(A, order='F')
使用Ipython进行基准测试:
In [7]: %timeit C.sum(axis=0)
10 loops, best of 3: 101 ms per loop
In [8]: %timeit C.sum(axis=1)
10 loops, best of 3: 149 ms per loop
In [9]: %timeit F.sum(axis=0)
10 loops, best of 3: 149 ms per loop
In [10]: %timeit F.sum(axis=1)
10 loops, best of 3: 102 ms per loop
所以它的表现与预期完全相反。但是让我们尝试一些其他功能:
In [17]: %timeit np.amax(C, axis=0)
1 loop, best of 3: 173 ms per loop
In [18]: %timeit np.amax(C, axis=1)
10 loops, best of 3: 70.4 ms per loop
In [13]: %timeit np.amax(F,axis=0)
10 loops, best of 3: 72 ms per loop
In [14]: %timeit np.amax(F,axis=1)
10 loops, best of 3: 168 ms per loop
当然,它是橘子的苹果。但是np.amax()和sum一样沿着轴工作,并返回一个向量,每个行/列有一个元素。并且表现得像人们期望的那样。
In [25]: C.strides
Out[25]: (80000, 8)
In [26]: F.strides
Out[26]: (8, 80000)
告诉我们数组实际上是按行顺序和列顺序打包的,并且在这个方向上循环应该快得多。除非例如总和每行逐行求和,因为它沿着列行进以提供列总和(轴= 0)。但是,如果没有在.pyd中窥视的意思,我只是在猜测。
编辑:
来自percusse的链接:http://docs.scipy.org/doc/numpy/reference/generated/numpy.ufunc.reduce.html
通过沿一个轴应用ufunc,将维度减少一个。
设a.shape =(N_0,...,N_i,...,N_ {M-1})。 然后ufunc.reduce(a,axis = i)[k_0,..,k_ {i-1},k_ {i + 1},..,k_ {M-1}] =在范围内迭代j的结果( N_i),累计将ufunc应用于每个 a [k_0,..,k_ {i-1},j,k_ {i + 1},..,k_ {M-1}]
所以在伪代码中,当调用F.sum(axis = 0)时:
for j=cols #axis=0
for i=rows #axis=1
sum(j,i)=F(j,i)+sum(j-1,i)
因此,在计算列总和时,它实际上将遍历行,在列主要顺序时会显着减慢。这样的行为可以解释其中的差异。
eric的链接为我们提供了实现,因为有些人好奇地想要通过大量的代码。答案 1 :(得分:1)
这是预料之中的。如果你检查
的结果%timeit f.sum(axis=1)
它也给出c
时间的类似结果。同样,
%timeit c.sum(axis=1)
比较慢。
一些解释:假设您有以下结构
|1| |6|
|2| |7|
|3| |8|
|4| |9|
|5| |10|
正如Eric所说,这些操作适用于reduce
。让我们说我们要求列总和。因此,直观的机制不起作用,使得每列都被访问一次,求和并记录。实际上它是相反的,因此访问每一行并且函数(这里求和)实质上类似于具有两个数组a,b
并执行
a += b
这是一种非正式的方式,可以重复documentation of reduce中超级密码提到的内容。 这需要连续访问行,尽管我们执行列和[1,6] + [2,7] + [3,8] ...因此,实现方向取决于操作而不是数组。