首页 > 解决方案 > 什么是 CUDA 11 中引入的 L2 缓存 accessPolicyWindow

问题描述

CUDA 11 中引入了新的运行时 API 来微调L2 访问策略

但是我不完全理解策略之类的含义hitRatio以及如何在实践中使用它们。

特别是,我在CUDA API 文档中看到cudaAccessPolicyWindow

指定窗口的访问策略,即从 base_ptr 开始到 base_ptr + num_bytes 结束的连续内存范围。划分为许多段并分配段这样。“命中段”/窗口的总和 == 约。比率。“未命中段”/窗口的总和 == 大约 1 比率。段和比率规范适合架构的功能。命中段中的访问应用 hitProp 访问策略。未命中段中的访问应用 missProp 访问策略。

我的问题:

  1. 内存的连续范围是如何“划分”成段的?这些分区是根据命中属性静态决定的,这样段的命中或未命中属性在分配后将保持不变,还是有一些运行中的计数器可以动态调整分配,例如在每个访问基础上?

  2. 在实践中应该如何应用这些属性来优化性能?例如,以一种过于简单和幼稚的方式来说,假设我留出了 1 MB L2 缓存,我应该为我最常用的数据创建一个 1 MB 的窗口并将 hitRatio 设置为 1 还是应该创建一个 2 的窗口MB 并将 hitRatio 设置为 0.5?为什么?

标签: parallel-processingcudagpu

解决方案


内存的连续范围是如何“划分”成段的?这些分区是根据命中属性静态决定的,这样段的命中或未命中属性在分配后将保持不变,还是有一些运行中的计数器可以动态调整分配,例如在每个访问基础上?

我认为没有任何明确规定。然而,我们可以基于两个想法做出一些非常可靠的(我相信)猜想:

  1. 很长一段时间以来,NVIDIA GPU L2 缓存都具有 32 字节的基本行大小。这旨在与具有 32 字节段边界的 DRAM 子系统的设计保持一致。

  2. 文档说明选择哪些行作为“随机”选择在缓存中持久存在。这几乎意味着它与访问模式无关。

结合这些想法,我会说分区是在不少于 L2 行(32 字节)的粒度上完成的,如果 L2 标签/TLB 系统需要,它可能是更高的粒度。这些 L2 细节中的大多数通常是 NVIDIA 未发布的。一旦随机选择,我不希望选择会根据访问模式而改变。

在实践中应该如何应用这些属性来优化性能?例如,以一种过于简单和幼稚的方式说,假设我留出了 1 MB L2 缓存,我应该为我最常用的数据创建一个 1 MB 的窗口并将 hitRatio 设置为 1 还是应该创建一个 2 的窗口MB 并将 hitRatio 设置为 0.5?为什么?

没有足够的信息来给出这个问题的具体答案。您决定保留 1MB L2 缓存的知识是第一个关键信息,但第二个关键信息是您实际上需要缓存多少数据?此外,预期的访问模式很重要。让我们介绍几种情况:

  1. 预留 1MB 缓存,1MB 窗口,hitRatio 1。这意味着您只有 1MB 的数据要使用此机制进行缓存。对于 1MB 缓存和 1MB 窗口,没有任何理由为 hitRatio 选择 1 以外的任何值。如果您只有 1MB 的数据要缓存,并且您有能力切出 1MB 的 L2,那么这是一个完全明智的选择。您实质上是在保证没有其他活动可以“驱逐”这些“受保护”数据,一旦它出现在缓存分割中。

  2. 预留 1MB 缓存,2MB 窗口,hitRatio 0.5。这当然意味着您至少有 2MB 的数据要使用此机制进行缓存,因此这与上述情况 1 无法直接比较。0.5的hitRatio可以被认为是防止抖动的“后卫”。让我们考虑几个子案例:

    A. 假设您的 2MB 数据被分成 1MB 区域 A 和 B,并且您的代码访问区域 A 中的所有数据(一次),然后访问区域 B 中的所有数据(一次),然后访问区域 A 中的所有数据(一次) 等。如果您选择命中率为 1 并使用 1MB 缓存分割但有 2MB 窗口,则此设置将失败。您将用区域 A 填充缓存,然后用区域 B 逐出并填充,然后用区域 A 逐出并填充,等等。如果没有“hitRatio”机制/控制,这种情况下的这种行为将是不可避免的。因此,如果您预计这种循环访问模式,0.5 的命中率会更好(50% 的数据访问将受益于 L2 剥离,50% 不会)而不是完全没有从缓存中受益。

    B. 假设您的 2MB 数据以高时间局部性访问。您的代码重复访问 2MB 数据中的 1MB(例如区域 A),然后重复访问另一个(区域 B)。在这种情况下,可能根本不需要拆分。如果您想使用分割,则命中率 1 可能是有意义的,因为这意味着该分割或多或少像普通缓存一样,除了可缓存窗口是用户定义的。在此示例中,您的缓存将填满您的前 1MB 区域 A 数据,然后您的代码将受益于缓存,因为它重用了该数据。当您的代码切换模式并开始使用第二个 1MB 数据(区域 B)时,第二个 1MB 将驱逐第一个 1MB,然后当您的代码重复使用第二个 1MB 时,它将再次从缓存中获得 100% 的收益。


推荐阅读