不,x86 不允许你这样做。您可以强制驱逐clfushopt
,或(在即将推出的 CPU 上)仅写回而不逐出clwb
,但您无法在缓存中固定一行或禁用一致性。
您可以将整个 CPU(或单个核心?)置于缓存即 RAM(又名无填充)模式,以禁用与内存控制器的同步,并禁用写回数据。Cache-as-Ram(无填充模式)可执行代码。它通常由 BIOS/固件在配置内存控制器之前的早期启动中使用。它不能按行使用,并且几乎可以肯定在这里没有实际用处。有趣的事实:离开此模式是的用例之一invd,它会删除缓存数据而不写回,而不是wbinvd
.
我不确定 no-fill 模式是否会阻止从 L1d 驱逐到 L3 或其他什么;或者如果数据在驱逐时被丢弃。因此,您只需避免访问 7 个以上的其他缓存行,这些缓存行是您在 L1d 中关心的缓存行的别名,或者是 L2/L3 的等效项。
如果能够强制一个核心无限期地挂在 L1d 行上,并且不响应将其写回/共享的 MESI 请求,那么如果其他核心触及该行,则它们很容易锁定。显然,如果存在这样的功能,则需要内核模式。 (对于硬件虚拟化,需要虚拟机管理程序特权。)它还可能阻止硬件 DMA(因为现代 x86 具有缓存一致性 DMA)。
因此,支持这样的功能需要 CPU 的很多部分来处理无限期的延迟,目前可能存在一些上限,如果存在这样的情况,该上限可能比 PCIe 超时短。 (我不编写驱动程序或构建真正的硬件,只是猜测这一点)。
正如@fuz 指出的,违反一致性的指令(xdcbt
) was 在 PowerPC 上尝试过(在 Xbox 360 CPU 中),由于错误推测指令的执行而导致灾难性的结果。所以就是hard来实施。
你通常不需要这个。
如果线路使用频繁,LRU 替换将使其保持热状态。如果它以足够频繁的间隔从 L1d 丢失,那么它可能会在 L2 中保持热度,L2 也是核心和私有的,并且在最近的设计中速度非常快(Intel 自 Nehalem 以来)。英特尔在 Skylake-AVX512 以外的 CPU 上的包容性 L3 意味着留在 L1d 也意味着留在 L3。
所有这些意味着对于一个核心大量使用的线路来说,在任何频率下,一直到 DRAM 的完整高速缓存未命中都不太可能发生。所以吞吐量应该不成问题。我想您可能需要这种实时延迟,其中函数的一次调用的最坏情况运行时间很重要。从代码的其他部分中的缓存行进行虚拟读取可能有助于保持热状态。
然而,如果来自 L3 高速缓存中其他核心的压力导致该行从 L3 中逐出,则具有包容性 L3 的 Intel CPU 也必须强制从仍处于热状态的内部高速缓存中逐出。 IDK 是否有任何机制可以让 L3 知道某条线路在核心的 L1d 中大量使用,因为这不会生成任何 L3 流量。
我不知道这在实际代码中是一个很大的问题。 L3 是高度关联的(如 16 路或 24 路),因此在发生驱逐之前需要发生很多冲突。 L3 还使用更复杂的索引函数(就像真正的哈希函数,而不仅仅是通过取连续的位范围进行取模)。在 IvyBridge 及更高版本中,它还使用自适应替换策略来减轻由于接触大量不会经常重用的数据而导致的驱逐。http://blog.stuffedcow.net/2013/01/ivb-cache-replacement/.
也可以看看intel core i7 处理器使用哪种缓存映射技术?
@AlexisWilke 指出对于某些用例,您可以使用向量寄存器而不是缓存行。 使用 ymm 寄存器作为“类似内存”的存储位置。您可以在全球范围内将一些向量规则专用于此目的。要在 gcc 生成的代码中得到这个,也许可以使用-ffixed-ymm8,或将其声明为易失性全局寄存器变量。 (如何通知 GCC 不要使用特定寄存器)
使用 ALU 指令或存储转发从向量寄存器获取数据将为您提供有保证的延迟,并且不会出现数据缓存缺失。但对于极低延迟来说,代码缓存未命中仍然是一个问题。