现代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 必须 …我已经阅读了各种优化指南,声称ADD 1比在x86中使用INC更快.这是真的吗?
我试图找出是否有任何想法来了解我的C代码正在运行的系统的CPU频率.
为了澄清,我正在寻找一个抽象的解决方案(一个不会与特定架构或操作系统绑定的解决方案),它可以让我了解我的代码正在执行的计算机的运行频率.我不需要准确,但我想进入球场(即我有一个2.2GHz处理器,我希望能够在我的程序中告诉我我在几百之内)那个MHz)
有没有人有想法使用标准C代码?
在x86汇编程序中,假设你有
为什么需要索引和基指针寻址模式?据我所知,每个都可以用循环代替.
间接模式似乎也没有太大用处,因为您可以简单地使用直接模式来引用内存地址.首先访问寄存器的目的是什么,然后包含指向存储器地址的指针?
简而言之,哪些寻址方式确实是必要的?
我不知道任何真正的汇编,但可以读取GCC -S输出来评估给定C代码的实际成本.
这个问题并不是关于分析和基准的问题,而是教育问题.我需要有人来解释为什么[1]片段不比第二片段快.
嗯,过去常常这样想:"是的,像MUL这样的操作非常昂贵,但是如果一个组件比另一个组件大X倍,它应该更慢".
在我遇到这两个之前,这是真的:
unsigned char bytes[4] = {0, 0, 0, 5};
// 1
int32_t val = *((int32_t*)bytes);
/* produces:
leaq -16(%rbp), %rax
movl (%rax), %eax
movl %eax, -4(%rbp)
movl $0, %eax
*/
// 2
val = bytes[3] |
(bytes[2] << 8) |
(bytes[1] << 16) |
(bytes[0] << 24);
/* produces:
movzbl -13(%rbp), %eax
movzbl %al, %eax
movzbl -14(%rbp), %edx
movzbl %dl, %edx
sall $8, %edx
orl %eax, %edx
movzbl -15(%rbp), %eax
movzbl %al, %eax
sall $16, …Run Code Online (Sandbox Code Playgroud)