• 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重继承的话,几乎慢了一倍。

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

  • 相关阅读:
    LeetCode 977 有序数组的平方
    LeetCode 24 两两交换链表中的节点
    LeetCode 416 分割等和子集
    LeetCode 142 环形链表II
    LeetCode 106 从中序与后序遍历序列构造二叉树
    LeetCode 637 二叉树的层平均值
    LeetCode 117 填充每个节点的下一个右侧节点
    LeetCode 75 颜色分类
    redhat 7.4 挂载ntfs格式的u盘并且使用
    redhat 查看CPU frequency scaling(CPU频率缩放)
  • 原文地址:https://www.cnblogs.com/coding-my-life/p/5352272.html
Copyright © 2020-2023  润新知