优化CUDA的特定内存使用

优化CUDA的特定内存使用,cuda,memory-optimization,Cuda,Memory Optimization,我有一个数据处理任务,可以按照以下方式进行样式化。我有~1-10GB的数据和一个函数,它根据这些数据和一些双输入x生成~1MB的摘要。我需要获得约1000个x值的总结,这对于GPU来说是一个完美的任务。为了重复,所有线程的输入数据都是相同的,并以线性方式读取,但每个线程都必须生成自己的摘要。函数针对不同的x独立执行 然而,在CPU上通过x的所有值进行暴力单线程循环只会产生比K520差3倍的性能。我知道这是一项内存密集型任务,线程必须访问并写入其摘要的随机部分,但我仍然难以理解GPU如何失去最初的

我有一个数据处理任务,可以按照以下方式进行样式化。我有~1-10GB的数据和一个函数,它根据这些数据和一些双输入x生成~1MB的摘要。我需要获得约1000个x值的总结,这对于GPU来说是一个完美的任务。为了重复,所有线程的输入数据都是相同的,并以线性方式读取,但每个线程都必须生成自己的摘要。函数针对不同的x独立执行

然而,在CPU上通过x的所有值进行暴力单线程循环只会产生比K520差3倍的性能。我知道这是一项内存密集型任务,线程必须访问并写入其摘要的随机部分,但我仍然难以理解GPU如何失去最初的1000倍优势。我尝试过使用_常量____________)内存将数据分块馈送到馈送,因为所有线程的输入都是相同的,没有明显的改进。nvprof报告的典型块运行时间为10-30秒

如果您能深入了解适合此任务的优化,我将不胜感激

编辑:下面是复制问题的示例代码。它可以在5秒的g++报告运行时间和7秒的nvcc报告运行时间下编译。分析结果如下所示

==23844==分析结果: 时间%Time调用平均最小最大名称 98.86%4.68899s 1 4.68899s 4.68899s 4.68899s内核观测*,int*,信息** 1.09%51.480ms 4 12.870ms 1.9200us 50.426ms【CUDA memcpy HtoD】 0.06%2.6634mS800 3.3290us 3.2950us 5.1200us[CUDA memcpy DtoD] 0.00%4.3200us 1 4.3200us 4.3200us 4.3200us[CUDA memcpy DtoH]

编辑2:我试图通过并行化艰难的分散内存访问来重新编写代码。简而言之,我的新内核如下所示

__global__ void Kernel(Observation * d_obs, 
                         int * d_bucket,
                         double * values,
                         Info ** d_summaries)
{
    Info * summary = d_summaries[blockIdx.x * 40 + blockIdx.y];

    __shared__ Info working_summary[1024];
    working_summary[threadIdx.x] = summary[threadIdx.x];
    __syncthreads();

    for (int i = 0; i < MAX_OBS; i++)
    {
        if (d_bucket[i] != threadIdx.x) continue;
        if (d_obs[i].core[blockIdx.x] < values[blockIdx.y])
            working_summary[threadIdx.x].left++;
        else
            working_summary[threadIdx.x].right++;
    }
    __syncthreads();

    summary[threadIdx.x] = working_summary[threadIdx.x];
} 

这需要18秒和172秒-这比单CPU线程更糟糕,并行任务的数量呈线性增加。

您使用的K520板有两个GPU,每个GPU有8个流式多处理器,每个GPU的峰值带宽约为160 GB/s。使用上面的代码,您应该受到带宽的限制,并且应该考虑每个GPU至少获得100 GB/s的速度,尽管我的目标是从单个GPU开始。也许你打不到它,也许你能打败它,但这是一个很好的目标

街区数 首先要做的是修复您的启动参数。这一行:

Kernel<<<1, dim3(20, 40, 1)>>>(d_obs, d_bucket, d_summaries);
将分散的4字节写入摘要。分散的内存事务在GPU上非常昂贵,为了在内存绑定代码上获得合理的性能,应该避免这些事务。在这种情况下我们能做些什么?在我看来,解决方案是增加更多的并行性。不是每个线程都有摘要,而是每个块都有摘要。每个线程都可以处理范围0…MAX_OBS的子集,并可以增加应该位于共享内存中的块范围的摘要数组。在内核的末尾,您可以将结果写回全局内存。幸运的是,这也解决了上面提到的缺乏并行性的问题

