为何处理vector元素时用cmp+setg+test+je而非cmp+jle?
关于vector元素条件判断的汇编指令疑问
示例代码与汇编输出
C++测试程序
#include <iostream> #include <vector> using namespace std; int main() { vector<int> a = {7}; int b = 7; if (a[0] > 6) { cout << "a bigger than 6" << endl; } if (b > 6) { cout << "b bigger than 6" << endl; } return 0; }
对应汇编代码片段
针对vector元素a[0]的判断:
;SOURCE: if (a[0] > 6) { 1321: 48 8d 45 b0 lea -0x50(%rbp),%rax 1325: be 00 00 00 00 mov $0x0,%esi 132a: 48 89 c7 mov %rax,%rdi 132d: e8 82 02 00 00 call 15b4 <std::vector<int, std::allocator<int> >::operator[](unsigned long)> 1332: 8b 00 mov (%rax),%eax 1334: 83 f8 06 cmp $0x6,%eax 1337: 0f 9f c0 setg %al 133a: 84 c0 test %al,%al 133c: 74 2b je 1369 <main+0xc0>
针对普通变量b的判断:
;SOURCE: if (b > 6) { 1369: 83 7d ac 06 cmpl $0x6,-0x54(%rbp) 136d: 7e 2b jle 139a <main+0xf1>
问题描述
对比两段汇编可以发现:
- 普通变量
b的判断直接用cmpl+jle,当b <= 6时跳转跳过后续代码。 - vector元素
a[0]的判断在cmp之后,多了setg、test和je三步:先通过setg根据“大于”条件设置%al,再用test把零标志位设为%al的反值,最后je在零标志位被设置时跳转(即a[0]不大于6时)。
疑问点:
- 为什么处理vector元素时需要这额外的三步?
- 能不能像普通变量那样只用
cmp+jle实现? - 后者是否性能更快?
注:该现象与objdump无关,gdb -S输出也有相同指令。使用工具版本:g++ (Ubuntu 11.3.0-1ubuntu1~22.04.1) 11.3.0;GNU objdump (GNU Binutils for Ubuntu) 2.38
解答
核心原因:编译器无优化模式下的通用逻辑
这是因为你使用了-g编译(默认开启-O0无优化级别),编译器此时更注重代码的调试友好性,而非执行效率:
- 普通变量
b是直接可访问的栈内存值,编译器可以直接生成“比较+条件跳转”的简洁指令。 vector::operator[]返回的是int&类型的引用,编译器在无优化模式下,会先通过函数调用获取引用地址、解引用得到值,之后会套用“将比较结果转为布尔值,再判断布尔值是否为真”的通用模板,于是就出现了setg+test+je的组合。
能不能用cmp+jle实现?
完全可以!手动改写的等价汇编逻辑如下,和普通变量的判断逻辑完全一致:
; 替换后的精简逻辑 cmp $0x6,%eax jle 1369 <main+0xc0>
这说明编译器具备生成精简指令的能力,只是无优化模式下没有做这个简化。
性能差异
在-O0模式下,额外的三条指令确实会多占用几个CPU周期,但差异微乎其微。当开启优化(比如-O2或-O3)后,编译器会自动消除这些冗余指令,生成和普通变量判断完全一致的高效汇编——两条指令完成比较和跳转。
内容的提问来源于stack exchange,提问作者charlescochran
相关产品推荐
相关产品推荐

