- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我有 2 个数组,其中一个的大小为:
A = np.random.uniform(size=(48, 1000000, 2))
另一个是
B = np.random.uniform(size=(48))
我想做以下总结:
np.einsum("i, ijk -> jk", B, A)
尽可能快。
求和需要进行数千万次,所以速度很重要。在每次迭代中,B
都会发生变化,而 A
会保持不变。我尝试了 2 个选项(见下文),但它们都具有可比性。有什么办法可以加快速度吗?
代码:
import numpy as np
def computation_einsum(x,y):
z = np.einsum("i, ijk -> jk", y, x)
return z
def computation_dot(x,y):
z = y @ x
return z
A = np.random.uniform(size=(48, 1000000, 2))
B = np.random.uniform(size=(48))
C = A.transpose(1,0,2)
时间:
%timeit -n 10 computation_einsum(A, B)
100 ms ± 238 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit -n 10 computation_dot(C, B)
107 ms ± 2.11 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
我知道对于较大的矩阵,有些选项会有所不同,但我有这些特定的形状和大小。
最佳答案
默认情况下,np.einsum
不会优化已完成的计算,因为执行优化的成本有时可能比计算本身更大(尤其是对于小型数组)。您可以使用以下方法解决此问题:
np.einsum("i, ijk -> jk", B, A, optimize='optimal')
有了这个,np.einsum
在内部使用一个 BLAS 实现(像默认情况下在大多数系统上的 OpenBLAS,包括在我的机器上)这是更有效的:它应该并行运行并使用更快的 SIMD 指令(尤其是在使用 AVX 而不是 SSE 的 x86 上)。
此操作主要是内存限制,上述解决方案成功达到了约 30 GiB/s 的内存吞吐量,而峰值约为 ~40/s GiB。这非常好(对于通用 Numpy 代码)但次优。
请注意@MichaelSzczesny 的解决方案给出了类似的性能结果(使用 64 位 float )。
虽然 OpenBLAS 在这里不是最优的,但编写更快的代码远非易事,因为这样的库积极优化以达到高性能(AFAIK,他们利用特定于体系结构的内核和甚至一些手写的汇编代码)。话虽这么说,它们是为一般情况编写的,并假设计算主要受计算限制,但这里不是这种情况。
击败 OpenBLAS 的解决方案是编写一个并行 Numba 代码来解决您的特定情况,其中:B
具有较小的固定大小,A 的最后一个维度
始终为 2,A
相对较大。 Numba 是一个 Python 模块,可以使用即时编译器将特定的 Python 代码编译为 native 二进制文件。
主要思想是沿着第二个维度虚拟地将 A
分成 block ,然后沿着第一个维度(不是连续存储的)执行缓存友好的加权和在内存中)。该代码使用断言和编译时常量来帮助编译器主动展开和向量化循环。输出矩阵在函数的参数中传递,因此无需支付昂贵的成本page-faults .这是最终代码:
import numpy as np
import numba as nb
block_size = 1024
B_size = 48
@nb.njit(fastmath=True, inline='never')
def compute_block(A, B, out, iStart, iEnd, complete):
n = A.shape[1]
# Check everything is Ok and help the compiler
# to optimize the loops more aggressively.
assert A.shape[0] == B_size
assert A.shape[2] == 2
assert out.shape[1] == 2
assert B.size == B_size
assert out.shape[0] == n
for i in range(iStart, iEnd):
out[i, 0] = out[i, 1] = 0.0
for j in range(B_size):
factor = B[j]
# Help the compiler to perform (better) loop unrolling
if complete:
for i in range(iStart, iStart+block_size):
out[i, 0] += A[j, i, 0] * factor
out[i, 1] += A[j, i, 1] * factor
else:
for i in range(iStart, iEnd):
out[i, 0] += A[j, i, 0] * factor
out[i, 1] += A[j, i, 1] * factor
@nb.njit('void(float64[:,:,::1], float64[::1], float64[:,::1])', parallel=True)
def compute(A, B, out):
n = A.shape[1]
# Parallel computation of many blocks
for bi in nb.prange(n // block_size):
iStart = bi * block_size
iEnd = iStart + block_size
compute_block(A, B, out, iStart, iEnd, True)
# Remaining part
bi = n // block_size
iStart = bi * block_size
if iStart < n:
compute_block(A, B, out, iStart, n, False)
# Example of use:
A = np.random.uniform(size=(48, 1000000, 2))
B = np.random.uniform(size=(48))
C = np.empty((A.shape[1], 2), dtype=np.float64)
compute(A, B, C)
以下是我的 i5-9600KF 处理器和约 40 GiB DRAM 的结果:
np.einsum("i, ijk -> jk", B, A): 54.4 ms
np.einsum("i, ijk -> jk", B, A, optimize='optimal'): 25.2 ms
compute(A, B, C) 19.5 ms
Theoretical optimal time (lower bound): 18.6 ms
最终实现成功达到了 38.2 GiB/s 的内存吞吐量,这非常接近我机器上的最佳值。
请注意,正如@MichaelSzczesny 所指出的,使用 32 位 float 会使时序快大约 2 倍,因为 32 位 float 占用的内存空间少两倍,而且操作受内存限制。不过,32 位 float 不太精确。
关于python - 加速特定矩阵和向量大小的 einsum,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71901637/
我正在处理一组标记为 160 个组的 173k 点。我想通过合并最接近的(到 9 或 10 个组)来减少组/集群的数量。我搜索过 sklearn 或类似的库,但没有成功。 我猜它只是通过 knn 聚类
我有一个扁平数字列表,这些数字逻辑上以 3 为一组,其中每个三元组是 (number, __ignored, flag[0 or 1]),例如: [7,56,1, 8,0,0, 2,0,0, 6,1,
我正在使用 pipenv 来管理我的包。我想编写一个 python 脚本来调用另一个使用不同虚拟环境(VE)的 python 脚本。 如何运行使用 VE1 的 python 脚本 1 并调用另一个 p
假设我有一个文件 script.py 位于 path = "foo/bar/script.py"。我正在寻找一种在 Python 中通过函数 execute_script() 从我的主要 Python
这听起来像是谜语或笑话,但实际上我还没有找到这个问题的答案。 问题到底是什么? 我想运行 2 个脚本。在第一个脚本中,我调用另一个脚本,但我希望它们继续并行,而不是在两个单独的线程中。主要是我不希望第
我有一个带有 python 2.5.5 的软件。我想发送一个命令,该命令将在 python 2.7.5 中启动一个脚本,然后继续执行该脚本。 我试过用 #!python2.7.5 和http://re
我在 python 命令行(使用 python 2.7)中,并尝试运行 Python 脚本。我的操作系统是 Windows 7。我已将我的目录设置为包含我所有脚本的文件夹,使用: os.chdir("
剧透:部分解决(见最后)。 以下是使用 Python 嵌入的代码示例: #include int main(int argc, char** argv) { Py_SetPythonHome
假设我有以下列表,对应于及时的股票价格: prices = [1, 3, 7, 10, 9, 8, 5, 3, 6, 8, 12, 9, 6, 10, 13, 8, 4, 11] 我想确定以下总体上最
所以我试图在选择某个单选按钮时更改此框架的背景。 我的框架位于一个类中,并且单选按钮的功能位于该类之外。 (这样我就可以在所有其他框架上调用它们。) 问题是每当我选择单选按钮时都会出现以下错误: co
我正在尝试将字符串与 python 中的正则表达式进行比较,如下所示, #!/usr/bin/env python3 import re str1 = "Expecting property name
考虑以下原型(prototype) Boost.Python 模块,该模块从单独的 C++ 头文件中引入类“D”。 /* file: a/b.cpp */ BOOST_PYTHON_MODULE(c)
如何编写一个程序来“识别函数调用的行号?” python 检查模块提供了定位行号的选项,但是, def di(): return inspect.currentframe().f_back.f_l
我已经使用 macports 安装了 Python 2.7,并且由于我的 $PATH 变量,这就是我输入 $ python 时得到的变量。然而,virtualenv 默认使用 Python 2.6,除
我只想问如何加快 python 上的 re.search 速度。 我有一个很长的字符串行,长度为 176861(即带有一些符号的字母数字字符),我使用此函数测试了该行以进行研究: def getExe
list1= [u'%app%%General%%Council%', u'%people%', u'%people%%Regional%%Council%%Mandate%', u'%ppp%%Ge
这个问题在这里已经有了答案: Is it Pythonic to use list comprehensions for just side effects? (7 个答案) 关闭 4 个月前。 告
我想用 Python 将两个列表组合成一个列表,方法如下: a = [1,1,1,2,2,2,3,3,3,3] b= ["Sun", "is", "bright", "June","and" ,"Ju
我正在运行带有最新 Boost 发行版 (1.55.0) 的 Mac OS X 10.8.4 (Darwin 12.4.0)。我正在按照说明 here构建包含在我的发行版中的教程 Boost-Pyth
学习 Python,我正在尝试制作一个没有任何第 3 方库的网络抓取工具,这样过程对我来说并没有简化,而且我知道我在做什么。我浏览了一些在线资源,但所有这些都让我对某些事情感到困惑。 html 看起来
我是一名优秀的程序员,十分优秀!