左右移位运算符(<<和>>)已在C++中可用.但是,我无法找到如何执行循环移位或旋转操作.
如何执行"向左旋转"和"向右旋转"等操作?
在这里向右旋转两次
Initial --> 1000 0011 0100 0010
Run Code Online (Sandbox Code Playgroud)
应该导致:
Final --> 1010 0000 1101 0000
Run Code Online (Sandbox Code Playgroud)
一个例子会有所帮助.
(编者注:如果旋转计数为零,许多常见的表达方式在C中旋转会受到未定义的行为的影响,或者编译为不止一个旋转机器指令.这个问题的答案应记录最佳实践.)
我想检查boost::variant
在我的代码中应用的程序集输出,以便查看哪些中间调用被优化掉了.
当我编译以下示例(使用GCC 5.3 g++ -O3 -std=c++14 -S
)时,似乎编译器优化了所有内容并直接返回100:
(...)
main:
.LFB9320:
.cfi_startproc
movl $100, %eax
ret
.cfi_endproc
(...)
Run Code Online (Sandbox Code Playgroud)
#include <boost/variant.hpp>
struct Foo
{
int get() { return 100; }
};
struct Bar
{
int get() { return 999; }
};
using Variant = boost::variant<Foo, Bar>;
int run(Variant v)
{
return boost::apply_visitor([](auto& x){return x.get();}, v);
}
int main()
{
Foo f;
return run(f);
}
Run Code Online (Sandbox Code Playgroud)
但是,完整的程序集输出包含的内容远远超过上面的摘录,对我而言,它看起来永远不会被调用.有没有办法告诉GCC/clang删除所有"噪音"并输出程序运行时实际调用的内容?
完整装配输出:
.file "main1.cpp"
.section .rodata.str1.8,"aMS",@progbits,1
.align 8
.LC0:
.string "/opt/boost/include/boost/variant/detail/forced_return.hpp"
.section .rodata.str1.1,"aMS",@progbits,1
.LC1: …
Run Code Online (Sandbox Code Playgroud) LOOP(英特尔参考手动输入)递减ecx/rcx,然后如果非零则跳转.这很慢,但是英特尔不能廉价地把它变得很快吗? dec/jnz
已经将宏观融合成 Sandybridge家族的一个 uop; 唯一的区别是设置标志.
loop
关于各种微体系结构,来自Agner Fog的说明表:
Bulldozer-family/Ryzen:1 m-op(与宏观融合测试和分支相同,或者jecxz
)
P4:4次(相同jecxz
)
loope
/ loopne
).吞吐量= 4c(loop
)或7c(loope/ne
).loope
/ loopne
). 吞吐量=每5个循环一个,这是将循环计数器保留在内存中的瓶颈!jecxz
只有2 uops,吞吐量与普通吞吐量相同jcc
难道解码器不能像lea rcx, [rcx-1]
/ 那样解码jrcxz
吗?这将是3 uops.至少那是没有地址大小前缀的情况,否则它必须使用ecx
和截断RIP
,EIP
如果跳转; 也许奇怪的地址大小选择控制减量的宽度解释了许多uops?
或者更好,只需将其解码为不设置标志的融合分支和分支? dec ecx …
enter
和之间的区别是什么?
push ebp
mov ebp, esp
sub esp, imm
Run Code Online (Sandbox Code Playgroud)
说明?是否存在性能差异?如果是这样,哪个更快,为什么编译器总是使用后者呢?
以相若方式将leave
和
mov esp, ebp
pop ebp
Run Code Online (Sandbox Code Playgroud)
说明.
0x0000000000400553 <main+59>: mov -0x4(%rbp),%eax
0x0000000000400556 <main+62>: cltq
0x0000000000400558 <main+64>: shl $0x3,%rax
0x000000000040055c <main+68>: mov %rax,%rdx
Run Code Online (Sandbox Code Playgroud)
事实上,我的程序很简单:
5 int main(int argc, char *argv[]) {
6 int i = 0;
7 while(environ[i]) {
8 printf("%s\n", environ[i++]);
9 }
10 return 0;
Run Code Online (Sandbox Code Playgroud)
但是程序集输出很长:
Dump of assembler code for function main:
0x0000000000400518 <main+0>: push %rbp
0x0000000000400519 <main+1>: mov %rsp,%rbp
0x000000000040051c <main+4>: sub $0x20,%rsp
0x0000000000400520 <main+8>: mov %edi,-0x14(%rbp)
0x0000000000400523 <main+11>: mov %rsi,-0x20(%rbp)
0x0000000000400527 <main+15>: movl $0x0,-0x4(%rbp)
0x000000000040052e <main+22>: jmp 0x400553 <main+59>
0x0000000000400530 <main+24>: mov -0x4(%rbp),%eax …
Run Code Online (Sandbox Code Playgroud) 来自Ira Baxter回答,为什么INC和DEC指令不会影响进位标志(CF)?
大多数情况下,我远离
INC
而DEC
现在,因为他们做的部分条件代码更新,这样就可以在管道中引起滑稽的摊位,和ADD
/SUB
没有.因此,无关紧要(大多数地方),我使用ADD
/SUB
避免失速.我使用INC
/DEC
仅在保持代码较小的情况下,例如,适合高速缓存行,其中一个或两个指令的大小产生足够的差异.这可能是毫无意义的纳米[字面意思!] - 优化,但我在编码习惯上相当老派.
我想问一下为什么它会导致管道中的停顿,而添加不会?毕竟,无论是ADD
和INC
更新标志寄存器.唯一的区别是INC
不更新CF
.但为什么重要呢?
我试图了解地址计算指令的工作原理,尤其是leaq
命令.然后当我看到leaq
用于进行算术运算的例子时,我感到困惑.例如,以下C代码,
long m12(long x) {
return x*12;
}
Run Code Online (Sandbox Code Playgroud)
在组装中
leaq (%rdi, %rdi, 2), %rax
salq $2, $rax
Run Code Online (Sandbox Code Playgroud)
如果我的理解是正确的,那么leaq应该移动任何(%rdi, %rdi, 2)
应该2*%rdi+%rdi
评估的地址%rax
.我感到困惑的是,因为值x存储%rdi
在内,这只是内存地址,为什么%rdi乘以3然后左移这个内存地址 2等于x乘以12?是不是当我们%rdi
用3时,我们跳到另一个没有值x的内存地址?
我不知道这段代码究竟是什么:
int rdtsc(){
__asm__ __volatile__("rdtsc");
Run Code Online (Sandbox Code Playgroud)
拜托,有人可以解释一下吗?为什么"rdtsc"?