对于哪些统计方法,GPU 比 CPU 快?

计算科学 r 显卡
2021-12-14 21:45:23

我刚刚在我的桌面上安装了一块 Nvidia GT660 显卡,经过一番努力,我设法将它与 R 接口。

我一直在玩几个使用 GPU 的 R 包,尤其是 gputools,我正在比较我的 GPU 和 CPU 执行一些基本操作所花费的时间:

  • 反转矩阵(CPU 更快)
  • qr 分解(CPU 更快)
  • 大相关矩阵(CPU 更快)
  • 矩阵乘法(GPU 快得多!)

请注意,我主要使用 gputools 进行了实验,因此其他软件包可能性能更好。

从广义上讲,我的问题是:哪些常规统计操作可能值得在 GPU 而不是 CPU 上执行?

4个回答

从广义上讲,在 GPU 上运行得更快的算法是在许多不同的数据点上执行相同类型的指令的算法。

一个简单的例子来说明这一点是矩阵乘法。

假设我们正在做矩阵计算

A×B=C

一个简单的 CPU 算法可能看起来像

//从C = 0开始

for (int i = 0; i < C_Width; i++)
{
    for (int j = 0; j < C_Height; j++)
    {
        for (int k = 0; k < A_Width; k++)
        {
            for (int l = 0; l < B_Height; l++)
            {
                C[j, i] += A[j, k] * B[l, i];
            }
        }
    }
}

这里要看到的关键是有很多嵌套的 for 循环,并且每个步骤都必须一个接一个地执行。

看这个图

请注意,C 的每个元素的计算不依赖于任何其他元素。因此,计算的顺序无关紧要。

所以在 GPU 上,这些操作可以同时进行。

用于计算矩阵乘法的 GPU 内核看起来像

__kernel void Multiply
(
    __global float * A,
    __global float * B,
    __global float * C
)
{
     const int x = get_global_id(0);
     const int y = get_global_id(1);
     for (int k = 0; k < A_Width; k++)
     {
         for (int l = 0; l < B_Height; l++)
         {
             C[x, y] += A[x, k] * B[l, y];
         }
     }
}

这个内核只有两个内部 for 循环。将此作业发送到 GPU 的程序将告诉 GPU 为 C 中的每个数据点执行此内核。GPU 将在许多线程上同时执行这些指令中的每一个。就像那句老话“便宜一打”GPU 被设计成可以更快地多次执行相同的操作。

然而,有一些算法会减慢 GPU 的速度。有些不太适合 GPU。

例如,如果存在数据依赖关系,即:想象 C 的每个元素的计算都依赖于先前的元素。程序员必须在内核中设置一个屏障来等待每个先前的计算完成。这将是一个重大的放缓。

此外,具有大量分支逻辑的算法即:

__kernel Foo()
{
    if (somecondition)
    {
        do something
    }
    else
    {
        do something completely different
    }
}

倾向于在 GPU 上运行较慢,因为 GPU 不再在每个线程中执行相同的操作。

这是一个简化的解释,因为还有许多其他因素需要考虑。例如,在 CPU 和 GPU 之间发送数据也很耗时。有时在 GPU 上进行计算是值得的,即使它在 CPU 上速度更快,只是为了避免额外的发送时间(反之亦然)。

现在,许多现代 CPU 也支持超线程多核处理器的并发性。

GPU 似乎也不太适合递归,请参阅此处,这可能解释了 QR 算法的一些问题。我相信一个人有一些递归数据依赖性。

GPU 是敏感的野兽。尽管 Nvidia 最强大的卡理论上可以执行您列出的任何操作,速度比最快的 CPU 快 100 倍,但大约100 万 件事情会阻碍这种加速。相关算法的每个部分,以及运行它的程序,都必须进行广泛的调整和优化,以便接近理论上的最大加速。通常不知道 R 是一种特别快的语言,因此它的默认 GPU 实现并不是那么好,至少在原始性能方面,我并不感到惊讶。但是,R GPU 功能可能具有优化设置,您可以调整这些设置以重新获得一些缺失的性能。

如果您正在研究 GPU,因为您发现需要运行的某些计算需要数周/数月才能完成,那么从 R 迁移到对性能更友好的语言可能是值得的。Python 并不比 R 更难使用。NumPy 和 SciPy 包具有与 R 大部分相同的 stat 函数,并且 PyCuda 可用于以相当简单的方式实现您自己的基于 GPU 的函数。

如果你真的想提高你的函数在 GPU 上运行的速度,我会考虑结合 C++ 和 CUDA 来实现你自己的函数。CUBLAS 库可用于处理所有与线性代数相关的繁重工作。但是,请记住,编写这样的代码可能需要相当长的时间(特别是如果这是您第一次这样做),因此这种方法应该只用于那些需要很长时间才能运行(几个月)的计算,并且/或者你会重复数百次。

对于您提到的所有应用程序,对于足够大的矩阵,GPU 应该比 CPU 更强大(从硬件的角度来看)。我对 R 的实现一无所知,但我已经使用cuBLASMagma在反演方面取得了巨大成功n=210和矩形矩阵的乘法/相关n,m210,k214. 令我特别惊讶的是,大型相关矩阵在使用 R 的 CPU 上会更快。

更广泛地说,我怀疑大多数将大部分时间花在密集线性代数(BLAS、Lapack 函数)上的统计运算都可以在 GPU 上有效地实现。

缺失数据的多种插补方法?就像 Alice-II (R) 中的那些。

我认为这些往往是令人尴尬的并行,因此适用于 GPU 架构。不过我自己从来没有试过。