字节码和解析树之间到底有什么区别,特别是 Perl 使用的那个?它们实际上是指相同的概念,还是有区别?
我熟悉 Python 和 Java 中字节码的概念,但是在阅读 Perl 时,我了解到它应该在其解释器中执行解析树(而不是字节码)。
如果确实有区别,那么 Perl 不使用字节码(或 Python 不使用解析树)的原因是什么?主要是历史原因,还是需要不同编译/执行模型的语言之间存在差异?Perl(通过合理的努力和执行性能)可以通过使用字节码解释器来实现吗?
字节码和解析树之间到底有什么区别,特别是 Perl 使用的那个?它们实际上是指相同的概念,还是有区别?
我熟悉 Python 和 Java 中字节码的概念,但是在阅读 Perl 时,我了解到它应该在其解释器中执行解析树(而不是字节码)。
如果确实有区别,那么 Perl 不使用字节码(或 Python 不使用解析树)的原因是什么?主要是历史原因,还是需要不同编译/执行模型的语言之间存在差异?Perl(通过合理的努力和执行性能)可以通过使用字节码解释器来实现吗?
Perl 使用的不是解析树,至少不是 Wikipedia 如何定义它。这是一个操作码树。
>perl -MO=Concise -E"for (1..10) { say $i }"
g <@> leave[1 ref] vKP/REFC ->(end)
1 <0> enter ->2
2 <;> nextstate(main 49 -e:1) v:%,{,2048 ->3
f <2> leaveloop vK/2 ->g
7 <{> enteriter(next->c last->f redo->8) lKS/8 ->d
- <0> ex-pushmark s ->3
- <1> ex-list lK ->6
3 <0> pushmark s ->4
4 <$> const[IV 1] s ->5
5 <$> const[IV 10] s ->6
6 <#> gv[*_] s ->7
- <1> null vK/1 ->f
e <|> and(other->8) vK/1 ->f
d <0> iter s ->e
- <@> lineseq vK ->-
8 <;> nextstate(main 47 -e:1) v:%,2048 ->9
b <@> say vK ->c
9 <0> pushmark s ->a
- <1> ex-rv2sv sK/1 ->b
a <#> gvsv[*i] s ->b
c <0> unstack v ->d
-e syntax OK
除了,尽管被称为树,它并不是真正的树。注意到箭头了吗?这是因为它实际上是一个类似列表的操作码图(就像任何其他可执行文件一样)。
>perl -MO=Concise,-exec -E"for (1..10) { say $i }"
1 <0> enter
2 <;> nextstate(main 49 -e:1) v:%,{,2048
3 <0> pushmark s
4 <$> const[IV 1] s
5 <$> const[IV 10] s
6 <#> gv[*_] s
7 <{> enteriter(next->c last->f redo->8) lKS/8
d <0> iter s
e <|> and(other->8) vK/1
8 <;> nextstate(main 47 -e:1) v:%,2048
9 <0> pushmark s
a <#> gvsv[*i] s
b <@> say vK
c <0> unstack v
goto d
f <2> leaveloop vK/2
g <@> leave[1 ref] vKP/REFC
-e syntax OK
Perl 的操作码和 Java 的字节码之间的区别在于 Java 的字节码被设计为可序列化的(存储在文件中)。
解析树是程序的标记,存储在显示其嵌套的结构中(哪些参数属于哪个函数调用,哪些语句在哪些循环中等),而字节码是转换为二进制符号的程序代码,以便更快地执行一个虚拟机。例如,如果您有以下虚构语言的代码:
loop i from 1 to 10 {
print i
}
解析树可能如下所示:
loop
variable
i
integer
1
integer
10
block
print
variable
i
同时,为面向堆栈的虚拟机编译的原始和符号形式的字节码可能如下所示:
0x01 0x01 PUSH 1
START:
0x02 DUP
0x03 PRINT
0x05 INCREMENT
0x02 DUP
0x01 0x0a PUSH 10
0x04 LESSTHAN
0x06 0xf9 JUMPCOND START
编译程序时,您首先需要解析源代码(通常生成解析树),然后将其转换为字节码。跳过第二步并直接从解析树执行会更容易。此外,如果语言语法非常复杂(例如它允许修改代码),那么生成字节码会变得更加复杂。如果有一个 eval 类型的函数来执行任何代码,则必须将整个编译器与应用程序一起分发,才能将虚拟机用于此类代码。只包括一个解析器更简单。
在 perl 的下一个版本 Perl 6 中,代码应该被编译为字节码并在 Parrot 虚拟机上运行。预计会提高性能。字节码可以相当简单地进一步编译为处理器的本机指令(这称为 JIT 编译器),以接近 C 等编译语言的速度。