Halide CUDA GPU SGEMM 实现

Evg*_*niy 5 halide

我正在尝试构建基于 Halide 的图像处理算法,该算法在其中一个阶段需要 SGEMM 函数。

我发现 Halide 有两种矩阵乘法实现:

  1. 线性代数算法(apps/linear_algebra 文件夹
  2. CUDA 矩阵乘法应用程序(apps/cuda_mat_mul 文件夹

对于大小为 1024x1024 的矩阵:

首先,它们在 CPU (Intel i7) 和 Fermi GPU (GF 540M) 上运行得很好,CPU 时间接近 OpenBlas,Fermi GPU 时间接近 cuBlas(约 18ms),但此实现在 Maxwell 上比 cuBlas 慢 10 倍GPU (TitanX) - 5 毫秒与 0.4 毫秒。第二个实现 (cuda_mat_mul) 比 Fermi 上的 cuBlas 慢 3 倍 - 大约 57 毫秒 vs 18 毫秒,在 Maxwell GPU 上比 cuBlas 慢 2 倍 - 1 毫秒 vs 0.4 毫秒

正如我所见 - Halide 可以为 Fermi GPU 生成最佳代码,但无法在 Maxwell 上快速运行。据我所知,SGEMM 函数是许多具有正确调度的 FusedMultiplyAdd 函数,但我找不到任何可以使其在 Maxwell 上快速工作的最佳调度。

我能想象到的最快的 Halide 代码放置在 cuda_mat_mul 文件夹中,时间表是:

    Func prod("prod");
    RDom r(0, size);
    prod(x, y) += A(x, r) * B(r, y);

    Var xi, yi, xio, xii, yii, xo;
    Func out = prod.in();
    out.bound(x, 0, size)
        .bound(y, 0, size)
        .tile(x, y, xi, yi, 8*32, 8)
        .split(xi, xio, xii, 32)
        .reorder(xio, yi, xii, x, y)
        .unroll(xio)
        .unroll(yi)
        .gpu_blocks(x, y).gpu_threads(xii);
    prod.compute_at(out, xii)
        .unroll(x)
        .unroll(y)
        .update()
        .unroll(r.x, 2)
        .reorder(y, x, r.x)
        .unroll(x)
        .unroll(y);
    B.in()
        .compute_at(prod, y)
        .vectorize(B.in().args()[0])
            ;
Run Code Online (Sandbox Code Playgroud)

我也尝试过使用更大的矩阵(2048x2048) - 图片看起来相似:

  • 古巴拉斯时间:0.003174
  • 卤化物线性SGEMM时间:0.042568
  • 卤化物 cuda_mat_mul 时间:0.006792

基准测试代码来自 apps/cuda_mat_mul/runner.cpp,但将迭代计数从 10 更改为 100,以获得更精确的计时

如何更改时间表以使其与 TitanX 上的 cuBlas 性能接近?

更新:在 Ubuntu 16.4、LLVM 3.8、Halide 上进行测试 - 来自 git 的最新版本、Cuda 8