主要观点:
- 现代认为写汇编是徒劳的,因为编译器更优,但有传言称用汇编写解释器可超编译器。
- 作者为 Uxn CPU 写了一个快速解释器,通过将所有操作码实现内联到
Uxn::run
函数中使其相对较快,比参考实现快 10 - 20%。 - 分析汇编发现编译器存在一些低效之处,如关键值存于内存而非寄存器、调度循环为间接分支不可预测等。
- LuaJIT 是快速解释器且用汇编写,作者开始自己写汇编优化解释器。
- 优化措施包括将重要数据存于寄存器以避免多余加载存储、使用线程代码消除调度循环、为每个操作码写单独的实现等。
- 还需通过 C Shims 从 Rust 调用汇编函数,处理设备 IO 时需写一些 shim 函数。
- 测试不同实现的性能,发现手写汇编的解释器比基线快约 30%,集中式调度会显著变慢,一些其他实验未使性能提升。
- 结论是用汇编写解释器既有趣又高效,在 Rust 中难以实现类似高性能策略,但可将汇编代码移植到 x86 - 64。
关键信息和重要细节:
- Uxn CPU 有四种内存,评估时跟踪程序计数器
pc
,操作码可读写 RAM 实现自修改代码。 - 编译器生成 256 个偏移的跳转表,通过读取特定值计算跳转目标。
- 汇编中发现一些值的存储方式可优化,如
INC
操作额外加载数据栈索引。 - LuaJIT 靠在寄存器中保存状态和间接线程提高速度。
- 寄存器分配使用 9 个寄存器及一些临时寄存器,因 AArch64 调用约定限制需 C ABI 风格入口点。
- 间接线程通过构建单独的跳表和宏实现跳转到下一个操作码实现。
- 其他 255 个操作码实现主要是普通汇编代码,用宏生成代码。
- C Shims 用于从 Rust 调用汇编函数,通过
EntryHandle
对象传递状态。 - 设备 IO 通过定义 trait 处理,因与 C ABI 不兼容需写 shim 函数。
- 性能测试用两个 CPU 密集型工作负载,发现手写汇编解释器性能提升明显,集中式调度变慢。
- 一些未使性能提升的实验,如扩展 RAM 和使操作码实现大小相同。
- 相关代码在 Github 上,
uxn-cli
和uxn-gui
可通过--native
标志选择汇编解释器后端。 - 帖子在 Hacker News 讨论,有人提出优化建议,如从
asm!
块分支进入aarch64_entry
、将JUMP_TABLE
移入汇编等。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。