September 19, 2021 之前写了个 WebAssembly 虚拟机,经过一番折腾,性能(解析执行)终于接近 Intel 的 WAMR 了,这里总结下其中一个重要的优化点:Threaded Code,其主要优化虚拟机执行时的指令分发性能。首先看一下 WebAssembly 虚拟机的运行流程(解析执行): 右上部分是程序输入,主要有函数、内存(堆)和全局变量,可以类比代码编译后产生的 binary 文件加载后的内容,函数体(code)则是一个指令序列,虚拟机运行期维护两个栈:Operand stack 和 Control stack,分别存储操作数(values)和代码结构(labels),每个函数调用都会产生一个函数调用帧(Frame),用于存储函数的局部变量(如参数),当各种数据结构都准备就绪,虚拟机从入口函数开始,顺序地执行函数的指令序列,当执行到 call 指令时,会产生新的 Frame,进入新的函数执行。 指令分发 各种解释型语言(如Java、Lua、WebAssembly) 都会定义自己的一套指令集,代码“编译”之后各函数体的内容就是一个指令列表,指令又分控制指令(如跳转、循环、函数调用等)、数值指令(如加减乘除等)、以及内存操作、变量操作相关的指令等等,而虚拟机在运行的时候,“解析执行”的过程实际上就是遍历指令列表,完成每个指令对应的操作。在经典的计算模型里,会有一个程序计数器(pc)指向指令列表的当前位置,每执行完一条指令,pc++,继续下一条指令,这个过程也可以看作是“指令分发”,“Theaded Code” 就是指这样的指令分发形式。 Switch Threading 使用 switch 做指令分发是最直观的,这里为方便测试,我们先定义 OpCode 如下: typedef enum { ADD, // *sp += 2 SUB, // *sp -= 1 MUL, // *sp *= 2 DIV, //Continue reading “WebAssembly 初探 – Threaded Code”