weval是一个WebAssembly部分求值器,通过将程序的部分输入固定为常量并让编译器优化,生成更高效的专用代码。其核心思想是将解释器与常量字节码结合,自动转换为针对特定程序的优化WebAssembly模块。
工作原理示例
文章通过一个自定义的简单解释器演示weval的效果。该解释器用C++编写,支持加载立即数、存储/加载局部变量、算术运算和分支等指令。当用weval处理时,通过三个步骤逐步优化:
- 常量特化:将整个字节码数组标记为常量,使weval能进行常量传播,消除解释器中因未知字节码而存在的条件判断开销。
- 控制流展开:通过
weval::update_context提示程序计数器(PC)作为特化上下文,使weval完全展开解释器的循环,将字节码指令序列直接转换为线性WebAssembly代码。
- 变量特化:利用
weval_read_reg和weval_write_reg内在函数,将解释器中的局部变量数组转换为WebAssembly本地变量,避免内存读写开销。
性能对比
对于一个计算1到1亿之和的示例程序:
- 原始C++解释器:约350ms
- 编译为WASI的WebAssembly版本:约530ms
- 经weval优化后的WebAssembly版本:约40ms
性能提升高达8.5倍,接近手写专用编译器的水平,但无需手动编写编译器。
关键机制
weval通过上下文敏感的数据流分析实现优化,以PC值作为上下文,使得常量传播等编译器优化能针对单次解释器循环迭代进行,而非保守地合并所有可能性。程序员可通过内在函数和初始化钩子(如wizer)提供提示,引导特化过程。
应用场景
weval适用于将大型解释器(如SpiderMonkey JavaScript引擎或CPython Python运行时)编译为WebAssembly的场景。通过特化,可将解释执行的开销转化为接近直接编译的性能,使得在WebAssembly沙箱中运行Python或JavaScript程序时获得显著加速。
未来方向
Chris Fallin计划进一步优化weval,并将其应用于SpiderMonkey等实际运行时。类似理念也可用于CPython,甚至可能通过CPython的vectorcall机制实现WebAssembly JIT编译。
总结
weval通过部分求值技术,允许程序员逐步添加特化提示,将通用解释器自动转换为高效的专用代码。这种方法结合了编译器的优化能力与解释器的灵活性,在保持可移植性的同时大幅提升性能。