qemu 中文文档 下载 App

TCG JIT 翻译引擎跨架构模拟的核心原理

TCG 是什么

TCG(Tiny Code Generator)是 QEMU 内置的 JIT(Just-In-Time)编译器。它的任务很纯粹:读取 Guest CPU 的指令序列,翻译成宿主 CPU 能直接执行的机器码。

没有 TCG 就没有 QEMU 的跨架构能力。当你在 x86 主机上模拟 ARM 时,ARM 指令不会被 x86 CPU 直接识别——TCG 是那个"翻译官"。

两级翻译架构

TCG 不是直接把 Guest 指令翻译成宿主指令。中间有一层抽象。

Guest 指令 → 前端翻译 → TCG IR → 后端代码生成 → 宿主机器码
(ARM/x86/etc)            (中间表示)          (x86/ARM/etc)

前端翻译器

每种 Guest 架构有自己的前端翻译器,负责把 Guest 指令解码成 TCG IR。QEMU 支持的架构决定了有多少套前端。目前有:x86、ARM、AArch64、MIPS、PPC、SPARC、RISC-V、LoongArch 等。

后端代码生成器

同样,每种宿主架构有自己的后端,把 TCG IR 编译成对应的宿主机器码。

TCG IR 是一种三地址码(SSA 形式),类似 LLVM IR 但更简单。所有前后端都面向这套中间格式,解耦了"Guest 架构"和"宿主架构"的任意组合。

翻译块与直接块链接

TCG 不是逐条翻译指令,而是按翻译块(Translation Block, TB) 批量翻译。一个 TB 的边界条件:

这样做的好处是:一次翻译多条指令,可以做跨指令优化。

直接块链接(Direct Block Chaining, DBC) 是另一个关键优化:当翻译完一个 TB 后,TCG 会在结尾追加一条跳转指令,直接跳到下一个翻译好的 TB。执行时完全不回 QEMU 主循环,速度最快。

TB_A → TB_B → TB_C → TB_D → ...    直接链接,无 QEMU 开销

如果 TB_B 还没翻译,就先回主循环翻译它,下次就直接跳了。

TCG 的多线程翻译

早期 QEMU 的 TCG 翻译是单线程的,一个 TB 一个 TB 顺序来。10.x 之后引入了 multi-threaded TCG:

多线程 TCG 在多核 CPU 上加速明显,特别是首次启动虚拟机时(需要翻译大量新 TB)。

启用多线程翻译:

qemu-system-x86_64 -accel tcg,thread=multi ...

TCG 性能:为什么比原生慢

TCG 模拟通常只能达到原生性能的 10%~30%,原因:

  1. 翻译开销:每条 Guest 指令都要经过解码 → IR → 宿主指令链
  2. 运行时检查:需要检查特权指令、内存访问有效性、页表翻译
  3. 内存模型模拟:Guest 的 MMU 和页表需要软件模拟
  4. 缓存不友好:TB 翻译产物常驻内存,挤占宿主 CPU 缓存

同架构时性能差别更悬殊:KVM 模式下 Guest 指令直接跑在物理核心上,性能 85%~95%。TCG 再怎么优化也追不上硬件加速。

TCG 常见调优

选项说明
-accel tcg,thread=multi多线程翻译
-machine accel=tcg强制用 TCG(有 KVM 也不用)
足够的内存TCG 翻译缓存随 Guest 运行持续增长
-bios /dev/null跳过 BIOS 加速首次启动
使用轻量 Guest OSAlpine、OpenWrt 比 Windows 快得多

TCG 的未来

TCG 一直在持续优化。11.0 版本引入了 "nitro" 加速器(AWS Nitro Enclaves 原生支持),还有 WHPX 和 MSHV 加速器的改进。整体方向是:

但根本限制是 TCG 无法绕开——它要模拟的是"另一套 CPU",本质上就需要翻译。要追求接近原生的性能,还是用 KVM/HVF/WHPX。