给定特定的计算机系统,是否有可能估算一段汇编代码的实际精确运行时间?


23

这是一段汇编代码

section .text
    global _start       ;must be declared for using gcc
_start:                     ;tell linker entry point
    mov edx, len    ;message length
    mov ecx, msg    ;message to write
    mov ebx, 1      ;file descriptor (stdout)
    mov eax, 4      ;system call number (sys_write)
    int 0x80        ;call kernel
    mov eax, 1      ;system call number (sys_exit)
    int 0x80        ;call kernel

section .data

msg db  'Hello, world!',0xa ;our dear string
len equ $ - msg         ;length of our dear string

给定特定的计算机系统,就可以准确地预测一段汇编代码的实际运行时间。


30
“在该计算机上运行代码并使用秒表”是否有效?
Draconis

4
我怀疑执行这段代码所花费的大部分时间都在等待I / O。如果您知道代码的内存位置以及有关处理器的所有详细信息(当今非常复杂),则执行单个指令所需的时间在某种程度上是可以预测的,但是速度也会受内存和磁盘的影响,因此, d还必须了解有关它们的大量详细信息。因此,除非您考虑物理现象(也会影响时间),否则您可以说它是可以预测的,但是很难做到。
IllidanS4希望莫妮卡

4
总是可以估计...
sudo rm -rf斜杠

3
由于停顿问题,这也不是不可能的吗?我们可以为某些代码证明它是否会暂停,但是我们无法找到一种算法来确定所有可能的代码。
kutschkem

2
@Falco那将是给定系统的属性。一些独立的C实现没有操作系统。正在运行的全部是一个主循环(或什至不是一个循环;-)),该主循环可能会也可能不会从硬件地址读取作为输入。
彼得-恢复莫妮卡

Answers:


47

我只能引用一个相当原始的CPU的手册,即1986年左右的68020处理器:“即使您对处理器实现有确切的了解,也很难计算出指令序列的确切运行时间”。我们没有。与现代处理器相比,该CPU是原始的

我无法预测该代码的运行时间,您也不能。但是,当处理器具有大量的缓存和大量的乱序功能时,您甚至无法定义一段代码的“运行时”。一个典型的现代处理器可以有200条指令“在运行中”,即处于执行的各个阶段。因此,从尝试读取第一个指令字节到退出最后一条指令的时间可能会很长。但是,处理器需要做的所有其他工作的实际延迟可能会(通常通常)要少得多。

当然,对操作系统进行两次调用会使此操作完全不可预测。您不知道“写入标准输出”的实际作用,因此您无法预测时间。

而且您无法在运行代码的确切时刻知道计算机的时钟速度。它可能处于某种节能模式,计算机可能会因为变热而降低了时钟速度,因此即使相同数量的时钟周期也可能花费不同的时间。

总而言之:完全不可预测。


11
我认为您的结论太强了。延迟和吞吐量是衡量程序“运行时间”的常用指标。您也可以简单地确定“运行时”的适当定义。此外,如果您具有系统状态,硬件和软件的完整快照,以及对CPU内部的全面了解,则可以预测运行时间。在英特尔,他们大概可以估计运行时间,即使在SO上,我们也可以以周期精度预测延迟和吞吐量。在这种情况下,除了syscalls之外,还没有那么困难。
玛格丽特·布鲁姆

9
@MargaretBloom甚至没有。我将手机放在烤箱附近,CPU不足以管理温度,您的运行时估计突然太低。而且即使您按周期计数并且不执行系统调用,其他线程和CPU也会在RAM内容上很好地发挥作用,或者在换出内存时它们可能会将您的内存转储到硬盘驱动器上,这基于不可预知的情况,从电源到电源浪涌使硬盘驱动器速度降低到足以使竞争线程及时获得足够的内存来破坏您的内存,一直到线程直接滚动骰子以查看浪费了多少时间。
John Dvorak

6
除此之外,methinks还说:“全面了解系统状态,硬件和软件”是一个相当高的要求。加上“提前10毫秒”,您已经在要求不可能了。而且,如果您的CPU的硬件随机数生成实现使用量子现象(可能确实如此),并且CPU上的某些线程调用了量子现象,那么即使不知道计算机周围3000 km的宇宙的完整状态也可以为您节省时间。在MWI中,您甚至无法猜对。
John Dvorak

8
@Nat:即使在密码学中,“恒定时间”也并不真正意味着绝对恒定,仅意味着运行时间没有依赖秘密数据的系统变化,并且可以与之进行统计相关。并且在实践中,通常仅假设如果采用的代码路径和执行的存储器访问模式不依赖于秘密数据,并且是否避免了已知耗时可变的特定指令(或将它们的输入屏蔽为希望消除相关性),可能就足够了。除此之外,您真的只需要测量它。
Ilmari Karonen

