我刚刚在台式机上安装了Nvidia GT660图形卡,经过一番挣扎,我设法将其与R接口。
我一直在玩几个使用GPU的R软件包,尤其是gputools,并且在比较我的GPU和CPU执行一些基本操作所花费的时间:
- 求逆矩阵(CPU更快)
- qr分解(CPU更快)
- 大相关矩阵(CPU速度更快)
- 矩阵乘法(GPU快得多!)
请注意,我主要是对gputools进行了实验,因此也许其他软件包的性能更好。
概括地说,我的问题是:哪些常规统计操作可能值得在GPU而不是CPU上执行?
我刚刚在台式机上安装了Nvidia GT660图形卡,经过一番挣扎,我设法将其与R接口。
我一直在玩几个使用GPU的R软件包,尤其是gputools,并且在比较我的GPU和CPU执行一些基本操作所花费的时间:
请注意,我主要是对gputools进行了实验,因此也许其他软件包的性能更好。
概括地说,我的问题是:哪些常规统计操作可能值得在GPU而不是CPU上执行?
Answers:
GPU是敏感的野兽。尽管从理论上说,英伟达最强大的显卡可以执行您列出的任何操作,而速度最快的CPU却要快100倍,但大约有100万种 事情可以阻止这种提速。相关算法以及运行该算法的程序的每个部分都必须进行广泛的调整和优化,以使其接近理论上的最大加速。通常,R并不是一种特别快速的语言,因此,至少在原始性能方面,R的默认GPU实现并不那么出色,这也不足为奇。但是,R GPU功能可能具有优化设置,您可以对其进行调整,以重新获得某些缺少的性能。
如果您正在研究GPU,因为您发现需要运行一些计算将需要数周/数月才能完成,那么从R迁移到性能更友好的语言可能是值得的。使用Python并不比R难得多。NumPy和SciPy软件包具有与R相同的大多数stat函数,并且PyCuda可用于以相当简单的方式实现您自己的基于GPU的函数。
如果您确实想提高函数在GPU上运行的速度,我会考虑结合C ++和CUDA来实现自己的函数。CUBLAS库可用于处理所有与线性代数有关的繁重运算。但是,请记住,编写这样的代码可能需要花费相当长的时间(特别是如果这是您第一次这样做),因此,这种方法只应保留给那些运行时间非常长(几个月)并且/或您将重复数百次。
概括地说,在GPU上运行更快的算法就是您在许多不同的数据点上执行相同类型的指令的算法。
一个简单的例子就是矩阵乘法。
假设我们正在做矩阵计算
一个简单的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之间发送数据也很耗时。有时,即使在CPU上速度更快,也值得在GPU上进行计算,只是为了避免额外的发送时间(反之亦然)。
现在,许多现代CPU以及超线程多核处理器都支持并发。
GPU似乎也不太适合递归,请参见此处,这可能解释了QR算法的一些问题。我相信其中有些具有递归数据依赖性。
缺少数据的多种插补方法?就像Alice-II(R)中的那些一样。
我认为这些往往常常令人尴尬地并行,因此适合于GPU架构。从来没有尝试过自己。