VC目标代码的阅读理解

来源:互联网 发布:mac 上给iphone传照片 编辑:程序博客网 时间:2024/05/20 17:23
 

第5章 VC目标代码的阅读理解
5.1 汇编语言形式的目标代码

5.2 C语言部分编译的解析

5.3 C++部分功能实现细节

5.4 目标程序的优化

5.5 C库函数分析

5.6 C程序的目标代码分析

 
 

5.4 目标程序的优化

5.4.1 关于程序优化
5.4.2 使大小最小化
5.4.3 使速度最大化
5.4.4 内存地址对齐   
 

5.4.1 关于程序优化
.关于优化


.
优化
就是提高目标程序的效率,体现在“时间”和“空
间”两个方面。在时间方面是执行速度最大化,在空间
方面是占用空间最小化

.

时间和空间两个方面的效率同时得到提高是最好。

时间
和空间
常常矛盾。
空间
换时间
,或者
时间换
空间

.
优化的
关键是算法优化

.
从汇编语言的角度看,
主要指利用恰当的指令。
.优化就是提高目标程序的效率,体现在“时间”和“空
间”两个方面。在时间方面是执行速度最大化,在空间
方面是占用空间最小化。
.在时间和空间两个方面的效率同时得到提高是最好。但
时间和空间常常矛盾。空间换时间,或者时间换空间。
.优化的关键是算法优化。
.从汇编语言的角度看,主要指利用恰当的指令。


这里

汇编语言的角度介绍目标程序的优化

假设:算法
已经优化,
或算法
已经确定。
这里从汇编语言的角度介绍目标程序的优化,
假设:算法已经优化,或算法已经确定。   
 

.关于优化


.
有多种不同方法实现同一功能
MOV
EBX, 0 ;5
字节
XOR
EBX, EBX ;2
字节
SUB
EBX, EBX ;2
字节
AND
EBX, 0 ;3
字节
.有多种不同方法实现同一功能
MOV EBX, 0 ;5字节
XOR EBX, EBX ;2字节
SUB EBX, EBX ;2字节
AND EBX, 0 ;3字节



寄存器
EBX
清寄存器EBX
采用哪条指令比较好,与具体的场合有关
采用哪条指令比较好,与具体的场合有关
5.4.1 关于程序优化   
 

.关于优化


.
一般而言,采用相同的算法,由汇编语言编写的
程序效
率最高
。因为汇编语言更能充分发挥机器的特性。但是,
用汇编语言编程的
工作效率却是最低

.
实际上
,现在高级语言的编译器功能很强劲,
由编译器
生成的目标代码
已经“
足够好
”,或者说好过普通汇编
语言程序员编写的程序

.一般而言,采用相同的算法,由汇编语言编写的程序效
率最高。因为汇编语言更能充分发挥机器的特性。但是,
用汇编语言编程的工作效率却是最低。
.实际上,现在高级语言的编译器功能很强劲,由编译器
生成的目标代码已经“足够好”,或者说好过普通汇编
语言程序员编写的程序。


某种意义上,这也是越来越少使用汇编语言
编写源程序的原因之一。
某种意义上,这也是越来越少使用汇编语言
编写源程序的原因之一。
代价!
代价!
5.4.1 关于程序优化   
 

.演示函数cf520


unsigned
int
cf520(
unsigned char
n)
{
unsigned
int
x, y, sum;
x
= n * 8;
y
= n / 8;
sum
= x + y;
return
sum;
}
unsigned int cf520(unsigned char n)
{
unsigned int x, y, sum;
x = n * 8;
y = n / 8;
sum = x + y;
return sum;
}
演示
VC
编译器的优化工作
演示VC编译器的优化工作
5.4.1 关于程序优化   
 

.演示函数cf520的目标代码


_
n$ = 8
cf520
PROC
push
ebp
mov
ebp
,
esp
movzx
eax
, BYTE PTR _n$[
ebp
]
;x = n
mov
ecx
,
eax
;y = x
shr
ecx
, 3
;y
= n/8
lea
eax
, DWORD PTR [
ecx+eax
*8]
;sum
= y + x*8
pop
ebp
ret
cf520
ENDP
_n$ = 8
cf520 PROC
push ebp
mov ebp, esp
movzx eax, BYTE PTR _n$[ebp] ;x = n
mov ecx, eax ;y = x
shr ecx, 3 ;y = n/8
lea eax, DWORD PTR [ecx+eax*8] ;sum = y + x*8
pop ebp
ret
cf520 ENDP
cf520(unsigned char n)
自动类型转换
乘法、加法,合并进行
移位指令代替除法指令
速度最大化
大小最小化
速度最大化
大小最小化
5.4.1 关于程序优化
演示
VC
编译器的优化工作
演示VC编译器的优化工作

 
 