2
68020是复杂的野兽……尝试使用MCS51 ....
rackandboneman

29

你不能在一般的做到这一点,但在某种意义上,你很可以了,已经有一些历史的情况下,您确实到。

雅达利2600(或雅达利电视电脑系统)是最早的家用视频游戏系统之一,并于1978年首次发布不同的是时代的更高版本的系统,Atari公司无力给该设备的帧缓冲,这意味着CPU过在每条扫描线上运行代码以确定要产生的内容-如果这段代码花费了17.08微秒(HBlank间隔)运行,则在扫描线开始绘制图形之前,将无法正确设置图形。更糟糕的是,如果程序员想要绘制比Atari通常所允许的内容更复杂的内容,则他们必须测量指令的准确时间并在绘制光束时更改图形寄存器,整个扫描线的跨度为57.29微秒。

但是,Atari 2600与许多其他基于6502的系统一样,具有非常重要的功能,可以实现这种情况所需的仔细时间管理:CPU,RAM和TV信号都基于同一主时钟而不再运行时钟时钟。电视信号以3.98 MHz的时钟运行,将上述时间分成管理电视信号的整数个“彩色时钟”,CPU和RAM时钟的周期恰好是三个彩色时钟,从而使CPU时钟可以相对于当前进度电视信号的准确时间度量。(有关更多信息,请查阅为Stella Atari 2600模拟器编写的《 Stella程序员指南》)。

此外,该操作环境意味着每条CPU指令都有在每种情况下都需要定义的周期数,并且许多6502开发人员在参考表中发布了此信息。例如,考虑这个条目的CMP指令,从取(用蓄电池比较内存)此表

CMP  Compare Memory with Accumulator

     A - M                            N Z C I D V
                                    + + + - - -

     addressing    assembler    opc  bytes  cycles
     --------------------------------------------
     immediate     CMP #oper     C9    2     2
     zeropage      CMP oper      C5    2     3
     zeropage,X    CMP oper,X    D5    2     4
     absolute      CMP oper      CD    3     4
     absolute,X    CMP oper,X    DD    3     4*
     absolute,Y    CMP oper,Y    D9    3     4*
     (indirect,X)  CMP (oper,X)  C1    2     6
     (indirect),Y  CMP (oper),Y  D1    2     5*

*  add 1 to cycles if page boundary is crossed

利用所有这些信息,Atari 2600(和其他6502开发人员)能够准确地确定他们的代码执行所花费的时间,并构建满足其要求并仍然符合Atari电视信号时序要求的例程。而且由于该计时非常精确(特别是对于诸如NOP这样的浪费时间的指令),他们甚至能够在绘制图形时使用它来修改图形。


当然,Atari 6502是一个非常特殊的情况,所有这一切都是可能的,因为该系统具有以下所有功能:

  • 一个主时钟运行所有内容,包括RAM。现代系统具有CPU和RAM的独立时钟,RAM时钟通常较慢,并且两者不一定同步。
  • 没有任何类型的缓存-6502始终直接访问DRAM。现代系统具有SRAM高速缓存,这使得更难以预测状态-尽管也许仍然可以预测具有高速缓存的系统的行为,但绝对要困难得多。
  • 没有其他程序同时运行-墨盒上的程序可以完全控制系统。现代系统使用非确定性调度算法一次运行多个程序。
  • 时钟速度足够慢,因此信号可能会在整个系统中及时传播。例如,在时钟速度为4 GHz的现代系统上,光子要经过6.67个时钟周期才能移动到半米母板的长度上-您永远都无法期望现代处理器与板上的其他器件发生交互只需一个周期,因为板上的信号甚至需要一个多周期才能到达器件。
  • 一个定义明确的时钟速度很少改变(在Atari情况下为1.19 MHz)-现代系统的CPU速度一直在变化,而Atari在不影响电视信号的情况下也无法做到这一点。
  • 发布的周期计时-x86并未定义其任何指令花费的时间。

所有这些东西加在一起创建了一个系统,可以在该系统上编写花费了确切时间的指令集-对于此应用程序,这正是所需要的。大多数系统之所以没有这种精确度,仅仅是因为不需要它-计算可以在完成时完成,或者如果需要精确的时间,则可以查询独立的时钟。但是,如果需求是正确的(例如在某些嵌入式系统上),它仍然可以出现,并且您将能够准确确定代码在这些环境中运行需要多长时间。


