GPU上的阵列总和基准 - 奇怪的结果?

时间:2013-11-10 12:25:00

标签: arrays opencl benchmarking gpu-programming jocl

我目前正在AMD Radeon HD 7870上使用OpenCL进行一些基准测试。

我在JOCL中编写的代码(OpenCL的Java绑定)只添加了两个2D数组(z = x + y),但它执行了很多次(z = x + y + y + y + y + y) + Y ......)。

我添加的两个数组的大小是500乘501,我循环遍历我想在GPU上添加它们的迭代次数。所以首先我添加一次,然后十次,然后一次,等等。

我循环的最大迭代次数为100,000,000。下面是我运行代码时日志文件的样子(计数器是我的程序在5秒内执行的次数):

Number of Iterations: 1
Counter: 87
FLOPS Rate: 0.0043310947 GFLOPs/s

Number of Iterations: 10
Counter: 88
FLOPS Rate: 0.043691948 GFLOPs/s

Number of Iterations: 100
Counter: 84
FLOPS Rate: 0.41841218 GFLOPs/s 

Number of Iterations: 1000
Counter: 71
FLOPS Rate: 3.5104263 GFLOPs/s

Number of Iterations: 10000
Counter: 8
FLOPS Rate: 3.8689642 GFLOPs/s

Number of Iterations: 100000
Counter: 62
FLOPS Rate: 309.70895 GFLOPs/s

Number of Iterations: 1000000
Counter: 17
FLOPS Rate: 832.0814 GFLOPs/s

Number of Iterations: 10000000
Counter: 2
FLOPS Rate: 974.4635 GFLOPs/s

Number of Iterations: 100000000
Counter: 1
FLOPS Rate: 893.7945 GFLOPs/s

这些数字有意义吗?我觉得0.97 TeraFLOPS相当高,我必须错误地计算FLOP的数量。

另外,我相信我计算的FLOP数量应该在一个点上随着迭代次数的增加而增加,但这里并不明显。似乎如果我继续增加迭代次数,计算出的FLOPS也会增加,这也让我相信我做错了。

仅供参考,我按以下方式计算FLOPS:

FLOPS = counter(500)(501)(迭代)/(time_elapsed)

非常感谢您对此问题的任何帮助。

谢谢

编辑:

我现在已经在一系列迭代(我将y添加到x的次数)以及数组大小上进行了相同的基准测试循环。我已经生成了以下表面图,可以在这个GitHub存储库中看到

https://github.com/ke0m/Senior_Design/blob/master/JOCL/Graphing/GoodGPUPlot.PNG

我已经问过其他人对这个情节的看法,他们向我提到虽然我计算的数字是可行的,但它们是人为的高。他们说这在情节的陡坡上很明显,并没有真正的物理意义。关于为什么斜率如此陡峭的一个建议的想法是因为编译器将控制迭代(类型为int)的变量转换为short并因此迫使该数字保持在32000(大约)以下。这意味着我在GPU上做的工作较少,然后我认为我正在计算更高的GFLOPS值。

任何人都可以确认这个想法,或提供任何其他想法,为什么情节看起来像它的方式?

再次感谢您

2 个答案:

答案 0 :(得分:0)

计数器(500)(501)(迭代) - 如果用整数计算,结果可能对于整数寄存器来说太大。如果是这样,在计算之前转换为浮点数。

答案 1 :(得分:0)

我做了一个使用本地内存优化的矩阵 - 矩阵乘法内核。在我的HD7870 @ stock设置上,它实现了大约500亿次总和和每秒5000亿次乘法,这使得1 Teraflops。如果您的卡也处于库存设置,这与您的计算非常接近。

是的,你的计算是有意义的,因为gpu的峰值大约是2.5 Tflops / s而你正在本地存储器/寄存器空间进行计算,这是接近卡的峰值所需的。

你只做加法,所以你只需要每次迭代加1(不进行任何乘法,每个核心留下一个管道空,我假设你有近一半的峰值)。

每个a = b + c

1个触发器

所以你对翻牌价值是正确的。

但是当你不给gpu一个“总项目数的共振条件”,如512的倍数(最大本地项目大小的倍数)或256或1280(核心数)时,你的gpu将无法高效计算将对小阵列的性能进行去除。

此外,如果你没有提供足够的总warp,线程将无法隐藏主存储器的延迟,就像在1,10,100次迭代中一样。隐藏内存延迟需要计算单元上的多个warp,以便所有ALU和ADDR单元(我的意思是所有管道)在大多数时间都被占用。占用在这里非常重要,因为每个内存操作的操作很少。如果将工作组大小从256减少到64,这可能会增加占用,从而隐藏更多延迟。

试验和错误可以为您提供最佳的峰值性能。否则,内核会因主内存带宽和线程启动/停止延迟而受到瓶颈。

下面:

HD 7870 SGEMM with 9x16x16 pblocking algorithm: 1150 Gflops/s for square matrix size=8208

此外,分区和特殊功能可以被视为每个项目50到200个触发器,并且受不同版本的影响(如软件rsqrt()与硬件rsqrt()近似)。

尝试使用256的倍数的数组大小和高1M的迭代次数,并尝试64或128作为每个计算单位的本地项目。如果您可以同时将它们相乘,则可以获得更高的触发器吞吐量。您可以使用2或3添加y的乘法以使用乘法管道!通过这种方式,您可以获得比以前更高的失败率。

x = y + z * 2.0f + z * 3.0f + z * 4.0f + z * 5.0f ----> 8 flops

或反对编译器的自动优化,

X = Y + Z randomv + Z randomval2 + Z randomval3 + Z randomval4

而不是

x = y + z + z + z + z -----> 4个触发器

编辑:我不知道HD7870是否使用不同(额外批次)ALU进行双精度(64位fp)操作,如果是,那么你可以使用它们进行混合精度操作以获得%10以上由于HD7870具有64位@ 1/8的32位速度,因此可以实现吞吐量!您可以通过这种方式使您的卡片爆炸。