我知道关于这个主题的多个问题,但是,我没有看到任何明确的答案或任何基准测量.因此,我创建了一个简单的程序,它使用两个整数数组.第一个数组a非常大(64 MB),第二个数组b很小,适合L1缓存.程序迭代a并将其元素添加到b模块化意义上的相应元素中(当到达结束时b,程序从其开始再次开始).测量的不同大小的L1缓存未命中数b如下:
测量是在具有32 kiB L1数据高速缓存的Xeon E5 2680v3 Haswell型CPU上进行的.因此,在所有情况下,都b适合L1缓存.然而,大约16 kiB的b内存占用量大大增加了未命中数.这可能因为两者的负载预期a并b导致缓存线失效从一开始b在这一点上.
绝对没有理由保留a缓存中的元素,它们只使用一次.因此,我运行一个具有非时间负载a数据的程序变体,但未命中数没有改变.我还运行了一个非暂时预取a数据的变体,但仍然有相同的结果.
我的基准代码如下(没有显示非时间预取的变体):
int main(int argc, char* argv[])
{
uint64_t* a;
const uint64_t a_bytes = 64 * 1024 * 1024;
const uint64_t a_count = a_bytes / sizeof(uint64_t);
posix_memalign((void**)(&a), 64, a_bytes);
uint64_t* b;
const uint64_t b_bytes = atol(argv[1]) * 1024;
const uint64_t b_count = b_bytes …Run Code Online (Sandbox Code Playgroud) 我试图更多地了解 CPU 缓存如何影响性能。作为一个简单的测试,我将矩阵第一列的值与不同数量的总列数相加。
// compiled with: gcc -Wall -Wextra -Ofast -march=native cache.c
// tested with: for n in {1..100}; do ./a.out $n; done | tee out.csv
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
double sum_column(uint64_t ni, uint64_t nj, double const data[ni][nj])
{
double sum = 0.0;
for (uint64_t i = 0; i < ni; ++i) {
sum += data[i][0];
}
return sum;
}
int compare(void const* _a, void const* _b)
{
double const a = *((double*)_a);
double …Run Code Online (Sandbox Code Playgroud) 引用英特尔 ®64 和IA-32架构优化参考手册,§2.4.6"REP String Enhancement":
使用REP字符串的性能特征可归因于两个组件: 启动开销和数据传输吞吐量.
[...]
对于较大粒度数据传输的REP字符串,随着ECX值的增加,REP String的启动开销呈逐步增加:
- 短串(ECX <= 12):REP MOVSW/MOVSD/MOVSQ的延迟约为20个周期,
快速字符串(ECX> = 76:不包括REP MOVSB):处理器实现通过移动尽可能多的16字节数据来提供硬件优化.如果其中一个16字节数据传输跨越缓存行边界,则REP字符串延迟的延迟会有所不同:
- 无拆分:延迟包括大约40个周期的启动成本,每个64字节的数据增加4个周期,
- 高速缓存拆分:延迟包括大约35个周期的启动成本,每64个字节的数据增加6个周期.
中间字符串长度:REP MOVSW/MOVSD/MOVSQ的延迟具有大约15个周期的启动成本加上word/dword/qword中数据移动的每次迭代的一个周期.
(强调我的)
没有进一步提及这种启动成本.它是什么?它做了什么,为什么总是需要更多的时间?
我一直有这个想法,减少迭代次数是的方式来使程序更加高效.由于我从未真正确认过,我开始测试这个.
我制作了以下C++程序来测量两个不同函数的时间:
完整的测试代码:
#include <iostream>
#include <chrono>
using namespace std;
int* list1; int* list2;
int* list3; int* list4;
int* list5; int* list6;
int* list7; int* list8;
int* list9; int* list10;
const int n = 1e7;
// **************************************
void myFunc1()
{
for (int i = 0; i < n; i++)
{
list1[i] = 2;
list2[i] = 4;
list3[i] = 8;
list4[i] = 16;
list5[i] = 32;
list6[i] = 64;
list7[i] = 128;
list8[i] = 256;
list9[i] = …Run Code Online (Sandbox Code Playgroud) 该PREFETCHNTA指令基本上用于通过预取器将数据从主存储器带到缓存,但是NT已知带有后缀的指令会跳过缓存并避免缓存污染。
那么PREFETCHNTA,与PREFETCH指令有何不同?
我从硬件抽象级别理解端口I / O(即断言一个引脚,该引脚向总线上的设备指示该地址是端口地址,这在具有简单地址总线模型的早期CPU上是有意义的),但我不是真的确保在微体系结构上如何在现代CPU上实现它,尤其要确保端口I / O操作在环形总线上的显示方式。
首先。IN / OUT指令在哪里分配给保留站或加载/存储缓冲区?我最初的想法是,将在加载/存储缓冲区中分配它,并且内存调度程序将其识别出来,将其发送到L1d,指示它是端口映射操作。分配了一个行填充缓冲区,并将其发送到L2,然后发送到环网。我猜环上的消息有一些端口映射的指示器,只有系统代理才能接受它,然后它检查其内部组件并将端口映射的指示请求转发给它们。即PCIe根网桥将拾取CF8h和CFCh。我猜测DMI控制器是固定的,可以拾取将出现在PCH上的所有标准化端口,例如用于传统DMA控制器的端口。
我问这个关于 Haswell 微架构(英特尔至强 E5-2640-v3 CPU)的问题。从CPU和其他资源的规格我发现有10个LFB,超级队列的大小是16。我有两个关于LFB和SuperQueue的问题:
1) 系统可以提供的最大内存级并行度是多少,10 还是 16(LFB 或 SQ)?
2)根据某些来源,每个 L1D 未命中都记录在 SQ 中,然后 SQ 分配行填充缓冲区,而在其他某些来源中,他们写道 SQ 和 LFB 可以独立工作。你能简单解释一下 SQ 的工作吗?
这是 SQ 和 LFB 的示例图(不适用于 Haswell)。
参考资料:https :
//www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-optimization-manual.pdf
写合并缓冲区一直是Intel CPU的功能,至少可以追溯到Pentium 4甚至更早。基本思想是这些高速缓存行大小的缓冲区将写操作收集到同一高速缓存行中,因此可以将它们作为一个单元进行处理。作为它们对软件性能的影响的一个示例,如果您不编写完整的缓存行,则可能会遇到性能下降的情况。
例如,在《Intel 64和IA-32体系结构优化参考手册》中,“ 3.6.10写合并”部分以以下说明(加了重点)开头:
写合并(WC)通过两种方式提高性能:
•在对第一级缓存的写入未命中时,它允许在从缓存/内存层次结构的更深层读取所有权(RFO)之前对该同一个缓存行进行多个存储。然后读取剩余的行,将尚未写入的字节与返回行中的未修改字节合并。
•写合并允许将多个写组合在一起,并作为一个单元在高速缓存层次结构中进一步写出。这样可以节省端口和总线流量。节省流量对于避免部分写入未缓存的内存特别重要。
有六个写合并缓冲区(在奔腾4和Intel Xeon处理器上,CPUID签名的族编码为15,模型编码为3;有8个写合并缓冲区)。这些缓冲区中的两个可以写出更高的缓存级别,并释放出来以供其他写未命中。保证只有四个写合并缓冲区可同时使用。写合并适用于存储器类型WC;它不适用于内存类型UC。
Intel Core Duo和Intel Core Solo处理器的每个处理器内核中都有六个写合并缓冲区。基于英特尔酷睿微体系结构的处理器在每个内核中都有八个写合并缓冲区。从英特尔微体系结构代码名称Nehalem开始,有10个缓冲区可用于写合并。
写合并缓冲区用于所有存储器类型的存储。它们对于写入未缓存的内存特别重要...
我的问题是,在使用普通存储区(非临时存储区以外的其他存储区,即您所使用的存储区)时,写合并是否适用于WB内存区域(即您在用户程序中使用99.99%的时间的“普通”内存)正在使用99.99%的时间)。
上面的文字很难准确解释,因为自Core Duo时代以来没有更新过。您有说写梳理的部分“适用于WC存储器,但不适用于UC”,但是当然不包括所有其他类型,例如WB。后来,您发现“ [WC对于写入未缓存的内存特别重要”,这似乎与“不适用于UC部分”相矛盾。
那么,现代英特尔芯片上用于正常存储到WB存储器的写合并缓冲区是否有效?
我已经做了一些关于Spectre v2的阅读,显然你得到了非技术性的解释.彼得科德斯有一个更深入的解释,但它没有完全解决一些细节.注意:我从未进行过Spectre v2攻击,因此我没有亲身体验.我只读过关于这个理论的内容.
我对Spectre v2的理解是你做了一个间接的分支误预测if (input < data.size).如果间接目标数组(我不太确定它的细节 - 即为什么它与BTB结构分开) - 在解码时重新检查间接分支的RIP - 不包含预测那么它将插入新的跳转RIP(分支执行最终将插入分支的目标RIP),但是现在它不知道跳转的目标RIP,因此任何形式的静态预测都不起作用.我的理解是它始终预测不会采用新的间接分支,并且当端口6最终计算出跳转目标RIP并预测它将使用BOB回滚并使用正确的跳转地址更新ITA然后更新本地和全局分支历史寄存器和相应的饱和计数器.
黑客需要训练饱和计数器,以便始终预测所采取的操作,我想,通过if(input < data.size)在循环中多次运行,其中input设置为确实小于data.size(相应地捕获错误)和循环的最后一次迭代,input超过data.size(例如1000); 将预测间接分支,它将跳转到发生缓存加载的if语句的主体.
if语句包含secret = data[1000](包含秘密数据的特定内存地址(data [1000]),目标是从内存加载到缓存)然后将推测性地将其分配给加载缓冲区.前面的间接分支仍在分支执行单元中,等待完成.
我相信前提是在错误预测中刷新加载缓冲区之前需要执行加载(分配行填充缓冲区).如果已为其分配了行填充缓冲区,则无法执行任何操作.有意义的是没有取消行填充缓冲区分配的机制,因为行填充缓冲区必须在将其返回到加载缓冲区之后存储到缓存之前挂起.这可能导致行填充缓冲区变得饱和,因为它不是在需要时解除分配(保持在那里以便其他负载的速度到同一地址,但在没有其他可用的行缓冲区时解除分配).在收到一些不会发生刷新的信号之前,它将无法解除分配,这意味着它必须暂停以执行前一个分支,而不是立即使行填充缓冲区可用于其他逻辑核心的存储.这种信令机制可能难以实现,也许它并没有超出他们的想法(史前思考者),并且如果分支执行需要足够的时间来挂起行填充缓冲区以引起性能影响,即它也会引入延迟,即在循环的最后一次迭代之前data.size有目的地从cache(CLFLUSH)中刷新,这意味着分支执行可能需要多达100个周期.
我希望我的想法是正确的,但我不是百分百肯定.如果有人有任何要添加或更正,那么请做.
我以为我了解L1D写未命中是如何处理的,但仔细想想却让我感到困惑。
这是一个汇编语言片段:
;rdi contains some valid 64-bytes aligned pointer
;rsi contains some data
mov [rdi], rsi
mov [rdi + 0x40], rsi
mov [rdi + 0x20], rsi
Run Code Online (Sandbox Code Playgroud)
假设[rdi]和[rdi + 0x40]行在 l1d 中不处于 Exclusive 或 Modified 状态。然后我可以想象以下动作序列:
mov [rdi], rsi 退休。mov [rdi], rsi尝试将数据写入 l1d。RFO 启动,数据放入 WC 缓冲区。mov [rdi + 0x40], rsi退休(mov [rdi], rsi已经退休,所以有可能)mov [rdi + 0x40], rsi 为连续的缓存行启动 RFO,数据被放入 WC 缓冲区。mov [rdi + 0x20], rsi退休(mov [rdi …我得到了错误的结果,因为我在测量时没有包括这里讨论的预取触发事件。话虽如此,AFAIKrep movsb与临时存储相比,我只看到 RFO 请求减少,memcpy因为在加载时预取更好,而没有对存储进行预取。不是因为 RFO 请求针对完整缓存行存储进行了优化。这种有意义的,因为我们没有看到RFO请求优化掉了vmovdqa一个zmm寄存器,我们预计如果真的在那里为整个缓存线存储情况。话虽如此,存储上缺乏预取和非临时写入的缺乏使得很难看出如何rep movsb具有合理的性能。
编辑:RFO 可能来自rep movsb不同的请求vmovdqa,因为rep movsb它可能不请求数据,只需在独占状态下取行即可。对于有收银机的商店,情况也可能如此zmm。但是,我没有看到任何性能指标来测试这一点。有谁知道吗?
rep movsb的memcpy作为相比,memcpy与实现的vmovdqa?rep movsb的memcpy作为相比,memcpy与实现vmovdqa两个单独的问题,因为我相信我应该看到 RFO 请求减少了rep movsb,但如果不是这种情况,我是否也应该看到增加?
CPU - Icelake: Intel(R) Core(TM) i7-1065G7 CPU @ 1.30GHz
我试图在使用不同的方法时测试 RFO 请求的数量,memcpy包括:
vmovdqa这个问题是此处发布的问题的衍生问题:Measurement Bandwidth on a ccNUMA system
我为配备 2 个 Intel(R) Xeon(R) Platinum 8168 的 ccNUMA 系统上的内存带宽编写了一个微基准测试:
作为参考,我使用 Intel Advisor 的屋顶线图,它描述了每个可用 CPU 数据路径的带宽。据此计算,带宽为230GB/s。
问题:如果您查看强扩展图,您可以看到峰值有效带宽实际上是在 33 个 CPU 上实现的,之后添加 CPU 只会降低峰值有效带宽。为什么会发生这种情况?