为什么未执行的编译时代码会增加Raku的字节码大小?它会降低运行时性能吗?

问题描述

请考虑以下两个程序:

unit module Comp;
say 'Hello,world!'

unit module Comp;
CHECK { if $*disTRO.is-win { say 'compiling on Windows' }}
say 'Hello,world!'

天真的,我希望两个程序都能编译为完全相同的字节码:CHECK块指定要在编译结束时运行的代码;检查变量然后不执行任何操作对程序的运行时行为没有影响,因此(我认为)不需要在编译的字节码中包含

但是,编译这两个程序并不会导致相同的字节码。具体而言,编译不带有CHECK块的版本会产生24K字节码,而使用该版本的字节码为60K。为什么这两个版本的字节码不同?字节码中的这种差异是否具有(或可能具有)运行时成本? (似乎必须,但我想确定)。

还有一个相关的问题:DOC CHECK块如何与以上内容相符?我的理解是,即使编译器不与DOC CHECK标志一起运行时,它们也会跳过--doc块。与此相一致,当给定DOC CHECK块时,hello-world程序的字节码不会增大大小。但是,如果该块包含use语句,则它的大小确实会增加。由此得出的结论是,use在某种程度上是特殊情况,甚至可以在DOC CHECK块中执行。那是对的吗?如果是这样,还有其他我应该知道的类似特殊形式的表格吗?

解决方法

CHECKBEGIN块(或其他BEGIN时间构造)可能包含转义的代码。例如:

BEGIN SomeClass.^add_method('foo',anon method foo() { 42 })

向类添加一个方法,该方法存在于BEGIN块的范围之外。因此,在编译输出中需要该方法的字节码。当前,Rakudo保守地将所有内容的字节码包含在BEGINCHECK块中。将来在某些简单情况下,有可能避免这种情况。

就运行时成本而言,该实现会花一些时间来最大程度地减少永远不会运行的字节码的成本(在这种情况下不算太多,但是因为标准库很大,但是许多程序只使用了一部分代码)它)。例如:

  • 字节码为mmap,因此它的一些未使用部分实际上可能不会分页到内存中
  • 仅在首次调用该帧时验证字节码
  • 仅在第一次调用框架时反序列化框架元数据(它具有的词汇)
  • 除非有人引用它,否则不会反序列化代码对象

use而言,其动作一经解析就立即执行。放在DOC CHECK块中并不能抑制这种情况,通常不能这样做,因为use可能会带来一些需要知道的内容才能完成对该块内容的解析。