现代x86 CPU将传入的指令流分解为微操作(uops 1),然后在输入准备就绪时将这些uop 无序调度.虽然基本思路很清楚,但我想了解准备好指令的具体细节,因为它会影响微优化决策.
例如,采取以下玩具循环2:
top:
lea eax, [ecx + 5]
popcnt eax, eax
add edi, eax
dec ecx
jnz top
Run Code Online (Sandbox Code Playgroud)
这基本上实现了循环(具有以下对应关系:) eax -> total, c -> ecx:
do {
total += popcnt(c + 5);
} while (--c > 0);
Run Code Online (Sandbox Code Playgroud)
通过查看uop细分,依赖链延迟等,我熟悉优化任何小循环的过程.在上面的循环中,我们只有一个携带的依赖链:dec ecx.环路(前三指令lea,imul,add)是开始新鲜每个环一个依赖关系链的一部分.
决赛dec和jne融合.因此,我们总共有4个融合域uop,以及一个仅循环携带的依赖链,延迟为1个周期.因此,基于该标准,似乎循环可以在1个周期/迭代时执行.
但是,我们也应该关注港口压力:
lea能够在端口1和5执行add可以在端口0,1,5和6执行jnz在端口6上执行因此,要进行1次循环/迭代,您几乎需要执行以下操作:
lea 必须 …x86架构上的Linux内核的默认内存页面大小是4 KB,我想知道这是如何计算的,为什么?
几年前,我正在学习x86汇编程序,CPU流水线,缓存未命中,分支预测以及所有爵士乐.
这是两个半的故事.我在处理器viz指令重新排序,缓存预加载,依赖性交错等方面阅读了冗长流水线的所有优点.
缺点是规范的任何偏差都是非常昂贵的.例如,IIRC在早期千兆赫时代的某个AMD处理器每次通过指针(!)调用函数时都会有40个周期的惩罚,这显然是正常的.
这不是一个可以忽略不计的"别担心"号码!请记住,"良好设计"通常意味着"尽可能地考虑您的功能"和"在数据类型中编码语义",这通常意味着虚拟接口.
权衡是不执行此类操作的代码每个周期可能获得两个以上的指令.这些是在编写高性能C++代码时需要担心的数字,这些代码在对象设计上很重要并且可以解决数字运算问题.
据我所知,随着我们进入低功耗时代,长CPU流水线趋势已经逆转.这是我的问题:
最新一代x86兼容处理器是否仍然遭受虚拟功能调用,坏分支预测等的严重处罚?
我遇到了几个关于双问题处理器概念的引用(我希望这句话在一个句子中有意义).我无法找到究竟是什么双重问题的任何解释.Google为我提供了微控制器规范的链接,但这个概念在任何地方都没有解释.这是这种参考的一个例子.我在找错了地方吗?关于它是什么的简短段落将非常有帮助.
此循环在英特尔Conroe/Merom上每3个周期运行一次,imul按预期方式在吞吐量方面存在瓶颈.但是在Haswell/Skylake上,它每11个循环运行一次,显然是因为setnz al它依赖于最后一个循环imul.
; synthetic micro-benchmark to test partial-register renaming
mov ecx, 1000000000
.loop: ; do{
imul eax, eax ; a dep chain with high latency but also high throughput
imul eax, eax
imul eax, eax
dec ecx ; set ZF, independent of old ZF. (Use sub ecx,1 on Silvermont/KNL or P4)
setnz al ; ****** Does this depend on RAX as well as ZF?
movzx eax, al
jnz .loop ; }while(ecx);
Run Code Online (Sandbox Code Playgroud)
如果setnz al …
在大多数处理器中,为什么L1缓存的大小小于L2缓存的大小?
我总是想知道一些CPU具有旋转指令的目的是什么(例如,ROL,x86上的RCL).什么样的软件使用这些说明?我首先想到它们可能用于加密/计算哈希码,但这些库通常用C编写,它没有映射到这些指令的运算符.
有没有人找到他们的用途?为什么他们在指令集中添加了哪些内容?
我写的代码看起来像以下......
if(denominator == 0){
return false;
}
int result = value / denominator;
Run Code Online (Sandbox Code Playgroud)
...当我考虑CPU中的分支行为时.
/sf/answers/785953171/ 这个答案说CPU将尝试正确猜测分支将走向哪个方向,并且如果发现分支错误地猜测分支,那么只停下该分支停止.
但是如果CPU预测上面的分支不正确,它将在以下指令中除以零.虽然这不会发生,我想知道为什么?CPU是否实际执行除零并在执行任何操作之前等待分支是否正确,还是可以告诉它在这些情况下不应该继续?这是怎么回事?
c++ error-handling optimization cpu-architecture branch-prediction
我记得假设在我的架构类中L1缓存命中是1个周期(即与寄存器访问时间相同),但在现代x86处理器上实际上是这样吗?
L1缓存命中多少个周期?它与寄存器访问相比如何?
performance x86 cpu-architecture micro-optimization cpu-cache
在最近的CPU上(至少在过去十年左右),除了各种可配置的性能计数器之外,英特尔还提供了三个固定功能硬件性能计数器.三个固定柜台是:
INST_RETIRED.ANY
CPU_CLK_UNHALTED.THREAD
CPU_CLK_UNHALTED.REF_TSC
Run Code Online (Sandbox Code Playgroud)
第一个计算退役指令,第二个计算实际周期,最后一个是我们感兴趣的."英特尔软件开发人员手册"第3卷的描述如下:
当核心未处于暂停状态而不处于TM停止时钟状态时,此事件计算TSC速率下的参考周期数.核心在运行HLT指令或MWAIT指令时进入暂停状态.此事件不受核心频率变化(例如,P状态)的影响,但计数与时间戳计数器的频率相同.当核心未处于暂停状态而不处于TM stopclock状态时,此事件可以估计经过的时间.
因此,对于CPU绑定循环,我希望该值与从中读取的自由运行TSC值相同rdstc,因为它们应该仅针对暂停的循环指令或"TM stopclock state"是什么发散.
我使用以下循环测试它(整个独立演示在github上可用):
for (int i = 0; i < 100; i++) {
PFC_CNT cnt[7] = {};
int64_t start = nanos();
PFCSTART(cnt);
int64_t tsc =__rdtsc();
busy_loop(CALIBRATION_LOOPS);
PFCEND(cnt);
int64_t tsc_delta = __rdtsc() - tsc;
int64_t nanos_delta = nanos() - start;
printf(CPU_W "d" REF_W ".2f" TSC_W ".2f" MHZ_W ".2f" RAT_W ".6f\n",
sched_getcpu(),
1000.0 * cnt[PFC_FIXEDCNT_CPU_CLK_REF_TSC] / nanos_delta,
1000.0 * tsc_delta / nanos_delta,
1000.0 * CALIBRATION_LOOPS / nanos_delta,
1.0 * cnt[PFC_FIXEDCNT_CPU_CLK_REF_TSC]/tsc_delta); …Run Code Online (Sandbox Code Playgroud)