优化编码技巧

lua-users home
wiki

Lua 5.1 注释

--DavidManura

请注意,Roberto Ierusalimschy 在优秀的 [Lua 编程宝石] 书籍中撰写的 Lua 性能技巧文章可以在 [网上获取]

Lua 4 注释

以下信息涉及 Lua 4 的优化,并保留在此处以供历史参考。

编码的一般技巧

(Joshua Jensen) 以下是一些我常用的优化策略(想到哪写到哪)

此信息是为 Lua(v4.0 之前的版本)编写的 - Nick Trout

断言

使用带有非平凡消息表达式的标准断言函数会对脚本性能产生负面影响。原因是即使断言为真,消息表达式也会被评估。例如,在
assert(x <= x_max, "exceeded maximum ("..x_max..")")
无论条件如何(通常为真),都会执行浮点数到字符串的转换和两次连接。以下替换使用 printf 风格的消息格式化,并且只有在使用时才会生成消息
function fast_assert(condition, ...)
    if not condition then
        if getn(arg) > 0 then
            assert(condition, call(format, arg))
        else
            assert(condition)
        end
    end
end
现在示例变为
fast_assert(x <= x_max, "exceeded maximum (%d)", x_max)

这是生成的 VM 代码

assert(x <= x_max, "exceeded maximum ("..x_max..")")
        GETGLOBAL  	0	; assert
        GETGLOBAL  	1	; x
        GETGLOBAL  	2	; x_max
        JMPLE      	1	; to 6
        PUSHNILJMP 	
        PUSHINT    	1
        PUSHSTRING 	3	; "exceeded maximum ("
        GETGLOBAL  	2	; x_max
        PUSHSTRING 	4	; ")"
        CONCAT     	3
        CALL       	0 0
fast_assert(x <= x_max, "exceeded maximum (%d)", x_max)
        GETGLOBAL  	5	; fast_assert
        GETGLOBAL  	1	; x
        GETGLOBAL  	2	; x_max
        JMPLE      	1	; to 17
        PUSHNILJMP 	
        PUSHINT    	1
        PUSHSTRING 	6	; "exceeded maximum (%d)"
        GETGLOBAL  	2	; x_max
        CALL       	0 0

Edit: April 23, 2012 By Sirmabus
The code above will not actually work with 5.1
Also added some enhancements like pointing back to the actual assert line number,
and a fall through in case the assertion msg arguments are wrong (using a "pcall()").

function fast_assert(condition, ...)
   if not condition then
      if next({...}) then
         local s,r = pcall(function (...) return(string.format(...)) end, ...)
         if s then
            error("assertion failed!: " .. r, 2)
         end
      end
      error("assertion failed!", 2)
   end
end

快速无序列表迭代

在 Lua 中,我们经常构建一个包含元素的表,例如
table = { "harold", "victoria", "margaret", "guthrie" } 

迭代此表的“正确”方法如下

for i=1, getn(table) do
    -- do something with table[i]
end

但是,如果我们不关心元素顺序,上面的迭代速度很慢。第一个问题是它调用了 getn(),假设如上所述“n”字段尚未设置,则 getn() 的顺序为 O(n)。第二个问题是必须执行字节码并执行表查找以访问每个元素(即“table[i]”)。

一种解决方案是使用表迭代器

for x, element in pairs(table) do
    -- do something with element
end

getn() 调用被消除,表查找也被消除。“x”是一个哑变量,因为在这种情况下通常不使用元素索引。

此解决方案有一个注意事项。如果在表上使用库函数 tinsert() 或 tremove(),它们将设置“n”字段,这将出现在我们的迭代中。

另一种方法是使用 LuaPowerPatches 中列出的列表迭代补丁。

表访问

问题:我担心的不是创建表的性能,而是对表内容的所有访问。

(lhf) 表是 Lua 中的核心数据结构。你不应该担心表的性能。我们付出了很多努力来使表变得更快。例如,有一个专门的 opcode 用于 a.x。请查看 a.xa[x] 之间的区别... 但是,就像你所说,这里的区别本质上是一个额外的 GETGLOBAL

a,c = {},"x"
        CREATETABLE	0
        PUSHSTRING 	2	; "x"
        SETGLOBAL  	1	; c
        SETGLOBAL  	0	; a
b=a.x
        GETGLOBAL  	0	; a
        GETDOTTED  	2	; x
        SETGLOBAL  	3	; b
b=a["x"]
        GETGLOBAL  	0	; a
        GETDOTTED  	2	; x
        SETGLOBAL  	3	; b
b=a[c]
        GETGLOBAL  	0	; a
        GETGLOBAL  	1	; c
        GETTABLE   	
        SETGLOBAL  	3	; b
        END


另请参阅:VmMerge(用于格式化合并的 Lua 源代码和 VM 代码)、OptimisationTipsOptimisingUsingLocalVariables


RecentChanges · preferences
edit · history
最后编辑于 2012 年 4 月 24 日上午 7:42 GMT (diff)