Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/assembly/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Assembly 这个移位/跳转会比切换…案例陈述更快吗?_Assembly_X86_Switch Statement_Emulation_Opcodes - Fatal编程技术网

Assembly 这个移位/跳转会比切换…案例陈述更快吗?

Assembly 这个移位/跳转会比切换…案例陈述更快吗?,assembly,x86,switch-statement,emulation,opcodes,Assembly,X86,Switch Statement,Emulation,Opcodes,我正试图最大限度地优化一个分支(一个开关…类似于case)以模拟x86CPU上的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

我正试图最大限度地优化一个分支(一个开关…类似于case)以模拟x86CPU上的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操作码时,我都会这样做:将其向左移位8位,然后跳转到该位置。执行此命令,并正常继续我的程序流程。我的问题是:1)这些操作码块是否可能如此紧密?2) 这在过去是一种常见的做法吗?

如果您通过一个开关块在256个操作码之间进行分支,那么您将进行CPU无法很好预测的间接跳转,这将导致每个操作码的管道中断

如果模拟操作码的工作是相当大的,那么这个管道中断可能并不重要。我想是的;“加载寄存器”操作码基本上是通过一次内存读取来模拟的,这并不是很多工作

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

sleazier的一个技巧是,如果可以的话,将管道中断的成本分摊到多条指令中。如果机器有很多单字节操作码,你可以考虑在下两个操作码字节上做一个65536路分支。(现在,您必须对许多开关案例进行编码,但其中许多案例 本质上是一样的。不知道你的编译器是否能处理它?)抽象地说,这将管道中断成本减少了两倍。对于具有非常常规指令集的特定机器,这可能不会给您带来很多好处

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

最好在缓存线边界上对齐每个开关大小写。如果指令模拟很简单,那么代码很可能适合缓存线,因此内存只能看到该缓存线的一次提取。如果不这样做,您的指令模拟将有更高的机会跨越缓存线边界,从而将内存获取成本提高到两倍。 这对于频繁执行的指令可能无关紧要,但很少执行的指令的代码可能会从缓存中掉出来。这将有助于您实际遇到其中之一


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

“这些操作码块可能会这么紧吗?”我们很难知道。无论如何,如果开关/大小写的大小写从0到255,编译器很可能会将其优化为间接跳转,并将大小写编号作为跳转表的索引。您可以研究编译器的程序集输出,看看是否值得尝试手动优化任何内容。虽然这当然是可能的,但编译器只需保留一个跳转地址表,使用switch语句将其索引,然后间接跳转。您的方式可能会更快一些,但它会浪费内存(除非存根实际上需要0x100字节),而且真正的成本是跳转,因为它需要跳转目标预测,这往往很糟糕。是的,它们肯定需要从0到0x10000的所有0x100字节块,而且它只是一个64kb的固定代码,因为它的行为“像rom”。但是什么预测呢?这都是确定的,因为它们是相同长度的固定块。但是好的。现在我明白了,我误导了所有人。我将编辑这个问题。它“像”一个开关盒。而且,我甚至不确定是否需要一个编译器。编译器肯定会优化掉所有内容,破坏块的0x100长度,破坏正确的内存偏移量。我认为这是以最纯粹的形式编程。“以最纯粹的形式编程?”这是描述它的一种方式。但我们大多数人会说它愚蠢,或者更糟。您浪费了大量内存,几乎没有性能提升。您将得到大量的页面错误。您可以使用一个包含256个条目的查找表来执行此操作,这些条目指向要执行的函数。这将需要比移位和跳转多一次内存访问,但总的来说,它将使用更少的内存,并导致更少的页面错误。这也是更清晰的代码。实际上,我会尽量使代码尽可能小,以尽可能多地保留在缓存中。如果您有65536条操作码解释器的“缓存线”,每个64字节,您只使用大约1Mb的缓存。这足够小,所以大部分都会留在缓存中,在现代CPU上为其他可缓存项留下大量缓存空间。CPU无法很好地预测-预测取决于这样一个事实,我必须在编译整个仿真器后找到第一个块的第一个偏移量?因为即使跳转是间接的,在我看来,它也是有决心的(跳转)(opcode@venge:CPU可以预测表中的位置,是的,它直接从jmp操作码计算。但是,如果不获取表项,它无法预测表项,例如jmp的有效目标,这需要时间。这就是管道中断的原因。通过添加特殊测试,您可能会获得一些明显的改进st在开关块之前,检查两个或三个最常见的操作码-我调用了一个空开关…用gcc编译了17000次,它在92Nano中运行。然后只添加一个分支用于停止模拟检查,它就转到115Nano。添加额外的检查肯定会加快速度,只要我偶然发现常见的指令,添加,ld,pop等,否则它会对我不利。