本文由zengl.com站长对
http://pan.baidu.com/share/link?shareid=3717576860&uk=940392313 汇编教程英文版相应章节进行翻译得来。
另外再附加一个英特尔英文手册的共享链接地址:
http://pan.baidu.com/share/link?shareid=2345340326&uk=940392313 (在某些例子中会用到)
研究汇编语言的关键在于了解你的程序所运行的硬件环境,其中最主要的部分就是处理器,理解处理器底层的原理将有助于你编写出高效的程序出来。
英文原著编写的时候,当时工作台和服务器上用的最多的应该是英特尔奔腾处理器,而奔腾处理器属于IA-32家族的一部分,所以下面将介绍IA-32处理器家族的基本组成元素,接着再介绍IA-32家族奔腾4处理器的一些高级功能,最后讨论其他类型的处理器以及这些处理器之间的不同特点。(限于篇幅,分几篇文章进行翻译)
Core Parts of an IA-32 Processor (IA-32处理器的核心部分):
尽管不同的处理器家族包含不同的处理器指令集和功能,但是仍然可以在大部分处理器中发现一些共同的核心组件,下图1-1显示了四个基础的核心组件:
图1-1
Processor处理器中包含了控制电脑操作的硬件和指令代码。它通过三个总线:control bus(控制总线),address bus(地址总线),data bus(数据总线)来和电脑的其他部分进行联系(例如内存存储单元,输入输出设备)。
control bus(控制总线)用于同步协调处理器和其他系统之间的功能,data bus(数据总线)用于处理器和外部系统之间传递数据,例如,假设处理器需要从某个内存位置读取数据,那么处理器会将需要访问的地址放到address bus(地址总线)上,内存存储单元就会将该内存位置的数据放到data bus(数据总线)上,以此响应处理器的访问请求。
处理器本身又包含很多组件,每个组件在处理器处理数据的能力上都有各自的作用。汇编语言就具有直接访问和控制这些组件的能力,所以有必要了解下这些组件,处理器主要由以下几个组件构成:
-
Control unit (控制单元)
-
Execution unit (执行单元)
-
Registers (寄存器)
-
Flags (标志寄存器中的各种标志,又称程序状态字,主要用于反映处理器的状态和运算结果的某些特征及控制指令的执行)
下图1-2显示了这些组件及这些组件在处理器中的相互作用:
图1-2
下面就对这几个处理器的核心组件进行详细介绍。
Control unit (控制单元):
控制单元在处理器中占有很重要的地位,控制单元主要执行下面四个基本的功能:
-
从内存中获取指令
-
对指令进行解码,以确定要执行的操作
-
根据需要从内存中获取数据
-
必要时存储指令执行的结果
指令计数器从内存中获取下一条将要执行的指令,为下一步处理做准备。指令解码器将获取的指令解码为micro-operation(微操作)。这些微操作是一些可以控制处理器内部芯片中的具体信号来执行指令功能的代码。
当micro-operation(微操作)准备好后,control unit(控制单元)就将它传递给execution unit(执行单元)去处理执行,并将执行完的处理结果存储到一个合适的位置。
control unit(控制单元)是处理器技术当中的研究热点,很多先进的技术都应用在这个部分,主要的技术重点在于控制单元如何去有效的获取和处理指令。
在英文原著编写的时候,最新的英特尔处理器是奔腾4处理器,它采用的控制单元技术被称作NetBurst架构。NetBurst架构包含四个子技术用以提升控制单元的处理速度。了解这些技术将有助于你更好的去优化自己的汇编程序,NetBurst架构的四个子技术如下:
-
Instruction prefetch and decoding 指令的预取和解码
-
Branch prediction 指令跳转分支的预测
-
Out-of-order execution 不按顺序执行指令的方式,控制单元中的技术重点和技术难点
-
Retirement 退休,这是Out-of-order执行过的指令在彻底执行完时的一种贴切的形容,有的资料里喜欢叫做graduation(毕业)
这几个技术工作在一起,共同构成了奔腾4处理器的控制单元。下图1-3显示了这些元素之间是如何相互作用的:
图1-3
下面讨论这些技术在奔腾4处理器中的实现。
Instruction prefetch and decoding pipeline (指令预取和解码的管道技术):
IA-32家族的旧处理器,只有在execution unit(执行单元)需要数据时才从系统内存中获取指令和数据,但是由于内存读取的速度远低于处理器的执行速度,就会造成处理器在没收到数据时处于一种等待状态,这样就浪费了很多宝贵的执行时间,利用这些等待的时间,处理器本来应该可以多执行很多的其他指令。要解决这个问题,就有了预取的概念。
通过管道技术,把执行单元将要执行的指令和数据先从系统内存中预先获取出来,然后存放到处理器内部的缓存中,那么当执行单元需要执行下一条指令时,就可以直接从缓存中快速的获取指令和相关的数据了。下图1-4演示了这个过程:
图1-4
IA-32平台通过采用两层(或更多层)的缓存来实现管道技术,第一级缓存(被称作L1)里面存储着从系统内存中获取过来的可能会被执行的指令和可能需要的数据。
在常规情况下,指令计数器是沿着内存递增的(在不考虑跳转指令的情况下),所以预取算法就可以将当前指令计数器后面的指令作为可能会被执行的指令给缓存起来。同时预取算法还会尝试检测哪些数据可能会被用到,然后也从系统内存中将这些数据读取并缓存起来。
当遇到跳转指令的情况下,因为指令计数器指向了完全不同的内存位置,所以整个缓存里的数据就都没用了,必须被清理掉,然后重新从新的位置读取指令,并重新填充缓存。为了避免这类问题,就有了二级缓存(被称作L2),这级缓存也能像一级缓存那样缓存指令和数据。当程序遇到跳转指令并跳转到新位置执行时,二级缓存里还存储着跳转之前的位置处的指令和数据,这样当程序跳转回原来的位置时,就可以直接从二级缓存中读取指令和数据。
尽管汇编语言不能访问这些缓存区域,但是还是有必要知道他们的工作原理,根据刚才所说的,如果你想加快程序的执行速度,就应该在程序逻辑允许的情况下,最大限度的减少程序中的跳转分支操作。
Branch prediction unit (分支预测单元):
尽管有了一,二级等缓存在一定程度上加快了处理器的执行速度,但是如果程序中跳转过多时,缓存也不可能将所有跳转的指令和数据都存储起来,所以就需要分支预测,通过专门的算法来尝试预测哪些指令可能会被执行,然后将这些预测的指令给缓存起来。
奔腾4处理器采用三种技术来实现分支预测:
深度分支预测会采用统计算法来预测出程序中最有可能的执行路径,当然这种技术并不总是万无一失。
动态数据流则是实时统计分析处理器中的数据流信息,然后根据数据流预测最有可能执行的指令,接着将这些指令传递给Out-of-Order(无序执行引擎),这种无序执行引擎可以执行任意位置的指令,在执行完后,再通过一种机制将执行结果按指令的原始顺序进行重排,从而让指令的执行好像和顺序执行效果一样,同时,处理器还可以将先准备好的指令先执行,从而不会浪费处理器的执行周期。
推理执行,顾名思义就是通过一些方法例如历史统计数据,分析推理出分支的可能跳转情况,然后将后面可能会执行的指令交给Out-of-Order引擎去执行。例如一般条件跳转指令有两种情况:采用则跳转,不采用则不跳转如JZ指令ZF为1跳转,否则不跳转(或者反过来采用则不跳转,不采用则跳转如JNZ指令ZF为1不跳转,否则跳转),在第一次遇到某个条件跳转指令时,因为没有历史数据可参考,就无法推理它会跳转还是不跳转,当多次执行到这个跳转指令时,就可以根据历史数据判断是否会发生跳转,例如历史统计显示跳转明显多于不跳转,或者每两次发生一次跳转等,就可以预测将会执行的指令,然后将这些指令送去执行,如果预测是错误的,那么前面预测执行过的指令和结果就会被丢弃掉。
因篇幅,先写到这里,下节继续。
OK,到这里,休息,休息一下 o(∩_∩)o~~