而且,我还应该添加一个巨大的免责声明,即所有这些仅适用于构建一组汇编指令,这将花费确切的时间。如果您想要做的是任意组装,即使在这些环境中,并询问“执行此操作需要多长时间?”,则您绝对无法做到这一点-那就是Halting Problem,已被证明是无法解决的。


编辑1:在此答案的先前版本中,我说过Atari 2600无法通知处理器它在电视信号中的位置,这迫使它从一开始就保持整个节目的计数和同步。正如我在评论中向我指出的那样,这在某些系统(例如ZX Spectrum)中是正确的,但在Atari 2600中则不是,因为它包含一个硬件寄存器,该寄存器可以暂停CPU直到下一个水平消隐间隔发生,以及随意开始垂直消隐间隔的功能。因此,计数周期的问题仅限于每条扫描线,并且仅当开发人员希望在绘制扫描线时更改内容时才变得精确。


3
还应该注意的是,大多数游戏不能完美运行-您可能会由于视频信号时序不匹配(由于程序员错误(CPU时序的估计不正确)或仅仅是过多)而在视频输出中看到许多失真现象要做的工作。它也非常脆弱-如果您需要修复错误或添加新功能,则很有可能会打乱时间,有时是不可避免的。这很有趣,但也带来了一场噩梦:)我什至不确定时钟速度是否始终正确(例如在过热,干扰等情况下)。但这绝对表明即使在那时也很难。
a安

1
很好的答案,尽管我想断言您不必计算Atari 2600上每条指令的周期数。它有两个功能可帮助您不必这样做:初始化的倒数计时器和然后轮询以查看其是否已达到0,并使用一个寄存器暂停CPU,直到下一次水平消隐开始为止。ZX Spectrum等许多其他设备都没有这样的设备,实际上,您必须计算垂直消隐中断后花费的每个周期,才能知道屏幕的位置。
Martin Vilcans

1
我认为停止问题并不严格适用于Atari。如果排除Atari的I / O功能并将其限制为典型的盒式ROM,则存储空间有限。此时,您拥有一个有限状态机,因此它上的任何程序都必须停止或进入它之前进入的状态,从而在有限时间内导致可证明的无限循环。
user1937198

1
@ user1937198 128个字节的状态(加上寄存器中的所有内容)要多得多,因此要有足够的状态空间来使它与Turing机器的理论无限磁带之间的区别成为仅在理论上重要的区别。地狱,我们实际上无法搜索AES密钥之类的128 BITS。...当您添加位时,状态空间会迅速增长。不要忘记相当于“禁用中断;几乎可以肯定的是“停止”。
Dan Mills

1
“这是暂停问题,已被证明是无法解决的。如果遇到此问题,则需要打破秒表并实际运行代码。” –这没有任何意义。您不能通过“实际”运行代码而不是模拟代码来逃避Turing的证明。如果停止,则可以计时停止的时间。如果它不停止,那么您将永远无法确定(一般而言)它将来会停止还是永远运行。真正的秒表或模拟的秒表都是同样的问题。至少在模拟中,您可以更轻松地检查内部状态是否存在循环迹象。
benrg

15

这里有两个方面的作用

正如@ gnasher729指出的那样,如果我们知道要执行的确切指令,由于诸如缓存,分支预测,缩放等操作,仍然很难估计确切的运行时间。

但是,情况甚至更糟。给定大量的汇编,就不可能知道将执行哪些指令,甚至无法知道将运行多少指令。这是因为赖斯定理:如果我们可以精确地确定这一点,那么我们就可以使用该信息来解决停止问题,这是不可能的。

汇编代码可以包含跳转和分支,这些跳转和分支足以使程序的完整跟踪成为可能。已经有一些关于执行时间的保守估计的工作,它通过诸如成本语义或带注释的类型系统之类的东西为执行提供了上限。我对组装没有什么特别的熟悉,但是如果存在类似的东西,我不会感到惊讶。


4
我的意思是,暂停问题直接适用于此处,因为如果我们知道运行时间,就知道它是否会暂停。另外,没有条件的事实在这里甚至都无济于事,因为在x86中,mov图灵完成
BlueRaja-Danny Pflughoeft

7
赖斯(Rice)和暂停问题(Halting Problem)是有关任意(任何)程序的语句-但此处的操作规范已在问题中指定了一段特定的代码。您可以确定有关单个或有限类别程序的语义和暂停属性,对吗?只是没有涵盖所有程序的通用程序。
Daniel R. Collins

2
我们可以确切地知道接下来将执行哪一条指令,我们无法确定是否曾经打过a sys_exit从而停止了秒表。如果我们限制终止程序,这对于这样一个实际问题是合理的,那么答案实际上是肯定的(当然,在启动程序之前,您已经获得了系统状态hw和sw的完美快照)。
玛格丽特·布鲁姆

