【问题标题】:CUDA Local memory register spilling overheadCUDA 本地内存寄存器溢出开销
【发布时间】:2018-06-04 00:29:15
【问题描述】:

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

    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 工具提取了更多指标。如果我很好地理解了here 提到的技术,那么由于寄存器溢出(4 * l1 命中和未命中/L2 的 4 个扇区的所有写入的总和 = (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 字节,而不是我想的位事务。

【问题讨论】:

  • 全局内存不在 128 位事务中读取。从全局内存加载的 L2 高速缓存线由 32 个字节组成。一个 L1 缓存线加载由 128 个字节组成。
  • @RobertCrovella 我想我当时的意思是设备内存。为了实现高内存带宽,需要利用这些 128 位事务(warp 中的线程以对齐的方式访问全局内存)。全局负载不会缓存在 L1 中,而是在 L2 中,并且由于 L2 一次提供 256 位,因此效果很好。溢出的寄存器会发生什么?当所有线程都请求已写入 dram 的溢出值时,是否使用这些 128 位事务提供服务?还是比合并的全局内存读取慢?
  • 来自本地的 l2 负载流量百分比 = (4 * l1_local_load_miss) / (4 * SUM(l2_sub*_total_read_sector_queries)。您不必计算点击次数。
  • 本地内存是 32 位交错的,用于 warp 中的线程。如果所有线程都访问同一个堆栈变量,则访问是完全合并的(默认情况)。全局和本地内存访问都经过 L1 并且具有相同的性能。带宽的减少可能是由于扭曲停止读取本地内存。使用 255 个寄存器/线程,您的理论占用率将
  • @GregSmith (1) 为什么要将 L2 的读取总和乘以 4? (2) 通过对使用的寄存器数量(127)施加限制,我能够使占用率增加一倍,但这会减慢内核速度。增加的溢出似乎具有主导作用。 (3) 如果本地内存是32位交错的,双精度浮点数的情况如何处理?我认为这仍然会合并?

标签: memory cuda gpu-local-memory


【解决方案1】:

根据 Local Memory and Register Spilling 寄存器溢出对性能的影响不仅仅是在编译时决定的合并;更重要的是:从/向二级缓存读取/写入已经非常昂贵,您想避免它。

演示文稿建议使用分析器在运行时统计由于本地内存 (LMEM) 访问而导致的 L2 查询数量,查看它们是否对所有 L2 查询的总数产生重大影响,然后优化共享与 L1 的比率有利于后者,例如通过单个主机调用 cudaDeviceSetCacheConfig(cudaFuncCachePreferL1);

希望这会有所帮助。

【讨论】:

  • 谢谢。全局内存读取也通过 L2,因此我希望这些溢出内存的读取具有相似的延迟(不同之处在于跳过 L1)。但是,我的 warp 中的所有线程都以合并的方式访问连续的内存位置,这意味着它们可以生成将数据放入寄存器所需的最少事务数。我想知道编译器是否足够聪明,可以对本地内存做同样的事情。
  • 本地和全局请求都经过 L1 并需要相同的时间来完成假设事务的所有 128 字节都已使用。
  • @GregSmith 感谢您的指出,我已将您的 cmets 阅读到帖子 Global memory access and L1 cache in Kepler 中,我将编辑我上面的暂定答案以删除违规行 :)
猜你喜欢
  • 1970-01-01
  • 2016-06-01
  • 2023-04-03
  • 2012-01-21
  • 1970-01-01
  • 2017-04-03
  • 2022-11-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多