毕设(15):JIT 技术
问题
通过 NES 模拟器,运行 mario 的效率太低了,主要的时间花费在了图像渲染上:
1 | |
就算是这样,还是很慢,差不多 15 秒一帧,这样是无法玩游戏的,通过反汇编发现,NES 模拟器中的指令模拟,每一条指令差不多都被模拟成了 20~30 条指令。而且本来 PPU 和 CPU 并行执行,模拟器是通过穿插交替执行来模拟并行执行,这样 CPU 推荐的就更慢了。
这里 JIT 技术就闪亮登场(可能会加快,但是也不要迷信 JIT)。
JIT
JIT (Just-In-Time) 编译是一种在运行时将字节码或中间代码编译成本机机器码的技术。与传统解释器相比,JIT 可以显著提升执行效率。
解释器 vs JIT
| 方式 | 执行过程 | 优点 | 缺点 |
|---|---|---|---|
| 解释器 | 取指令→解码→执行→循环 | 实现简单,启动快 | 每条指令都要重复解码 |
| JIT | 编译→执行本机代码 | 执行速度快 | 编译有开销,实现复杂 |
JIT Demo:一个简单的例子
为了理解 JIT 的核心原理,我们先实现一个简单的虚拟机和对应的 JIT 编译器。
虚拟机定义
定义一个极简虚拟机:
- 1 个寄存器 A(累加器)
- 4 条指令
1 | |
解释器实现
传统解释器通过 switch-case 循环执行:
1 | |
每次执行都要:取指令 → switch 判断 → 执行 → 循环,开销很大。
JIT 编译器实现
JIT 的核心思想:把虚拟机代码翻译成本机代码,然后直接执行。
LoongArch32R 指令编码
首先需要了解目标平台的指令编码:
1 | |
寄存器分配
1 | |
JIT 编译函数
1 | |
核心编译逻辑:
1 | |
JIT 编译的关键点
- 两遍扫描:第一遍生成代码,第二遍回填跳转地址;
- 地址映射:
addr_map[vm_pc] = native_pos记录虚拟机地址到本机代码的对应关系; - 函数指针转换:
(jit_func_t)jit_code_buffer将数据数组转为可执行函数。
编译示例
对于程序 LOAD 1; ADD 2; ADD 3; HALT:
1 | |
JIT 生成的 LoongArch32R 代码:
1 | |
性能对比
1 | |
实际测试结果(在 LoongArch32R 上):
1 | |
JIT 版本快约 10 倍!
毕设(15):JIT 技术
http://blog.luliang.online/2026/01/29/毕设15:JIT/