【问题标题】:Arrayfire VectorizationArrayfire 矢量化
【发布时间】:2020-04-22 06:22:15
【问题描述】:

我正在尝试加快以下计算,但未能达到所需的速度。我确定问题出在我的代码上,而不是 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 和 sum 组合成一个需要大约 2 毫秒的 JIT 内核。

我对arrayfire 的了解有限,所以我猜有一些我没有想到的东西。有谁知道如何提高这个算法的速度?

谢谢!

【问题讨论】:

  • 嗨,我是 Pradeep,来自 ArrayFire 核心团队的开发人员。我有一些疑问。 1)您有一个 10k x 6 的矩阵和另一个 6 x 1 的矩阵。 2)您希望将这两个矩阵相乘以获得 10k x 1 矩阵。但是有 6 x 10k 这样的操作。是这样吗?
  • 10k x 6 x6 和 6 x 1k。这导致 10k x 1k x 6(使用 arrayfire)或者我可以做 6 个矩阵乘法,每个乘法是 [10k x 6] * [6 x 1k]。
  • 我想我了解您现在想要做什么。让我在运行代码检查运行时后回复您。你的 GPU 是什么?
  • NVIDIA GeForce GTX 1070

标签: c++ math matrix matrix-multiplication arrayfire


【解决方案1】:

我可以确认您的发现,循环版本的速度大约是批处理 matmul 的两倍。 Matmul 本身本质上并不是在您的代码 sn-p 中花费较长运行时间的那个,它是另一个在 abs 之后沿第三维求和的操作,这是昂贵的。原因如下。

1) sum(abs(result)) - abs 在这里又不是问题。 Sum 是归约算法,通常在快速移动的维度上非常快。然而,沿更高维度减少元素步幅是连续元素的矩阵大小。与沿连续位置的减少相比,这代价高昂。

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 日):- 实际上,这不起作用,因为 abs 对每个 matmul 的结果进行了操作。

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

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

遵循Carray from cuBLAS gemm API的原则。

【讨论】:

  • 我的目标运行时间是 AS_FAST_AS_POSSIBLE ms :-) 由于循环与批处理等简单更改具有如此大的影响这一事实让我认为仍有很大的加速空间。我目前正在考虑编写我自己的修改后的矩阵乘法内核,它将在 6 个矩阵上循环,执行 mag-sqr 并将它们相加,所有这些都在一个内核中,所以我不会从全局内存中加载超过我需要的内容。我目前的问题是我用于共享内存的块大小是 6x6,这没有多大帮助(由于矩阵的维度)。
  • 是的,我认为您的尺寸也是性能问题的一部分。一个维度比另一个维度长 1000 倍可能会导致问题。 ArrayFire 的矩阵乘法使用 CUDA 工具包的 cuBLAS 库,该库针对不同的大小集和批处理模式进行了微调。因此,我个人不会为矩阵乘法编写自定义内核 - 获得比这更快的机会非常渺茫。您的矩阵 mul op 的大小是否固定为 [10k x 6] * [6 x 1k]
猜你喜欢
  • 2017-06-18
  • 1970-01-01
  • 2013-05-28
  • 2016-08-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多