欢迎光临
梦想从学习开始!

Lua面向对象封装及元表(metatable)性能测试| 小熊测试

本文主要介绍 Lua面向对象封装及元表(metatable)性能测试| 小熊测试,小熊希望对大家的学习或者工作具有一定的参考学习价值,在测试领域有所提升和发展。

  Lua本身是没有面向对象支持的,但面向对象编程在逻辑复杂的大型工程却很有用。于是很多人用Lua本身的数据结构table来模拟面向对象。最简单的一种方法是把对象的方法、成员都放到table中。如:

— file:test.lua

local test = {}

function test:get_x()

return self.x or 0

end

function test:set_x( _x )

self.x = _x

end

local test_module = {}

function test_module.new()

local t = {}

for k,v in pairs( test ) do

t[k] = v

end

return t

end

return test_module

  调用也比较简单:

  – file:main.lua

  local test = require "test"

  local _t = test.new()

  _t:set_x( 999 )

  print( _t:get_x() )

  这已经很像面向对象编程。但我们可以看到这样写有些缺点:

  1.数据和方法混在一起(当然这不是什么大问题,C++也是这样)

  2.每创建一个对象,都要将方法复制一遍

  3.没法继承

  Lua有强大的元表(metatable),利用它我们可以更优雅地封装一下:

  1.先统一封装一个面向对象函数:

— file:oo.lua

local oo = {}

local cls = {}

local function new( clz )

local t = {}

setmetatable(t, clz)

return t

end

function oo.class( parent,name )

local t = {}

cls[name] = t

parent = parent or {}

rawset( t,"__index",t )

setmetatable( t,{ __index = parent,__call = new } )

return t

end

return oo

  2.然后重新写类的实现:

— file:test.lua

local oo = require "oo"

local test = oo.class( nil,… )

function test:get_x()

return self.x or 0

end

function test:set_x( _x )

self.x = _x

end

return test

  3.调用也更加简单了:

  – file:main.lua

  local Test = require "test"

  local _t = Test()

  _t:set_x( 999 )

  print( _t:get_x() )

  可以看到,利用元表,我们可以把方法全部放到元表中,与对象成员数据分开。元表本身是一个表,它也有元表,可以把父类作为元表的元表实现继承。我们如果再扩展一下,还可以实现对象方法的热更,对象统计…

  虽然元表很巧妙,但它的实现是有代价的。Lua得先在table中查找是否有相同的值,如果没有,再去元表找。如果是多重继承,那么还得一层层元表找下去。下面我们来测试一下元表的效率。

— lua metatable performance test

— 2016-04-01

— xzc

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

local empty_mt1 = {}

local empty_mt2 = {}

local empty_mt3 = {}

local empty_mt4 = {}

local empty_mt5 = {}

local empty_mt6 = {}

local empty_mt7 = {}

local empty_mt8 = {}

local mt = {}

mt.test = test

local mt_tb = {}

setmetatable( empty_mt8,{__index = mt} )

setmetatable( empty_mt7,{__index = empty_mt8} )

setmetatable( empty_mt6,{__index = empty_mt7} )

setmetatable( empty_mt5,{__index = empty_mt6} )

setmetatable( empty_mt4,{__index = empty_mt5} )

setmetatable( empty_mt3,{__index = empty_mt4} )

setmetatable( empty_mt2,{__index = empty_mt3} )

setmetatable( empty_mt1,{__index = empty_mt2} )

setmetatable( mt_tb,{__index = empty_mt1} )

local tb = {}

tb.test = test

local ts = 10000000

f_tm_start()

local cnt = 0

for i = 1,ts do

cnt = test( cnt,1 )

end

f_tm_stop( "call function native" )

f_tm_start()

local cnt = 0

for i = 1,ts do

cnt = tb.test( cnt,1 )

end

f_tm_stop( "call function as table value" )

f_tm_start()

for i = 1,ts do

cnt = empty_mt6.test( cnt,1 )

end

f_tm_stop( "call function with 3 level metatable" )

f_tm_start()

for i = 1,ts do

cnt = mt_tb.test( cnt,1 )

end

f_tm_stop( "call function with 10 level metatable" )

  在我的笔记本上测试,结果为:

  local ts = 10000000

  call function native    1091772 microsecond

  call function as table value    1287172 microsecond

  call function with 3 level metatable    2014431 microsecond

  call function with 10 level metatable   3707181 microsecond

  可以看到,采用第一种方法封闭的面向对象比原生函数调用慢不了多少,但用第二种方法实现3重继承的话,几乎慢了一倍。

  在实际项目中,我们用的是第二种封装方式,最主要是可以继承和热更代码。虽然效率有一定影响,但实际应用中逻辑消耗的时间比函数调用的时间仍大得多,这点损耗可以接受。这个世界上没有最快,只有更快,不必盯着程序的效率看。在满足项目要求的情况下,开发效率也是很值得考虑的。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持小熊分享邦(www.xxfxb.com),希望大家能坚持软件测试之路,谢谢。

赞(0) 打赏
未经允许不得转载:小熊分享邦 » Lua面向对象封装及元表(metatable)性能测试| 小熊测试

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