Lua性能優化

LuaJIT本身對Lua作了很多方面的優化工作,對很多Lua自帶的庫函數進行了優化。

優化詳情:http://wiki.luajit.org/NYI

wiki:http://wiki.luajit.org/Home


關於Lua優化的一些細節:


1.經常使用的庫函數,使用local方式來調用,注意僅一次調用是不起作用的。

--this is the lowest method
    string.rep()

--this is a better way
    local rep = string.rep

2.創建table時,事先分配好大小。

--allocate the table when you declare it
    local tbl = {nil,nil}
    table.insert(tbl,1)
    table.insert(tbl,2)

lua table的內存分配按照2的N次方遞增,所以在不斷table.insert的過程中,會在前期頻繁的申請內存空間因此事先申請好內存能夠提升性能。


3.避免一些長字符串的format操作,長字符串的format操作效率低下,對於長字符串可以考慮使用..操作來進行字符串連接,效率比format高。

local str = [==[ hello %s ]==]
string.format(str,"world")

local str = `hello`
return str.. "world"


4.一個文件中的函數儘量使用local方式來定義

    local plus = function(a,b)
        return a+b
    end

5.一些結構的優化。這裏我們定義了一個Filter,其中有typeA和typeB 2種類型的filter,遍歷這些filter,我們有3種方式封裝API:

    

--now we define a filter
--we get three ways to define it

---all filters listed
local Filter = {nil,nil}
local Filter.typeA = {nil,nil}
local Filter.typeB = {nil,nil}


Filter.typeA.filter1 = function()
end

Filter.typeA.filter2 = function()
end

Filter.typeB.filter1 = function()
end 

Filter.typeB.filter2 = function()
end

--first way
function handler(arg)
    if not Filter.typeA.filter1() then
        return false
    end
    
    if not Filter.typeA.filter2() then
        return false
    end
    
    if not Filter.typeB.filter1() then
        return false
    end
    
    if not Filter.typeB.filter2() then
        return false
    end
    
    return true
end 

--second way
function handler(arg)
    for k,func in pairs(Filter.typeA) do
        if not func() then
            return false
    end
   
    for k,func in pairs(Filter.typeB) do
        if not func() then
            return false
        end
    end
    
    return true
end

--third way
local func_name_list = { 'filter1','filter2'}

function handler(arg)
    for _,name in ipairs(func_name_list) do
        if not Filter.typeA[name]() then
            return false
        end
        
        if not Filter.typeB[name]() then
            return false
        end
     end

     return true
end


--fourth way
local typeA_func_list = {
                             Filter.typeA.filter1(),
                             Filter.typeA.filter2()                   
                        }
                        
local typeB_func_list = {
                             Filter.typeB.filter1(),
                             Filter.typeB.filter2()
                        }
function handler(arg)
    for _,func in ipairs(typeA_func_list) do
        if not func() then
            return false
        end
    end
    
    for _,func in ipairs(typeB_func_list) do
        if not func() then
            return false
        end
    end
    
    return true
end

這裏值得注意的是,LuaJIT針對ipairs作了相當的優化,但是對於pairs,LuaJIT中沒有作優化,在一些table中我們儘量避免使用hash表,遍歷的時候使用iparis。

第一種方式是效率最高的,但是結構很糟糕,代碼維護起來比較難。

第二種方式遍歷效率極低。

第三種效率不錯,代碼結構一般。

第四種是最好的遍歷方式,結構也很不錯。我們採用這種方式是最優的。


6.關於table的遍歷


在LuaJIT中針對ipairs作了相當的優化,但是對pairs沒有優化。另外對於一些遍歷,儘量使用for i,#table do end 來進行遍歷。LuaJIT中針對for遍歷也進行了優化。對於數組儘量使用for來遍歷,這是最快的遍歷方式。


7.關於config的加載,首先看測試腳本

-- conf_table.lua
   
   local conf_table = {nil,nil,nil} 
   
   conf_table.A = "string A"
   conf_table.B = "string B"
   conf_table.C = "string C"
  
   return conf_table
 -- conf_notbl.lua
     
     A = "string A"
     B = "string B"
     C = "string C"
-- conf_test.lua
    require 'conf_notbl'
    local conf = require 'conf_table'
    local socket = require 'socket'
    local round = 10000000
    local time = socket.gettime()
    for i=1,round do
        local x = A
        local y = B
        local z = C
    end
    print("conf before :" .. (socket.gettime()-time)*1000 .. "ms")
    local time = socket.gettime()
    for i=1,round do
        local x = conf.A
        local y = conf.B
        local z = conf.C
    end    
    print("conf table  :" .. (socket.gettime()-time)*1000 .. "ms")
    local a1=A
    local a2=B
    local a3=C
    local time = socket.gettime()
    for i=1,round do
        local x = a1
        local y = a2
        local z = a3
    end
    print("local conf :" .. (socket.gettime()-time)*1000 .. "ms")  
測試結果:
Lua:
    conf before :742.80691146851ms
    conf table  :789.02816772461ms
    local conf :403.17010879517ms
LuaJIT:
    conf before :4.1639804840088ms
    conf table  :4.1368007659912ms
    local conf :3.9420127868652ms

在使用lua文件來加載config的時候,儘量使用local方式來將經常使用的conf的數據存起來,如果使用次數不多,這樣做就沒有必要了。

另外特意使用一個table來存儲conf數據並沒有必要,但是這樣做config結構比較好。


8.關於table.insert

luajit中對於table.insert作了優化。對於數組類型的table,可以利用tbl[#tbl+1]的方式來進行賦值操作,直接用lua解釋器可以看到明顯的性能差距,用luajit的話差距就沒有這麼明顯了。

local tbl = {}
table.insert(tbl,1)
tbl[#tbl+1] = 1
--運行1000000次的結果
Lua:
    table.insert time :293.66397857666ms
    tbl[#tbl+1] time :186.48815155029ms
    local insert time :222.85604476929ms  --添加了 local insert = table.insert
LuaJIT:
    table.insert time :137.73202896118ms
    tbl[#tbl+1] time :134.56797599792ms
    local insert time :137.2811794281ms

可以看到LuaJIT對於table.insert的操作作了優化,所以使用luajit時可以不必刻意使用tbl[#tbl+1]=1的方式來進行insert操作。另外LuaJIT對於local調用也作了優化。


9.對於數組table是否爲空的判斷

    儘量使用table.getn,對於hash表則必須使用next,table.getn不能判斷hash表是否爲空。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章