.演示函数cf520的目标代码(另一种)


演示
VC
编译器的优化工作
演示VC编译器的优化工作
cf520
PROC
movzx
eax
, BYTE PTR
[
esp+4
]
;x = n
mov
ecx
,
eax
;y = x
shr
ecx
, 3
;y
= n/8
lea
eax
, DWORD PTR [
ecx+eax
*8]
;sum
= y + x*8
ret
cf520
ENDP
cf520 PROC
movzx eax, BYTE PTR [esp+4] ;x = n
mov ecx, eax ;y = x
shr ecx, 3 ;y = n/8
lea eax, DWORD PTR [ecx+eax*8] ;sum = y + x*8
ret
cf520 ENDP
不建立堆栈框架
不建立堆栈框架
演示

演示!
VC2010
的编译器相当
“聪明”。
不仅
用寄存器作为局部变量,而且还充分
利用
IA
-
32
系列处理器的相关指令

这样
的目标代码在“时空”两个方面都是高效的。
VC2010的编译器相当“聪明”。
不仅用寄存器作为局部变量,而且还充分利用
IA-32系列处理器的相关指令。
这样的目标代码在“时空”两个方面都是高效的。
5.4.1 关于程序优化

速度最大化
大小最小化
速度最大化

大小最小化

 
 

.关于优化


.

寄存器作为局部变量能大大提高
效率
.
寄存器
位于
CPU
内部,存取寄存器速度最快

.
表示
寄存器的编码比较短,相应指令的长度也就比
较短

.
优化
与处理器关系
密切
.
优化依赖于处理器。
.用寄存器作为局部变量能大大提高效率
.寄存器位于CPU内部,存取寄存器速度最快;
.表示寄存器的编码比较短,相应指令的长度也就比
较短。


.优化与处理器关系密切
.优化依赖于处理器。




5.4.1 关于程序优化   
 

5.4.2 使大小最小化
.关于大小最小化


.
“使
大小最小化

,就是
使得目标程序长度最短,
也即
把组成目标程序的
所有指令长度相加最小

.
IA
-
32
系列处理器属于复杂指令系统的处理器,其指令
长度少则
1
字节,多则超过
10
字节

.
大小最小化的方法
.
采用
寄存器作为
变量
.
采用
长度较短的指令或者指令片段
.“使大小最小化”,就是使得目标程序长度最短,也即
把组成目标程序的所有指令长度相加最小。
.IA-32系列处理器属于复杂指令系统的处理器,其指令
长度少则1字节,多则超过10字节。
.大小最小化的方法
.采用寄存器作为变量
.采用长度较短的指令或者指令片段




 
 

.演示函数cf37


int
cf37(
int n
)
{
int
i, sum;
sum
= 0;
for
( i=1; i <= n; i++ )
sum
+= i;
return
sum;
}
int cf37(int n)
{
int i, sum;
sum = 0;
for ( i=1; i <= n; i++ )
sum += i;
return sum;
}
计算
1

n
之间的整数之

计算1到n之间的整数之和
5.4.2 使大小最小化
演示

演示!   
 

.演示函数cf37的目标代码(大小最小化)


 
push ebp
mov
ebp,
esp
xor
ecx,
ecx
inc ecx
xor
eax,
eax
cmp
DWORD PTR _n$[ebp],
ecx
jl
SHORT
LN1@cf37
LL3@cf37:
add
eax,
ecx
inc ecx
cmp
ecx, DWORD PTR _n$[ebp
]
jle
SHORT
LL3@cf37
LN1@cf37
:
pop ebp
ret
push ebp
mov ebp, esp
xor ecx, ecx
inc ecx
xor eax, eax
cmp DWORD PTR _n$[ebp], ecx
jl SHORT LN1@cf37
LL3@cf37:
add eax, ecx
inc ecx
cmp ecx, DWORD PTR _n$[ebp]
jle SHORT LL3@cf37
LN1@cf37:
pop ebp
ret
计算
1

n
之间的整数之

计算1到n之间的整数之和
5.4.2 使大小最小化
寄存器作为变量
变量
sum

EAX
表示
变量
i

ECX
表示
寄存器作为变量
变量sum由EAX表示
变量i由ECX表示
_n$ = 8
_n$ = 8   
 

