2009年3月19日星期四

针对VM未来内存方面优化的考虑

我这里只考虑一些立竿见影,成本很低的优化方案。

目前每个值(value)都有一个引用字段,用了4个字节,我在考虑如何不用复杂的垃圾回收机制就能够消除这4个字节。我想了一下,能对值进行引用的只有VM中的栈变量。当对类似如下语句:
a = 7
m["x"] = 99
这样的语句进行操作的时候,会产生对变量a、m、值m["x"]的引用。而a、m只可能是全局变量、对象变量、局部变量这三种情况。针对于这三种变量,都无需考虑引用的问题。全局变量只在driver clean environment时才释放,对象变量在对象析构时释放,局部变量则在返回时释放,都不需要考虑引用的问题。关键在于改写容器内成员值时,有可能发生引用。

事实上这不难解决,我只要为对容器赋值做一些特别操作(比如set_member)即可,问题在于,我支持调用时传入引用,比如foo(&m["x"]),这给我带来很大的麻烦:我必须有一种可以传递容器内值引用的方法。虽然,我可以使用记录容器和容器内索引的方法,但是我仔细思考了一下,这样有点浪费效率(在处理赋值、传递引用指针都会慢一些),并且代码有点复杂,不是正途。

说实话,如果现在能够自由的做决定,我就干掉函数调用时传递引用这种行为,因为很少有需求要使用。然而现在我应该保持和以前代码的兼容性,毕竟我不希望将来问道试图升级driver的时候花费太大的经历。

我从另一个角度考虑了一下,可以对这种引用做一个记录。每次进行这种引用时,就将被引用的指针加入到一个数组里面。当取消引用时则遍历数组然后删除 - 听起来这样开销很惊人,不过实际上,代码中几乎没有对非变量的普通值的引用,所以效率影响微乎其微。

在一个值被释放时,需要检查一下是否在引用数组里面,如果是,则先不释放 - 等到引用消除时在释放,否则直接释放。为了提高速度,我给值保留一个比特的是否被引用字段,因为目前一个值除了引用还由2个32位数表示,找出1位不是难事。这样就可以将值压缩到8个字节。

算法描述:
当对值进行引用时,即I_QUERY_MEMBER_PTR指令,需要进行如下操作:
SET value.attrib.ref = 1

当对引用进行赋值传递时,则:
IF src_value.attrib.ref == 1 THEN
PUT src_value TO ref_array (此时ref_array有两个重复的src_value)
END

当清除引用时,则:
IF value.attrib.ref == 1 THEN
REMOVE value FROM ref_array(此时清除的一般是后面增加的value,这样速度更快)
IF value.attrib.free == 1 THEN
FREE value (原先容器试图释放该值,清除引用)
END
END

当值被试图释放时,则:
value.attrib.free = 1
IF value.attrib.ref == 0 THEN
FREE value (大部分情况都会如此)
END

为了应付最坏的情况,我需要准备一个size能够包容所有VM线程栈变量的数组 - 虽然几乎没可能会用到这么大的情况。但是在赋值的时候出现数组溢出是一个很糟糕的问题,为了简化代码,保证稳定性,这点空间应该无足轻重(大概是线程数x1K空间即可)

当然,我还有另外一种比较简单的做法,就是从8个字节的value中找出16位作为引用。但是这不太可靠,如果非常不幸的,对值的引用超过了65535(比如有意让栈变量全部去引用这个值),可能会造成意想不到的后果。

没有评论:

发表评论