Nic*_*las 10 python numpy scipy multidimensional-array
下面,我比较了处理C连续和Fortran连续数组之间的和运算时的性能(C vs FORTRAN内存顺序).我设置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')
Run Code Online (Sandbox Code Playgroud)
在Jupyter笔记本中,运行
%timeit c.sum(axis=0)
10 loops, best of 3: 84.6 ms per loop
Run Code Online (Sandbox Code Playgroud)
%timeit f.sum(axis=0)
10 loops, best of 3: 137 ms per loop
Run Code Online (Sandbox Code Playgroud)
我认为这是在 np.sum() 的实现中。例如:
\n\nimport numpy as np\n\nA = np.random.standard_normal((10000,10000))\nC = np.array(A, order=\'C\')\nF = np.array(A, order=\'F\')\nRun Code Online (Sandbox Code Playgroud)\n\n使用 Ipython 进行基准测试:
\n\nIn [7]: %timeit C.sum(axis=0)\n10 loops, best of 3: 101 ms per loop\n\nIn [8]: %timeit C.sum(axis=1)\n10 loops, best of 3: 149 ms per loop\n\nIn [9]: %timeit F.sum(axis=0)\n10 loops, best of 3: 149 ms per loop\n\nIn [10]: %timeit F.sum(axis=1)\n10 loops, best of 3: 102 ms per loop\nRun Code Online (Sandbox Code Playgroud)\n\n所以它的行为与预期完全相反。但让我们尝试一下其他功能:
\n\nIn [17]: %timeit np.amax(C, axis=0)\n1 loop, best of 3: 173 ms per loop\n\nIn [18]: %timeit np.amax(C, axis=1)\n10 loops, best of 3: 70.4 ms per loop\n\nIn [13]: %timeit np.amax(F,axis=0)\n10 loops, best of 3: 72 ms per loop\n\nIn [14]: %timeit np.amax(F,axis=1)\n10 loops, best of 3: 168 ms per loop\nRun Code Online (Sandbox Code Playgroud)\n\n当然,这是从苹果到橙子。但 np.amax() 与 sum 一样沿着轴工作,并返回一个向量,每行/列有一个元素。并且表现得一如人们所期望的那样。
\n\nIn [25]: C.strides\nOut[25]: (80000, 8)\n\nIn [26]: F.strides\nOut[26]: (8, 80000)\nRun Code Online (Sandbox Code Playgroud)\n\n告诉我们数组实际上是按行顺序和列顺序打包的,并且沿该方向循环应该要快得多。除非例如当总和沿着列行进时逐行求和以提供列总和(轴=0)。但如果没有办法查看 .pyd 内部,我只是猜测。
\n\n编辑:
\n\n来自 percusse 的链接:http://docs.scipy.org/doc/numpy/reference/ generated/numpy.ufunc.reduce.html
\n\n\n\n\n通过沿一个轴应用 ufunc,将 a\xe2\x80\x98s 维度减少一。
\n\n设 a.shape = (N_0, ..., N_i, ..., N_{M-1})。\n 然后 ufunc.reduce(a, axis=i)[k_0, ..,k_{i-1}, k_{i+1}, .., k_{M-1}] = 迭代 j 的结果range(N_i),累积地将 ufunc 应用于每个\na[k_0, ..,k_{i-1}, j, k_{i+1}, .., k_{M-1}]
\n
所以在伪代码中,当调用 F.sum(axis=0) 时:
\n\nfor j=cols #axis=0\n for i=rows #axis=1\n sum(j,i)=F(j,i)+sum(j-1,i)\nRun Code Online (Sandbox Code Playgroud)\n\n因此,在计算列总和时,它实际上会遍历行,在按列优先顺序时会大大减慢速度。像这样的行为可以解释这种差异。
\n\neric 的链接为我们提供了实现,让那些好奇的人可以通过大量代码来了解原因。
\n这是预期的。如果您检查以下结果
%timeit f.sum(axis=1)
Run Code Online (Sandbox Code Playgroud)
它也给出了与 的时间类似的结果c。相似地,
%timeit c.sum(axis=1)
Run Code Online (Sandbox Code Playgroud)
速度较慢。
一些解释:假设您有以下结构
|1| |6|
|2| |7|
|3| |8|
|4| |9|
|5| |10|
Run Code Online (Sandbox Code Playgroud)
正如 Eric 提到的,这些操作适用于reduce. 假设我们要求列总和。因此,直观的机制并未发挥作用,使得每列都被访问一次、求和并记录。事实上,情况恰恰相反,每行都被访问,函数(这里求和)的执行本质上类似于拥有两个数组a,b并执行
a += b
Run Code Online (Sandbox Code Playgroud)
这是一种非常非正式的方式,重复了在reduce 文档中超级神秘地提到的内容。尽管我们正在执行列求和 [1,6] + [2,7] + [3,8]...,但这需要连续访问行。因此,实现方向很重要,具体取决于操作而不是数组。
| 归档时间: |
|
| 查看次数: |
1328 次 |
| 最近记录: |