在OpenCL in Action中,Matthew Scarpino评论说,将他的前缀和从32位浮点数切换到128位4浮点数的集合会带来显着的性能提升,几乎快4倍!
运行Ch10 / reduction应用程序时,它将同时执行 reduction_scalar和reduction_vector内核。除了检查 结果,它测量每个内核执行所花费的时间。上 我的系统,结果如下:
reduction_scalar:检查通过。总时间= 489031
reduction_vector:检查通过。总时间= 136157
类似的声明是由GPU Gems提出的,引用了一个似乎是课程的课程,不再可用。
我们采用David Lichterman建议的技术,该技术可以处理 每个线程八个元素而不是两个加载两个元素 每个线程的float4元素而不是两个浮点数 元素(Lichterman 2007)。
http://http.developer.nvidia.com/GPUGems3/gpugems3_ch39.html
一种解释是每个线程的内存请求会增加,但这对我没有意义,因为我预计会发出相同的内存请求总数,这可能会导致相同的整体性能。
或者,每个warp发出一个请求,然后进入休眠状态。在float4
情况下,它会唤醒4倍的数据,但在float
情况下,数据未被缓存,因此后续线程也需要进入休眠状态并等待新数据。另一方面。我希望内存能够在warp中的线程被唤醒时进行流式处理。
我想知道某些专家是否可以插入并提供顺序解释,解释为什么float4
在内存访问或计算方面的执行速度明显快于float
。
答案 0 :(得分:3)
这里实际上有两件事情在起作用。一种是更有效地利用内存带宽,另一种是改进的计算与内存事务的比率,从而改善了延迟隐藏。
为了阐明内存带宽方面,例如考虑NVIDIA GPU的情况:硬件的设计使其能够提供128个字节(来自128字节对齐的段)单笔交易变形;对于具有L1高速缓存(高速缓存行为128字节)的架构和没有高速缓存的旧架构都是如此。因此,当使用float
s insead更广泛的数据类型时,您最多只能使用一半的可用带宽。
因此,只需从float
切换到float2
,或多或少会使带宽加倍,因为相同数量的内存事务将加载两倍的数据。此外,对于每个事务,工作项现在将具有两倍的数据来处理:这使得更容易隐藏负载的延迟(您只需要float
所需的工作项的一半)。
从float2
到float4
的改进相当不那么激烈,但你通常会看到一些。这是因为GPU设计为工作项加载128位数据(考虑4个投影坐标或4个颜色分量(RGBA))。硬件方面,这并不比加载64位数据更有效(实际上,提供float4
的事务通常需要两倍才能提供float2
s。 但是,硬件设计用于通过单个指令来实现128位数据的加载(这导致后台的两个事务)。这样更有效,特别是在循环中,因为它导致更少的循环迭代来处理相同数量的数据(请记住,在循环中,下一个加载指令依赖于先前的索引增加,因此您是有效的摆脱一对依赖指令。)
请注意,继续float8
或float16
将不进一步改进,因为硬件不是为这些数据类型设计的(在GPU上),实际上这些类型的负载通常会破坏合并或恶化高速缓存行使用,导致性能下降。