我正在尝试加快以下计算速度,但未能达到所需的速度。我确定问题出在我的代码上,而不是GPU的物理限制。

我有一个10,000 x 6 x 6的矩阵V。
另一个矩阵P是6 x 1,000

两者都复杂

我需要做V * P(结果应该是10,000 x 6 x 1000)
取其大小(或磁平方),然后求和为6维。
得出10,000 x 1000的实际值。

我尝试了以下方法:

af::array V{ 10000, 6, 6, c32 };
af::array P{ 6, 1000, c32 };
af::array VP = af::matmul(V, P); (results in 10,000x1000x6 - ok, as long as i still sum in the 6 dim)
af::array res = af::sum(af::abs(VP),2);

这还不够快。然后我尝试将V转换为数组,所以我有:
af::array V[6] = { af::array{ 10000, 6, c32 },
            af::array{ 10000, 6, c32 }, af::array{ 10000, 6, c32 }, af::array{
                    10000, 6, c32 }, af::array{ 10000, 6, c32 }, af::array{
                    10000, 6, c32 } };
af::array VP[6];
af::array res;
for (int i = 0; i < 6; i++)
{
    VP[i] = af::matmul(V[i], P);
}
res= af::abs(mCalledData[0]);

for (int i = 1; i < 6; i++)
{
    res+= af::abs(VP[i]);
}

这有大约2倍的加速。我想出了另一种解决方案,但是带3个数组的af::matmult不支持选项(如hermitian),也不支持gfor,因此我无法尝试这种方法。

当前,矩阵乘法(在两种方法中)大约需要2.2毫秒,而arrayfire似乎可以将ab和求和后合并为一个JIT内核,大约需要2毫秒。

我对arrayfire的了解有限,所以我猜测有些事情我没有想到。有谁知道如何提高该算法的速度?

谢谢!

最佳答案

我可以确认您的发现,循环版本的速度大约是批处理matmul的两倍。 Matmul本身并不是在代码片段中花费较长时间的操作,而是在abs之后沿三维进行求和的另一种操作,这很昂贵。这是由于以下原因。

1)sum(abs(result))-绝对不会在这里发布abs。总和是归约算法,通常沿着快速移动维度非常快。但是,沿着较大尺寸减小元素步幅是连续元素的矩阵大小。与沿连续位置进行还原相比,这很昂贵。

2)looped abs additions-但是,此版本正在访问在内存中连续的元素,因为我们基本上是在添加6个矩阵的各个元素。最重要的是,整个循环(以及abs OP)将被转换为单个JIT内核,该内核执行以下操作非常有效。
res = res + ptr0[i] + ptr1[i] + ptr2[i] + ptr0[i] + ptr1[i]
上面的行只是为了说明,而不是确切的JIT内核。

因此,在这种特定情况下,批处理版本比循环版本快,这是因为对matmul的结果执行了归约运算。

我的测试GPU:GTX 1060

单个[10k x 6] * [6 x 1k]的matmul本身在GTX 1060上大约为半毫秒。至少在我的GTX 1060上,六个这样的matmul不能在毫秒内完成。您的目标运行时是什么?

编辑过(2020年1月10日):-实际上,由于对每个matmul的结果进行abs操作,因此无法使用。

您可以尝试在ArrayFire的master分支中查看我们关于gemm类别的最新条目。但是,您必须从源代码构建arrayfire,直到我们的下一个功能版本3.7。您可以在下一页查看文档。

https://github.com/arrayfire/arrayfire/blob/master/include/af/blas.h#L230

它遵循cuBLAS gemm API中的Carray原理。

关于c++ - Arrayfire向量化,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/59582770/

10-11 16:25