从这里我知道英特尔近年来实施了几种静态分支预测机制:
80486年龄:永远不被采取
Pentium4年龄:未采取后退/前锋
像Ivy Bridge,Haswell这样的新型CPU变得越来越无形,请参阅Matt G的实验.
英特尔似乎不想再谈论它,因为我在英特尔文档中找到的最新资料大约是十年前写的.
我知道静态分支预测(远远不是)比动态更重要,但在很多情况下,CPU将完全丢失,程序员(使用编译器)通常是最好的指南.当然,这些情况通常不是性能瓶颈,因为一旦频繁执行分支,动态预测器就会捕获它.
由于英特尔不再在其文档中明确声明动态预测机制,因此GCC的builtin_expect()只能从热路径中删除不太可能的分支.
我不熟悉CPU的设计,我不知道究竟是什么机制,目前英特尔使用其静态预测,但我还是觉得英特尔的最佳机制应该清楚地记录他的CPU",我打算去当动态预测失败,向前或向后',因为通常程序员是当时最好的指南.
更新:
我发现你提到的主题逐渐超出我的知识范围.这里涉及一些动态预测机制和CPU内部细节,我在两三天内无法学习.所以请允许我暂时退出你的讨论并充电.
这里仍然欢迎任何答案,也许会帮助更多人
compiler-construction x86 intel cpu-architecture branch-prediction
假设你有一个(1)Intel/AMD x86-64 bit 2 GHz 8核处理器.
8个内核中的每一个都是在完整的2 GHz运行,还是每个内核运行的时间是整个2 GHz时钟的一部分(例如250 MHz)?
英特尔有助于提供预取编译指示; 例如
#pragma prefetch a
for(i=0; i<m; i++)
a[i]=b[i]+1;
Run Code Online (Sandbox Code Playgroud)
将由a编译器确定预先获取一定数量的循环周期.
但是如果a不是一个数组而是一个[]被覆盖的类呢?如果operator[]一个简单的数组访问,可以预取仍然以这种方式使用?
(据推测这个问题也适用于此std::vectors).
为什么DS和ES寄存器的初始化必须由程序员手动完成?
例如:
MOV AX, DTSEG
MOV DS, AX
Run Code Online (Sandbox Code Playgroud)
另一方面,CS和SS寄存器由操作系统(in MS-DOS)初始化.为什么会这样?
在Agner Fog的优秀微体系结构中 .pdf (第9.14节)我读到:
存储转发在以下情况下有效:[...]当写入128或256位后,读取相同大小和相同的地址,对齐16.
另一方面,我阅读了英特尔架构优化参考手册(2.2.5.2 Intel Sandy Bridge,L1 DCache)
在以下情况下,存储无法转发到负载:[...]任何跨越32字节存储的16字节边界的负载.
任何负载听起来像32字节加载..我写了以下简单的代码来测试这一点,似乎32字节存储不转发到Sandy Bridge架构上的后续32字节加载.这是代码:
#include <stdlib.h>
#include <malloc.h>
int main(){
long i;
// aligned memory address
double *tempa = (double*)memalign(4096, sizeof(double)*4);
for(i=0; i<4; i++) tempa[i] = 1.0;
for(i=0; i<1000000000; i++){ // 1e9 iterations
#ifdef TEST_AVX
__asm__("vmovapd %%ymm12, (%0)\n\t"
"vmovapd (%0), %%ymm12\n\t"
:
:"r"(tempa));
#else
__asm__("movapd %%xmm12, (%0)\n\t"
"movapd (%0), %%xmm12\n\t"
:
:"r"(tempa));
#endif
}
}
Run Code Online (Sandbox Code Playgroud)
在循环中唯一做的是从4k对齐的存储器位置和向量寄存器读取/写入.使用AVX指令集(gcc -O3 -DTEST_AVX)编译时,我的2.7GHz i7-2620M的执行时间为3.1秒.使用SSE2指令集时,时间为2.5秒.我看了一下性能指标.在AVX情况下,我计算每次迭代一次存储转发块事件(计数器03H 02H LD_BLOCKS.STORE_FORWARD).计数器为SSE2情况读取0.
任何人都可以对此有所了解吗?SB确实不支持将32字节存储转发到32字节加载吗?如果是后者,溢出ymm …
我有两个__m128is,a并且b,我想要洗牌,以便在64位a的低位64位dst和64位的高位b落在64位的高位64位dst.即
dst[ 0:63] = a[64:127]
dst[64:127] = b[0:63]
Run Code Online (Sandbox Code Playgroud)
相当于:
__m128i dst = _mm_unpacklo_epi64(_mm_srli_si128i(a, 8), b);
Run Code Online (Sandbox Code Playgroud)
要么
__m128i dst = _mm_castpd_si128(mm_shuffle_pd(_mm_castsi128_pd(a),_mm_castsi128_pd(b),1));
Run Code Online (Sandbox Code Playgroud)
有没有比第一种方法更好的方法呢?第二个只是一条指令,但切换到浮点SIMD执行比第一条指令的额外指令更昂贵.
在为AVX256,AVX512和一天AVX1024设计前瞻性算法时,考虑到大SIMD宽度的完全通用置换的潜在实现复杂性/成本,我想知道即使在AVX512中通常保持隔离128位操作是否更好?
特别是考虑到AVX有128位单元来执行256位操作.
为此,我想知道在所有512位向量中AVX512置换类型操作之间是否存在性能差异,而在 512位向量的每个4x128位子向量中是否存在置换类型操作?
我已经了解了不同的缓存映射技术,如直接映射,关联映射和集合关联映射技术,还学习了权衡.但我很好奇现在在intel core i7或AMD处理器中使用了什么.以及这些技术是如何演变的.还有哪些事情需要改进?
我一直试图谷歌我的问题,但老实说,我不知道如何简洁地陈述问题.
假设我在多核Intel系统中有两个线程.这些线程在同一个NUMA节点上运行.假设线程1写入X一次,然后只是偶尔读取它向前移动.进一步假设,线程2连续读取X. 如果我不使用内存栅栏,在线程1写入X和线程2看到更新值之间可以有多长时间?
我知道X的写入将转到存储缓冲区并从那里到缓存,此时MESIF将启动,线程2将通过QPI查看更新的值.(或者至少这是我收集到的).我假设存储缓冲区将被写入存储围栏中的缓存或者是否需要重用该存储缓冲区条目,但我不知道存储缓冲区是否已分配给写入.
最终我要为自己回答的问题是,如果线程2有可能在一个相当复杂的应用程序中看到线程1的写入几秒钟而正在做其他工作.
我正在尝试为英特尔的clwb指令找到不会使缓存行无效的配置或内存访问模式。我正在使用 NVDIMM 对 Intel Xeon Gold 5218 处理器进行测试。Linux 版本是 5.4.0-3-amd64。我尝试使用 Device?DAX 模式并直接将此字符设备映射到地址空间。我还尝试将此非易失性内存添加为新的 NUMA 节点,并使用numactl --membind命令将内存绑定到它。在这两种情况下,当我使用clwb缓存地址时,它都会被驱逐。我正在观察 PAPI 硬件计数器的驱逐,并禁用预取器。
这是我正在测试的一个简单循环。array 和 tmp 变量,都被声明为 volatile,所以加载是真正执行的。
for(int i=0; i < arr_size; i++){
tmp = array[i];
_mm_clwb(& array[i]);
_mm_mfence();
tmp = array[i];
}
Run Code Online (Sandbox Code Playgroud)
两次读取都会导致缓存未命中。
我想知道是否还有其他人试图检测是否有某种配置或内存访问模式会在缓存中留下缓存行?