当我的程序执行带有释放语义或可能是全栅栏的获取语义/存储操作的加载操作时,它会使CPU的缓存无效.
我的问题是:缓存的哪一部分实际上是无效的?只有持有我用过的变量的缓存行获取/释放?或者整个缓存可能无效?(L1 + L2 + L3 ..等等?).当我使用获取/释放语义或使用全栅栏时,这个主题是否有区别?
英特尔手动优化(2019 年 9 月修订版)显示了用于 Ice Lake 微架构的 48 KiB 8 路关联 L1 数据缓存。
这让我感到困惑,因为:
总而言之,缓存的处理成本似乎更高,但延迟仅略有增加(如果确实如此,则取决于英特尔对该数字的确切含义)。
有一点创造力,我仍然可以想象一种快速索引 96 组的方法,但第二点对我来说似乎是一个重要的突破性变化。
我错过了什么?
我正在阅读 MDS 攻击论文RIDL:Rogue In-Flight Data Load。他们讨论了 Line Fill Buffer 如何导致数据泄漏。有关于 RIDL 漏洞和负载的“重放”问题讨论了漏洞利用的微架构细节。
阅读该问题后,我不清楚的一件事是,如果我们已经有了存储缓冲区,为什么还需要行填充缓冲区。
John McCalpin 在WC-buffer 与LFB 有什么关系?中讨论了存储缓冲区和行填充缓冲区是如何连接的?在英特尔论坛上,但这并没有真正让我更清楚。
对于存储到 WB 空间,存储数据将保留在存储缓冲区中,直到存储退出之后。退役后,数据可以写入 L1 数据缓存(如果该行存在且具有写入权限),否则会为存储未命中分配一个 LFB。LFB 最终会收到缓存行的“当前”副本,以便它可以安装在 L1 数据缓存中,并且可以将存储数据写入缓存。合并、缓冲、排序和“捷径”的细节尚不清楚......与上述合理一致的一种解释是 LFB 用作缓存行大小的缓冲区,其中存储数据在发送到L1 数据缓存。至少我认为这是有道理的,但我可能忘记了一些事情......
我最近才开始阅读乱序执行,所以请原谅我的无知。这是我关于商店如何通过商店缓冲区和行填充缓冲区的想法。
这是一个边缘话题。因为我想了解编程、CPU 缓存、读取 CPU 缓存行等,所以我把它贴在这里。
我在 C/C++ 中实现了 AES 算法。由于在没有硬件支持的情况下执行 GF(2 8 ) 乘法在计算上是昂贵的,我已经优化为使用 AES S-box 的查找表。但不幸的是,基于查找表的实现容易受到缓存定时攻击。因此,由于对 CPU 缓存非常天真,我开始学习它的工作原理,以及如何在不增加任何计算成本的情况下规避这种攻击。
我意识到实际上有 AES NI 指令和 Bit Slice AES 实现,但它们远远超出了我目前的理解。
我从 crypto.SE 了解到,最简单的方法是在查找之前读取所有缓存行或读取整个表。(这也影响了我的表现)但是我不知道如何在软件中实现它,或者它背后的复杂概念。
在OpenSSLaes-x86core.c的C 实现参考指南中—— 作者实现了一个功能:
static void prefetch256(const void *table)
{
volatile unsigned long *t=(void *)table,ret;
unsigned long sum;
int i;
/* 32 is common least cache-line size */
for (sum=0,i=0;i<256/sizeof(t[0]);i+=32/sizeof(t[0])) sum ^= t[i];
ret = sum;
}
Run Code Online (Sandbox Code Playgroud)
for循环i中增加8 …共享内存多处理系统通常需要为缓存一致性生成大量流量。核心 A 写入缓存。Core B 稍后可能会读取相同的内存位置。因此,内核 A,即使它本来可以避免写入主内存,也需要向内核 B 发送通知,告诉 B 如果该地址正在缓存中,则该地址无效。
究竟什么时候需要这样做,这是一个复杂的问题。不同的 CPU 架构有不同的内存模型,这里上下文中的内存模型是一组关于观察到的事情发生的顺序的保证。内存模型越弱,A 在发送通知的确切时间就越放松对于 B,A 和 B 更容易并行做更多的事情。不同 CPU 架构的内存模型总结:https : //en.wikipedia.org/wiki/Memory_ordering#Runtime_memory_ordering
所有的讨论似乎是关于当失效发生时,什么为了事情发生英寸
但在我看来,在许多工作负载中,A 写入的大部分数据永远不会被 B 使用,因此如果可以完全消除那些缓存失效的总线流量会更好。专用于执行缓存一致性的硬件仍然需要存在,因为 A 和 B 有时需要共享数据,但写入共享总线是 CPU 可以做的更耗能的事情之一,并且电池寿命和散热通常是现在限制资源,因此减少总线流量将是一个有用的优化。有没有办法做到这一点?
从效率的角度来看,理想的情况是如果忽略总线流量是默认的(因为大多数写入的数据不与其他线程共享),并且您必须在需要缓存一致性的地方显式地发出内存屏障。另一方面,这可能是不可能的,因为假设它在 x86 或 ARM 上运行的现有代码量很大;有没有办法反过来,向 CPU 指示给定的缓存行永远不会对任何其他线程感兴趣?
我会对任何系统的答案感兴趣,但最特别是 x64、ARM 或 RISC-V 上 Linux 最常见的当前/未来服务器配置。
multithreading cpu-architecture memory-model memory-barriers cpu-cache
Intel 硬件预取器 Intel 网站显示有四种硬件预取器。由第 3 位控制的预取器是 L1 步幅预取器。我正在运行一个测试代码来测试 stride 预取器的触发条件是什么。我按照以下步骤运行代码(将 MSR0x1a4 设置为 0x7,这意味着仅启用基于 L1 IP 的 strider 预取器):
repeat following for 10000 times:
flush
training phase: access line 0 3 6 9
sleep for near 1000 cycles
measure phase: measure line 12
Run Code Online (Sandbox Code Playgroud)
我希望看到第 12 行被预取到缓存中。但是我只能看到缓存中的第 0 3 6 9 行被命中。即使我更改了步幅或访问模式的长度,也无法观察到步幅预取活动。所以我想知道是否有人在英特尔处理器中看到过预取活动,或者有一些我没有注意到的特殊触发条件?
对这个案例有兴趣的可以试一下测试代码。运行sudo ./run.sh就可以了。我的机器上的结果显示,第 12 行的访问时间大多大于 180 个周期。我认为时间测量代码没有问题,因为如果我将测量的行从缓存行 12 更改为缓存行 6(只需在 test.c,第 103 行更改),那么访问时间主要是 25 个周期。
我对算法LRU有一点疑问.如果您有一个包含四个块的缓存,那么您需要多少位才能实现此算法?
这是关于跨不同层的缓存一致性协议cache。我的理解(X86_64)L1是,它由一个核心独占,L2介于两个核心之间,并且L3适用于一个CPU插槽中的所有核心。我已经阅读了MESI协议功能,关于存储缓冲区、无效队列、无效消息等。我的疑问是,它仅MESI适用于L1还是适用于L2并且L3也适用。L2或者 for和之间是否有不同的缓存同步L3。
在 C 等语言中,不同线程对同一内存位置的不同步读取和写入是未定义的行为。但在 CPU 中,缓存一致性表示,如果一个核心写入某个内存位置,然后另一个核心读取它,则另一个核心必须读取写入的值。
如果上一层将丢弃内存层次结构的连贯抽象,为什么处理器需要费心暴露它呢?为什么不让缓存变得不连贯,并要求软件在想要共享某些内容时发出特殊指令呢?
我有一段用C编写的代码,我想找到这段代码的瓶颈!使用perf工具并注释汇编代码,我发现push %r12函数开头的指令是瓶颈(见图):
我对此有点困惑!一条简单的推送指令如何导致成为瓶颈?是否有可能CPU在将寄存器内容压入堆栈之前被迫从缓存更新内存内容?如果是,如果您让我知道背后的原因是什么,我将不胜感激。
这是我的函数的输入参数,以防需要:
nt32_t
rte_hash_lookup(const struct rte_hash *h, const void *key)
{
RETURN_IF_TRUE(((h == NULL) || (key == NULL)), -EINVAL);
return __rte_hash_lookup_with_hash(h, key, rte_hash_hash(h, key), NULL);
}
Run Code Online (Sandbox Code Playgroud)