1
@ BlueRaja-DannyPflughoeft Mov是图灵完整的,但是在此OP中没有这段代码。但是,除了该点的反正-的intS能够执行任意我任意代码,等待/ O操作等
Luaan

2

“计算机系统”的选择是否会包括微控制器?一些微控制器具有非常可预测的执行时间,例如8位PIC系列每条指令具有四个时钟周期,除非该指令分支到另一个地址,从闪存读取或为特殊的两字指令。

中断会明显破坏这种时序,但是如果没有“裸机”配置中的中断处理程序,则可能会做很多事情。

使用汇编和特殊的编码样式,可以编写总是花费相同时间执行的代码。现在,大多数PIC变体具有多个定时器并不普遍,但是有可能。


2

早在8位计算机时代,一些游戏就做到了这一点。程序员将根据执行指令所花费的时间以及CPU的已知时钟速度来使用执行指令所花费的确切时间,以与视频和音频硬件的准确时序进行同步。那时,显示器是阴极射线管监视器,它将以固定的速率循环通过屏幕的每一行,并通过打开和关闭阴极射线以激活或关闭荧光粉来绘制该行像素。因为程序员需要在光束到达屏幕的那部分之前告诉视频硬件要显示的内容,并使剩余的代码适合剩余的任何时间,所以他们称其为“竞赛光束”。

它绝对不能在任何现代计算机上运行,​​也无法在示例代码中运行。

为什么不?以下是一些可能会弄乱简单可预测时间的事情:

CPU速度和内存获取都是执行时间的瓶颈。运行CPU的速度比获取指令的执行速度快,或者安装的内存交付字节的速度比CPU接受它们的速度快,这是浪费金钱。因此,旧计算机都在同一时间运行。现代CPU的运行速度比主内存快得多。他们通过具有指令和数据高速缓存来进行管理。如果CPU需要等待不在缓存中的字节,它将仍然停顿。因此,如果相同的指令已经在缓存中,则它们的运行将比未缓存的指令快得多。

此外,现代CPU具有很长的流水线。他们通过让芯片的另一部分对流水线中的下一条指令进行初步工作来保持高吞吐量。如果CPU不知道下一条指令是什么,则将失败,如果有分支,则可能发生。因此,CPU 尝试预测条件跳转。 (此代码段中没有任何内容,但是可能有一个错误预测的有条件跳转到该语句,从而阻塞了管道。此外,还有很好的借口来链接这个传奇的答案。)类似地,调用int 80陷阱进入内核模式的系统实际上使用复杂的CPU功能,即中断门,会引入无法预料的延迟。

如果您的操作系统使用抢占式多任务处理,则运行此代码的线程可能随时会丢失其时间片。

加速光束运行也仅是因为程序在裸机上运行并直接在硬件上撞击。在这里,您正在打电话int 80进行系统调用。这会将控制权移交给操作系统,因此无法保证时间。然后,您告诉它在任意流上执行I / O,该流可能已重定向到任何设备。您说I / O花费多少时间实在是太抽象了,但是它肯定会支配执行指令所花费的时间。

如果要在现代系统上获得准确的计时,则需要引入一个延迟循环。您必须使较快的迭代以最慢的速度运行,这是不可能的。人们在现实世界中这样做的原因之一是防止将密码信息泄漏给攻击者,后者可能花费更多的时间进行请求。


1

这有点切线,但是航天飞机有4台冗余计算机,这些计算机依赖于精确同步,即它们的运行时精确匹配。

当后备飞行软件(BFS)计算机拒绝与四台主要航空电子软件系统(PASS)计算机进行同步时,航天飞机的首次发射尝试被取消。此处的 “ Bug Heard环游世界”中的详细信息。引人入胜的有关该软件如何开发以适应不同周期的信息,可能会给您带来有趣的背景。


0

我认为我们在这里混合了两个不同的问题。(是的,我知道其他人已经说过这一点,但我希望我可以更清楚地表达出来。)

首先,我们需要从源代码获取到实际执行的指令序列(这需要输入数据以及代码的知识-您循环执行多少次?测试之后采取哪个分支? )。由于暂停问题,指令的顺序可能是无限的(不终止),即使知道输入数据,也无法始终静态确定。

建立了要执行的指令序列之后,您便需要确定执行时间。当然可以用一些系统架构知识来估计。但是问题在于,在许多现代机器上,执行时间在很大程度上取决于内存提取的缓存,这意味着它不仅取决于输入数据,还取决于执行的指令。它还取决于对条件分支目标的正确猜测,这又取决于数据。因此,这仅是一个估计,并不准确。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.