.演示函数cf37的目标代码(大小最小化)


 
push ebp
mov
ebp,
esp
xor
ecx, ecx
;
33
C9
inc
ecx
;
41
xor
eax, eax
;
33
C0
cmp
DWORD PTR _n$[ebp],
ecx
jl
SHORT
LN1@cf37
LL3@cf37:
add
eax, ecx
;
03 C1
inc
ecx
;
41
cmp
ecx, DWORD PTR _n$[ebp
]
jle
SHORT
LL3@cf37
LN1@cf37
:
pop ebp
ret
push ebp
mov ebp, esp
xor ecx, ecx ;33 C9
inc ecx ;41
xor eax, eax ;33 C0
cmp DWORD PTR _n$[ebp], ecx
jl SHORT LN1@cf37
LL3@cf37:
add eax, ecx ;03 C1
inc ecx ;41
cmp ecx, DWORD PTR _n$[ebp]
jle SHORT LL3@cf37
LN1@cf37:
pop ebp
ret
计算
1

n
之间的整数之

计算1到n之间的整数之和
5.4.2 使大小最小化
mov
ecx,1
mov ecx,1
mov
eax,0
mov eax,0
add ecx,1
add ecx,1
利用长度较短指令
或者代码片段
利用长度较短指令

或者代码片段

 
 

.演示函数cf521


int
cf521(
unsigned
int
year)
{
int
leap = 0;
if
(((year % 4 == 0
) && (
year %
100!=0
)) || (year
% 400==0
))
leap
= 1;
return
leap;
}
int cf521(unsigned int year)
{
int leap = 0;
if (((year % 4 == 0) && (year % 100!=0)) || (year % 400==0))
leap = 1;
return leap;
}
根据年份
判断某年
是否为闰年
根据年份判断某年是否为闰年
5.4.2 使大小最小化   
 

.演示函数cf521的目标代码(大小最小化)


_
year$ =
8
cf521
PROC
push
ebp
mov
ebp
,
esp
xor
ecx
,
ecx
test
BYTE PTR _year$[
ebp
],
3
push
esi
jne
SHORT
LN1@cf521
mov
eax
, DWORD PTR _year$[
ebp
] ;EAX = year
push 100
xor
edx
,
edx
pop
esi
div
esi
test
edx
,
edx
jne
SHORT
LN2@cf521
_year$ = 8
cf521 PROC
push ebp
mov ebp, esp
xor ecx, ecx
test BYTE PTR _year$[ebp], 3
push esi
jne SHORT LN1@cf521
mov eax, DWORD PTR _year$[ebp] ;EAX = year
push 100
xor edx, edx
pop esi
div esi
test edx, edx
jne SHORT LN2@cf521
year被4整除吗?

year被100整除吗?
5.4.2 使大小最小化
寄存器作为变量
变量
leap

ECX
表示
寄存器作为变量
变量leap由ECX表示
利用长度较短指令
或者代码片段
利用长度较短指令
或者代码片段
mov esi, 100

 
 

.演示函数cf521的目标代码(续)


LN1@cf521
:
mov
eax
, DWORD PTR _year$[
ebp
] ;EAX = year
xor
edx
,
edx
mov
esi
,
400
div
esi
test
edx
,
edx
jne
SHORT
LN3@cf521
LN2@cf521
:
xor
ecx
,
ecx
inc
ecx
LN3@cf521
:
mov
eax
,
ecx
pop
esi
pop
ebp
ret
LN1@cf521:

 mov eax, DWORD PTR _year$[ebp] ;EAX = year

 xor edx, edx

 mov esi, 400

 div esi

 test edx, edx

 jne SHORT LN3@cf521

LN2@cf521:

 xor ecx, ecx

 inc ecx

LN3@cf521:

 mov eax, ecx

 pop esi

 pop ebp

 ret

大小最小化
大小最小化
leap=1
year被400整除吗?
优化依赖
处理器!
优化依赖
处理器!
5.4.2 使大小最小化

 
 

5.4.3 使速度最大化
.关于速度最大化


.
“使速度最大化”就是使得执行目标程序的速度最快

.
影响
目标程序执行速度的
因素

.
指令
执行的时钟

.
高速缓存

cache
)的
命中
.
指令
执行
流水线及其配对
.
等等
.“使速度最大化”就是使得执行目标程序的速度最快。
.影响目标程序执行速度的因素:
.指令执行的时钟数
.高速缓存(cache)的命中
.指令执行流水线及其配对
.等等




 
 

5.4.3 使速度最大化
.关于速度最大化


.
速度最大化方
法:
.
避免时钟数多的
指令
.
减少
转移指令
.
减少
循环执行
次数
.
存储器地址对齐
.
等等
.速度最大化方法:
.避免时钟数多的指令
.减少转移指令
.减少循环执行次数
.存储器地址对齐
.等等




