caching - cuda原子操作可以使用L1缓存吗?

标签 caching cuda gpu atomic gpgpu

抄送:7.5 Windows:10.0 cuda:11.7

我正在设备内存上执行一系列原子操作。 warp 中的每个线程都在连续的 uint32_t 上运行。 block 中的每个扭曲都会更新这些相同的值,然后它们全部移动到下一行。

由于我没有使用任何共享内存,所以我希望它将用于缓存设备内存,有效地对共享内存执行原子操作,而无需同步线程和复制数据的所有开销和麻烦。

但表现表明事实并非如此。

确实,看看 NSight,它说 L1 缓存的命中率为 0%。哎哟。内存工作负载分析还显示全局原子 ALU 下的命中率为 0%。

Google 出现 one hit (有点过时)表明原子总是通过设备内存的 L2 完成。不完全是权威来源,但它与我所看到的相符。另一方面,有this这似乎表明它确实(确实?)通过了 L1。更权威的来源,但并不完全正确。

我可能配置错误吗?也许我的代码没有按照我的想法进行?或者针对设备内存的原子操作总是通过 L2?

  • 我尝试使用 RED而不是原子,但这没有任何区别。
  • 我还尝试使用atomicAnd_block 而不仅仅是atomicAnd,不知怎的,这让事情变得更慢?不是我所期望的。
  • 我想尝试 redux ,但 cc 8.0 还不是我的选择。 __shfl_sync 结果令人失望(性能方面)。

在这一点上,我倾向于相信在 7.5 中,设备内存上的原子总是通过 L2。但如果有人有相反的证据,我可以继续挖掘。

最佳答案

与 Nvidia 一样,很难获得具体信息。但我们可以看一下PTX documentation并推断出一些事情。

原子加载和存储

原子加载和存储使用其常规 ld 的变体和st具有以下模式的指令:

ld{.weak}{.ss}{.cop}{.level::cache_hint}{.level::prefetch_size}{.vec}.type  d, [a]{, cache-policy};
ld.sem.scope{.ss}{.level::eviction_priority}{.level::cache_hint}{.level::prefetch_size}{.vec}.type;

st{.weak}{.ss}{.cop}{.level::cache_hint}{.vec}.type   [a], b{, cache-policy};
st.sem.scope{.ss}{.level::eviction_priority}{.level::cache_hint}{.vec}.type [a], b{, cache-policy};

weak加载和存储是常规的内存操作。 cop部分指定缓存行为。就我们的目的而言,有 ld.cg (缓存全局)仅使用 L2 缓存和 ld.ca (cache-all),它使用L1和L2缓存。正如文档所述:

Global data is coherent at the L2 level, but multiple L1 caches are not coherent for global data. If one thread stores to global memory via one L1 cache, and a second thread loads that address via a second L1 cache with ld.ca, the second thread may get stale L1 cache data, rather than the data stored by the first thread. The driver must invalidate global L1 cache lines between dependent grids of parallel threads. Stores by the first grid program are then correctly fetched by the second grid program issuing default ld.ca loads cached in L1.

同样,还有st.cg仅在 L2 中缓存。它“绕过 L1 缓存”。措辞并不准确,但听起来好像这应该使 L1 缓存失效。否则,即使在单个线程中,ld.ca; st.cg; ld.ca 的序列会读取过时的数据,这听起来像是一个疯狂的想法。

第二个相关cog写入的是 st.wb (回写)。文档中的措辞非常奇怪。我猜这会写回到 L1 缓存,稍后可能会被逐出到 L2 及以上。

ld.semst.sem (其中 sem 是放宽、获取或释放之一)是真正的原子加载和存储。范围给出了同步的范围,例如,意味着获取是在线程 block 内同步还是在整个 GPU 上同步。

注意这些操作如何没有 cop元素。所以你甚至不能指定缓存层。您可以提供缓存提示,但我不知道这些提示如何足以指定所需的语义。 cache_hintcache-policy仅适用于 L2。

eviction_priority提到L1。但仅仅因为性能提示被接受并不意味着它有任何效果。我认为它适用于弱内存操作,但对于原子来说,只有 L2 策略有任何效果。但这只是推测。

原子读取-修改-写入

atom指令用于原子交换、比较和交换、加法等。red用于减少。它们具有以下结构:

atom{.sem}{.scope}{.space}.op{.level::cache_hint}.type d, [a], b{, cache-policy};
red{.sem}{.scope}{.space}.op{.level::cache_hint}.type     [a], b{, cache-policy};

使用这些元素:

  • sem:内存同步行为,例如获取、释放或放松
  • scope:内存同步范围,例如CTA(线程 block )或 GPU 内的获取-释放
  • 空间:全局或共享内存
  • 缓存策略、级别和提示:缓存驱逐策略。但是L1没有选项,只有L2

鉴于无法指定 L1 缓存或回写行为,因此无法在 L1 缓存上使用原子 RMW 操作。 这对我来说很有意义。为什么 GPU 要浪费晶体管来实现这一点?共享内存的存在正是为了允许在线程 block 内进行快速内存操作。

关于caching - cuda原子操作可以使用L1缓存吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72758469/

相关文章:

php - 取消设置 php 中的变量会影响缓存吗?

c++ - 在新线程中调用支持 CUDA 的库

python - 如何检查opencv是否使用GPU?

video - 如何在 macOS 上使用带有 gpu 支持的 ffmpeg

java - 为什么使用 Hibernate 进行查询缓存会使查询速度慢十倍?

asp.net - 在 ASP.NET MVC 3 中为未经身份验证的用户缓存主页

django - Django 模板缓存刷新的问题

pointers - 复制包含指向 CUDA 设备的指针的结构

data-structures - CUDA 上网格的高效拓扑数据结构?

css - 大图像层在 chrome 中消失