这个shift/jump会比switch. case语句快吗?



我正在尝试优化分支(开关…在x86 CPU上模拟X CPU。我想到了这一点:在内存中,我将加载具有固定长度0x100字节的x86操作码块,如下所示:

first block 
0
...[my code, jump at 0x10000, nop nop nop until 0x9F...]
0x9F
second block 
0x100
...
019F
third block
0x200
...
0x29F
...
etc, etc
...
0x10000

将是有限的,从内存$0开始(可能加上一些偏移量)并以$0xFFFF结束(就像0x10000大小的"rom")。现在每次一个X CPU的opCode被获取和模拟,我将这样做:左移8位,并跳转到那个位置。执行此命令,并正常地继续我的程序流程。我的问题是:1)这甚至可能是如此紧密与那些opCode块?2)这是过去的惯例吗?

如果你通过一个开关块跨256个操作码分支,你将做一个CPU不能很好地预测的间接跳转,这将使你在每个操作码上都有一个管道中断。

如果模拟操作码的工作相当大,那么这个管道中断可能无关紧要。我想是的;一个"加载寄存器"操作码实际上是通过内存读取来模拟的,这不是很多工作。

您可能会购买一些明显的改进,通过在开关块之前添加特殊测试,检查两个或三个最常见的操作码(可能是LOAD, CMP, JMP条件)[如果opcode = JMP那么…]这些测试的CPU 可以通常预测良好。如果你这样做,衡量,衡量,再衡量。

一个更低级的技巧是,如果可以的话,将管道中断的代价分摊到多个指令上。如果机器有很多单字节操作码,您可以考虑在接下来的两个操作码字节上进行65536路分支。(现在您必须编写很多开关用例,但是很多本质上是一样的。不知道你的编译器是否可以处理它?)从理论上讲,这将管道断裂成本降低了两倍。对于具有非常规则的指令集的特定机器,这可能买不到很多东西。

但是,您可能没有很多单字节操作码,但是您可能需要为每条指令解码一个或多个字节。x86是这样的(前缀,操作码,MODRM, SIB,偏移量…)。大开关箱应该可以很好地完成这个任务。

在缓存线边界上对齐每个开关箱可能是好的。如果指令模拟很简单,则代码可能适合缓存行,因此内存只看到该缓存行的一次读取。如果您不这样做,您的指令模拟将有更高的机会越过缓存线边界,将内存获取成本提高到2。对于频繁执行的指令来说,这可能无关紧要,但是很少执行的指令的代码可能会从缓存中掉出来。这将在您实际遇到这些问题时提供帮助。

最后一点建议:测量,测量,再测量。

最新更新