首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >提高寄存器机器VM上循环的简单自制JIT的性能

提高寄存器机器VM上循环的简单自制JIT的性能
EN

Stack Overflow用户
提问于 2021-10-29 01:26:54
回答 1查看 106关注 0票数 2

现在我正在学习汇编,我的项目是将字节码从幻想的体系结构转换成用字节码编写的真正的汇编,并使用JIT执行。

为了做到这一点,我必须实现来自另一个架构的指令。它们中的一些很简单,就像常见的汇编指令一样,其中有两个需要更多的字节才能实现,比如这两个:

RX和RY - 32位寄存器memory -以小端排序的字节数组,包含原始字节码

mov RX,memoryRY -读取内存中接下来的4个字节(从RY开始),右移这些字节并将它们连接到RX中。mov memoryRX,RY -逆操作。读取RY中的值,将字节左移以按小端排序。

在C代码中,这些指令是(考虑到R和mem是全局的):

代码语言:javascript
复制
 // mov RX, mem[RY]
 void movRxMemRy(unsigned char x, unsigned char y) {

   if (R[y]+3 > 128) endExecution = 1;

   else R[x] = mem[R[y]+3] << 24 | mem[R[y]+2] << 16 | mem[R[y]+1] << 8 | mem[R[y]];
}

// mov mem[RX], RY
void movMemRxRy(unsigned char x, unsigned char y) {
    if (R[x]+3 > 128) {
       endExecution = 1;
    } else {
       mem[R[x]] = (R[y]);  
       mem[R[x]+1] = (R[y]) >> 8;  
       mem[R[x]+2] = (R[y]) >> 16;  
       mem[R[x]+3] = (R[y]) >> 24;        
    }
    return;
}

这些指令是作为解释器的一部分实现的,解释器应该比汇编/jit实现慢5-10倍(或更多),但现在运行这些指令需要1/3的时间(大约1,7~1,8s)。我们的指令实现必须在原始字节码上运行教授给我们的以下指令:

代码语言:javascript
复制
mov R0, 0x006C
mov R1, 0x0001
mov R2, [R0]       # start of huge the loop. [R0] contains the loop counter
cmp R15, R2        # R15 = 0
je 0x0030          # ends the loop execution
mov R14, R2
add R13, R14
sub R2, R1         # decrements the loop counter by 1
mov [R0], R2       # saves the loop counter
jmp 0xFFC8         # returns to the start of the loop

由于循环占用了超过99%的执行时间,所以我决定在这里只粘贴这一部分。循环计数器是包含在R0中的值,它从0x03885533开始(每次迭代递减1)。一旦该值达到零,它就会退出循环。

除了add和sub指令之外,最复杂的指令是负责大部分执行时间的指令。我需要优化它们,使其更快,如果可能,使用最少的字节,因为我认为它们可能有问题,因为它运行在4,5秒。程序集/jit版本比解释版本更快,并且必须在1秒内运行(此项目的时间限制)。我目前对它们的实现是:

r15:包含来自幻想体系结构的16个32位“寄存器”数组,用于存储最终结果rbx:包含具有原始字节码(和计数器值)的内存数组。

// Sub指令与add指令相同,但改变了操作码字节。

代码语言:javascript
复制
void add(unsigned char opcode, unsigned char x, unsigned char y) {

start = c;

// 0x09 - add rx, ry

// mov r14d, [r15+4*y]
machine[c++] = 0x45;
machine[c++] = 0x8b;
machine[c++] = 0x77;
machine[c++] = 4*y;

// add [r15+4x], r14d
machine[c++] = 0x45;
machine[c++] = 0x01;
machine[c++] = 0x77;
machine[c++] = 4*x;

end = c;

for (k= 0; k < (88 - (end-start)); k++) {
    machine[c++] = 0x90;
}
end = c;

}

// mov RX, mem[RY]
void movRxMemRy(unsigned char opcode, unsigned char x, unsigned char y) {

// xor r14, r14
machine[c++] = 0x4d;
machine[c++] = 0x31;
machine[c++] = 0xf6;

// xor r13, r13
machine[c++] = 0x4d;
machine[c++] = 0x31;
machine[c++] = 0xed;

// xor r12, r12
machine[c++] = 0x4d;
machine[c++] = 0x31;
machine[c++] = 0xe4;

// mov    r12d,DWORD PTR [r15+4*Y]
machine[c++] = 0x45;
machine[c++] = 0x8b;
machine[c++] = 0x67;
machine[c++] = 0x4*y;

// mov    r13b,BYTE PTR [rbx+r12*1+0x3]    
machine[c++] = 0x46;
machine[c++] = 0x8a;
machine[c++] = 0x6c;
machine[c++] = 0x23;
machine[c++] = 0x03;

// shl    r13,0x18
machine[c++] = 0x49;
machine[c++] = 0xc1;
machine[c++] = 0xe5;
machine[c++] = 0x18;

// or     r14,r13
machine[c++] = 0x4d;
machine[c++] = 0x09;
machine[c++] = 0xee;

// xor r13, r13
machine[c++] = 0x4d;
machine[c++] = 0x31;
machine[c++] = 0xed;

// mov    r13b,BYTE PTR [rbx+r12*1+0x2]    
machine[c++] = 0x46;
machine[c++] = 0x8a;
machine[c++] = 0x6c;
machine[c++] = 0x23;
machine[c++] = 0x02;

// shl    r13,0x10
machine[c++] = 0x49;
machine[c++] = 0xc1;
machine[c++] = 0xe5;
machine[c++] = 0x10;

// or     r14,r13
machine[c++] = 0x4d;
machine[c++] = 0x09;
machine[c++] = 0xee;

// xor r13, r13
machine[c++] = 0x4d;
machine[c++] = 0x31;
machine[c++] = 0xed;

// mov    r13b,BYTE PTR [rbx+r12*1+0x1]    
machine[c++] = 0x46;
machine[c++] = 0x8a;
machine[c++] = 0x6c;
machine[c++] = 0x23;
machine[c++] = 0x01;

// shl    r13,0x18
machine[c++] = 0x49;
machine[c++] = 0xc1;
machine[c++] = 0xe5;
machine[c++] = 0x08;

// or     r14,r13
machine[c++] = 0x4d;
machine[c++] = 0x09;
machine[c++] = 0xee;

// xor r13, r13
machine[c++] = 0x4d;
machine[c++] = 0x31;
machine[c++] = 0xed;

// mov    r13b,BYTE PTR [rbx+r12*1]    
machine[c++] = 0x46;
machine[c++] = 0x8a;
machine[c++] = 0x2c;
machine[c++] = 0x23;

// or     r14,r13
machine[c++] = 0x4d;
machine[c++] = 0x09;
machine[c++] = 0xee;

// mov    r13b,BYTE PTR [rbx+r12*1+0x3]    
machine[c++] = 0x45;
machine[c++] = 0x89;
machine[c++] = 0x77;
machine[c++] = x*4;


end = c;
}


