可虚拟化对象¶
注意: 本文档没有关于如何理解基础知识的适当介绍。我们应该写一些。如果您碰巧在这里并且缺少上下文,请随时在 IRC 上骚扰我们。
问题描述¶
JIT 非常擅长确保某些对象如果不出现在跟踪之外则永远不会被分配。此类对象称为 virtuals
。但是,如果我们正在处理帧,虚拟对象通常不够好。帧可以逸出,并且在我们进入 JIT 时也可能已经被分配。在这种情况下,我们需要一些额外的对象,即使它存在于堆上,仍然可以被优化掉。
解决方案¶
我们引入了可虚拟化对象。它们是存在于堆上的对象,但其字段并不总是与汇编器中发生的情况同步。一个例子是可虚拟化字段可以存储虚拟对象而不强制它们。这对帧非常有用。声明一个对象可虚拟化就像这样
class Frame(object):
_virtualizable_ = ['locals[*]', 'stackdepth']
我们在 JitDriver
中这样使用它们
jitdriver = JitDriver(greens=[], reds=['frame'], virtualizables=['frame'])
此声明意味着 stackdepth
是一个可虚拟化的字段,而 locals
是一个可虚拟化的数组(存储在可虚拟化对象上的列表)。关于使用可虚拟化对象,特别是使用可虚拟化数组,有很多规则,可能会让人感到困惑。这些通常会导致编译时错误(而不是奇怪的行为)。规则是
可虚拟化数组必须是固定大小的列表。初始化后(例如在
Frame.__init__
中),您根本无法调整其大小。您不能将不同的列表分配给该字段,甚至不能传递列表。您只能直接访问frame.array[index]
。每次数组访问都必须使用已知的正索引,该索引不能引发
IndexError
。使用index = jit.hint(index, promote=True)
可能有助于获得常量访问。只有当索引实际上是常量或在用户代码上下文中很少更改时,这才是安全的。如果您在 JIT 中初始化一个新的可虚拟化对象,则必须像这样完成(例如,如果我们在
Frame.__init__
中)self = hint(self, access_directly=True, fresh_virtualizable=True)
这样您就可以直接填充字段。
如果您在 JIT 之外使用可虚拟化对象——这非常昂贵,有时会导致跟踪中止。请仔细考虑如何只将其用于调试目的,而不是每次都使用(例如,
sys._getframe
调用)。如果您有类似于 Python 生成器的东西,其中可虚拟化对象存活更长时间,则需要在返回之前强制它。这样做比稍后通过外部调用更好。它是使用
jit.hint(frame, force_virtualizable=True)
完成的您的解释器应该有一个类似于上面
frame
的局部变量。只要它运行其jit_merge_point
循环,它就不能被修改,并且在循环中它必须直接传递到jit_merge_point()
和can_enter_jit()
调用中。如果从每次迭代中的某个地方获取可虚拟化对象,而不是重用相同的未修改局部变量,则已知 JIT 生成器会生成有错误的代码。