简述影响指令执行时间的主要不确定因素.docx
- 文档编号:11913142
- 上传时间:2023-04-16
- 格式:DOCX
- 页数:7
- 大小:19.63KB
简述影响指令执行时间的主要不确定因素.docx
《简述影响指令执行时间的主要不确定因素.docx》由会员分享,可在线阅读,更多相关《简述影响指令执行时间的主要不确定因素.docx(7页珍藏版)》请在冰豆网上搜索。
简述影响指令执行时间的主要不确定因素
简述影响指令执行时间的主要不确定因素
——处理器流水线机制
论坛上经常有人问,某段语句的执行时间是多少;或者是某几段语句,那段执行时间快。
绝大多数朋友也会带着好奇的观点,在旁边观战;通常的回答是:
你看芯片手册吧。
类似的帖子和类似的回答很多,但是很少有人能把这个问题回答的清晰和彻底。
我觉得,这种提问本来就不专业,答案也不唯一。
至于原因?
因为一条指令的执行时间不仅取决于处理器的频率,还取决于许多处理器以外的因素。
芯片手册上指令的执行时间,通常是不考虑外界因素的:
不考虑总线冲突、不考虑内存延迟、不考虑高速缓存机制、不考虑流水线的相关性等。
这里我打算分两次主要介绍下,高速缓存和处理器的流水线机制如何影响指令的执行时间的。
本次先介绍流水线相关机制。
1. 流水线技术
现代绝大多数的处理器在某个时刻,并不是只处理一个指令,而是按照流水线的形式处理,这和我们实际生活中,车间里的流水线是一个原理。
下面我们假设某个处理器有三级流水线:
译指/运算/写存。
这里只是假设,不要对号入座。
译指:
处理器读取并分析指令的功能,比如mov是赋值,add是加法;译指为下一步的运算提供数据输入和选择相应的硬件单元。
运算:
处理器进行加减乘除移位比较等等运算,不需要运算的指令进行下一步骤“排队”。
写存:
处理器将运算单元的输入写回内存或者寄存器,并更新pc。
假设该处理器正在处理四条指令ABCD。
每条指令又有三个处理过程,即译指/运算/写存。
某个时刻处理器的状态如下:
流水线某个时刻各处理单元的状态为:
| C指令译指 | B指令运算 | A指令写存 |
一个coreclock(处理器时钟)后,流水线的状态为:
| D指令译指 | C指令运算 | B指令写存 |
流水线的每个阶段,都可以设计出独立的硬件单元,这些单元之间可以并行运行,从而提高了处理器的并行处理能力。
我们假设流水线的每个阶段需要耗时1ns,一条指令的完整执行时间就是3ns;不采用流水线时,指令的完整执行时间是3ns。
那么,流水线每1ns有一条指令执行完毕,而不采用流水线则3ns才执行完毕一条完整的指令;可以看出使用流水线技术后的执行效率是原处理器的3倍。
(该例子只说明流水线的优势,实际并不那么简单的数学公式)
但是,流水线各个阶段的实现需要考虑很多因素。
一方面,流水线的多个阶段之间相互独立,前一阶段的执行输出是后一阶段的执行输入,所以必须有一个地方存储前一阶段的输出,这样就必须在流水线的各阶段之间插入“暂存器”。
通常它是一类寄存器,对该寄存器的访问是需要时间的。
另一方面,流水线各个阶段的功能划分和执行时间的划分会影响流水线的性能。
这里不讨论流水线的划分了,^_^水平不够。
那么,流水线是否会任何时间都这样平稳执行呢?
有些主要并常见的因素会影响指令的有条不紊执行。
2. 数据相关和数据冒险
所谓相关,指前后的指令存在某种关联;相关分为数据相关和控制相关。
我们先介绍数据相关。
比如下面的程序:
1 main()
2 {
3 inta,b;
4 a=3;
main [0xe3a01003]*mov r1,#3
5 b=a;
000080e0 [0xe1a02001] mov r2,r1
6 }
以上程序是在ADS1.2上的编译输出,变量a对应寄存器r1,变量b对应寄存器r2。
可以看出,第5行语句和第4行的语句存在直接关系。
以我们前面介绍的三级流水线来看,变量a(寄存器r1)的值必须要等到写存完毕后,其值才变为3;而紧接着的语句“movr2,r1”是把变量a的值赋值到变量b(寄存器r2)里,若等到对变量a赋值的语句写存完毕了,那么对变量b的赋值语句也运算完毕了,显然运算阶段输入的数据可能不是3。
这里,前者的输出数据是后者的输入数据,我们把这种情况叫:
数据相关。
处理器在处理数据相关时,可能会导致的计算失误叫数据冒险(所以,处理器必须避免数据冒险)。
为了避免处理器真正产生这样的数据冒险,我们可以把上面的例子修改下:
1 main()
2 {
3 inta,b;
4 a=3;
main [0xe3a01003]*mov r1,#3
5 __asm
6 {
7 nop;
000080e0 [0xe1a00000] nop
8 nop
9 }
000080e4 [0xe1a00000] nop
10 b=a;
000080e8 [0xe1a02001] mov r2,r1
11 }
我们在语句“a=3”和“b=a”之间人为加上了nop指令(nop指令,处理器不会做任何显示的事情)。
那么,当语句“a=3”执行完写存时,语句“b=a”才准备开始译指。
这样,b就能从对应的寄存器中取得正确的值了。
通过上面的例子,我们知道,要避免处理器产生数据冒险,可以加入nop指令。
但是,上层开发者如何明白这些道理呢!
!
!
所以,处理器可能会帮助你加入类似的操作(不一定是nop指令)。
下面我们介绍处理器通常的做法。
另外,不光是寄存器在前后指令引用可能产生数据相关,内存数据、其他内部寄存器等都可能产生数据相关。
如何解决数据相关是处理器设计者考虑的事情,但是我们看到:
处理器手册上讲到某个指令的执行时间时,不会提到这些。
就意味着,若干汇编执行完毕后,你会发现时间总和不是手册上的时间总和!
3. 流水线停滞
处理器为了自动处理数据相关,通常会采取流水线停滞的做法。
流水线停滞时,处理器会“暂停”相关指令的后续指令,止到冒险行为不可能发生为止。
我们一听就觉得这个棘手吧。
处理器通常需要尽早的发现数据相关,从而尽早的把后续指令阻塞在译指阶段。
阻塞在译指阶段,处理器可以放弃这条相关的指令,转而插入一个(每个周期插入一个,但pc值不因此增加)nop指令。
当冒险条件不满足时,不再插入nop指令。
在前面我们介绍使用nop指令可以解决数据冒险,很多处理器会设计为这样。
同时,插入的这个nop指令叫气泡。
所以,使流水线停滞可以解决数据相关的问题。
但是,处理器停滞不是解决数据相关的唯一答案。
比如,采取流水线前后的数据反馈和转发等机制,不过这样会加大设计难度。
通常很多处理器会将停滞和转发等技术综合使用。
要注意,流水线停滞和插入气泡,实际上是有区别的。
这里为了简便,我们把插入气泡也视着流水线停滞。
本节主要是想让读者明白,我们查处理器的指令手册来知道指令的执行时间并不可靠。
4. 控制相关和控制冒险
前面讲到处理器会因为数据相关可能改变已有指令的有条不紊执行,那么当遇到分支指令时呢?
我们先看下面例子:
4 main()
5 {
6 inta=2;
main [0xe3a01002]*mov r1,#2
7 if(a==1)
000080e0 [0xe3510001] cmp r1,#1
000080e4 [0x1a000000] bne 0x80ec ;(main+0x10)
8 {
9 a=3;
000080e8 [0xe3a01003] mov r1,#3
10 }
11 }
000080ec [0xe3a00000] mov r0,#0
000080f0 [0xe1a0f00e] mov pc,r14
上面的例子同样是在ADS1.2上的编译结果。
首先把变量a的值赋为2,然后判断变量a的值是否为1,若a为1则把a赋值为3。
假设指令“movr1,#2”为指令
(1);指令“cmp r1,#1”为指令
(2);指令“bne0x80ec”为指令(3);指令“movr1,#3”为指令(4);指令“movr0,#0”为指令(5)。
同样,依前面讲到的三级流水线来看。
当指令
(2)准备执行写存阶段时,指令(4)是否执行呢?
?
摆在处理器面前的是两条选择:
译指指令(4)?
译指指令(5)?
我们把前面这种分支语句的相关叫控制相关。
同样,处理器可能会因为控制相关而产生错误的行为叫控制冒险。
如果你注意思考,你会发现,使用流水线停滞同样可以解决控制相关。
不错!
所以流水线停滞是一个影响指令执行时间的常见原因。
实际实现时,处理器也可能会在某些控制相关停滞流水线。
下面介绍处理器通常还会用什么样的做法来解决控制相关呢?
5. 分支预测
前面介绍了控制相关,产生控制相关的情况依处理器不同可能有不同,但是分支语句通常会产生控制相关;另外,函数调用和函数返回会产生控制相关;允许直接对pc赋值的处理器,在对pc赋值时,产生控制相关。
所以这里我要强调依处理器不同,汇编指令有所不同。
比如同样是函数返回:
X86采用ret指令;而ARM的MOV指令也可以用于函数返回(见前一节例子的最后一句汇编)。
总的来说,流水线如果在译指(流水线前端)不容易判断的分支,那么处理器就会进行一个预测。
这种预测就是分支预测。
而对某些指令,流水线一看就知道要跳转的,那么流水线通常会停滞,等待正确的pc确定。
所以,我又要强调不同处理器了!
!
!
!
既然是预测,那么就会存在预测失误的时候。
预测失误,处理器就会尝试恢复正确的执行“轨迹”。
要恢复正确的执行“轨迹”就势必会做额外的事情。
我们把这种由于预测错误,而带来的额外开销就叫预测错误惩罚。
当处理器预测正确分支时,没多大改变,继续往前执行。
但是,当预测错误时,处理器必须丢弃预测部分的执行结果,并恢复回刚才的预测点,重新在正确的pc处取指令开始执行。
根据很多资料介绍,预测错误的处罚开销,相比停滞的开销大很多。
所以,处理器在处理预测的时候,会比较谨慎,这个部分也是处理器性能的体现。
我们可以发现,一个循环次数庞大的循环代码,在一个好的分支预测处理器上运行和在一个预测能力差的处理器上运行,他们的时间可能会差距相当远。
通常,在没有分支预测的处理器上,会采用停滞等简单做法。
预测的算法也有多种,限于水平,这里不能多介绍了。
请大家见谅。
通过前面几节的介绍,我们知道:
表面平静的湖面,湖面以下可能波涛汹涌。
它可能无形的影响你的代码运行时间。
6. 乱序执行处理器
前面主要介绍流水线对指令执行时间的影响,后面做为附加介绍吧。
在前面的章节,我们主要介绍的传统的处理器的流水线。
当编译器把指令选取完成后,处理器只能被动的运用各种技巧来提高运行效率。
有一种处理器,它可以根据自己的算法来重新选择指令的执行先后,从而提高执行效率。
这种处理器就叫乱序执行处理器。
不管乱序处理器如何“乱”,它必须保证执行结果和不“乱”的结果一致。
比如,数据相关的例子。
如果语句“b=a”之后,有其他不产生任何与正在执行的指令相关的指令时,乱序执行的处理器就可能先执行这样的不相关指令了;等冒险消失后,再执行语句“b=a”。
这样,乱序执行,提高了流水线的利用率,流水线减少了停滞的概率。
不过乱序对分支语句的效果应该不理想。
所以,当你在处理器手册上找指令的执行时间时,再当这个处理器还是乱序执行的处理器时,这个时间越来越偏离理论值。
7. 超标量处理器
前面知道了流水线的一些知道,也许有的人会想到使用级数很多的流水线来提高流水线效率,我们把这种流水线级数的多少叫深度。
超标量处理器通常就是指这种流水线深度较深的处理器,不过具体多少深度才叫超标量处理器,我也没找到具体定义。
公认的奔腾系列处理器属于超标量处理器。
就是说,能否通过提高流水线的深度,来提高处理器的执行效率?
答案当然是可以。
流水线深度越深时钟周期可以越小;但是,不可能无限小,因为暂存器的访问延迟有下限,数量越多越不妙;深度越深各个阶段的同步实际会成问题;再加上,为前面讲到的各种冒险,会加剧处理器的设计难度。
所以,可能会出现深度越深处理器的性能反而下降的情况。
以上的介绍都是流水线里最常见的早期理论,现在处理器的设计发展也很快,多流水线多处理器等等层出不穷。
笔者实在不能介绍出更多的原理。
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 简述 影响 指令 执行时间 主要 不确定 因素
![提示](https://static.bdocx.com/images/bang_tan.gif)