7

考虑以下两个程序:

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,hello-world 程序的字节码不会增加大小。DOC CHECK但是,如果块包含语句,它的大小确实会增加。use由此,我得出结论,这use是某种特殊情况,甚至可以在DOC CHECK块中执行。那是对的吗?如果是这样,我应该知道其他类似的特殊形式吗?

4

1 回答 1

8

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

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

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

就运行时成本而言,实现会花费一些时间来最小化从未运行的字节码的成本(在这种情况下不是那么多,但因为标准库很大,但许多程序只使用它的一小部分)。例如:

  • 字节码是mmap'd,所以它的一些未使用的部分实际上可能没有被分页到内存中
  • 仅在第一次调用该帧时验证字节码
  • 帧元数据(它有什么词法)仅在第一次调用帧时反序列化
  • 除非有东西引用它,否则代码对象不会被反序列化

就目前use而言,它的动作在解析后立即执行。在一个DOC CHECK块内并不会抑制这一点——而且通常不能,因为这use可能会带来需要知道的东西才能完成对该块内容的解析。

于 2020-08-26T14:08:40.620 回答