我用 C 编写了一个虚拟机,它有一个调用表,其中填充了指向提供 VM 操作码功能的函数的指针。当虚拟机运行时,它首先解释一个程序,为提供的操作码在调用表中创建对应于适当函数的索引数组。然后它遍历数组,调用每个函数直到它到达末尾。
每条指令都非常小,通常是一行。非常适合内联。问题是编译器不知道何时调用虚拟机的任何指令,因为它是在运行时决定的,所以它不能内联它们。函数调用和参数传递的开销正在扼杀我的虚拟机的性能。关于如何解决这个问题的任何想法?
我用 C 编写了一个虚拟机,它有一个调用表,其中填充了指向提供 VM 操作码功能的函数的指针。当虚拟机运行时,它首先解释一个程序,为提供的操作码在调用表中创建对应于适当函数的索引数组。然后它遍历数组,调用每个函数直到它到达末尾。
每条指令都非常小,通常是一行。非常适合内联。问题是编译器不知道何时调用虚拟机的任何指令,因为它是在运行时决定的,所以它不能内联它们。函数调用和参数传递的开销正在扼杀我的虚拟机的性能。关于如何解决这个问题的任何想法?
以下是一些减少开销的选项:
fastcall
(或类似的)以减少参数传递的开销最终,您将到达 JIT 编译、在线分析和重新优化以及各种其他很棒的东西。
您可能想研究许多好的技术。以下是我熟悉的两个:
内联缓存- 本质上,查找不断被调用的内容,然后从 vtable 查找切换到仅添加一堆分发到静态已知位置的 if 语句。这种技术在 Self 语言中得到了很好的应用,并且是 JVM 的主要优化之一。
跟踪 - 为可能最终被使用的每种类型编译一个多态调度的一个版本,但将编译推迟到代码运行足够多次。 Mozilla 的 TraceMonkey JavaScript 解释器在许多情况下使用它来获得巨大的性能提升。
希望这可以帮助!