java - 编译为字节码,Java 与 Python。所用时间不同的原因是什么?

标签 java python compilation jvm cpython

Java 和 python(仅讨论 CPython)都被分别解释为 Java 和 CPython 字节码。然后,两个字节码都由它们各自的虚拟机(JVM 和 Cpython VM)解释。 (这里我忽略了运行 10K 后启动的 JIT 编译部分。)
我对此有两个问题:

  • 与 python 相比,为什么 Java 编译为 java 字节码需要这么多时间?在 java 中,编译是一个显式步骤,而在 python 中,它发生在运行时。
  • 为什么在第一次运行时编译到 CPython 字节码并缓存在所有后续运行中使用的 .pyc 文件中时,python 的第一次运行和第 n 次运行之间没有明显差异。这个字节码编译真的是python中几乎零成本的任务吗?

  • 尽管它在运行时中扮演着重要角色,但我认为静态类型与动态类型在编译过程中不应该扮演太大的角色,也不应该是造成这种时序差异的唯一原因。另外,我认为在这两种实现中,在字节码生成期间都会进行一些优化。
    我在这里缺少什么吗? (我没有太多使用 Java 的经验。)
    更新:
    我实际上对 python 第一次运行和后来运行进行了时间分析,发现语句 2 是错误的。运行大型 python 文件时有一个非常明显的区别。
    方法很简单。创建了一个带有重复行的大文件
    a = 5
    b = 6
    c = a*b
    print(str(c))
    
    然后将其导入文件large.py并运行 time python large.py首次运行结果:
    python large.py  1.49s user 0.33s system 97% cpu 1.868 total
    
    第二次运行结果:
    python large.py  0.20s user 0.08s system 90% cpu 0.312 total
    
    删除 __pycache__ 文件夹后:
    python large.py  1.57s user 0.34s system 97% cpu 1.959 total
    
    所以基本上在 python 中,编译为字节码是一个昂贵的过程,只是它不像 java 那样昂贵。

    最佳答案

    Java 字节码编译器必须比 Python 字节码编译器做更多的检查。为了说明这一点,请从“hello world”程序中提取这一行:

    System.out.println("Hello World!");
    
    为了编译这行代码,编译器必须找出它所有部分的含义。这比听起来更复杂:System可以是一个包。或者它可以是一个类,或者在代码所在的同一个包中,或者在一个导入的包中,或者在 java.lang 中。 .所以编译器必须按顺序检查所有这些选项。一旦找到 System类,它必须检查它的访问修饰符是否允许这种使用。
    之后,编译器必须弄清楚 out 是什么是:它是一个嵌套类,还是一个类成员,它的访问修饰符是什么?编译器发现它是 PrintStream 的静态成员变量。类型。然后它必须对 println 进行相同的检查。 .编译器在知道所有这些之前不能为这行代码发出任何代码,因为生成的字节码根据所涉及的对象的类型而有所不同。
    所有这些检查都需要时间,最重要的是,即使对于最简单的程序,编译器也必须从标准库中加载大量的类定义。
    相比之下,Python字节码编译器只需要解析一行,就可以立即生成代码,无需查看额外的模块。在 Python 中,代码将被编译为:
  • 从当前范围 (LOAD_NAME)
  • 中查找“系统”对象
  • 从系统 (LOAD_ATTR)
  • 中查找“out”属性
  • 从“out”(LOAD_METHOD)查找“println”
  • 生成调用它的代码 (CALL_METHOD)

  • Python 编译器并不关心其中一些查找是否在运行时失败。
    另一个重要的区别是 Java 编译器完全用 Java 编写,并在运行时编译为机器代码,而大部分 CPython 实现是提前编译的 C 代码。这意味着与 Python 相比,Java 存在一些“冷启动”问题。
    更新:从 Java 9 开始,您可以直接从源代码运行 Java 程序,而无需将其编译为字节码。运行一个简单的“hello world”程序可以让您了解通过提前将 Java 编译为字节码可以节省多少,即使对于一个简单的程序也是如此:
  • 根据 time python hello.py 测量,python 程序运行时间为 45-50 毫秒。 .
  • 没有提前编译成字节码的 Java 程序运行时间为 350-400 毫秒,用 time java Hello.java 测量。
  • 编译为字节码后的 Java 程序运行时间为 70-80 毫秒,用 time java Hello 测量。

  • 免责声明:没有遵循科学方法或进行统计分析,因此请谨慎对待。测试环境:Python 3.8.5,Java 11.0.8,Fedora 32,Intel i7 8750H CPU
    你好.py:
    print("hello world")
    
    你好.java:
    public class Hello {
        public static void main(String[] args) {
            System.out.println("Hello world");
        }
    }
    

    关于java - 编译为字节码,Java 与 Python。所用时间不同的原因是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63401983/

    相关文章:

    java - 如何防止抛出异常时用户输入被结转?

    java - 通过 maven 插件使用 jar 文件创建 rpm

    python - OpenCV - 查找没有清晰边界的区域轮廓

    python - 如何在 matplotlib 中制作自定义图例

    powershell - 从Powershell编译名称为1.0的gcc库

    java - 无需 I/O 将 BufferedImage 转换为 byte[]

    java - 使用 AndEngine 的加速器传感器方向

    python - 某些 ttk 样式无法从 python 脚本中访问

    emacs - 如何使Emacs识别要编译的bash环境变量?

    java - 无论如何要获取构建 .class 文件时使用的编译标志吗?