我做了一个简单的实验,通过实现naive char搜索算法,在CPU和GPU上搜索每行50个字符(50 mil char map)的1.000.000行(使用iOS8 Metal计算管道)。
CPU实现使用简单的循环,Metal实现为每个内核提供1行处理(下面的源代码)。
令我惊讶的是,Metal实现平均比简单的线性CPU(如果我使用1个核心)慢2-3倍,如果我使用2个核心(每个都搜索一半数据库),则慢3-4倍! 我尝试了每组不同的线程(16,32,64,128,512)但仍然得到非常相似的结果。
iPhone 6:
CPU 1 core: approx 0.12 sec
CPU 2 cores: approx 0.075 sec
GPU: approx 0.35 sec (relEase mode, validation disabled)
我可以看到Metal shader花费超过90%的访问内存(见下文)。
可以采取哪些措施来优化它?
任何见解都将受到赞赏,因为互联网上没有太多来源(除了标准的Apple编程指南),提供有关内存访问内部和内容的详细信息。对Metal框架特定的权衡。
金属实施细节:
主机代码要点: https://gist.github.com/lukaszmargielewski/0a3b16d4661dd7d7e00d
内核(着色器)代码: https://gist.github.com/lukaszmargielewski/6b64d06d2d106d110126
GPU帧捕获分析结果:
答案 0 :(得分:3)
GPU着色器也在垂直通过内存,而CPU正在水平移动。当您读取charTable时,请考虑在着色器中以锁步执行的每个线程或多或少同时触及的地址。如果您的charTable矩阵被转置,GPU可能会更快地运行。
此外,由于此代码以SIMD方式执行,因此每个GPU线程可能必须将循环运行到完整的搜索短语长度,而CPU将利用早期出局。如果你删除早期的输出并保持代码简单,GPU代码实际上可能会运行得更快一些。很大程度上取决于搜索短语的长度和匹配的可能性。
答案 1 :(得分:0)
我也会猜测,gpu没有针对if / else进行优化,它不会预测分支(它可能同时执行),尝试以更线性的方式重写算法而不需要任何条件或减少它们最低限度。