解释器如何翻译 for 循环?
How does an interpreter translate a for loop?
我理解解释器是把你的源代码逐行翻译成机器码,遇到错误就停止
我想知道,当你给它 for 循环时,解释器会做什么。
例如我有以下 (MATLAB) 代码:
for i = 1:10000
pi*pi
end
真的运行逐行翻译for循环10000次吗?
编译器的机器代码更短,仅由一组语句组成,其中包括对 10000 次迭代有效的 go to control 语句。
很抱歉,如果这没有意义,我对编程的基本原理不是很了解,但我想快速理解。
I understand that interpreter translates your source code into machine code line by line, and stops when it encounters an error.
这是错误的。有许多不同类型的解释器,但很少有逐行执行代码的,而那些执行的解释器(主要是 shell)根本不生成机器代码。
通常有四种或多或少常见的代码解释方式:
逐个语句执行
这大概就是你所说的逐行的意思,只是分号通常可以用来代替换行符。正如我所说,这几乎只能由 shell 完成。
它的工作原理是一次解析一个语句。那就是解析器读取标记,直到语句完成。对于直到达到语句终止符的简单语句,例如行尾或分号。对于其他语句(如 if 语句、for 循环或 while 循环),直到找到相应的终止符(endif、fi 等)。无论哪种方式,解析器 returns 语句的某种表示形式(通常是某种类型的 AST),然后执行。任何时候都不会生成机器代码。
这种方法有一个不寻常的 属性,即文件末尾的语法错误不会阻止文件开头的执行。然而,所有内容仍然最多被解析一次,即使条件为假,if 语句的主体等仍将被解析(因此 if false
中的语法错误仍将中止脚本)。
AST-行走解释
此处一次解析整个文件并从中生成一个 AST。然后解释器简单地遍历 AST 来执行程序。原理和上面一样,只是先解析整个程序。
字节码解释
再次一次解析整个文件,但解析器生成的不是 AST 类型的结构,而是某种字节码格式。该字节码然后逐条指令执行。
JIT 编译
这是唯一实际生成机器代码的变体。这有两种变体:
- 在调用所有函数之前为它们生成机器代码。这可能意味着在加载文件后立即翻译整个文件,或者在调用每个函数之前翻译每个函数。生成代码后,执行它。
- 首先解释字节码,然后在执行多次后分别对特定函数或代码路径进行 JIT 编译。这使我们能够根据解释期间收集的使用数据进行某些优化。这也意味着我们不会为不常调用的函数支付编译开销。一些实现(特别是一些 JavaScript 引擎)还会重新编译已经 JIT 的代码,以根据新收集的使用数据进行优化。
所以总而言之:逐行(或更确切地说逐条语句)执行代码的实现与生成机器代码的实现之间的重叠应该非常接近于零。而那些逐个语句执行的实现仍然只解析一次代码。
我理解解释器是把你的源代码逐行翻译成机器码,遇到错误就停止
我想知道,当你给它 for 循环时,解释器会做什么。
例如我有以下 (MATLAB) 代码:
for i = 1:10000
pi*pi
end
真的运行逐行翻译for循环10000次吗?
编译器的机器代码更短,仅由一组语句组成,其中包括对 10000 次迭代有效的 go to control 语句。
很抱歉,如果这没有意义,我对编程的基本原理不是很了解,但我想快速理解。
I understand that interpreter translates your source code into machine code line by line, and stops when it encounters an error.
这是错误的。有许多不同类型的解释器,但很少有逐行执行代码的,而那些执行的解释器(主要是 shell)根本不生成机器代码。
通常有四种或多或少常见的代码解释方式:
逐个语句执行
这大概就是你所说的逐行的意思,只是分号通常可以用来代替换行符。正如我所说,这几乎只能由 shell 完成。
它的工作原理是一次解析一个语句。那就是解析器读取标记,直到语句完成。对于直到达到语句终止符的简单语句,例如行尾或分号。对于其他语句(如 if 语句、for 循环或 while 循环),直到找到相应的终止符(endif、fi 等)。无论哪种方式,解析器 returns 语句的某种表示形式(通常是某种类型的 AST),然后执行。任何时候都不会生成机器代码。
这种方法有一个不寻常的 属性,即文件末尾的语法错误不会阻止文件开头的执行。然而,所有内容仍然最多被解析一次,即使条件为假,if 语句的主体等仍将被解析(因此
if false
中的语法错误仍将中止脚本)。AST-行走解释
此处一次解析整个文件并从中生成一个 AST。然后解释器简单地遍历 AST 来执行程序。原理和上面一样,只是先解析整个程序。
字节码解释
再次一次解析整个文件,但解析器生成的不是 AST 类型的结构,而是某种字节码格式。该字节码然后逐条指令执行。
JIT 编译
这是唯一实际生成机器代码的变体。这有两种变体:
- 在调用所有函数之前为它们生成机器代码。这可能意味着在加载文件后立即翻译整个文件,或者在调用每个函数之前翻译每个函数。生成代码后,执行它。
- 首先解释字节码,然后在执行多次后分别对特定函数或代码路径进行 JIT 编译。这使我们能够根据解释期间收集的使用数据进行某些优化。这也意味着我们不会为不常调用的函数支付编译开销。一些实现(特别是一些 JavaScript 引擎)还会重新编译已经 JIT 的代码,以根据新收集的使用数据进行优化。
所以总而言之:逐行(或更确切地说逐条语句)执行代码的实现与生成机器代码的实现之间的重叠应该非常接近于零。而那些逐个语句执行的实现仍然只解析一次代码。