并行计算在大数据集上进行比较的适用性

时间:2016-11-22 19:42:57

标签: algorithm parallel-processing cuda opencl

假设以下假设任务:

我给了一个整数A(比方说,32位双)和一个大整数B的数组(相同的类型)。整数数组的大小在运行时是固定的(不会在运行中增长)但是具有任意大小,除非它总是适合RAM或VRAM(以最小者为准)。为了这种情况,整数数组可以位于RAM和VRAM中;忽略在启动时传输此初始数据集的任何时间成本。

任务是将A与每个B进行比较,并且仅当测试对于所有B的测试为真时才返回true,否则返回false。为了这个场景,让比大于比较(尽管如果你的答案对于稍微复杂的比较有所不同,我会感兴趣)。

一个天真的并行实现可能涉及切割集合B并将比较工作负载分布在多个核心上。核心的工作负载将完全独立,除非失败的比较会中断所有其他工作负载,因为结果将立即为假。中断在此实现中发挥作用;虽然我想象随着整数数组的增大,概率会不断下降。

我的问题有三个:

  1. 这种情况是否适合在GPU上进行并行处理。如果是这样,在什么情况下呢?或者这是一个误导性的情况,直接CPU实现实际上是最快的?

  2. 你能否建议一个改进的并行算法超过天真的算法?

  3. 你能否建议任何阅读以获得直觉来决定这些问题?

3 个答案:

答案 0 :(得分:1)

如果我正确地理解了您的问题,那么您要执行的是还原操作。有问题的操作相当于MATLAB / Numpy all(A[:] == B)。要回答这三个部分:

  1. 是。 GPU /多核CPU的减少速度可能比顺序版本快。请参阅GPU减少的演示文稿here
  2. 演示文稿应提供减少的分级方法。更现代的方法是在共享内存和全局内存以及warp聚合上使用原子操作。但是,如果您不希望处理GPU实现的复杂细节,可以使用高度优化的库,例如CUB
  3. 见1和2。
  4. 祝你好运!希望这会有所帮助。

答案 1 :(得分:1)

  

忽略传输此初始数据集的任何时间成本   启动

如果在数百万或数十亿元素中只有少数flase条件,你可以尝试一个opencl示例:

        // A=5 and B=arr
        int id=get_global_id(0);
        if(arr[id]!=5)
        {
            atomic_add(arr,1);
        }

和它一样快。如果所有条件都是" true"

,则arr [0]必须为零

如果你不确定只有少数几个或数百万个(这会使原子功能变慢),你可以进行单程预处理以减少错误次数:

         int id=get_global_id(0);
         // get arr[id*128] to arr[id*128+128] into local/private mem
         // check if a single false exists.
         // if yes, set all cells true condition except one
         // write results back to a temporary arr2 to be used

这会将整个数组复制到另一个数组,但如果您可以忽略从主机设备传输的时间增量,则也应该忽略它。除此之外,只有两个内核不应该花费超过1毫秒的开销(不包括内存读写)

如果数据适合缓存,则第二个内核(具有原子功能的内核)将访问它而不是全局内存。

如果转移时间开始,如果线程可以从整个阵列中分离,则可以使用流水线上传计算下载操作隐藏其延迟。

答案 2 :(得分:1)

我认为在这种情况下,您可以从使用GPU中获得最小的好处。我也认为这种情况下,任何形式的并行都很难获得良好的回报。

关于内存速度与CPU的评论

为什么我相信这个?看哪:性能差距(可疑的单位)。

Performance gap

这里的重点是CPU已经非常快。并且,随着SIMD成为一种东西,它们将变得更快。

与此同时,内存变得越来越慢。图表中未显示的是存储器总线,它将数据传送到CPU或从CPU传送数据。这些也越来越快,但速度很慢。

由于RAM和硬盘速度很慢,CPU会尝试将数据存储在称为L1,L2和L3缓存的“小RAM”中。这些缓存超快,但超小。但是,如果您可以设计一个算法重复使用相同的内存,这些缓存可以加快一个数量级的速度。例如,this site讨论了优化矩阵乘法以进行缓存重用。加速是戏剧性的:

Cache aware matrix multiplication performance

对于350x350矩阵的所有内容,天真实现(3Loop)的速度急剧下降。为什么是这样?因为正在使用双精度数字(每个8字节),所以这是测试机器上的1MB L2缓存被填充的点。您在其他实现中看到的所有速度增益都来自策略性地重用内存,因此此缓存不会快速清空。

在您的算法中缓存

根据定义,您的算法不会重用内存。实际上,它具有最低的内存重用率。这意味着您无法从L1,L2和L3缓存中获益。就好像你已经将CPU直接插入RAM中一样。

如何从RAM获取数据?

这是CPU的简化图:

Connections between multiple cores and caches

请注意,每个核心都有自己的专用L1缓存。核心对共享L2缓存。 RAM由每个人共享,并通过总线访问。

这意味着如果两个内核想要同时从RAM获取内容,那么其中只有一个内核将会成功。另一个将坐在那里做 nothing 。你试图从RAM中获取内容的核心越多,这就越糟糕。

对于大多数代码来说,问题不是太糟糕,因为RAM很少被访问。但是,对于您的代码,我之前谈到的性能差距,加上您的算法的不可缓存设计,意味着您的代码的大部分时间花在从RAM获取内容上。这意味着内核几乎总是相互冲突,因为内存带宽有限。

使用GPU怎么样?

GPU并没有真正解决问题:大部分时间仍然用于从RAM中提取内容。除了没有一个慢速总线(从CPU到RAM),你有两个(另一个是从CPU到GPU的总线)。

是否加速取决于CPU,GPU-CPU总线和GPU的相对速度。不过,我怀疑你不会加快速度。 GPU适用于SIMD类型的操作,或maps。您描述的操作是减少或fold:本质上非并行操作。由于映射函数(相等)非常简单,因此GPU将大部分时间用于还原操作。

<强> TL;博士

这是memory-bound操作:更多核心和GPU无法解决此问题。