为什么操作int 类型的指令要快于操作short 的,却可能慢于操作char 的

11 篇文章 0 订阅
6 篇文章 0 订阅

唔,写这篇博客的原因是给人解释了半天,觉得不复制出来整理下做成一篇blog 可惜了。。。。


1. 为什么对int 变量的操作比操作short 变量的指令快

如果你看过操作系统引导部分的资料的话,你就明白在描述符中D/B 位决定了CPU 指令的默认地址,如果置位则表示是32 位地址,复位就是8/16 位地址(我没看过64 位保护模式的资料原谅我吧)。一般来说我们的机器都是32 位的,所以32 位的机器只能操作32 位的地址。
但是这里有一个0x66 指令前缀(Prefixes ),这个前缀的作用就是切换默认操作数大小,所以当你在32 位系统上去操作一个8/16 位的变量的时候,那么就会在机器码前面增加一个0x66 前缀,这是就会多话费一个CPU 周期去执行。

但是对于char 呢?操作int 的指令比操作short 的快,并不代表操作int 的指令都要比操作长度小于sizeof (int) 变量的指令要快。

事实上操作int 的语句确实比操作short 的快,但是至少和操作char 的一样快。


2. 为什么操作int 类型的指令要快于操作short 的,却可能慢于操作char 的

程序:

short aShort = 0;
int aInt = 0;
objdump 之后:
4004b4: 66 c7 45 fe 00 00 movw $0x0,-0x2(%rbp)
4004ba: c7 45 f8 00 00 00 00 movl $0x0,-0x8(%rbp) 
程序:
char aChar = 0;
int aInt = 0;
objdump 之后:
4004b4: c6 45 ff 00 movb $0x0,-0x1(%rbp)
4004b8: c7 45 f8 00 00 00 00 movl $0x0,-0x8(%rbp) 
对于short 类型的确实加了x66 前缀了,但是char 类型的mov 中就没加,差了下指令表:
MOVmi 1100011w oo000www disp data 
蛋疼的是objdump 输出的att 格式的汇编,不顺眼写成MOVim 也行。
对于char 的操作,汇编器选择了直接复位w 位,直接换成对字节的操作了。
short 则不是,w 是置位的,对字操作,但是因为D/B 的关系,默认是操作双字,所以才加的x66 前缀改变了默认操作地址大小为字。

所以操作int 的语句确实比操作short 的快,但是至少和操作char 的一样快。


3. 对于0x66 Prefix 如何起作用的的进一步说明

对于0x66 Prefix,可以用一条x86_64 架构上的机器码手动反汇编过程来说明:

4004b4: 66 c7 45 fe 00 00 
4004b4: 66 c7 45 fe 00 00
66:Operand-Size Override prefix,用来更改默认Operand-Size(Descriptor 上的D/B 位)。
c7:Opcode。查找指令表110001(0xc7 的高6 位,因为最后2 位为后缀),发现有多条指令符合。再看ModR/M 位45,其中4、5、6 位为000,增加这个条件发现只有一条MOVmi 指令符合(MOVmi     1100011w     oo000www     disp     data),因为指令的寻址方式是立即寻址,所以第2 位应该是s 位,该位置位说明立即数位8 位但要求扩展成16 位(下面会解释为什么会这样扩展)。其中第1 位w 置位,说明对字操作(16 位)。所以0xc7 是一条mov 指令,将大小为字的立即数送到一块内存。但是对于CPU 而言这条指令有Group 属性,需要配合ModR/M.reg 来配合定位(也就是0xc7 并不是完整的opcode,当然人工也没法确定,之前已经见识到了)。
45:0b0100_0101,ModR/M 位,为了直观写作0b01_000_101
ModR/M.mod = 0b01:寻址模式base+disp8。
ModR/M.reg = 0b000:MOVmi 中对应的ModR/M.reg 位是oo000www,由ModR/M.reg 和Opcode 共同确定了指令MOVmi。
ModR/M.r/m = 0b101:结合ModR/M.mod = 0b01,查表可得rbp+disp8
fedisp8,8 位偏移量,-2
00 00:imm8,8 位立即数(实际上是16 位):0。之所以实际上是16 位,原因是该指令的目的操作数是16 位的。之所以目的操作数是16 位的,原因是原本目的操作数是8 位的,但是因为D/B 位被置位,目的操作数大小变为32 位,这里又加了0x66 前缀,所以目的操作数大小变为16 位。
结合0x66 前缀,操作数由双字变为字,对应指令就是

movw $0x0000, -0x2(%rbp)

这就是0x66 Prefix 起作用的一个例子,事实上手动反汇编的结果和objdump 之后得到的结果完全一致。


事实上假设你的编译环境是16 位以上的,那么立即数的位数如果被要求如下(至于怎么出来的自行反汇编):

立即数位数决定立即数位数的原因决定目的操作数位数的原因
8w 位复位指令中存在8 位立即数D/B 位复位,无0x66 Prefix
16w 位置位,指令中存在16 位立即数D/B 位置位,有0x66 Prefix
32w 位置位指令中存在32 位立即数D/B 位置位,无0x66 Prefix
64w 位置位指令中存在32 位立即数D/B 位置位,有0x48 Prefix

没了,就是这样。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值