CUDA本地内存寄存器溢出开销

3

我有一个内核,使用大量寄存器并且频繁地将它们溢出到本地内存中。

    4688 bytes stack frame, 4688 bytes spill stores, 11068 bytes spill loads
ptxas info    : Used 255 registers, 348 bytes cmem[0], 56 bytes cmem[2]

由于泄漏量似乎相当高,我认为它超过了L1甚至L2缓存。由于本地内存对每个线程都是私有的,编译器如何将对本地内存的访问合并?这个内存是否像全局内存一样以128字节事务读取?由于存在这么多泄漏,我的内存带宽利用率很低(50%)。我有一些类似的内核没有泄漏,可以获得高达80%的峰值内存带宽。
编辑 我已经使用nvprof工具提取了更多指标。如果我正确理解这里提到的技术,则由于寄存器溢出而产生了大量内存流量(4 * l1 hits and misses / sum of all writes across 4 sectors of L2 = (4 * (45936 + 4278911)) / (5425005 + 5430832 + 5442361 + 5429185) = 79.6%)。请问是否有人可以验证我在这里的想法?
Invocations                                Event Name         Min         Max         Avg
Device "Tesla K40c (0)"
Kernel: mulgg(double const *, double*, int, int, int)
     30        l2_subp0_total_read_sector_queries     5419871     5429821     5425005
     30        l2_subp1_total_read_sector_queries     5426715     5435344     5430832
     30        l2_subp2_total_read_sector_queries     5438339     5446012     5442361
     30        l2_subp3_total_read_sector_queries     5425556     5434009     5429185
     30       l2_subp0_total_write_sector_queries     2748989     2749159     2749093
     30       l2_subp1_total_write_sector_queries     2748424     2748562     2748487
     30       l2_subp2_total_write_sector_queries     2750131     2750287     2750205
     30       l2_subp3_total_write_sector_queries     2749187     2749389     2749278
     30                         l1_local_load_hit       45718       46097       45936
     30                        l1_local_load_miss     4278748     4279071     4278911
     30                        l1_local_store_hit           0           1           0
     30                       l1_local_store_miss     1830664     1830664     1830664

编辑

我意识到我想的是128字节而不是位的交易。


1
全局内存不会以128位事务进行读取。从全局内存中进行的L2高速缓存行加载包含32个字节。L1高速缓存行加载包含128个字节。 - Robert Crovella
@RobertCrovella 我想我是指设备内存。为了实现高内存带宽,需要利用这些128位事务(warp中的线程以对齐的方式访问全局内存)。全局负载不会被缓存到L1中,但会缓存在L2中,由于L2每次提供256位,因此这很好地起作用。溢出寄存器会发生什么?当所有线程请求已写入DRAM的溢出值时,使用这些128位事务来服务它吗?还是比匀齐的全局内存读取慢? - user1096294
本地L2负载流量的百分比=(4 * L1本地负载未命中次数)/(4 * SUM(L2子*_总读扇区查询数)。您无需计算命中。 - Greg Smith
2
本地内存为线程交错的32位。如果所有线程访问相同的栈变量,则访问完全汇合(默认情况)。全局和本地内存访问都通过L1并具有相同的性能。带宽减少可能是因为warp在读取本地内存时停顿了。每个线程有255个寄存器,您的理论占用率将<= 12.5%,这意味着warp调度程序由于缺乏合格的warp而经常停顿。 - Greg Smith
@GregSmith(1)为什么要将L2读取的总和乘以4?(2)通过对使用的寄存器数量(127)施加限制,我能够将占用率翻倍,但这会减慢内核速度。增加的溢出似乎具有主导作用。(3)如果本地内存是32位交错的,那么双精度浮点数的情况如何处理?我认为这仍然会被合并吗? - user1096294
1个回答

3
根据《本地内存和寄存器溢出》,寄存器溢出对性能的影响不仅仅是编译时决定的合并;更重要的是,从L2高速缓存读取/写入已经非常昂贵,因此我们要避免它。
该演示建议使用分析器,您可以在运行时计算由于访问局部存储器(LMEM)而导致的L2查询次数,查看它们是否对所有L2查询的总数产生重大影响,然后通过单个主机调用优化共享到L1的比例,例如cudaDeviceSetCacheConfig(cudaFuncCachePreferL1)。
希望这有所帮助。

谢谢。全局内存读取也经过L2,因此我预计这些溢出内存的读取具有类似的延迟(跳过L1的差异)。然而,我的warp中的所有线程以协同的方式访问连续的内存位置,这意味着它们可以生成最小数量的事务来将数据带入寄存器。我想知道编译器是否足够聪明,能够对本地内存做到同样的事情。 - user1096294
3
本地和全局请求都经过L1,假设交易的所有128字节被使用,则完成所需的时间相同。 - Greg Smith
@GregSmith 感谢您的指正,我已经阅读了您对Kepler中全局内存访问和L1缓存帖子的评论,我将编辑我的初步答案以删除有问题的内容 :) - Giovanni Faglia

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接