为什么未执行的编译时代码会增加 Raku 的字节码大小?它会降低运行时性能吗?
Why does non-executed compile-time code increase Raku's bytecode size? Does it slow runtime performance?
考虑以下两个程序:
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
标志的 运行 时也会跳过 DOC CHECK
块。与此一致,当给定一个 DOC CHECK
块时,hello-world 程序的字节码确实 而不是 增加大小。但是,如果该块包含 use
语句,它 会 增加大小。由此,我得出结论 use
在某种程度上是特殊情况,甚至在 DOC CHECK
块中也会执行。那是对的吗?如果是这样,我应该了解其他类似的特殊形式吗?
CHECK
或 BEGIN
块(或其他 BEGIN
时间构造)可能包含转义代码。例如:
BEGIN SomeClass.^add_method('foo', anon method foo() { 42 })
向 class 添加方法,该方法存在于 BEGIN
块的边界之外。因此,编译输出中需要该方法的字节码。目前,Rakudo 保守地将所有内容的字节码包含在 BEGIN
或 CHECK
块中。以后有可能在一些简单的情况下避免这种情况。
就 运行 时间成本而言,实现会竭尽全力将永远不会 运行 的字节码成本降至最低(对于这种情况来说不是很多,但是因为标准图书馆很大,但许多程序只使用其中的一小部分)。例如:
- 字节码是
mmap
的,所以它的一些未使用的部分实际上可能不会被分页到内存中
- 字节码仅在第一次调用该帧时验证
- 框架meta-data(它有什么词法)只在第一次调用框架时反序列化
- 除非有东西引用它,否则代码对象不会被反序列化
就use
而言,它的动作在解析后立即执行。在 DOC CHECK
块内并不会抑制这一点 - 通常不能,因为 use
可能会带来需要知道的东西才能完成对该块内容的解析。
考虑以下两个程序:
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
标志的 运行 时也会跳过 DOC CHECK
块。与此一致,当给定一个 DOC CHECK
块时,hello-world 程序的字节码确实 而不是 增加大小。但是,如果该块包含 use
语句,它 会 增加大小。由此,我得出结论 use
在某种程度上是特殊情况,甚至在 DOC CHECK
块中也会执行。那是对的吗?如果是这样,我应该了解其他类似的特殊形式吗?
CHECK
或 BEGIN
块(或其他 BEGIN
时间构造)可能包含转义代码。例如:
BEGIN SomeClass.^add_method('foo', anon method foo() { 42 })
向 class 添加方法,该方法存在于 BEGIN
块的边界之外。因此,编译输出中需要该方法的字节码。目前,Rakudo 保守地将所有内容的字节码包含在 BEGIN
或 CHECK
块中。以后有可能在一些简单的情况下避免这种情况。
就 运行 时间成本而言,实现会竭尽全力将永远不会 运行 的字节码成本降至最低(对于这种情况来说不是很多,但是因为标准图书馆很大,但许多程序只使用其中的一小部分)。例如:
- 字节码是
mmap
的,所以它的一些未使用的部分实际上可能不会被分页到内存中 - 字节码仅在第一次调用该帧时验证
- 框架meta-data(它有什么词法)只在第一次调用框架时反序列化
- 除非有东西引用它,否则代码对象不会被反序列化
就use
而言,它的动作在解析后立即执行。在 DOC CHECK
块内并不会抑制这一点 - 通常不能,因为 use
可能会带来需要知道的东西才能完成对该块内容的解析。