sgemm的netlib文档指出,数组步长
LDA
和LDB
必须是>= 1
,并且足够大,以便列不会重叠。事实上,Apple 的 Accelerate/veclib 框架中的实现会检查这些条件,如果违反则存在。
我真的不明白为什么会有这个限制。 BLAS 不能简单地相信我真的想要零步幅或负步幅吗?据我了解,Fortran 整数默认是带符号的,因此参数类型似乎不是原因(免责声明:我不太了解 Fortan)。
确实,存在非正数组步幅的非常合理的用例:
我对两个方面感兴趣:
我最近发现自己有效地做到了这一点:
double s[4] = {10., 2., 3.1, 4.1};
dscal_(4, 3., s, -1);
assert( s[1] == 2.*3. );
dscal_
是最简单的BLAS函数,将数组乘以标量,其签名是:
void sscal(int, double, double*, int); // doesn't seem to return anything useful
在我的 BLAS 特定发行版(Fedora 28 附带)中,这给出了一个无声错误,因为该函数似乎没有执行任何操作。 此外,
dscal_
甚至似乎没有返回错误代码,因此如果没有包装函数,我无法捕获此错误(我的数组具有运行时正或负步幅,但我无法在所有情况下控制该值)。
所有这些案例都失败了:
double s[4] = {10., 2., 3.1, 4.1};
dscal_(4, 3., s, -1); // negative incx does nothing
dscal_(4, 3., &s[4], -1); // negative incx does nothing
dscal_(-4, 3., &s[4], 1); // negative n does nothing
dscal_(-4, 3., &s[4], -1); // negative n or incx does nothing
assert( s[1] == 2. );
这告诉我,虽然可能没有在任何地方记录,但步幅 (
incx
) 必须是正值(还有大小)。
幸运的是,对于许多 BLAS 函数来说,调用可以转换为正步幅。
我需要一个包装函数来以任何方式调试它,所以编写了以下包装函数:
void signed_dscal(int n, double alpha, double* x, int incx){
int prod = incx*n;
if(prod > 0) dscal(abs(n), alpha, x, abs(incx));
else dscal(abs(n), alpha, x + prod, abs(incx));
}
通过这种方式,我可以用正或负的步幅和大小来调用
signed_dscal
。
int main(){
{
double d[4] = {10., 2., 3.1, 4.1};
signed_dscal(4, 3., d, 1);
assert( d[1] == 6. );
}
{
double d[4] = {10., 2., 3.1, 4.1};
signed_dscal(4, 3., &d[4], -1);
assert( d[1] == 6. );
}
{
double d[4] = {10., 2., 3.1, 4.1};
signed_dscal(-4, 3., &d[4], 1);
assert( d[1] == 6. );
}
{
double d[4] = {10., 2., 3.1, 4.1};
signed_dscal(-4, 3., d, -1);
assert( d[1] == 6. );
}
return 0;
}
(注意
incx=0
仍然是无法修改的情况。)
我还是不明白这背后的逻辑是什么。也许 BLAS 的某些实现将允许您默认执行此操作,而其他实现将尝试防止无限循环,其副作用是假设正步幅值。
Intel BLAS 似乎暗示它支持负步幅,因为它提到
abs(incx)
,至少对于 AXPY:https://www.intel.com/content/www/us/en/develop/documentation/onemkl-developer-参考-c/top/blas-and-sparse-blas-routines/blas-routines/blas-level-1-routines-and-functions/cblas-axpy.html
我没有尝试,是否适用于其他功能还有待观察。
关于零步幅的情况: 我正在使用一个 C++ 数组库(免责声明:我自己的),它支持广播并且还支持 BLAS 接口。 我可以快速尝试很多情况,看看哪些 BLAS 版本支持零步幅。
这是
axpy
的示例,在Linux中至少我可用的所有BLAS版本都支持零步幅(广播数组)。
#include<multi/array.hpp> // from https://gitlab.com/correaa/boost-multi
#include<multi/adaptors/blas.hpp>
namespace multi = boost::multi;
int main() {
multi::array<double, 1> y = {1.0, 2.0, 3.0};
// regular arrays, stride != 0
multi::array<double, 1> const x = {1.0, 1.0, 1.0};
multi::blas::axpy(1.0, x.begin(), y);
assert(y[0] == 2.0);
assert(y[1] == 3.0);
assert(y[2] == 4.0);
// broadcasted array, stride == 0
multi::array<double, 0> const x0(1.0);
multi::blas::axpy(1.0, x0.broadcasted().begin(), y);
assert(y[0] == 3.0);
assert(y[1] == 4.0);
assert(y[2] == 5.0);
}
该程序可以在所有 BLAS、openBLAS 和 MKL 上正确编译和运行。 (不幸的是,我没有苹果设备可以尝试。) 好消息是这些库都没有检查零步幅,至少对于
axpy
。
c++ axpy.cpp -Iinclude/ `pkg-config --libs openblas` && ./a.out
c++ axpy.cpp -Iinclude/ `pkg-config --libs mkl` && ./a.out
c++ axpy.cpp -Iinclude/ `pkg-config --libs mkl-static-ilp64-seq` && ./a.out