2009年3月7日星期六

微小的性能优化

昨天睡觉的时候想起一个事情可以做做,早晨过来就实验了一下。

LPC支持用文件名直接访问对象,也就是说对象名就是文件名。比如“/daemons/filed.c”,即是文件名,也是这个文件载入后的对象名。因此其他模块可以直接进行类似“/daemons/filed.c”->do_somthing()这样的调用。

我之前的实现针对这点有一定的优化:每个字符串结构带了一个16位的hash值,当字符串做过hash运算以后会保存这个hash值,将来不需要再重复一次。但是,查找对象时,最后比较是否匹配还是要进行整个字符串的比较:STRCMP。

我尝试将对象的名字放入共享字符串池,当进行通过字符串查找对象时,先从共享池中取得字符串,然后直接判断指针是否相等就可以判断名字是否相同了。在实际编码的时候,还是遇到了一些小问题:比如调用者如果没有按照标准的路径名书写对象名,比如“daemons/filed.c”,这样也可以查找到对象才可以。显然,不可能从共享池中取得这个字符串 - 即是取得了,也无法用于判断。因此最终我实现的逻辑如下:

IF string in shared strings pool THEN
DO lookup object
RETURN when found

DO regular string name
IF string name isn't changed
RETURN null

DO raise warning - 这是为了提醒调用者,注意传入正确的字符串,免得每次都要进行规范化操作

DO find string in shared strings pool
IF not found THEN
RETURN null - 字符串名字不在共享池中,自然不可能找到对象

DO lookup object
RETURN result - 不管结果如何,都需要返回了

这样,如果字符串名字本身是标准的,并且在共享池中(代码中的常量都是在字符串的共享池中的),那么将只需要进行整数操作就可以进行hash检索,无需比较字符串的内容了。

实测效率从一次调用耗时0.42us下降到了0.3us。考虑本身循环调用、进出入函数的开销就需要0.18us,相当于检索耗时从0.24us下降到了0.12us,提高了一倍。

不过我做的另一个实验没有达到预期的效果:VM在执行指令时,需要记录一些“当前”信息,比如ip、sp、thisObject等,当进入函数时需要保存这些信息,即将它们记录到context中。原先是逐个变量赋值的,类似:

context->ip = vm_ip;
context->sp = vm_sp;
context->thisObject = vm_thisObject;
...
// 一共5个成员

我尝试将这些信息归入到一个结构中,然后通过结构赋值(编译后即为repz movs)保存,结果效率反而下降了一些。对CPU来说,如果数据不多,它更喜欢“内存->寄存器->内存”这样几个连续的操作。可以并行处理,优于repz movs,因为它还需要准备esi、edi、ecx。

没有评论:

发表评论