【问题标题】:Time taken to load value from registers in x86从 x86 中的寄存器加载值所花费的时间
【发布时间】:2014-04-05 21:52:14
【问题描述】:

我目前正在从英特尔 x86 组装艺术一书中学习 x86。在详细说明不同指令类和操作码如何工作的部分中,它说一字节操作码编码为 iiirrmmm,其中三个 is 表示指令类,rr表示4个主寄存器,mmm可以是多个值,表示可选的2字节内存操作数,形式为AX[AX][AX+XXXX]等。例如101对应[XXXX+BX]100对应[BX]等。开头还提到了访问寄存器中的值所花费的时间是零时钟周期,因为它是在芯片上实现的。

但是,在解释一条指令完全工作所花费的时间时,为了弄清楚 CPU 计算内存操作数的地址所花费的时间,它是这样说的:

会不会分别是 1 和 0 周期,因为书上一开始就明确提到要使用零时钟周期来访问寄存器中的值?为什么说访问BX中的值需要1个周期?

【问题讨论】:

  • 这是一本 15 年前的书,从那时起处理器发生了很大变化。使用Agner Fog's指令表文档。
  • 在您提到的页面中找不到具体信息。你能提供确切的链接吗?
  • 第4号,链接到instruction_tables.pdf的那个

标签: assembly x86 cpu-registers mov


【解决方案1】:

非常非常重要的是,您要明白本书告诉您的有关代码执行速度的任何内容都是无稽之谈。书很老了,15年是处理器开发的很多狗命。即使您的屏幕截图中可见的内容在当时已经不再真实,但在今天它变得危险不真实了。

接下来,CPU计算内存操作数的地址

不,不是真的。操作数地址计算是“地址生成单元”AGU 的工作。处理器内核上的独立电路,独立于主执行内核运行。这就是为什么做额外的工作可能需要 0 个 cpu 周期,工作是并发完成的。这不仅限于 AGU,现代处理器具有 许多 可以同时完成作业的执行单元。

我们假设时钟周期和内存周期是等价的

当时不真实,今天非常不真实。内存总线比处理器内核慢数百倍。与距离有关的问题是,电信号传播得越远,它就越难在不损坏的情况下将其传送到目的地。只有慢一点才能解决这个问题。具有千兆赫时钟速率的现代处理器大量投资于 缓存,即在 RAM 中存储数据副本的额外内存。 L1 缓存非常重要,它可以存储 32 KB 的数据和 32 KB 的指令,并且离处理器内核最近。仍然需要 3 个 cpu 周期才能读取。 L2 和 L3 更大,不可避免地会坐得更远,因此需要更多的周期。任何因为从 RAM 读取数据需要 150 个 cpu 周期而遭受执行停顿的程序当然是性能非常差的程序,无论它使用什么指令。

这不是不适的地方,这本书的整个前提在今天非常具有误导性。现代处理器实际上并不执行 x86 指令。它们相当于即时编译器,即在 Java 或 .NET 中使用的那种。他们将 x86 指令翻译成“微操作”,CISC 指令被翻译成 RISC 指令。易于跨多个执行子单元乱序和并发执行的类型。确切地说,这看起来是一个非常保密的秘密,英特尔和 AMD 等公司将其视为知识产权,任何人都不应该知道任何事情。最重要的是,任何人都不应该依赖于,因为这会使他们难以改进他们的处理器设计。

这项创新的一个明显损失是,谈论一条指令占用一定数量的 CPU 周期不再有意义。我已经向您指出了 Agner Fog 的手册。它讨论了延迟,即解码指令并获得结果所需的时间。而吞吐量,受可以同时执行的相同指令数量的影响。这些数字只是给你一个处理器需要工作多难的暗示,它们对于预测程序的实际执行时间完全没有用。添加缓存的状态、内存总线的速度、预取器猜测需要提前检索的内存位置的能力,以及分支预测器在猜测代码流时作为强随机器的运气量.只有分析器才能告诉您花费了多长时间。

【讨论】:

  • 如果这本书像你说的那样过时了,你能指出一本非常容易学习的书来掌握Assembly x86吗?
  • 你问错人了,我30年前就知道了。我今天只使用Intel processor manuals,它们非常棒。我确信这本书可以教你 x86 汇编,只是不要假设它告诉你任何关于处理器实际工作的信息。并且不要认为编写汇编很有用,机器比人类擅长得多。
【解决方案2】:

其他人指出,您正在阅读的这本书已经很老了,所以它告诉您的有关指令时间的内容与今天并不真正相关。关于“内存周期”和“时钟周期”等价的那一点尤其可以追溯到 80 年代初。

