将3D矩阵与2D矩阵相乘CUDA

将3D矩阵与2D矩阵相乘CUDA,cuda,gpgpu,Cuda,Gpgpu,3D矩阵中的数据是由层(从上到下)生成的,我想将该数据与2D矩阵相乘B,但除了获取每个层,我需要从第1层获取一个向量,从第2层获取一个向量,依此类推 目前我正在做的是将这些向量从3D矩阵复制到2D矩阵tmpA,然后乘以B(使用CUBLAS)并将结果存储到tmpB,最后逐行复制到3D矩阵C中对应的位置 总体而言,我的整个应用程序的运行速度至少是CPU版本的两倍,但在我看来,从一个设备到另一个设备的内存拷贝(甚至)在性能上一点也不好 进行这种计算的更好方法是什么?我考虑在乘法之前重新排列数据,以避免

3D矩阵中的数据是由层(从上到下)生成的,我想将该数据与2D矩阵相乘B,但除了获取每个层,我需要从第1层获取一个向量,从第2层获取一个向量,依此类推

目前我正在做的是将这些向量从3D矩阵复制到2D矩阵tmpA,然后乘以B(使用CUBLAS)并将结果存储到tmpB,最后逐行复制到3D矩阵C中对应的位置

总体而言,我的整个应用程序的运行速度至少是CPU版本的两倍,但在我看来,从一个设备到另一个设备的内存拷贝(甚至)在性能上一点也不好

进行这种计算的更好方法是什么?我考虑在乘法之前重新排列数据,以避免内存拷贝

3D矩阵AC以及2D矩阵B已经在GPU的内存中

编辑

设M,N,p为3D矩阵A的尺寸,该矩阵以行大顺序存储在设备存储器上的线性阵列中。我的代码如下所示:

cudaMalloc((void**)&d_tmpIn, sizeof(float)*M*P);
cudaMalloc((void**)&d_tmpOut, sizeof(float)*M*P);
cudaMalloc((void**)&d_C, sizeof(float)*M*N*P);

for (int iN = 0; iN < N; iN++)
{
  dst = d_tmpIn;
  for (int iM = 0; iM < M; iM++)
  {
    cudaMemcpy(dst, &(d_A[iN*P+0+iM*N*P]), sizeof(float)*P, cudaMemcpyD2D);
    dst += P;
  }

  cublasDgemm(cublasHandle, CUBLAS_OP_N, CUBLAS_OP_N, P, M, M, &alpha, d_tmpIn, P, d_B, M, &beta, d_tmpOut, P);

  src = d_tmpOut;
  for (int iM = 0; iM < M; iM++)
  {
    cudaMemcpy(&(d_C[iN*P+0+iM*N*P]), src, sizeof(float)*P, cudaMemcpyD2D);
    src += P;
  }
}
cudamaloc((void**)和d_tmpIn,sizeof(float)*M*P);
Cudamaloc((无效**)和d_tmpOut,尺寸(浮动)*M*P);
Cudamaloc((无效**)和d_C,浮点数)*M*N*P;
for(int iN=0;iN

希望这有帮助。

您不需要进行内存复制!BLAS和LAPACK API是以这样一种方式创建的,您可以指定起点、步幅长度、前导尺寸的长度等等

通过这种方式,您可以按原样使用3D阵列A和C,但可以使用正确的参数调用cublasDgemm

在您的情况下(如果我正确理解代码),看起来每个矩阵都应该是
pxm
,并且您有
N
。但看起来3D阵列的排列方式是
PxNxM
。因此,如果不为
d_tmpIn
d_tmpOut
分配内存,您可以这样做:
A
的行数是
P
。列数为
M
。但是,前导维度(
lda
)应称为
N*P
。这同样适用于
C

int lda = N * P;
int ldc = N * P;
for (int iN = 0; iN < N; iN++)
{
  double *d_tmpIn = d_A + iN * P;
  double *d_tmpOut = d_C + iN * P;
  cublasSetStream(streams[iN]); // Optional
  cublasDgemm(cublasHandle, CUBLAS_OP_N, CUBLAS_OP_N,
              P, M, M, &alpha, d_tmpIn, lda, d_B, M, &beta, d_tmpOut, ldc);

}
intlda=N*P;
int ldc=N*P;
for(int iN=0;iN
您还可以在流中创建,并在单独的流中运行每个cublas运行。请注意,这仅在M和P足够小时才有用(即GPU在计算上尚未饱和)


编辑如果您确实计划继续使用流,请尝试在程序开始时创建一次流并重新使用。请勿在与Dgemm相同的循环中创建和销毁流。这会增加开销。

您能描述一下数据是如何存储在GPU内存中的,以及您在进行此计算时使用的CUBLAS调用吗?从文本中你到底想做什么还不是很清楚(提示:方程式和简短的代码片段值千言万语),通常cudaMemcpyD2D应该非常快。你是否分析过应用程序以确定时间花在哪里了?@RobertCrovella确实很快,但我想知道是否有更好的方法来避免这些内存拷贝。我会看一下给出的答案,看看这是否有帮助。矩阵应该是
mxp
,并且有N个(我使用行主顺序,只是为了避免更改为列主顺序),并且3D数组的排列方式是MxNxP。我认为我的部分有一个错误,应该是BA,而不是AB(否则尺寸不一致)。产生混淆的原因是,使用cublas I必须反转矩阵,以避免从行主顺序更改为列主顺序(如上述指南所述).不管怎样,我明白你的意思了,我会立即尝试,并带着结果/疑问回来。谢谢!我正在考虑使用流,但我不知道“足够小”是什么意思。通常M大约是3到10,P大约是2e6-4e6,N可能在30到1800之间。我分析了应用程序,显然我没有使GPU饱和,你能给一些关于使用流的建议吗。谢谢。使用流看起来是一个很好的问题集。特别是如果你在开普勒(3.5)上设备。只需创建10个流并循环它们(即使用
流[iN%10]
)。我正在使用开普勒设备(Quadro K4000-3.0)。我想我误解了GPU上的并发性。如果我使用流,内核执行之间会有计算重叠吗?这只会发生在设备3.5(HyperQ)上吗或者重叠只会发生在内存传输和内核执行之间?我这样问是因为在我的应用程序的其他部分我使用流,但我没有看到重叠,所以我不知道我是否会在应用程序的这一部分看到一些重叠。