矩阵乘向量、矩阵转置乘向量和复制矩阵转置乘向量之间存在非直观的性能差异,使用 BLAS 库进行优化。

4

来自Julia Discourse的问题。

我正在使用 Julia 1.2。这是我的测试:

a = rand(1000, 1000)
b = adjoint(a)
c = copy(b)

@btime a * x setup=(x=rand(1000)) # 114.757 μs
@btime b * x setup=(x=rand(1000)) # 94.179 μs
@btime c * x setup=(x=rand(1000)) # 110.325 μs

我原本期望ac至少不会变慢。

检查了stdlib/LinearAlgebra/src/matmul.jl之后,发现Julia将b.parent(即a)传递给BLAS.gemv,而不是b,并且将LAPACK的dgemv_切换到了另一种显然更快的模式。

如果我的假设正确,速度提升是否源于内存以更有利的方式对齐,以便于dgemv_trans = T模式下进行操作?如果是这样,那么我猜除了可能在文档中提到这个陷阱之外,没有别的可做的。如果我的假设是错误的,那么有什么可以做的吗?

1个回答

6

在同一讨论串中,@stevengj的回答如下:

我是否正确地认为,加速是因为内存以更有利于 dgemv_ 的方式对齐,当它处于trans = T 模式时?

差不多。它确实与内存有关,但是与对齐无关,而是与局部性有关。基本的理解是,从内存中访问连续(或至少相邻)数据比分离的数据更有效率,这是由于高速缓存行的存在。(连续访问还具有利用SIMD指令的一些优点。)

Julia将矩阵存储在列主序中,因此列在内存中是连续的。因此,当您将一个转置矩阵(未复制)乘以一个向量时,它可以将其计算为连续列(=转置行)与连续向量的点积,这具有良好的空间局部性,因此可以有效地利用高速缓存行。

相比之下,对于将一个 -转置矩阵乘以一个向量,您正在将矩阵的不连续与向量进行点积,更难以有效地利用高速缓存行。为了改善这种情况下的空间局部性,优化的BLAS(如OpenBLAS)实际上计算了一次与向量的多个行(一个“块”)的点积,我认为 —— 这就是为什么它只比转置情况慢10%而不是更糟糕的原因。(实际上,即使在转置的情况下,也可能会进行一些分块以使向量保持在高速缓存中。)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接