我试图在两种算法之间做出决定。一个写入8个字节(两个对齐的4字节字)到2个高速缓存行,另一个写入3个整个高速缓存行。
如果CPU仅将更改的8个字节写回内存,则第一个算法使用的内存带宽要少得多:8个字节对192个字节。如果CPU写入整个高速缓存行,则128和192字节之间的差异不那么显着。
那么Intel Xeon CPU如何写回内存?你会惊讶地发现在谷歌找到一个应该众所周知的答案是多么困难。
据我了解,写入进入存储缓冲区,然后进入缓存。当脏缓存行从缓存中逐出时,它们可能只被写入内存,但是英特尔是否跟踪缓存行的哪些部分是脏的,或者只是转储整个内容?我更怀疑他们跟踪缓存行粒度以下的事情。如果在高速缓存行被驱逐之前发生任何事情,我也会感到非常惊讶。
答案 0 :(得分:17)
即使对于DRAM本身来说,局部性也很重要,甚至可以忽略缓存。对于脏缓存行的64B连续字节的突发写入比从4B到16个不同地址的16次写入快得多。换句话说,写回整个缓存行并不比写回缓存行中的几个更改字节慢得多。
Ulrich Drepper撰写的What Every Programmer Should Know About Memory解释了很多关于在编程时避免内存瓶颈的问题。他介绍了DRAM寻址的一些细节。 DRAM控制器必须选择一行,然后选择一列。访问另一个虚拟内存页面也可能导致TLB未命中。
DRAM确实具有用于传输顺序数据块的突发传输命令。 (显然是为了回写缓存行的CPU而设计的)。现代计算机中的内存系统针对编写整个缓存行的使用模式进行了优化,因为这几乎总是发生。缓存行是 CPU跟踪脏或不跟踪的单位。可以使用比现有或未高速缓存行更小的线路大小来跟踪脏度,但这将需要额外的晶体管并且不值得。设置多级缓存以传输整个缓存行,因此当需要读取整个缓存行时,它们可以尽可能快。
有所谓的非临时读/写(movnti/movntdqa
)绕过缓存。这些用于数据,直到它无论如何都要从缓存中逐出(因此是非时间的),将不再被触摸。对于可以从缓存中受益的数据来说,它们是个坏主意,但是可以让你将4个字节写入内存,而不是整个缓存行。根据该存储器范围的MTRR,写入可能会或可能不会写入组合。 (这与存储器映射的I / O区域相关,其中两个相邻的4B写入与一个8B写入不同。)
只触及两个缓存行的算法当然具有该分数的优势,除非需要更多的计算,或者特别是分支,以确定要写入哪个内存。如果你想要帮助决定,也许会问一个不同的问题。 (请参阅https://stackoverflow.com/tags/x86/info的链接,特别是Agner Fog的指南,了解可帮助您自行决定的信息。)
请参阅Cornstalks的回答,警告有关在不同CPU上触摸同一内存时有多个线程的危险。这可能导致比单线程程序的额外写入更大的减速。
答案 1 :(得分:8)
为了让CPU只将脏字节写回内存,需要为缓存中的每个字节存储一个脏位。这是不可行的,并没有在现代CPU上完成(据我所知)。 CPU只有一个脏位用于缓存行。写入高速缓存行中的任何字节都会导致整行被标记为脏。
当需要刷新脏缓存行时,需要写入整行,因为CPU不知道哪个字节发生了变化。
这可以在高速缓存失效策略中看到,其中写入核心中的一个高速缓存行可以使不同核心中的高速缓存行无效(因为两个高速缓存行映射到相同的地址),即使第一个核心正在使用高速缓存行的低半部分和第二个核心正在使用高速缓存行的高半部分。也就是说,如果核心1写入字节N,核心2使用字节N + 1,则核心2仍然必须刷新其缓存行,即使您和我知道它不是必需的。