一条指令执行需要多少时钟周期取决于指令触发的数据依赖关系,以及 CPU 指令集设计人员在优化指令解码和执行方面的重点。

许多较旧的机器会使用很多时钟来获取和解码复杂的指令,然后通常会使用几个时钟来访问内存(80 年代后期的时钟频率为 10 Mhz)。许多时钟解码是由复杂的指令集设计、缺乏大量资源(空间和晶体管)来进行指令解码以及通过更大的硅几何形状造成的长时间延迟造成的。存储器的访问时间为 70 ns,因此只需要几个时钟(例如 10 个),因为时钟周期要慢得多。

在实现上比现代 CPU 简单得多,CPU 内部通常有一个简单的有限状态机来控制指令执行。通过大致了解此 FSA 的工作原理,您可以预测指令解码时间,包括解码内存寻址的时间,正如您的书所建议的那样。不再正确。

现代机器的时钟频率非常高:2-4 Ghz。这就像 1000 倍一样快。这是可能的,因为晶体管要小得多,因此电流通过它们所需的时间更少。此外,由于晶体管数量众多,设计人员可以在解码/执行和缓存方面投入大量额外的硅。奇怪的是,记忆并没有变得更快,因此以时钟为单位访问记忆所需的相对时间出人意料地增加了。 40 ns 存储器需要 160 个 4Ghz 时钟。

可以说,芯片可以对指令进行解码,并将执行该指令所需了解的所有信息存储在缓存中(现代英特尔 CPU 为复杂指令执行了大部分此操作)。这意味着第一次遇到复杂指令可能需要数十个周期来解码,而再次遇到时可能需要一个时钟(“在解码的指令缓存中查找”),这经常发生(考虑循环)。好消息是平均指令解码时间非常短。坏消息是,您无法准确估计一条指令需要多长时间才能解码和运行,因为这取决于涉及多少优化和缓存,缓存是否因特定指令执行而命中,以及其他哪些指令由于数据依赖性,仍在处理并优先访问资源。

实际上,晶体管仍然(大量但)供应有限,因此指令解码器无法缓存所有内容。设计师们仍在权衡取舍。一个关键的权衡是“简单”(RISC)指令(往往被执行很多)分配了大量资源,使它们能够在每次遇到时快速解码,而复杂指令(往往执行较少)得到更少硬件资源扔给他们。

具体到解码寄存器访问需要多长时间的问题:很明显,访问寄存器首先需要您知道要访问哪个寄存器,即使它在芯片上。所以读完指令后,需要一些时间来提取寄存器字段。如果您认为芯片上的最少时间是以时钟为单位测量的,那么获得该字段需要的时间不止零,而且至少需要 1 个时钟。真正激进的硬件可能会在单个时钟周期内执行多个操作,因此实际上可以设计解码寄存器的 CPU,并且获取指定寄存器的时间少于一个时钟。一些现代 Intel 处理器只需一步即可解码成对指令,例如“compare; jmp conditional”。

从您的角度来看,这意味着平均而言,指令往往执行得相当快。对非缓存位置的内存访问会造成伤害。

我的一阶 CPU 模型是它们的速度无限快,您所要做的就是担心内存访问。这意味着我倾向于用寄存器中的计算(速度很快)来换取内存访问时间。压缩你的数据结构,它没有坏处:-}

【讨论】:

  • 压缩你的数据结构,它没有坏处 :-} - 只是避免虚假共享 :)
  • 是的,如果数据需要同时从多个线程访问,可能不想完全压缩它。尽可能做一个缓存行而不是两个缓存行仍然非常有效,尤其是当您有多个线程同步访问数据时。
【解决方案3】:

寄存器操作数的零周期意味着仅寄存器操作的延迟是操作的基线(寄存器与 ALU 有快速连接)。使用内存操作数时,内存访问延迟会添加到操作延迟中。
内存访问延迟的一部分是地址计算。包含地址(或其中的一部分,在复杂的寻址模式中)的寄存器必须路由到 CPU 的寻址单元而不是其 ALU。
然后该地址用于访问内存,此时 CPU 中的路由是用了 0 个周期还是一个周期的问题变得荒谬:内存延迟可能要大几个数量级。
底线:没有人关心那个周期。

【讨论】:

  • Ummm,所以你是说如果指令中的某个步骤需要访问内存位置以及访问寄存器值,前者所需的时间要大得多,因此寄存器访问时间,就算有,也无所谓?
猜你喜欢
  • 1970-01-01
  • 2016-09-12
  • 1970-01-01
  • 2012-02-17
  • 2022-01-13
  • 2023-04-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多