lua 常量表优化

yanchengfuke 5年前
   <p>来自: <a href="/misc/goto?guid=4959670313799632638" rel="nofollow">http://blog.codingnow.com/2016/04/lua_table_constants.html</a></p>    <p>今天花了一天尝试给 lua vm 做了一点优化:</p>    <p>现在 lua 的函数原型里保留有一张常量表,引用了 string ,number ,nil ,boolean 类型的常量。</p>    <p>table 是不能为常量的,所以当你想迭代一个常量数组的时候,</p>    <pre>  <code class="language-cpp">for _, v in ipairs { "one", "two", "three" } do</code></pre>    <p>其实每次都会临时构建一张表,并依次插入 "one", "two", "three" 。</p>    <p>或者你想返回一个常量构成的表:</p>    <pre>  <code class="language-cpp">function foo()    return { x=1, y=2 }  end</code></pre>    <p>每次 foo 函数都会为返回值重新构建 table 。</p>    <p>其实,我们可以认为所有键值都是常量的 table ,都有可能是一个常量表。那么,在 parser 阶段,和 string 等类型一样,预先建立好 table 对象,保留在 prototype 的常量表中,可以极大的提高运行性能。</p>    <p>那么在上面这种 return {x=1, y=2} 的写法中,实际返回的可以仅仅是一个 table proxy ,里面只有一个指向常量表的指针即可。</p>    <p>如果事后我们真的需要改写这个 table ,再 clone 一份常量表,把这个 table proxy 对象还原成真正的 table 对象即可。也就是所谓的 COW 技术。</p>    <p>由于常量表一定是一个单层简单表结构,可以直接 memcpy 复制,比一项项添加 key/value 要快很多(实测可以快 3-4 倍)。</p>    <p>我们为之付出的代价仅仅是每次访问 Table 对象时要多做一次判断,但收益巨大。</p>    <p>我的实现放在 <a href="/misc/goto?guid=4959670313882662829" rel="nofollow,noindex">自己的仓库</a> ,有兴趣的同学可以看看最近的提交。btw, 暂时只支持 parser ,还没有支持 dump/undump 。</p>    <p>下面是一个简单的测试:</p>    <pre>  <code class="language-cpp">function f(a)  end    local t = os.clock()    for i=1, 10000000 do      f { x=1, y=2 }  end    print(os.clock() -t)</code></pre>    <p>上面这段代码,在原始 lua 5.3.2 中运行花掉 4.977s ,修改过的版本减少到 1.278s 。</p>    <p>如果在 function f(a) 中修改 a 这个表,加入了 mutable 化过程:</p>    <pre>  <code class="language-cpp">function f(a)      a.x = 3  end    local t = os.clock()    for i=1, 10000000 do      f { x=1, y=2 }  end    print(os.clock() -t)</code></pre>    <p>修改后的版本也有巨大的优势:原始版本 5.234s ,修改版本 1.413s 。</p>