编写高效Lua代码的方法

基础

1 尽量使用local

      使用变量的效率:local > upvalue > global

      local变量存放在栈中,upvalue存放在链表中,global变量存放在全局的表中。

      例子:

使用local

[javascript] view plain copy print ?
  1. function Add() 
  2.    local x, y 
  3.    return x + y 
  4. nd    
 function Add()
    local x, y
    return x + y
end   

使用upvalue      

[javascript] view plain copy print ?
  1. local x, y 
  2. function Add() 
  3.     return x + y 
  4. end 
local x, y
function Add()
    return x + y
end

使用global

[javascript] view plain copy print ?
  1. function Add() 
  2.     return x + y 
  3. end 
function Add()
    return x + y
end

2 尽量不使用loadstring等函数来动态调用代码

      编译工作是很繁重的,动态调用代码时Lua虚拟机需要动态地编译Lua代码,效率很低。

3 减少表的插入次数

      Lua中的表由一个数组和一个哈希表组成,每一次从表中插入数据,Lua都会重新计算每个元素新的存放位置,导致插入的效率很低。

  

[javascript] view plain copy print ?
  1. local a = {} 
  2.    a[1] = 1 -- 重新哈希,数组部分大小为1 
  3.    a[2] = 2 -- 重新哈希,数组部分大小为2 
  4.    a[3] = 3 -- 重新哈希,数组部分大小为4 
 local a = {}
    a[1] = 1 -- 重新哈希,数组部分大小为1
    a[2] = 2 -- 重新哈希,数组部分大小为2
    a[3] = 3 -- 重新哈希,数组部分大小为4

以上的代码产生三次重新哈希计算以及三次数组部分扩容,相比以下代码只执行了一次哈希和扩容。

[javascript] view plain copy print ?
  1. local a = {true, true, true} -- 重新哈希,数组部分大小为4 
  2. a[1] = 1 
  3. a[2] = 2 
  4. a[3] = 3 
    local a = {true, true, true} -- 重新哈希,数组部分大小为4
    a[1] = 1
    a[2] = 2
    a[3] = 3


4 不要试图通过删除表元素来节省内存空间

      对表中的元素赋nil值不会导致表元素被删除,但是在插入新元素时,表会根据现有的非nil元素的数量来更改表的大小。

字符串

5 避免字符串连接

      Lua在虚拟机内部对于相同的字符串只保留一份copy,保存在一个哈希表中,字符串变量保存这个copy的引用。这种实现机制导致Lua中字符串的查找和比较效率比较高,创建的效率比较低。

减少

6 减少表的创建

[javascript] view plain copy print ?
  1. polyline = { { x = 10.3, y = 98.5 }, 
  2.              { x = 10.3, y = 18.3 }, 
  3.              { x = 15.0, y = 98.5 }, 
  4.                ... 
  5.             } 
polyline = { { x = 10.3, y = 98.5 },
             { x = 10.3, y = 18.3 },
             { x = 15.0, y = 98.5 },
               ...
            }

同样表达点的序列的表,以上代码每个点都创建一个表,相比以下代码只创建了两个表。

[javascript] view plain copy print ?
  1. polyline = { x = { 10.3, 10.3, 15.0, ...}, 
  2.              y = { 98.5, 18.3, 98.5, ...} 
  3.            } 
polyline = { x = { 10.3, 10.3, 15.0, ...},
             y = { 98.5, 18.3, 98.5, ...}
           }

7 避免不必要的创建

      Lua虚拟机每执行到local就创建一个变量,执行到function就创建一个闭包。参考 Lua 5.0实现 4 函数和闭包

重用

8 重用表是一个比较有效的手段

[javascript] view plain copy print ?
  1. local t = {} 
  2. for i = 1970, 2000 do 
  3.     t[i] = os.time({year = i, month = 6, day = 14}) 
  4. end 
local t = {}
for i = 1970, 2000 do
    t[i] = os.time({year = i, month = 6, day = 14})
end

以下是与之等价的代码,但是重用了表:

[javascript] view plain copy print ?
  1. local t = {} 
  2. local aux = {year = nil, month = 6, day = 14} 
  3. for i = 1970, 2000 do 
  4.     aux.year = i 
  5.     t[i] = os.time(aux) 
  6. end 
local t = {}
local aux = {year = nil, month = 6, day = 14}
for i = 1970, 2000 do
    aux.year = i
    t[i] = os.time(aux)
end

9 使用记忆化方法

[javascript] view plain copy print ?
  1. function memoize (f) 
  2.     local mem = {} --记忆化的表,存储每一个输入的计算结果 
  3.     setmetatable(mem, {__mode = "kv"}) -- 定义成弱表 
  4.     return function (x) -- 这个函数就是记忆化的f 
  5.         local r = mem[x] 
  6.         if r == nil then -- 如果没有对应的计算结果 
  7.             r = f(x) -- 调用f计算 
  8.             mem[x] = r -- 保存在记忆化表中 
  9.         end 
  10.         return r -- 已经计算过,直接返回结果 
  11.     end 
  12. end 
function memoize (f)
    local mem = {} --记忆化的表,存储每一个输入的计算结果
    setmetatable(mem, {__mode = "kv"}) -- 定义成弱表
    return function (x) -- 这个函数就是记忆化的f
        local r = mem[x]
        if r == nil then -- 如果没有对应的计算结果
            r = f(x) -- 调用f计算
            mem[x] = r -- 保存在记忆化表中
        end
        return r -- 已经计算过,直接返回结果
    end
end

假如有以下函数:

[javascript] view plain copy print ?
  1. function fab(x) 
  2. if x == 1 or x == 2 then 
  3.         return
  4. end 
  5. return fab(x-1) + fab(x-2) 
  6. end 
function fab(x)
if x == 1 or x == 2 then
        return 1
end
return fab(x-1) + fab(x-2)
end

对比不使用记忆化方法和记忆化方法:

fab(35) -- 执行8s

fab = memoize(fab)

fab(35) -- 不超过1s

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值