乱序执行(乱序执行译作异步执行更贴切),是指在cpu中运行的指令并不按照代码中的顺序执行,而是按照一定的策略打乱顺序执行,也许后面的指令先执行,当然,得保证指令之间不具备相关性。
举个简单的例子,比如如下两行代码就无法乱序执行。
mov eax, [0x1234]
add eax, ebx
第2行的add加法,需要知道eax的值,但eax的值需要在第1行中的mov操作后才能确定,而且内存访问相对来说非常慢,第2步不得不等待第1步完成后才能进行。所以只能是先执行第1步,再执行第2步。
如果将上面第2步的代码修改一下,如下:
mov eax, [0x1234]
add ecx,ebx。
这样就可以在执行第1步内存访问后的等待中执行第2步啦。由于第2步不依赖第1步,所以有利于放在流水线上。
x86最初用的指令集是CISC,Complex Instruction Set Computer,意为复杂指令集计算机,为什么复杂呢?当初的cpu工程师们为了让cpu更加强大,不断地往cpu中添加各种指令,甚至在cpu硬件一级直接支持软件中的某些操作,以至于指令集越来越庞大笨重复杂。如push指令,它相当于多个子操作的合成,拿保护模式中的栈来说,push eax相当于:
- push指令先将栈指针esp减去操作数的字长,如sub esp,4。
- 再将操作数mov到新的esp指向的地址,如mov [esp],eax。
这两个子操作合成了一个指令,其中每一个子操作称为微操作。
与CISC指令集相对应的是RISC,Reduced Instruction Set Computer。意为精简指令集计算机。根据二八定律,最常用的指令只有20%,但它们占了整个程序指令数的80%。而不常用的指令占80%,但它们只占整个程序指令数的20%。这就是RISC指令集的由来,它精简保留了那些常用的指令,这些指令大多数都是不可再细分的,也就是,它们基本上都是属于微操作级别的指令啦。所以,x86发展到后来,虽然还是CISC指令集,但其内部已经采用RISC内核,译码对于x86体系来说,除了按照指令格式分析机器码外,还要将CISC指令分解成多个RISC指令。当一个“大”操作被分解成多个“微”操作时,它们之间通常是独立无关联,所以非常适合乱序执行。
还是拿栈举例。如下三行代码:
mov eax , [0x1234]
push eax
call function
第1步需要内存访问,由于内存较慢,所以寻址等待过程中可以做其它事。
第2步的push指令拆分成 sub esp ,4和mov [esp], eax。
第3步的call函数调用,需要在栈中压入返回地址,所以说call指令需要用栈指针。
由于第2步中的微操作sub esp,4,可以让cpu知道esp的最新值,不用等到mov [esp], eax完成,第3步call指令向栈中压入返回地址的操作就可以执行了。故第2步未执行完就开始第3步的执行了,也许第3步先于第2步才完成。
总结下,乱序执行的好处就是后面的操作可以放到前面来做,利于装载到流水线上提高效率.