除法指令时钟数多
除法指令时钟数多

影响执行流水线
影响执行流水线   
 

.演示函数cf521


int
cf521(
unsigned
int
year)
{
int
leap = 0;
if
(((year % 4 == 0
) && (
year %
100!=0
)) || (year
% 400==0
))
leap
= 1;
return
leap;
}
int cf521(unsigned int year)
{
int leap = 0;
if (((year % 4 == 0) && (year % 100!=0)) || (year % 400==0))
leap = 1;
return leap;
}
根据年份
判断某年
是否为闰年
根据年份判断某年是否为闰年
5.4.3 使速度最大化
分析
速度最大化
的目标代码
分析速度最大化
的目标代码   
 

.演示函数cf521的目标代码(速度最大化)


_
year$ = 8
cf521
PROC
push
ebp
mov
ebp
,
esp
mov
ecx
, DWORD PTR _year$[
ebp
] ;ECX
作为参数
year
push
esi
xor
esi
,
esi
;
ESI
作为变量
leap
test
cl,
3
jne
SHORT LN1@cf521
mov
eax
,
1374389535
mul
ecx
shr
edx
,
5
imul
edx
, 100
mov
eax
,
ecx
sub
eax
,
edx
jne
SHORT LN2@cf521 ;
_year$ = 8
cf521 PROC
push ebp
mov ebp, esp
mov ecx, DWORD PTR _year$[ebp] ;ECX作为参数year
push esi
xor esi, esi ;ESI作为变量leap
test cl, 3
jne SHORT LN1@cf521
mov eax, 1374389535
mul ecx
shr edx, 5
imul edx, 100
mov eax, ecx
sub eax, edx
jne SHORT LN2@cf521 ;
EDX = year / 100
year % 100
year % 100
EAX = year-(year/100)*100
5.4.3 使速度最大化
避免除法指令
避免除法指令
(year % 4)==0 ?   
 

.演示函数cf521的目标代码(速度最大化)


LN1@cf521
:
mov
eax
,
1374389535
mul
ecx
shr
edx
,
7
imul
edx
, 400
sub
ecx
,
edx
jne
SHORT
LN6@cf521
LN2@cf521
:
mov
eax
,
1
pop
esi
pop
ebp
ret
LN6@cf521
:
mov
eax
,
esi
pop
esi
pop
ebp
ret
LN1@cf521:
mov eax, 1374389535
mul ecx
shr edx, 7
imul edx, 400
sub ecx, edx
jne SHORT LN6@cf521
LN2@cf521:
mov eax, 1
pop esi
pop ebp
ret
LN6@cf521:
mov eax, esi
pop esi
pop ebp
ret
leap = 1
year % 400
year % 400
ECX = year-(year/400)*400
leap = 0
5.4.3 使速度最大化
避免除法指令
避免除法指令
减少转移指令
减少转移指令

 
 

.演示函数cf37


int
cf37(int n)
{
int
i, sum;
sum
= 0;
for
( i=1; i <= n; i++ )
sum += i;
return
sum;
}
int cf37(int n)
{
int i, sum;
sum = 0;
for ( i=1; i <= n; i++ )
sum += i;
return sum;
}
5.4.3 使速度最大化
计算
1

n
之间的整数之

计算1到n之间的整数之和
作为演示,

3.1.3
介绍过禁止优化的目标代码,

5.4.2
介绍过大小最小化的目标代码,
现在观察速度最大化的目标代码
作为演示,
在3.1.3介绍过禁止优化的目标代码,
在5.4.2介绍过大小最小化的目标代码,
现在观察速度最大化的目标代码   
 

.演示函数cf37的目标代码(速度最大化)


_
n$ = 8
cf37
PROC
push
ebp
mov
ebp, esp
push ebx
push edi
;
mov
edi, DWORD PTR _n$[ebp]
;EDI
存放
n
xor
edx, edx
;EDX
作为
sum1


0
xor
ecx, ecx
;ECX
作为
sum2


0
xor
ebx, ebx
;EBX
作为“零头”
lea
eax, DWORD PTR [edx+1]
;EAX
作为
i

i=1
cmp
edi, 2
;
循环次数
n
太小?
jl
SHORT LC9@cf37
;
确实太小,则

_n$ = 8

