C DGEMM和DGEMV给出了不同的结果
我想在C中实现以下等式:C DGEMM和DGEMV给出了不同的结果,c,blas,C,Blas,我想在C中实现以下等式: C[l,q,m] = A[m,q,k] * B[k,l] 其中重复索引k被求和 我通过三种方式实现了这一点: 使用循环的朴素实现 使用BLAS例程DGEMV(矩阵向量乘法) 使用BLAS例程DGEMM(矩阵乘法) 这是最小的不工作代码: #include <stdio.h> #include <stdint.h> #include <stdlib.h> #include <string.h> #include <
C[l,q,m] = A[m,q,k] * B[k,l]
其中重复索引k被求和
我通过三种方式实现了这一点:
- 使用循环的朴素实现
- 使用BLAS例程DGEMV(矩阵向量乘法)
- 使用BLAS例程DGEMM(矩阵乘法)
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <cblas.h>
void main()
{
const size_t n = 3;
const size_t n2 = n*n;
const size_t n3 = n*n*n;
/* Fill rank 3 tensor with random numbers */
double a[n3];
for (size_t i = 0; i < n3; i++) {
a[i] = (double) rand() / RAND_MAX;
}
/* Fill matrix with random numbers */
double b[n2];
for (size_t i = 0; i < n2; i++) {
b[i] = (double) rand() / RAND_MAX;
}
/* All loops */
double c_exact[n3];
memset(c_exact, 0, n3 * sizeof(double));
for (size_t l = 0; l < n; l++) {
for (size_t q = 0; q < n; q++) {
for (size_t m = 0; m < n; m++) {
for (size_t k = 0; k < n; k++) {
c_exact[l*n2+q*n+m] += a[m*n2+q*n+k] * b[k*n+l];
}
}
}
}
/* Matrix-vector */
double c_mv[n3];
memset(c_mv, 0, n3 * sizeof(double));
for (size_t m = 0; m < n; m++) {
for (size_t l = 0; l < n; l++) {
cblas_dgemv(
CblasRowMajor, CblasNoTrans, n, n, 1.0, &a[m*n2],
n, &b[l], n, 0.0, &c_mv[l*n2+m], n);
}
}
/* Matrix-matrix */
double c_mm[n3];
memset(c_mm, 0, n3 * sizeof(double));
for (size_t m = 0; m < n; m++) {
cblas_dgemm(
CblasRowMajor, CblasTrans, CblasTrans, n, n, n, 1.0, b, n,
&a[m*n2], n, 0.0, &c_mm[m], n2);
}
/* Compute difference */
double diff_mv = 0.0;
double diff_mm = 0.0;
for (size_t idx = 0; idx < n3; idx++) {
diff_mv += c_mv[idx] - c_exact[idx];
diff_mm += c_mm[idx] - c_exact[idx];
}
printf("Difference matrix-vector: %e\n", diff_mv);
printf("Difference matrix-matrix: %e\n", diff_mm);
}
i、 e.DGEMV的实施是正确的,DGEMM不是-我真的不理解这一点。我切换了乘法(矩阵乘法是非交换的)并对两者进行了转置,以获得正确的顺序C[l,q,m],而不是C[q,l,m],但我也尝试了在没有切换/转置的情况下使用它,但它不起作用
有人能帮忙吗?
谢谢
编辑:我想了一下,觉得我在尝试做一些DGEMM不支持的事情?也就是说,我尝试在C[:,:,m]中插入一个子矩阵,这意味着前导索引和尾随索引在内存中都不是连续的。DGEMM允许我设置参数LDC,在本例中,该参数需要为n^2,但它不知道第二个索引也不连续,跨距为n(并且没有参数告诉它?)。那么为什么DGEMM不支持尾随维度跨距的第二个参数呢?DGEMM支持足够的跨距(存储间距)参数,以允许对
a
、B
和C
的任意矩形子矩阵进行操作。这似乎很合理,但如果您需要了解更多有关设计原理的信息,请尝试联系BLAS的原始创建者之一。@njuffa任意子矩阵?那我一定错过了什么。假设所有维度的范围都是N,我可以看到如何插入子矩阵C[I,:,:]
(设置LDC
to N pass&C[I*N^2]
),我可以看到如何插入C[:,I,:]
(将LDC
设置为N^2,pass)&C[I*N>,但我看不到如何插入C[,:,:,:,](LDC
需要是N^2,我需要通过&C[I]
,但BLAS不知道中间索引也有N的跨步).我遗漏了什么?在矩形子矩阵的索引计算中,N**2
一词在我看来是错误的,这不应该是类似于LDC*N
的吗?此外,没有插入带有GEMM
的子矩阵这样的事情,比如(缩放)产品已添加到C
。假设您试图实现的目标可以通过GEMM
实现(我对此不确定),您可以尝试绘制矩阵,并使用GEMM
参数对图形进行注释。请记住,列主存储顺序用于存储矩阵。但我的子矩阵是方形的(所有扩展都是N),不是矩形的。否则它就不是N**2
,而是N*M
或类似的。我的参数alpha是1.0,beta是0.0,这相当于插入到C
。我使用CblasRowMajor作为第一个参数,我假设它与我的行主存储一致。这对我来说意味着什么“绘制矩阵”?很抱歉,我忘记了C
上的比例因子。我发现总是区分子矩阵('N',…)的维度和完整矩阵('LDC',…)的维度是很有用的,即使它们在特定情况下恰好重合“我的意思是拿一支铅笔和一张纸来画2D矩阵的布局,作为BLAS操作推理过程的一部分。这就是我过去所做的,当时我试图混淆一些BLAS函数及其众多参数的内部工作,特别是转置模式。
Difference matrix-vector: 0.000000e+00
Difference matrix-matrix: -1.188678e+01