我正在尝试加快以下计算速度,但未能达到所需的速度。我确定问题出在我的代码上,而不是 GPU 的物理限制。
我有一个 10,000 x 6 x 6 的矩阵 V。 另一个矩阵 P 为 6 x 1,000
既复杂
我需要做 V * P(这应该导致 10,000 x 6 x 1000( 取它的星等(或 mag sq(,然后在 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 可以将 abs 和总和组合成一个大约需要 2 毫秒的 JIT 内核。
我对阵列火的了解有限,所以我猜有些东西我没有想到。有没有人知道我如何提高这个算法的速度?
谢谢!
我可以确认您的发现,循环版本的速度大约是批处理矩阵的两倍。 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
在 GTX 1060 上,单[10k x 6] * [6 x 1k]
的矩阵本身约为半毫秒。至少我认为在我的 GTX 1060 上,六个这样的矩阵无法在毫秒内完成。您的目标运行时是什么?
已编辑(2020 年 1 月 10 日(: -实际上,这是行不通的abs
因为对每个矩阵的结果进行操作。
您可以尝试查看我们在 ArrayFire 主分支中进入 gemm 类别的最新条目。 但是,您必须从源代码构建 arrayfire,直到我们的下一个功能版本 3.7。您可以查看下一页的文档。
https://github.com/arrayfire/arrayfire/blob/master/include/af/blas.h#L230
它遵循从 cuBLAS gemm APICarray
的原则。