我知道CPU缓存的步幅值可以等于或大于缓存行大小,但我不明白为什么需要步幅值?
什么时候CPU缓存的步长值会大于缓存行大小?
何时实际使用步幅值?是冲水的时候吗?
我已经搜索了很多有关 CPU 缓存跨度的更多信息,但找不到太多信息。
考虑一下 C++ 中的这个函数:
void foo(uint32_t *a1, uint32_t *a2, uint32_t *b1, uint32_t *b2, uint32_t *o) {
while (b1 != b2) {
// assert(0 <= *b1 && *b1 < a2 - a1)
*o++ = a1[*b1++];
}
}
Run Code Online (Sandbox Code Playgroud)
其目的应该足够明确。不幸的是,b1包含随机数据并垃圾缓存,成为foo我的程序的瓶颈。无论如何我可以优化它吗?
这是一个 SSCCE,应该类似于我的实际代码:
#include <iostream>
#include <chrono>
#include <algorithm>
#include <numeric>
namespace {
void foo(uint32_t *a1, uint32_t *a2, uint32_t *b1, uint32_t *b2, uint32_t *o) {
while (b1 != b2) {
// assert(0 <= *b1 && *b1 < a2 - …Run Code Online (Sandbox Code Playgroud) 我不知道为什么L1 Cache和L2 Cache保存相同的数据。
例如,假设我们想要第一次访问 Memory[x]。Memory[x] 首先被映射到 L2 Cache,然后相同的数据块被映射到 L1 Cache,CPU 寄存器可以从中检索数据。
但是我们在L1和L2缓存上都存储了重复的数据,这不是一个问题或者至少是浪费存储空间吗?
这个问题是在本科计算机体系结构课程中有关虚拟内存的部分中提出的。助教和教授都无法充分回答这个问题,而且网上资源也有限。
问题:
假设处理器具有以下规格:
现在假设该处理器有一个 32KB L1 缓存,其标签是根据物理地址计算的。在计算与虚拟地址相对应的物理地址之前,缓存必须具有允许访问适当的缓存集的最小关联性是多少?
直觉:
我的直觉是,如果缓存中的索引数量和虚拟页面(也称为页表条目)数量可以被彼此整除,那么我们可以直接从缓存中检索物理页面中包含的字节,而无需计算物理页,从而提供小的加速。但是,我不确定这是否是正确的直觉,并且绝对不知道如何遵循它。有人可以解释一下吗?
注意:我计算出页表条目的数量为 2^19,如果这对任何人有帮助的话。
我对缓存一致性系统在现代多核 CPU 中的功能有些困惑。我已经看到基于侦听的协议,如基于 MESIF/MOESI 侦听的协议已在 Intel 和 AMD 处理器中使用,另一方面,基于目录的协议似乎对多核更有效,因为它们不广播但发送消息具体节点。
AMD 或 Intel 处理器中的现代缓存一致性解决方案是什么,它是基于侦听的协议,如 MOESI 和 MESIF,还是仅基于目录的协议,还是两者的组合(基于侦听的协议,用于同一节点内的元素之间的通信? ,以及基于节点到节点通信的目录)?
我知道写入组合写入将被缓存,并且不会直接到达内存.但是,在其他人可以访问之前,程序员是否有必要明确地清除这个内存?
我从图形驱动程序代码中得到了这个问题.例如,CPU填充顶点缓冲区(映射为WC).但在GPU访问之前,我没有在代码中看到任何刷新操作.架构(x86)已经为我们解决了这个问题吗?关于这个的更详细的文件?
我读过一篇2006年的文章,关于CPU如何在整个l1缓存行上进行操作,即使在你只需要用l1行包含的一小部分内容做的事情的情况下(例如,加载整个l1行来写入布尔值)变量显然是矫枉过正的).本文通过以l1缓存友好的方式管理内存来鼓励优化.
假设我有两个int恰好在内存中连续的变量,在我的代码中,我连续写入两个变量.
硬件是否将我的两个代码操作合并到单个l1行上的一个物理操作中(授予CPU具有足以容纳两个变量的l1高速缓存行),或者不是?
有没有办法在C++或C中向CPU提出这样的建议?
如果硬件没有以任何方式进行整合,那么如果在代码中实现这样的东西,你认为它可以产生更好的性能吗?分配一个大小为l1行的内存块并用尽可能多的热数据变量填充它?
在x86-64平台上,CLFLUSH汇编指令允许刷新对应于给定地址的缓存行.相反,冲洗与特定地址的缓存,会有一种通过使其充满了虚拟的内容,以刷新整个高速缓存(或者相关程序的缓存中执行,或整个高速缓存),例如(或任何我不会意识到的其他方法):
以下函数的内容是什么:(无论编译器优化如何,该函数都应该工作)?
void flush_cache()
{
// Contents
}
Run Code Online (Sandbox Code Playgroud) 我对L1缓存的理解是内存提取加载了缓存行.假设高速缓存行大小为64个字节,如果我在地址访问存储器p,它将从加载整个块p到p + 64到缓存中.因此,最好从左到右(而不是从右到左)迭代一个数组,以最大化缓存局部性.
但是,我编写了一个示例C代码,它分配了一个包含1亿个字符的数组,将随机值写入其中并对其求和(下面复制以供参考).一个版本的代码从左到右,另一个从右到左.当我对它进行基准测试时,我得到了非常类似的结果(其中"时钟周期"是根据测量的clock.代码是在没有优化的情况下编译的.
所以我的问题是:现代处理器做的不仅仅是"缓存读取+ 64字节"吗?他们是向前和向后缓存吗?编译器可以"告诉"处理器代码向后迭代吗?
作为参考,我正在Mac OS X 10.13.3使用gcc-7 (Homebrew GCC 7.2.0_1) 7.2.0具有64字节缓存行的x86-64 Intel处理器.
Benchmakrs:
$ ./a.out
Backward Iterating...took 150101 clock cycles
$ ./a.out
Forward Iterating...took 146545 clock cycles
Run Code Online (Sandbox Code Playgroud)
我希望前向迭代速度大约快64倍,因为每64个元素应该是缓存命中,而对于反向迭代,每个元素应该是缓存未命中.
所以,我打电话给cachegrind.两者的缓存命中率几乎相同:
# Left to right iteration
==21773==
==21773== I refs: 4,006,996,067
==21773== I1 misses: 5,183
==21773== LLi misses: 3,019
==21773== I1 miss rate: 0.00%
==21773== LLi miss rate: 0.00%
==21773==
==21773== D refs: 1,802,393,260 (1,401,627,925 rd + …Run Code Online (Sandbox Code Playgroud) 据我所知,内存屏障用于避免乱序执行。但是,在谈论缓存一致性时,也经常提到存储障碍。我不确定这两个概念是如何连接的,因为-根据我的发现-缓存一致性应该已经通过各种协议(例如MESI等)在硬件级别上得到了保证。使用内存屏障来防止乱序执行是(手动)授予缓存一致性的另一种方法吗?
assembly multithreading cpu-architecture memory-barriers cpu-cache
cpu-cache ×10
optimization ×3
x86 ×3
assembly ×2
c ×2
c++ ×2
caching ×2
algorithm ×1
architecture ×1
gcc7 ×1
graphics ×1
linux ×1
memory ×1
mesi ×1
performance ×1