device emulator V2中改进的地方

Oct 13th, 2008 | Posted by yajin | Filed under emulation

目前以shared source发布的device emulator的版本是v1,我手头的版本也就是这个版本。在MSDN的这篇文章中,Barry给出了device emulator的V2版本对于V1的改进的地方。通过这些改进,模拟速度的提升达到40%!

1. TLB 查找的优化

我们知道在带有MMU的ARM中,当访问memory的时候,需要先将虚拟地址转换成物理地址。这涉及到查TLB和页表的过程。如果能在TLB中找到表项,则不用去页表中进行查找。

在V1中,实现了TLB的查找。整个TLB的表项有64个。在硬件实现中,TLB的查找是并行的,而对于emulator来说,查找是线性的。最坏的情况需要遍历整个64个entry。因此,如果能够快速的寻找到TLB表项,无疑对于性能是一个很大的帮助。

根据程序的memory访问具有相关性(locality)的特点(也就是说,下一次访问的memory地址和上一次访问的memory地址不会相差太远),因此,在进行TLB查找的时候,总是从上一次查找成功的那个TLB Slot开始查找,而不是从TLB 表项0开始查找。

但这仍然有一个问题,最坏的情况下,仍然是线性搜索64个entry。V2做了一个改进,将线性的搜索变为hash搜索。这样,在最坏的情况下,不用遍历整个64个entry。不过具体的hash算法并没有给出。

2. Interrupt polling

这个也比较有意思。对于emulator来说,interrupt的产生是异步的,因此emulator是需要不断polling interrupt的状态。如果有interrupt pending,必须修改guest的PC,使得guest跳到异常处理程序的地方继续执行。

根据我之前开发emulator的经验,会在emulator的JIT生成的X86汇编中阶段性的去polling interrupt的状态。比如

if (g_fInterruptPending) {

SimulateHardwareInterrupt();

}

也就是说,JIT生成的X86代码需要非常多的MOV/CMP/BRANCH的序列。而在V2中,将interrupt polling 改成

void SimulateHardwareInterruptIfPending(void)

{ /* note: no code here */

}

这个函数是一个空函数。让有中断产生的时候,会patch这个函数,将这个函数变成RET到中断处理函数的地址。 这样就可以省掉MOV/CMP/BRANCH的序列。其实还可以减少translated后的代码大小。

3. 优化memcpy和memset.

emulator能识别出guest的memcpy和memset,把它变成手工优化的X86代码。
如何识别memcpy和memset,没有讲明。我想应该和所使用的 libc库以及compiler有关。

4. 窥孔优化
对IR进行窥孔优化。

str r0, [sp+10]
ldr r3, [sp+10]

变成

str r0, [sp+10]
mov r0, r3

5. 一次尽可能多的去ARM指令

也就是一次去指令的时候,至少应该可以取1K的指令。而不用每一次取指令的时候都去跑完整个memory load(guest虚拟地址->TLB->[页表->]guest物理地址->host虚拟地址->取指令)的整个过程。这个优化方法在dynamips和virtualmips中也有应用。

6. Reduce x86 processor stalls due to mixed code and data
这个应该是X86处理器相关的。不具体介绍了。

其实这些优化方面都不是很难,但是取得的性能提升确实显著的:40%。看来我要多学学怎么用工具来寻找程序的瓶颈,如何去优化。特别对于emulator来说,性能是非常非常关键的。

Tags:
No comments yet.