The netlib documentation of sgemm声明数组步幅LDA
和LDB
必须为>= 1
,并且足够大以使列不重叠。实际上,Apple的Accelerate / veclib框架中的实现会检查这些条件,并且如果它们被违反则存在。
我真的不明白为什么存在这种限制。 BLAS不能简单地相信我,我真的想要一个零步或消极步幅吗?据我了解,默认情况下Fortran整数是签名的,所以参数类型似乎不是起源(免责声明:我不太了解Fortan)。
实际上,非正数组步幅存在非常合理的用例:
我对两个方面感兴趣:
答案 0 :(得分:0)
我最近发现自己有效地做到了这一点:
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的某些实现将默认允许您执行此操作,而另一些实现将尝试防止无限循环(其副作用是假定步幅为正)。