【内存优化】Lua配置表导出优化

文章出处: 【内存优化】Lua配置表导出优化

前言

随着游戏的开发,项目的配置表数据越来越多,占用的内存越来越;配置表占用太大就会影响游戏加载速度,游戏流畅度的每一毫秒都是我们的必争之路。
如果配置文件中的数据过大,或着是有冗余的无用数据,那么势必会导致输出的Lua文件过大,这将严重影响加载速度和增大内存占用量。

我们项目采用的配置表存储方式是Lua表格,策划配表用excel配置,然后使用网上的开源工具excel2lua导出Lua表格,业务使用的时候直接import表格就好了。

在观察了导出的Lua表格后,我整理出一堆待优化的问题:

  1. 默认值提取
  2. 字段名优化
  3. 客户端、服务端表格分离

搞清楚了数据冗余的原因,我们就可以制定优化方案:

1. 默认值提取:优化效果显著

通过观察可以发现其中有部分字段很容易重复,这些字段通常为枚举或者有固定的分类,只有几个不同的值,然而配置表中每个item都需要为这些内容创建一个字段。
我们的优化方案是,利用Lua的特性–原表(metatable),建一张defaultValues表,在导出的时候,选取出现次数最多的值作为每个字段的原表,存到defaultValues,然后剔除每行中与默认值相同的字段,从而节省内存。
优点:一般来说配置表文件优化后只有之前不到一半的大小,大量的重复数据被优化掉了,极大地提升了加载时间和内存占用。
缺点:可读性变差了、然后每行都加了个原表,table变多了,然后访问效率变差了,每次访问如果没找到都要再找一次原表

local defaultValues = {
	powerExpendReduce = 264,
	magicDefenseIgnored = 27, 
	stiffnessResistance = 14,
	bloodTransferLv = 2, 
	damageIncrease = 95,
	phyShield = 15, 
	magicDefense = 8000,
}

local ABILITY = {
	[101] = { bloodTransferLv = 8000, damageIncrease = 9000, phyShield = 1000, magicDefense = 1864, },
	[102] = { magicDefenseIgnored = 33,},
	[103] = { magicDefenseIgnored = 36, bloodTransferLv = 7, damageIncrease = 185,},
	……
}

do
    local base = {
        __index = defaultValues, --基类,默认值存取
        __newindex = function()
            --禁止写入新的键值
            error("Attempt to modify read-only table")
        end
    }
    for k, v in pairs(ABILITY) do
        setmetatable(v, base)
    end
    base.__metatable = false --不让外面获取到元表,防止被无意修改
end

2. 字段名优化:内存优化效果显著

热心网友给我提出这种优化,但是这种类似CSV存储格式,可想而知,内存肯定可以降低很多,我简单实现了一下,比对了一下内存,差别还是很大的!!!
但是这种存储格式和默认值方案冲突,只能取舍,而且可读性比较差。现在项目已经做完了,下个项目可以试一下这种方案。

local KeyMap = {
	powerExpendReduce = 1,
	magicDefenseIgnored = 2,
	stiffnessResistance = 3,
	bloodTransferLv = 4,
	damageIncrease = 5,
	phyShield = 6,
	magicDefense = 7,
}

local ABILITY = {
	[101] = { 264, 27, 14, 8000, 9000, 1000, 1864, },
	[102] = { 264, 33, 14, 2, 95, 15, 8000,},
	[103] = { 264, 36, 14, 7, 185, 15, 8000,},
	……
}

do
    local base = {
        __index = function(table,key)
            local keyIndex = KeyMap[key]
            if not keyIndex then
                print("key not found: ",key)
                return nil
            end
            return table[keyIndex]
        end, --基类,默认值存取
        __newindex = function()
            --禁止写入新的键值
            error("Attempt to modify read-only table")
        end
    }
    for k, v in pairs(ABILITY) do
        setmetatable(v, base)
    end
    base.__metatable = false --不让外面获取到元表,防止被无意修改
end

3. 客户端、服务端表格分离

服务器对于这么点内存,根本不会在意,但是客户端大不一样,是个精打细算的好媳妇,每1KB都要计较的,所以,我们设计一个参数来控制配置表导出对象,C:客户端、S:服务端,在和策划设计表的时候,告知哪些是客户端需要的参数,导出的时候,按C、S导出2张表。

本文中使用的配置表优化工具源码已经放在github,需要的朋友可以自取
链接: https://github.com/Aver58/Tools

Todo:带元表对象的遍历

这样做有个缺点就是,如果是迭代器去遍历的话,会少字段
云风有篇文章讲了这个:正确的序列化 Lua 中带元表的对象
Lua 5.2 之后的版本,约定了在元表中可以给出一个__pairs方法可以用这个元方法实现元表的访问。lua中 table 重构index/pairs元方法优化table内存占用

参考

使用元表优化 Lua 配置文件
Lua配置表存储优化方案 - UWA Blog
Lua元表源码学习一下:lua中metatable源码分析总结_boyhailong的专栏-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值