接下来呢?
在这一点上,您应该找出一种方法来衡量有多少改进空间。你会想知道你是多么接近峰值带宽,我发现最好考虑两个你必须移动的数据,以及你实际移动的数据,如果你仍然很明显,你想看看减少内存访问和进一步优化访问,如果可能的话。p> 通常,GPU和CPU之间的1000倍差异意味着您的CPU算法严重未优化。在一般情况下,使用现代处理器的所有内核和向量单元的精细优化CPU程序将比精细优化的GPU版本慢约3-8倍。现在,如果您可以发布一些代码,我们可以提出改进建议,因为优化可能来自许多不同的地方:您的内存访问是联合的吗?您是否有足够的数据在飞行中保持GPU忙?等等,Park,我正在对IST 1 CPU线程进行基准测试;我也没有在微观层面上优化代码。今天晚些时候,我将尝试编写最小工作示例,但我想了解在对代码进行最小假设的情况下,我应该期望什么样的加速。为了回答您的具体问题:对数据的内存访问是线性的,内核从开始到结束都在循环,但是对于读取和写入,摘要都是随机访问的,我担心这是主要的瓶颈。一般情况下,数据传输不太重要,nvprof将100%的时间归因于内核。您可以比较CPU和GPU的峰值触发器和峰值内存带宽,以大致了解性能比。你可能会在@ParkYoung-Bae注意到的范围内结束。任何比这更确定的事情都需要您发布带有特定评测/基准测试结果的代码。没有任何代码,这只是为了
o开放式。说真的,您正在启动一个块,并抱怨性能和优化较差?这是代码的演示版本;原始版本使用了多个块,没有明显的改进,即执行时间从1个块几乎线性增长到10个块。我实际上认为在线程中使用并行比在块中使用并行要好。不管怎样,我会再次向代码和基准添加更多块。谢谢你有意义的回答。这里又是基准测试:在上面的例子中,从1个块扩展到10个块到100个块,CPU上的8s/75s/745s与预期的完全线性,GPU上的5s/19s/186s。请注意,并行性在1到10之间较差,在10到100个块之间不存在。这些或多或少都是我在生产代码中遇到的问题。对于100个块,我们谈论的是100k线程,然而GPU提供的性能只有单CPU线程的4倍。考虑到你现在可以得到一个28核的服务器,这看起来很糟糕。对于GPU来说,这是一种错误的任务类型吗。问题似乎是一种组织编程。我没有仔细研究过代码,但是如果GPU和CPU版本的代码产生相同的结果,尤其是扩展到1个以上的块,我会感到惊讶。内核中巨大的for循环暗示了将工作分解为更小的部分的一些可能性,这些部分可以分布在更多的线程和块中。但它的直方图方面可能很有挑战性。如果你能将问题重新转换成一种可以通过推力或cub处理的直方图,这可能是你最好的选择。我已经编辑了我的答案,并就如何解决导致问题的分散访问提出了建议。正如@RobertCrovella所说,也许有一个图书馆可以帮助你,而不是这个。此外,如果可能的话,我建议避免使用单块示例,因为您自己发现,只需添加更多块,您就可以获得4倍的改进,这一点从一开始就非常重要!罗伯特-示例代码确实是一种毫无意义的历史编程形式;实际的生产代码做了一些类似的事情,但更复杂,因此使用推力/立方似乎不是一个选项。将“数据”分解成更小的片段是可能的,但会使生产代码中已经达到极限的内存消耗成倍增加。我也认为零散的写作正在扼杀表演。杰兹-谢谢你修改了答案。我将用10块基准重新编写这个示例,以减少性能差异的可耻性:我相信您在确定问题方面是正确的,但我不愿意按照您建议的方式重新编写它,因为我认为我将消除每个块的并行性,并且不会获得什么好处。让我现在试一试,然后汇报。
Kernel<<<1, dim3(20, 40, 1)>>>(d_obs, d_bucket, d_summaries);
summary[d_bucket[i]].left++;