Cuda 为什么对于小的输入,cpu比gpu快?

Cuda 为什么对于小的输入,cpu比gpu快?,cuda,gpu,cpu,Cuda,Gpu,Cpu,我曾经体验过,对于较小的输入大小,CPU的执行速度比GPU快。为什么会这样?准备、数据传输还是什么 例如,对于内核和CPU函数CUDA代码: __global__ void squareGPU(float* d_in, float* d_out, unsigned int N) { unsigned int lid = threadIdx.x; unsigned int gid = blockIdx.x*blockDim.x+lid; if(gid < N) {

我曾经体验过,对于较小的输入大小,CPU的执行速度比GPU快。为什么会这样?准备、数据传输还是什么

例如,对于内核和CPU函数CUDA代码:

__global__ void squareGPU(float* d_in, float* d_out, unsigned int N) {
    unsigned int lid = threadIdx.x;
    unsigned int gid = blockIdx.x*blockDim.x+lid;
    if(gid < N) {
        d_out[gid] = d_in[gid]*d_in[gid]; 
    }
}

void squareCPU(float* d_in, float* d_out, unsigned int N) {
    for(unsigned int i = 0; i < N; i++) {
        d_out[i] = d_in[i]*d_in[i]; 
    }
}
将数组大小增加到1000000,我得到:

Size of array:
1000000
Block size:
256

You chose N=1000000 and block size: 256

Total time for GPU: 1777 microseconds (1.78ms)
Total time for CPU: 48339 microseconds (48.34ms)
我不包括主机和设备之间传输数据的时间,反之亦然,事实上,以下是我测试程序的相关部分:

gettimeofday(&t_start, NULL);

for(int i = 0; i < 100; i++) {
    squareGPU<<< num_blocks, block_size>>>(d_in, d_out, N);
} cudaDeviceSynchronize();

gettimeofday(&t_end, NULL);

选择块大小后,我计算相对于数组大小的块数:unsigned int num_blocks=数组大小+块大小-1/块大小

回答CPU与GPU性能比较的一般问题相当复杂,通常需要考虑至少3或4个我能想到的不同因素。然而,通过将测量与实际计算隔离,而不是数据传输或完整操作,您已经在某种程度上简化了问题

在这种情况下,可能至少要考虑两件事:

内核启动开销-在GPU上启动内核会带来大约固定的开销,通常在每次内核启动5到50微秒的范围内。这意味着,如果你调整工作量的大小,使你的CPU可以在少于这个时间内完成,那么GPU就没有办法更快了。甚至在这个级别之上,还有一个描述开销模型的线性函数,我相信如果您愿意的话,您可以计算出来,在存在固定成本开销的情况下,比较CPU和GPU的性能。当比较小的测试用例时,这是一个需要考虑的重要因素,但是我的猜测是,因为大多数测试用例的时间都在50微秒以上,所以我们可以安全地忽略这个因素,作为一个近似值。 实际CPU与实际GPU的实际性能/能力。这通常很难建模,取决于您使用的特定硬件,而您尚未提供该信息。不过,我们还是可以根据您提供的数据进行一些观察和猜测,并在下一节对此进行扩展

考虑到N=5000和N=1000000,您的两个案例涉及N所描述的总工作量。构建一个小图表:

      N  |  CPU time    |  GPU time
   5000  |    137       |  403
1000000  |  48339       | 1777
因此,我们看到,在CPU的情况下,当工作增加200倍时,执行时间增加约352倍,而在GPU的情况下,执行时间增加约4.5倍。我们需要解释这两种非线性,以便对发生的情况进行合理的猜测

缓存的效果-因为您运行测试用例100次,缓存可能会产生效果。在CPU的情况下,这是我唯一的猜测,为什么你看不到一个线性关系。我猜,在非常小的尺寸下,您在某个CPU内部缓存中,可以看到40KB的数据。对于更大的大小,您可以看到8MB的数据,尽管这可能适合CPU上的外部缓存,但也可能不适合,即使如此,外部缓存的总体性能也可能低于内部缓存。我想这就是数据越大CPU性能越差的原因。您的CPU正受到更大数据集的负面非线性影响。在GPU的情况下,外部缓存最多为6MB,除非您在安培GPU上运行,因此较大的数据集不能完全放入外部缓存

机器饱和的影响-CPU和GPU都可以完全加载或部分加载,具体取决于工作负载。在CPU的情况下,我猜您没有使用任何多线程,因此您的CPU代码仅限于一个内核。而且,您的CPU几乎肯定有多个可用内核。您的单线程代码将接近饱和,即保持单核繁忙。然而,GPU有很多内核,我猜,你的小测试用例会产生5000个线程,这只会部分饱和你的GPU。我的意思是,在较小的情况下,一些GPU线程处理资源将处于空闲状态,除非您恰好在最小的GPU上运行。5000个线程仅足以使2条GPU SMs保持忙碌,因此,如果您的GPU有2条以上的SMs,则在较小的测试用例中,其部分资源处于空闲状态,而在任何当前CUDA GPU上,百万线程较大的测试用例足以使所有线程处理资源处于忙碌状态。这样做的效果是,虽然CPU没有从更大的测试用例中获益,但是你应该考虑使用多线程,你的GPU很可能会受益。较大的测试用例允许您的GPU在与较小的测试用例相同的时间内完成更多的工作。因此,GPU从更大的工作负载中以一种积极的方式非线性受益

当GPU有足够大的工作负载时,它还能够更好地减轻外部缓存中丢失的影响
大卡车。这称为GPU在存在大量并行工作负载时的延迟隐藏效应,CPU没有或没有相应的机制。因此,根据您的确切CPU和GPU,这可能是一个额外的因素。我不打算在这里给出一个关于延迟隐藏的完整教程,但这个概念部分基于上面的第2项,因此您可以从中获得总体想法/好处。

回答CPU与GPU性能比较的一般问题相当复杂,通常我会考虑至少3到4个不同的因素。然而,通过将测量与实际计算隔离,而不是数据传输或完整操作,您已经在某种程度上简化了问题

在这种情况下,可能至少要考虑两件事:

内核启动开销-在GPU上启动内核会带来大约固定的开销,通常在每次内核启动5到50微秒的范围内。这意味着,如果你调整工作量的大小,使你的CPU可以在少于这个时间内完成,那么GPU就没有办法更快了。甚至在这个级别之上,还有一个描述开销模型的线性函数,我相信如果您愿意的话,您可以计算出来,在存在固定成本开销的情况下,比较CPU和GPU的性能。当比较小的测试用例时,这是一个需要考虑的重要因素,但是我的猜测是,因为大多数测试用例的时间都在50微秒以上,所以我们可以安全地忽略这个因素,作为一个近似值。 实际CPU与实际GPU的实际性能/能力。这通常很难建模,取决于您使用的特定硬件,而您尚未提供该信息。不过,我们还是可以根据您提供的数据进行一些观察和猜测,并在下一节对此进行扩展

考虑到N=5000和N=1000000,您的两个案例涉及N所描述的总工作量。构建一个小图表:

      N  |  CPU time    |  GPU time
   5000  |    137       |  403
1000000  |  48339       | 1777
因此,我们看到,在CPU的情况下,当工作增加200倍时,执行时间增加约352倍,而在GPU的情况下,执行时间增加约4.5倍。我们需要解释这两种非线性,以便对发生的情况进行合理的猜测

缓存的效果-因为您运行测试用例100次,缓存可能会产生效果。在CPU的情况下,这是我唯一的猜测,为什么你看不到一个线性关系。我猜,在非常小的尺寸下,您在某个CPU内部缓存中,可以看到40KB的数据。对于更大的大小,您可以看到8MB的数据,尽管这可能适合CPU上的外部缓存,但也可能不适合,即使如此,外部缓存的总体性能也可能低于内部缓存。我想这就是数据越大CPU性能越差的原因。您的CPU正受到更大数据集的负面非线性影响。在GPU的情况下,外部缓存最多为6MB,除非您在安培GPU上运行,因此较大的数据集不能完全放入外部缓存

机器饱和的影响-CPU和GPU都可以完全加载或部分加载,具体取决于工作负载。在CPU的情况下,我猜您没有使用任何多线程,因此您的CPU代码仅限于一个内核。而且,您的CPU几乎肯定有多个可用内核。您的单线程代码将接近饱和,即保持单核繁忙。然而,GPU有很多内核,我猜,你的小测试用例会产生5000个线程,这只会部分饱和你的GPU。我的意思是,在较小的情况下,一些GPU线程处理资源将处于空闲状态,除非您恰好在最小的GPU上运行。5000个线程仅足以使2条GPU SMs保持忙碌,因此,如果您的GPU有2条以上的SMs,则在较小的测试用例中,其部分资源处于空闲状态,而在任何当前CUDA GPU上,百万线程较大的测试用例足以使所有线程处理资源处于忙碌状态。这样做的效果是,虽然CPU没有从更大的测试用例中获益,但是你应该考虑使用多线程,你的GPU很可能会受益。较大的测试用例允许您的GPU在与较小的测试用例相同的时间内完成更多的工作。因此,GPU从更大的工作负载中以一种积极的方式非线性受益

当GPU有足够大的工作负载时,它还能够更好地减轻外部缓存中丢失的影响。这称为GPU在存在大量并行工作负载时的延迟隐藏效应,CPU没有或没有相应的机制。苏德普
在您的CPU和GPU上结束,这可能是一个额外的因素。我不打算在这里给出关于延迟隐藏的完整教程,但这个概念部分基于上面的第2项,因此,您可能会从中获得总体想法/好处。

这可能是因为CPU通常比GPU快吗?只有当您有计算成本高且易于并行化的任务时,将负载转移到GPU才有意义。否则CPU几乎总是会更快。@Lundin如果CPU通常比GPU快,那么GPU在输入大小达到默认值后不会变得更快。我想知道为什么CPU对于小的输入速度更快。你看到我测试的输出了吗@Peter对于简单的平方运算任务,GPU已经开始变得更快,如我的示例所示,当输入大小为1000000时。我不会考虑用昂贵的Taski思想来排列1000000个元素。但是,对于较小的输入(如平方5000个元素的数组),CPU执行速度更快的可能原因是什么?@Tihi认为GPU由许多内核组成,这些内核的功能不如CPU的几个内核。只有当有大量的工作要做,你可以有效地分布在这些GPU核心的GPU将更快。当然,一般来说,昂贵是相对的,对于任何这种性质的问题,您总是会观察到一个输入大小截止点,在该截止点之后GPU的性能会优于CPU。这可能是因为CPU通常比GPU快吗?只有当您有计算开销大且易于并行化的任务时,才有意义将负载转移到GPU。否则CPU几乎总是会更快。@Lundin如果CPU通常比GPU快,那么GPU在输入大小达到默认值后不会变得更快。我想知道为什么CPU对于小的输入速度更快。你看到我测试的输出了吗@Peter对于简单的平方运算任务,GPU已经开始变得更快,如我的示例所示,当输入大小为1000000时。我不会考虑用昂贵的Taski思想来排列1000000个元素。但是,对于较小的输入(如平方5000个元素的数组),CPU执行速度更快的可能原因是什么?@Tihi认为GPU由许多内核组成,这些内核的功能不如CPU的几个内核。只有当有大量的工作要做,你可以有效地分布在这些GPU核心的GPU将更快。昂贵当然是相对的,一般来说,对于任何这种性质的问题,你总是会观察到一个输入大小截止点,在这个截止点之后,GPU的性能会超过CPU。哇,没想到会有这么好的答案。非常非常感谢!哇,没想到会有这么好的答案。非常非常感谢!