在检查英特尔x86处理器的指令集时,我注意到有'直观'指令,如'mov','add','mul'......而其他人似乎有些不自然,如'sete'。问题更多的是出于好奇而不是实际问题:为什么设计师会选择在单个指令中实施特定的执行方案?你知道任何可以解释这种设计决定的阅读材料吗?
答案 0 :(得分:6)
设计人员用来决定“特定执行方案”是否是指令的合理候选者的一些标准:
无状态行为 - 操作必须仅依赖于执行时的操作数或其他可见的机器状态(例如算术标志)。不允许隐藏状态。此限制排除了在指令显示完成后保持忙碌的非阻塞指令。
有限的内存触摸 - 内存访问通常是速率限制器。除了提高代码密度之外,如果由于内存瓶颈而执行相同操作,将离散操作组合成一个大指令是没有意义的。
计算上有趣 - 新指令应该比其他方式更有效地执行某些操作。 x86 AES指令是极端的例子。如果经常发生这种比较混乱的相对简单的操作也很重要。
商业价值 - 实施指令的芯片面积和验证工作是否能为其付出代价?
兼容性价值 - 最后,但并非最不重要的是,除了支持旧版软件之外,其他任何说明都不存在。
答案 1 :(得分:4)
在sete
的情况下,可能是在指令集中编写的代码的实际经验问题。至少如果内存服务,则从386开始添加sete
,因此到那时指令集已经被激活了几年。在猜测中,他们可能花了一些时间查看代码来查找已经完成的很多事情,但是在指令集中没有直接支持。他们可能会筛选那些通过直接在CPU中支持它们来轻松提高效率的那些。
很多情况与此类似 - 工作基本上是用软件原型来找到一个相当灵活,高效且易于实现的设计。然后,当设计相对完善时,CPU设计人员会查看它,看看它们是否通过在硬件中实现(至少部分)来使它至少不能提高效率。
大多数所谓的RISC处理器是通过收集现有处理器上现有编译器的源代码生成代码的统计数据而设计的。然后他们查看了使用指令的频率,并(试图)优化那些经常使用的那些,并简单地删除了那些没有使用过的那些。
答案 2 :(得分:4)
至少有两种可能的序列来实现代码。这是我对它们的分析
; "classic" code
cmp edx,15
jne past
mov al,20
past:
和
; "evolved" code
cmp edx,15
sete al,20
有条理的,简单的赋值“应该”涉及相反条件下的条件跳转,即使它只跳转到单个指令也是一种心态。但是 - 用你自己的话说 - 经常发生的特定执行场景,所以如果有更好的替代方案,为什么不呢?
当代码执行时,影响执行速度的因素很多。其中两个是比较/算术/ booloean操作的结果到达标志寄存器所需的时间,另一个是跳转时的执行惩罚(我过度简化了这一点)。
因此,经典代码将执行移动或跳转。前者可能与其他代码并行执行,后者可能导致预取器从新位置加载数据,从而导致等待状态。可能涉及处理器的分支预测,并且可能 - 取决于许多因素 - 不正确地预测会导致额外的惩罚。
在演进的情况下,preftecher根本不受影响,这对执行速度有利。此外,sete序列可能比mov + jne组合更少的字节,这意味着执行中将涉及相对较少的代码缓存行容量/工作,这意味着将有相对更多的数据缓存容量/工作将被释放为好。它不需要立即分配内容,可以将sete重新安排到与周围代码更好地(执行方式)混合的位置。这种重新安排可以显式地(由编译器)或隐式地(由CPU本身)执行。
对于普通(通常是未调整的)膨胀的应用程序代码,使用此类指令对整体性能影响不大。在具有非常紧密循环的高度专业化,手动调整的代码中,在三个而不是四个或五个高速缓存行中执行之间的差异可能产生巨大差异,尤其是如果代码的多个副本在不同的核上运行。