我目前正在从英特尔x86程序集这本书中学习x86。在它关注不同指令类和操作码如何工作的部分中,它表示一个字节的操作码被编码为 iiirrmmm
,其中三个i
表示指令类,rr
表示4个主寄存器,mmm
可以是多个值,表示AX
,[AX]
,[AX+XXXX]
形式的可选2字节内存操作数,例如,101
对应[XXXX+BX]
,100
对应[BX]
等。它还首先提到访问寄存器中的值所花费的时间为零时钟周期,因为它是在芯片上实现的。
然而,在解释指令完全工作所花费的时间时,为了弄清楚CPU计算内存操作数地址所花费的时间,它说:
分别是1和0周期,因为本书在开始时明确提到采用零时钟周期来访问寄存器中的值?为什么说要用1个周期来访问BX
中的值?
答案 0 :(得分:11)
非常非常重要的是,您了解本书告诉您有关代码执行速度的任何内容都是完全无稽之谈。这本书很老,15年是很多狗生活在处理器开发中。即使你的屏幕截图中显示的内容已经不再适用了,它今天也会危险地不真实。
接下来,CPU计算内存操作数的地址
不,不是真的。操作数地址计算是AGU的工作,即“地址生成单元”。处理器内核上独立于主执行核心运行的独立电路。这就是为什么做额外的工作需要0个cpu周期,工作是并发。这并不止于AGU,现代处理器有许多执行单元,可以同时完成工作。
我们假设时钟周期和存储周期是等效的
当时不是真的,今天非常不真实。内存总线比处理器内核慢几百倍。与距离相关的问题,电信号必须越远,在目的地传送它就越难以使其损坏。只有慢下来才能解决这个问题。具有千兆赫兹时钟频率的现代处理器大量投资于缓存,这是一种将数据副本存储在RAM中的额外内存。 L1缓存非常重要,它可以存储32千字节的数据和32千字节的指令,并且最接近处理器核心。仍需要3个cpu周期才能读取。 L2和L3更大,并且不可避免地坐得更远,因此需要更多周期。任何遭受执行停顿的程序,因为从RAM中读取数据需要150个cpu周期,当然程序性能很差,无论它使用什么指令。
这不是令人不适的地方,这本书的整个前提今天都是非常误导。现代处理器实际上并不执行x86指令。它们具有等效的即时编译器,即Java或.NET中使用的编译器。他们将x86指令转换为“微操作”,CISC指令转换为RISC指令。易于在多个执行子单元中无序执行和同时执行的类型。正是这样看起来是一个非常保守的秘密,像英特尔和AMD这样的公司将其视为知识产权,没有人应该知道任何事情。最重要的是,没有人应该依赖,因为这将使他们难以改进他们的处理器设计。
这项创新的明显牺牲品是,谈论采取一定数量CPU周期的指令不再有意义。我已经向你指出了Agner Fog的手册。它讨论了延迟,即解码指令和获得结果所需的时间。和吞吐量,受同时执行的相同指令数量的影响。这些数字只能提示处理器需要工作的难度,它们对于预测程序的实际执行时间是完全没用的。添加缓存的状态,内存总线的速度,预取器猜测需要提前检索的内存位置的能力,以及分支预测器在猜测代码流时作为强随机化器的运气量。只有剖析器可以告诉你需要多长时间。
答案 1 :(得分:4)
其他人已经指出,你正在阅读的这本书已经很老了,所以它告诉你关于指令时间的内容在今天并不真实。关于“记忆周期”和“时钟周期”等同的那一点尤其可以追溯到80年代早期。
指令执行的时钟周期取决于指令触发的数据依赖性,以及CPU指令集设计人员关注优化指令解码和执行的机制。
许多旧机器将使用许多时钟来获取和解码复杂指令,然后通常需要几个时钟来访问存储器(在80年代后期时钟速率为10 Mhz)。许多时钟到解码是由复杂的指令集设计,没有大量资源(空间和晶体管)引发指令解码,以及通过更大的硅几何形状的长延迟引起的。存储器具有70ns的访问时间,因此仅需要几个时钟(例如,10),因为时钟周期慢得多。
实现比现代CPU简单得多,CPU内部通常有一个简单的有限状态机来控制指令执行。通过大致了解FSA的工作原理,您可以预测指令解码时间,包括解码内存寻址的时间,如您的书所示。不再是真的。
现代机器具有非常高的时钟频率:2-4 Ghz。这就像1000倍一样快。这是可能的,因为晶体管要小得多,因此电流需要更少的时间来穿过它们。此外,有这么多晶体管,设计人员可以在解码/执行和缓存时投入大量额外的芯片。奇怪的是,记忆的速度并没有那么快,因此在时钟中访问内存所需的相对时间却出乎意料地增加了。 40 ns存储器需要160个4Ghz时钟。
可以说,芯片可以解码指令,并存储它需要知道的关于在缓存中执行该指令的所有内容(而现代英特尔CPU在复杂指令中执行大部分操作)。这意味着一个复杂的指令可能需要几十个周期来解码第一次遇到的时间,以及一个时钟(“在解码的指令缓存中查找”)再次遇到时,这经常发生(考虑循环)。这里的好消息是平均指令解码时间非常短;坏消息是您无法对指令解码和运行所需的时间进行硬估计,因为它取决于涉及多少优化和缓存,缓存是否因特定指令执行而被命中,以及其他指令是什么由于数据依赖性,仍然会对资源访问进行处理并具有优先权。
实际上,晶体管仍处于(巨大但有限的)供电状态,因此指令解码器无法缓存所有内容。设计师仍在进行权衡。一个关键的权衡是“简单”(RISC)指令(往往会被执行很多)分配了大量资源,使它们能够在每次遭遇时快速解码,而复杂的指令(往往执行频率较低)会减少向他们投掷硬件资源。
特定于解码寄存器访问需要多长时间的问题:很明显,访问寄存器,首先要求您知道要访问哪个寄存器,即使它在片上。因此,在读完指令后,必须花费一些时间来提取寄存器字段。如果您认为芯片上的最短时间是以时钟测量的,则需要大于零,并且至少需要1个时钟才能获得该字段。真正积极的硬件可能在单个时钟周期内执行多个操作,因此实际上可以设计解码寄存器的CPU,并且取指定的寄存器小于一个时钟。一些现代英特尔处理器只需一步即可解码成对指令,例如“比较; jmp条件”。
从您的角度来看,这意味着平均指令往往执行速度非常快。对非缓存位置的内存访问会带来什么伤害。
我的CPU的一阶模型是它们无限快速,您所要做的就是担心内存访问。这意味着我倾向于在寄存器中交换计算(这很快)用于存储器访问时间。压缩你的数据结构,它没有伤害: - }
答案 2 :(得分:1)
寄存器操作数的零周期意味着仅寄存器操作的延迟是操作的基线(寄存器与ALU的快速连接)。使用内存操作数时,会将内存访问延迟添加到操作延迟中
部分内存访问延迟是地址计算。包含地址(或复杂寻址模式的一部分)的寄存器必须路由到CPU的寻址单元而不是ALU。
然后该地址用于访问存储器,此时CPU中的路由选择是零还是一个周期的问题变得荒谬:存储器延迟可能会大几个数量级。
一句话:没有人关心这个循环。