WEB开发网
开发学院软件开发汇编语言 简明x86汇编语言教程(7) 阅读

简明x86汇编语言教程(7)

 2010-01-10 09:37:35 来源:WEB开发网   
核心提示:聪明的读者马上就会指出,我们不是可以在定义do_something()的时候加上inline修饰符,简明x86汇编语言教程(7)(4),让它在本地展开吗?没错,本地展开以增加代码量为代价换取性能,此时另一个人去砍柴,第三个人打水,但这只是问题的一半,编译器尽管完成了本地展开

聪明的读者马上就会指出,我们不是可以在定义do_something()的时候加上inline修饰符,让它在本地展开吗?没错,本地展开以增加代码量为代价换取性能,但这只是问题的一半。编译器尽管完成了本地展开,但它仍然需要做许多额外的工作。因为寄存器只有那么有限的几个,而我们却有这么多的循环变量。

把四个变量按照它们在循环中使用的频率排序,并决定在do_something()块中的优先顺序(放入寄存器中的优先顺序)是一个解决方案。很明显,我们可以按照l, k, j, i的顺序(从高到低,因为l将被进行1000*1000*1000*1000次运算!)来排列,但在实际的问题中,事情往往没有这么简单,因为你不知道do_something()中做的到底是什么。而且,凭什么就以for(l=0; l<1000; l++)作为优化的分界点呢?如果do_something()中还有循环怎么办?

如此复杂的计算问题交给计算机来做通常会有比较满意的结果。一般说来,编译器能够对程序中变量的使用进行更全面地估计,因此,它分配寄存器的结果有时虽然让人费解,但却是最优的(因为计算机能够进行大量的重复计算,并找到最好的方法;而人做这件事相对来讲比较困难)。

编译器在许多时候能够作出相当让人满意的结果。考虑以下的代码:

int a=0;

for(int i=1; i<10; i++)
 for(int j=1; j<100; j++){
  a += (i*j);
 }

让我们把它变为某种形式的中间代码:

00: 0 -> a
01: 1 -> i
02: 1 -> j
03: i*j -> t
04: a+t -> a
05: j+1 -> j
06: evaluate j < 100
07: TRUE? goto 03
08: i+1 -> i
09: evaluate i < 10
10: TRUE? goto 02
11: [继续执行程序的其余部分]

程序中执行强度最大的无疑是03到05这一段,涉及的需要写入的变量包括a, j;需要读出的变量是i。不过,最终的编译结果大大出乎我们的意料。下面是某种优化模式下Visual C++ 6.0编译器生成的代码(我做了一些修改):

xor eax, eax        ; a=0(eax: a)
mov edx, 1         ; i=1(edx: i)
push esi          ; 保存esi(最后要恢复,esi作为代替j的那个循环变量)
nexti:
mov ecx, edx        ; [t=i]
mov esi, 999        ; esi=999: 此处修改了原程序的语义,但仍为1000次循环。
nextj:
add eax, ecx        ; [a+=t]
add ecx, edx        ; [t+=i]
dec esi          ; j--
jne SHORT nextj      ; jne 等价于 jnz. [如果还需要,则再次循环]
inc edx          ; i++
cmp edx, 10         ; i与10比较
jl SHORT nexti       ; i < 10, 再次循环
pop esi          ; 恢复esi

这段代码可能有些令人费解。主要是因为它不仅使用了大量寄存器,而且还包括了5.2节中曾提到的子表达式提取技术。表面上看,多引入的那个变量(t)增加了计算时间,但要注意,这个t不仅不会降低程序的执行效率,相反还会让它变得更快!因为同样得到了计算结果(本质上,i*j即是第j次累加i的值),但这个结果不仅用到了上次运算的结果,而且还省去了乘法(很显然计算机计算加法要比计算乘法快)。

这里可能会有人问,为什么要从999循环到0,而不是按照程序中写的那样从0循环到999呢?这个问题和汇编语言中的取址有关。在下两节中我将提到这方面的内容。

5.4 x86体系结构上的并行最大化和指令封包

考虑这样的问题,我和两个同伴现在在山里,远处有一口井,我们带着一口锅,身边是树林;身上的饮用水已经喝光了,此处允许砍柴和使用明火(当然我们不想引起火灾:),需要烧一锅水,应该怎么样呢?

一种方案是,三个人一起搭灶,一起砍柴,一起打水,一起把水烧开。

另一种方案是,一个人搭灶,此时另一个人去砍柴,第三个人打水,然后把水烧开。

这两种方案画出图来是这样:

上一页  1 2 3 4 5 6  下一页

Tags:简明 汇编语言 教程

编辑录入:爽爽 [复制链接] [打 印]
赞助商链接