让我们忘记VMs一秒钟(我们会回到下面的那个,我保证),并从这个重要的事实开始:
C没有垃圾收集。
对于提供垃圾回收的语言,必须要执行一些“运行时”/运行时环境/事物。
这就是为什么Python,Java和Haskell需要一个“运行时”,而C不能直接编译为本地代码。
请注意,psyco是一个Python优化器,它将Python代码编译为机器代码,但是很多机器代码包含对C-Python运行时函数的调用,如PyImport_AddModule,PyImport_GetModuleDict等。
Haskell / GHC与psyco编译的Python类似。 Ints作为简单的机器指令添加,但更复杂的东西分配对象等,调用运行时。
还有什么?
C没有“异常”
如果我们添加C的例外,我们生成的机器代码将需要为每个函数和每个函数调用做一些事情。
如果我们再添加“关闭”,还会添加更多的东西。
现在,我们可以将每个功能中的代码机器代码重复,而不用再调用子程序来执行必要的操作,比如PyErr_Occurred。
所以现在,基本上每一个原始的源代码行映射到一些函数的一些调用和一个较小的独特的部分。
但是,只要我们每个原始源代码行都做这么多的东西,为什么还要打扰机器代码?
这是一个想法(btw我们称之为“虚拟机”)。
我们来代表你的Python代码,例如:
def has_no_letters(text):
return text.upper() == text.lower()
作为内存数据结构,例如:
{ 'func_name': 'has_no_letters',
'num_args': 1,
'kwargs': [],
'codez': [
('get_attr', 'tmp_a', 'arg_0', 'upper'), # tmp_a = arg_0.upper
('func_call', 'tmp_b', 'tmp_a', []), # tmp_b = tmp_a() # tmp_b = arg_0.upper()
('get_attr', 'tmp_c', 'arg_0', 'lower'),
('func_call', 'tmp_d', 'tmp_c', []),
('get_global', 'tmp_e', '=='),
('func_call', 'tmp_f', 'tmp_e', ['tmp_b', 'tmp_d']),
('return', 'tmp_f'),
]
}
现在,我们来写一个执行这个内存数据结构的解释器。
我们来讨论这个直接来自文本解释器的好处,然后讨论编译到机器代码的好处。
虚拟机对直接从文本解释器的好处
>在执行代码之前,VM系统会提供所有的语法错误。
>在评估循环时,VM系统每次运行时都不会解析源代码。
使虚拟机比直接从文本解释器更快。
因此,直接解释器运行速度较慢,具有长变量名称,并且具有较短的变量名称。这鼓励人们写出诸如wt(f,d(o,e),s)=(i,s)cr(a,p * d o)的令人印象深刻的数学家风格的代码,
虚拟机对编译到机器代码的好处
>描述该程序的内存中数据结构或“VM代码”可能会比全面的机器代码要紧凑得多,对于每个原始代码行,它都会一次又一次地执行相同的操作。这将使VM系统运行更快,因为需要从内存中提取更少的“指令”。>创建虚拟机比创建机器代码的编译器要简单得多。现在你甚至可以不知道任何程序集/机器代码。