2

在检查 Intel x86 处理器的指令集时,我注意到有一些“直观”的指令,如“mov”、“add”、“mul”……而其他指令似乎有点不自然,比如“sete”。这个问题更多是出于好奇而不是实际问题:为什么设计人员会选择在单个指令中实现特定的执行场景?您知道任何可以解释此类设计决策的阅读材料吗?

4

3 回答 3

6

设计人员用来决定“特定执行场景”是否是指令的合理候选者的一些标准:

  1. 无状态行为- 操作在执行时必须仅依赖于操作数或其他可见的机器状态(例如算术标志)。不允许隐藏状态。此限制排除了在指令似乎已完成后仍处于忙碌状态的非阻塞指令。

  2. 有限的内存接触- 内存访问通常是速率限制器。除了提高代码密度之外,如果由于内存瓶颈而导致两者执行相同的操作,那么将离散操作组合成一条大指令是没有意义的。

  3. 计算上有趣- 新指令应该比其他方式更有效地执行某些操作。x86 AES 指令是极端的例子。相对简单的操作(如 bit swizzles)如果经常发生也很重要。

  4. 商业价值——执行指令的硅片面积和验证工作是否能收回成本?

  5. 兼容性值——最后但并非最不重要的一点是,存在许多指令只是为了支持旧版软件。

于 2012-05-10T04:37:09.900 回答
4

在 的情况下sete,这可能是在指令集中编写代码的实际经验问题。至少如果内存服务,sete从 386 开始添加,所以到那时指令集已经活跃使用了几年。猜测一下,他们可能花了一些时间查看代码,以找到已经做了很多但指令集中不直接支持的事情。他们可能会筛选那些,通过直接在 CPU 中支持它们来找到那些容易提高效率的。

很多情况都与此类似——工作基本上是在软件中进行原型设计,以找到一个相当灵活、高效且易于实现的设计。然后,当设计相对完善时,CPU 设计人员会检查它,看看他们是否不能通过在硬件中实现(至少部分)它来使其至少更有效率。

大多数所谓的 RISC 处理器都是通过在现有处理器上使用现有编译器收集源代码生成的代码的统计数据而设计的。然后他们查看了指令使用的频率,并(试图)优化那些经常使用的指令,并简单地删除那些不常用的指令。

于 2012-04-30T09:05:58.920 回答
4

至少有两种可能的序列来实现代码。这是我对它们的分析

; "classic" code

cmp edx,15
jne past
mov al,20
past:

; "evolved" code

cmp edx,15
sete al,20

有条件的简单赋值“应该”涉及在相反条件下的条件跳转,即使它只围绕一条指令跳转,这是一种心态。但是,用您自己的话说,它是一个经常发生的特定执行场景,所以如果有更好的替代方案,为什么不呢?

当代码执行时,影响执行速度的因素有很多。其中两个是比较/算术/布尔运算的结果到达标志寄存器所需的时间,另一个是执行跳转时的执行惩罚(我有点过于简化了)。

所以经典代码要么执行移动,要么跳转。前者可能会与其他代码并行执行,而后者可能会导致预取器从新位置加载数据,从而导致等待状态。可能涉及处理器的分支预测,并且可能 - 取决于许多因素 - 预测不正确,这会导致额外的惩罚。

在进化的情况下,preftecher 完全不受影响,这有利于执行速度。此外,与 mov+jne 组合相比,sete 序列可能适合更少的字节,这意味着执行中将涉及相对较少的代码缓存行容量/工作,这意味着将释放相对更多的数据缓存容量/工作好。如果不需要立即分配的内容,则可以将 sete 重新安排到与周围代码更好地(执行方面)融合的位置。这种重新调度可以显式(由编译器)或隐式(由 CPU 本身)执行。

对于正常(通常未调整)、臃肿的应用程序代码,使用此类指令对整体性能影响不大。在高度专业化、手动调整且循环非常紧凑的代码中,在三个而不是四个或五个缓存行内执行之间的差异可能会产生巨大的差异,尤其是如果代码的多个副本在不同的内核上运行时。

于 2012-09-09T10:40:01.343 回答