2

我有一个应用程序,我需要将全局内存中的单个(非常量,只是普通的旧数据)值广播到所有线程。线程只需要读取该值,而不是写入它。我无法明确告诉应用程序使用常量缓存(例如 cudaMemcpyToSymbol),因为我使用的内存包装库没有给我显式的低级控制。

我想知道这个广播是如何在幕后发生的,以及它与每个线程访问唯一全局内存位置的通常访问模式有何不同(为简单起见,假设这种“通常”访问模式是合并的)。我对广播案例中可能发生的任何隐式序列化以及不同架构如何影响它特别感兴趣。

例如,对于 Fermi,大概第一个访问该值的线程会将其拉到 L2 缓存,然后拉到其 SM 的 L1 缓存,此时驻留在 SM 上的每个线程都会尝试从 L1 缓存中获取它。当所有线程都尝试访问相同的 L1 缓存值时,是否有任何序列化惩罚?

对于 Kepler,大概第一个访问该值的线程会将其拉入 L2 缓存(然后可能会或可能不会将其拉入 L1 缓存,具体取决于是否启用了 L1 缓存)。当所有线程都尝试访问 L2 中的相同值时,是否有任何序列化惩罚?

另外,分区露营是否值得关注?

我发现了另外几个问题,它们解决了类似的主题,但细节程度不足以满足我的好奇心。

提前致谢!

4

1 回答 1

2

我有一个应用程序,我需要将全局内存中的单个(非常量,只是普通的旧数据)值广播到所有线程。线程只需要读取该值,而不是写入它。

顺便说一句,这几乎是常量数据的定义,因为它与 CUDA 内核使用有关。您可能无法利用它,但这种访问称为“统一”访问,如果重复访问这种类型,对于线程读取而不写入的值,那么__constant__内存是可以考虑的可能优化。

我想知道这个广播是如何在幕后发生的

需要明确的是,广播和/或序列化应该只在同一个 warp中的线程访问特定数据项时才有可能。当不同经线中的线程访问同一位置时,这些术语不适用;这些将由单独的经线读取请求提供服务。

当所有线程都尝试访问相同的 L1 缓存值时,是否有任何序列化惩罚?

没有序列化惩罚。同一经线中的线程可以读取同一位置,无需额外费用;从同一位置读取的所有线程将在同一周期(“广播”)中得到服务。读取 Fermi 上相同位置的单独 warp 中的线程将由单独的读取请求提供服务,就像您期望由单独的 warp 执行的任何指令一样。在这种情况下也没有额外的或不寻常的成本。

当所有线程都尝试访问 L2 中的相同值时,是否有任何序列化惩罚?

在这种情况下,上述 L1的相同陈述适用于 L2。

另外,分区露营是否值得关注?

分区驻留与从 L1 或 L2 缓存中检索的值无关。分区预占通常是指一种数据访问模式,该模式导致 DRAM 请求不成比例地由具有多个内存分区的 GPU 上的一个分区处理。对于多个线程/warp 正在读取的单个位置,缓存将为此提供服务。最多需要一个 DRAM 事务来及时为彼此足够接近的所有请求提供服务(即忽略缓存抖动的可能性),针对单个位置。

于 2016-02-25T12:40:29.550 回答