void movMemRxRy(unsigned char opcode, unsigned char x, unsigned char y) {
start = c;

// xor r14, r14
machine[c++] = 0x4d;
machine[c++] = 0x31;
machine[c++] = 0xf6;

// xor r13, r13
machine[c++] = 0x4d;
machine[c++] = 0x31;
machine[c++] = 0xed;

// xor r12, r12
machine[c++] = 0x4d;
machine[c++] = 0x31;
machine[c++] = 0xe4;

// r12d,DWORD PTR [r15+0xc] (atual)
machine[c++] = 0x45;
machine[c++] = 0x8b;
machine[c++] = 0x67;
machine[c++] = 0x4*x;

// mov r14d,DWORD PTR [r15+4*Y]
machine[c++] = 0x45;
machine[c++] = 0x8b;
machine[c++] = 0x77;
machine[c++] = 0x4*y;

// mov DWORD PTR [rbx+r12*1], r14b
machine[c++] = 0x46;
machine[c++] = 0x88;
machine[c++] = 0x34;
machine[c++] = 0x23;

// mov r14d,DWORD PTR [r15+4*Y]
machine[c++] = 0x45;
machine[c++] = 0x8b;
machine[c++] = 0x77;
machine[c++] = 0x4*y;

// shl r14d, 0x8
machine[c++] = 0x41;
machine[c++] = 0xc1;
machine[c++] = 0xee;
machine[c++] = 0x08;

// mov BYTE PTR [rbx+r12*1+0x1],r14b
machine[c++] = 0x46;
machine[c++] = 0x88;
machine[c++] = 0x74;
machine[c++] = 0x23;
machine[c++] = 0x01;

// mov r14d,DWORD PTR [r15+4*Y]
machine[c++] = 0x45;
machine[c++] = 0x8b;
machine[c++] = 0x77;
machine[c++] = 0x4*y;


// shl r14d, 0x10
machine[c++] = 0x41;
machine[c++] = 0xc1;
machine[c++] = 0xee;
machine[c++] = 0x10;

// mov BYTE PTR [rbx+r12*1+0x2],r14b
machine[c++] = 0x46;
machine[c++] = 0x88;
machine[c++] = 0x74;
machine[c++] = 0x23;
machine[c++] = 0x02;

// mov r14d,DWORD PTR [r15+4*Y]
machine[c++] = 0x45;
machine[c++] = 0x8b;
machine[c++] = 0x77;
machine[c++] = 0x4*y;


// shl r14d, 0x18
machine[c++] = 0x41;
machine[c++] = 0xc1;
machine[c++] = 0xee;
machine[c++] = 0x18;

// mov BYTE PTR [rbx+r12*1+0x2],r14b
machine[c++] = 0x46;
machine[c++] = 0x88;
machine[c++] = 0x74;
machine[c++] = 0x23;
machine[c++] = 0x03;

end = c;

for (k= 0; k < (88 - (end-start)); k++) {
    machine[c++] = 0x90;
}
end = c;

}

因为对于第一个项目,我们不需要检查Rn+3 > 128的条件,所以我决定首先让它工作,而不必在汇编代码上实现条件。

有什么想法可以让我的代码运行在1s以下吗?任何帮助都将不胜感激。

EN

回答 1

Stack Overflow用户

发布于 2021-10-30 04:33:57

我犯了一些新手错误,通过实现mov操作来将C代码直接转换为汇编语言,以逐个字节地连接,而我本可以将它们视为双字。

通过这样做,指令变得更干净、更快。此外,我必须添加NOPs的填充,以对齐指令并使跳转更容易,这是不必要的执行。因此,我在每条指令的末尾添加了到下一条指令的跳转。作为示例,下面是mov指令现在是如何实现的:

代码语言:javascript
复制
mov Rx, mem[Ry]:

mov r12d, dword ptr [r15+4y]
mov r12d, dword ptr [rbx+r12]
mov DWORD PTR [r15+4x],r12d
jmp nextInstruction

Mov mem[Rx], Ry:

mov r12d,DWORD PTR [r15+4*y]
mov r14d,DWORD PTR [r15+4*x]
mov DWORD PTR [rbx+2*x],r12d
jmp nextInstruction

通过这样做,代码可以在0.6秒内执行。

感谢Peter Cordes和fuz,他们帮助发现了这些问题。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69762616

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档