代码之家  ›  专栏  ›  技术社区  ›  B.Gen.Jack.O.Neill

如何解释代码的效率甚至很低?(理论的)

  •  4
  • B.Gen.Jack.O.Neill  · 技术社区  · 14 年前

    好吧,首先,我不想在这里爆发任何形式的火焰战或类似的战争。我更大的问题更具理论性,将包括几个例子。

    所以,正如我写的,我无法理解如何解释语言的效率甚至很低。由于它的现代性,我将以Java为例。

    让我们回到没有JIT编译器的时代。Java有它的虚拟机,它基本上是它的硬件。你写代码,而不是编译成字节码,这样至少可以从虚拟机上完成一些工作。但是考虑到甚至RISC指令集在硬件上有多复杂,我甚至想不出在软件仿真硬件上实现它的方法。

    我没有写虚拟机的经验,所以我不知道它是如何在最有效的水平上完成的,但是我想不出比测试每一条指令是否匹配和执行适当的操作更有效的方法。你知道,比如: if(instruction=="something") { (do it) } else if(instruction=="something_diffrent"){ (do it) } 等。。。。

    但这必须非常缓慢。而且,即使有JAVA编译器在JAVA编译器之前慢的文章,他们仍然说它不是很慢。但要进行模拟,实际硬件的许多时钟周期必须执行一个字节码指令。

    而且,甚至整个平台都是基于Java的。例如,Android。第一代Android没有JIT编译器。他们被解释了。但不应该比安卓慢得多吗?但事实并非如此。我知道,当你从android库调用一些api函数时,它们是用机器代码编写的,所以它们是有效的,所以这有很大的帮助。

    但是想象一下,您可以从sratch编写自己的游戏引擎,使用api只是为了显示图像。您需要执行许多数组复制操作,许多计算在模拟时会非常慢。

    现在有一些我承诺的例子。因为我主要是和MCU一起工作,所以我找到了用于AtmelAVR单片机的JVM。说明8MHz单片机每秒可以完成20K的Java OPTC码。但由于AVR可以在一个或两个周期内完成大多数指令,所以我们假设平均有6000000条指令。这就使得没有JIT编译器的JVM比机器代码慢300倍。那么为什么JAVA在没有JIT编译器的情况下如此流行呢?这不是太糟糕的性能损失吗?我就是不明白。谢谢。

    4 回复  |  直到 11 年前
        1
  •  3
  •   David Thornley    14 年前

    我们已经有很长时间的字节代码了。在旧的AppleII上,USCDP系统非常流行,它将Pascal编译成字节码,这将由一个8位6502(可能以2兆赫运行)来解释。这些程序确实运行得相当快。

    字节码解释器通常基于一个跳转表,而不是一个 if/then/else 声明。在C或C++中,这将涉及一个 switch 语句。从根本上讲,解释器将具有相当于处理代码数组的功能,并使用字节代码指令中的操作码作为数组的索引。

    也可以有比机器指令更高级别的字节代码,这样一个字节代码指令就可以转换成几个,有时甚至是许多机器代码指令。为特定语言构造的字节代码可以很容易地做到这一点,因为它只需要匹配该特定语言的控制和数据结构。这就扩展了解释开销,使解释程序更高效。

    与编译语言相比,解释语言可能会有一些速度损失,但这通常并不重要。许多程序以人的速度处理输入和输出,这会导致大量的性能浪费。即使是一个网络绑定的程序,其可用的CPU电源也可能远远超过它所需要的。有些程序可以使用它们所能获得的所有CPU效率,而且由于明显的原因,它们往往不使用解释语言编写。

    当然,还有一个问题,就是你能从一些效率低下中得到什么,而这些效率低下可能会影响你,也可能不会影响你。解释语言实现比编译实现更容易移植,而实际的字节代码通常是可移植的。在语言中使用更高级的功能可能更容易。它允许编译步骤更短,这意味着可以更快地开始执行。如果出现问题,它可能允许更好的诊断。

        2
  •  0
  •   James Curran    14 年前

    但安卓不应该太慢吗?

    定义“非常慢”。这是一部电话。在你拨第二个数字之前,它必须处理“拨第一个数字”。

    在任何交互应用中,限制因素总是人的反应时间。它可能比用户慢100倍,而且仍然比用户快。

    所以,要回答你的问题,是的,口译员速度很慢,但他们通常足够快,特别是在硬件越来越快的时候。

    请记住,当Java被引入时,它被出售为Web applet语言(现在替换为JavaScript——也被解释)。只有在JIT编译之后,它才在服务器上流行起来。

    字节码解释器可以比使用跳转表的if()行更快:

     void (*jmp_tbl)[256] = ...;  /* array of function pointers */
     byte op = *program_counter++;
     jmp_tbl[op]();
    
        3
  •  0
  •   vanza    14 年前

    处理这个问题有两种不同的方法。

    (i)“为什么可以运行慢代码”

    正如詹姆斯前面提到的,有时候执行速度并不是你所感兴趣的。对于许多以解释模式运行的应用程序来说,速度可以“足够快”。你必须考虑到你写的代码将如何被使用。

    (ii)“为什么解释代码是有意义的”

    有许多方法可以实现解释器。在您的问题中,您将讨论最幼稚的方法:基本上是一个大开关,在阅读时解释每个JVM指令。

    但是您可以优化它:例如,您不必查看单个JVM指令,而是可以查看它们的序列,并查找具有更有效解释的模式。Sun的JVM实际上在解释器本身中进行了一些优化。在以前的一项工作中,一个人花了一些时间来优化解释器,并且解释了Java字节码在他改变之后运行速度明显快。

    但是在包含JIT编译器的现代JVM中,解释器只是JIT完成工作之前的垫脚石,因此人们不会花太多时间优化解释器。

        4
  •  0
  •   Jerry Coffin    14 年前

    12兆赫将是一个阁楼,这是一个8位微处理器。这意味着(例如)一个本机“添加”指令只能将两个8位数字加在一起以得到9位结果。JVM基本上是一个虚拟的32位处理器。这意味着它的加法指令将两个32位数字相加,得到33位的结果。

    因此,当您比较指令速率时,您应该期望指令速率降低4:1作为绝对最小值。实际上,虽然用4个8位加法(带进位)来模拟32位加法很容易,但有些东西的比例并不是这样的。例如,根据Atmel自己的 app note ,产生32位结果的16x16乘法在约218个时钟周期内执行。同样的应用程序注释显示了一个16/16位的除法(产生一个8位的结果),在255个周期内运行。

    假设这些比例是线性的,我们可以预期32位版本的乘法需要大约425-450个时钟周期,除法需要大约510个周期。在现实中,我们可能会期望一点开销,这将进一步降低速度——至少增加10%的估计可能会使它们更现实。

    底线:当你将苹果和苹果进行比较时,很明显你所说的所有速度差异都是不真实的(或者根本不是由JVM开销造成的)。