cf37 PROC

 push ebp

 mov ebp, esp

 push ebx

 push edi

 ;

 mov edi, DWORD PTR _n$[ebp] ;EDI存放n

 xor edx, edx ;EDX作为sum1,清0

 xor ecx, ecx ;ECX作为sum2,清0

 xor ebx, ebx ;EBX作为“零头”

 lea eax, DWORD PTR [edx+1] ;EAX作为i,i=1

 cmp edi, 2 ;循环次数n太小?

 jl SHORT LC9@cf37 ;确实太小,则转

5.4.3 使速度最大化
寄存器作为变量
寄存器作为变量
edi 作为参数 n
edx 作为 sum1
ecx 作为 sum2
ebx 作为“零头”
eax 作为循环变量 i   
 

.演示函数cf37的目标代码(续)


 
push esi
lea
esi, DWORD PTR [edi
-
1] ;ESI
相当于(
n
-
1

npad 6
LL10@cf37
:
add
edx, eax
;sum1 += i
lea
ecx, DWORD PTR [ecx+eax+1]
;sum2 += (i+1)
add eax, 2
;i = i+2
cmp eax, esi
;i <= n
-
1 ?
jle SHORT LL10@cf37
;
是,继续循环
pop esi
LC9@cf37
:
cmp
eax, edi ;i > n

jg
SHORT LN8@cf37 ;
是,跳转
mov
ebx, eax ;
准备“零头”
LN8@cf37:
push esi
lea esi, DWORD PTR [edi-1] ;ESI相当于(n-1)
npad 6
LL10@cf37:
add edx, eax ;sum1 += i
lea ecx, DWORD PTR [ecx+eax+1] ;sum2 += (i+1)
add eax, 2 ;i = i+2
cmp eax, esi ;i <= n-1 ?
jle SHORT LL10@cf37 ;是,继续循环
pop esi
LC9@cf37:
cmp eax, edi ;i > n ?
jg SHORT LN8@cf37 ;是,跳转
mov ebx, eax ;准备“零头”
LN8@cf37:
伪指令!
伪指令!为了地址对齐
重复累加操作
循环体内:重复累加操作

5.4.3 使速度最大化
减少循环
减少循环
地址对齐
地址对齐   
 

.演示函数cf37的目标代码(续二)


LN8@cf37
:
lea
eax, DWORD PTR [ecx+edx] ;EAX = sum1+sum2
pop edi
add
eax, ebx
;
加上可能存在的“零头”
pop ebx
pop
ebp
ret
cf37
ENDP
LN8@cf37:
lea eax, DWORD PTR [ecx+edx] ;EAX = sum1+sum2
pop edi
add eax, ebx ;加上可能存在的“零头”
pop ebx
pop ebp
ret
cf37 ENDP
5.4.3 使速度最大化   
 

5.4.4 内存地址对齐

.关于内存地址对齐


.
内存
地址对齐
指,访问存储单元的地址是存储单元尺寸
(字节数)的倍数。例如,访问某双字存储单元,那么
当地址是
4
的倍数时,就是对齐的

.
在采用
IA
-
32
系列处理器的系统中,存储器的读写地址
必须是
4
的倍数。如果不是双字地址对齐,那么将自动
分解为两次读写操作,导致多读写操作一次

.内存地址对齐指,访问存储单元的地址是存储单元尺寸
(字节数)的倍数。例如,访问某双字存储单元,那么
当地址是4的倍数时,就是对齐的。
.在采用IA-32系列处理器的系统中,存储器的读写地址
必须是4的倍数。如果不是双字地址对齐,那么将自动
分解为两次读写操作,导致多读写操作一次。


 
 

.关于内存地址对齐


.
示例

MOV
EAX, [0000137FH] ;
地址不对齐
MOV
EAX, [00001380H] ;
地址
对齐
第一
条指令读存储器的操作分解为


地址为
[0000137CH]
的双

读地址为
[
00001380H]
的双

形成
地址为
[0000137FH]
的双

.示例:
MOV EAX, [0000137FH] ;地址不对齐
MOV EAX, [00001380H] ;地址对齐
第一条指令读存储器的操作分解为:
读地址为[0000137CH]的双字
读地址为[00001380H]的双字
形成地址为[0000137FH]的双字


5.4.4 内存地址对齐   
 

.关于内存地址对齐


.
示例
.示例


00001384000013830000138200001381000013800000137F0000137E0000137D0000137C. . . . . .
. . . . . .00001384000013830000138200001381000013800000137F0000137E0000137D0000137C. . . . . .
. . . . . .
(a)读[0000137F]双字(b)读[00001380]双字
5.4.4 内存地址对齐

MOV EAX,[0000137F]
MOV EAX,[0000137F] MOV EAX,[00001380]
MOV EAX,[00001380]   

原创粉丝点击