lua table vs closure

  最近在重構自己寫的框架中的定時器模塊,需要把回調函數保存起來,大概如下:

function timer_mgr:save_timer( this,callback )
    return { this = this,callback = callback}
end

-- 創建新定時器
-- @after:延遲N秒後啓動定時器
-- @repeated:N秒循環
-- @this:回調對象
-- @callbakck:回調函數
function timer_mgr:new_timer( after,repeated,this,callback )
    local timer_id = self:next_id()
    self.timers[timer_id] = save_timer( this,callback )
end

function timer_mgr:do_timer( timer_id )
    local timer = self.timers[timer_id]

    -- 調用之前保存的定時器回調
    return timer.callback( timer.this )
end

正常情況下,用table保存定時器的回調參數,畢竟lua中也沒有太多的數據結構可以選擇。不過,我們也可以這樣用closure來保存:

function timer_mgr:save_timer( this,callback )
    return function()
        return callback( this )
    end
end

-- 創建新定時器
-- @after:延遲N秒後啓動定時器
-- @repeated:N秒循環
-- @this:回調對象
-- @callbakck:回調函數
function timer_mgr:new_timer( after,repeated,this,callback )
    local timer_id = self:next_id()
    self.timers[timer_id] = save_timer( this,callback )
end

function timer_mgr:do_timer( timer_id )
    local timer = self.timers[timer_id]

    -- 調用之前保存的定時器回調
    return timer()
end

這樣似乎看起來更優雅更方便一些,不過,頻繁創建closure也是很消耗內存和cpu的,需要和table對比一下:

function test_table_object( this,cb )
    return { this = this,cb = cb }
end

function test_closure_object( this,cb )
    return function()
        return cb( this )
    end
end

local function cb()
    print("do nothing ...")
end

local max = 1000000

local table_mgr = {}
local closure_mgr = {}

function test_table()
    
    local beg_m = collectgarbage("count")
    local beg_tm = os.clock()

    for idx = 1,max do
        table_mgr[idx] = test_table_object( {},cb )
    end

    local end_m = collectgarbage("count")
    local end_tm = os.clock()

    print("table test",end_m - beg_m,end_tm - beg_tm)
end

function test_closure()
    
    local beg_m = collectgarbage("count")
    local beg_tm = os.clock()

    for idx = 1,max do
        table_mgr[idx] = test_closure_object( {},cb )
    end

    local end_m = collectgarbage("count")
    local end_tm = os.clock()

    print("closure test",end_m - beg_m,end_tm - beg_tm)
end

collectgarbage("stop")
collectgarbage("collect")

test_closure()
test_table()

這段代碼,分別在win10 - I3 cpu和debian7(虛擬機) - A8 cpu下測試:

closure test	117946.5	0.489
table test	125000.0	0.446

closure test	180446.5	0.75
table test	171875.0	0.72

可以看到,table和closure的消耗其實都差不多。但closure在這個應用場景下就優雅得多,因爲可以很方便地傳更多的參數,比如:

function timer_mgr:save_timer( this,callback,... )
    return function()
        return callback( this,... )
    end
end

function timer_mgr:new_timer( after,repeated,this,callback,... )
    local timer_id = self:next_id()
    self.timers[timer_id] = save_timer( this,callback,... )
end

function timer_mgr:do_timer( timer_id )
    local timer = self.timers[timer_id]

    -- 調用之前保存的定時器回調
    return timer()
end

而table要傳可變參則不太好處理了,需要另外創建一個table來pack參數,調用時再unpack,或者只用一個table把callback函數和參數存一起,調用時把函數移除再unpack。總而言之,在這種需要保存參數的回調,closure更合適。當然,如果你的回調函數不帶參數,那就是另外一碼事了。

  查了下,之前也有人做了類似的測試,結果也差不多:http://lua-users.org/wiki/ObjectOrientationClosureApproach

 

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