Lua配置表存储优化方案
2017-05-22 10:59
211 查看
原文链接:https://blog.uwa4d.com/archives/1490.html
这是侑虎科技第183篇原创文章,感谢作者卢建供稿。欢迎转发分享,未经作者授权请勿转载。作者QQ:345005607。如果您有任何独到的见解或者发现也欢迎联系我们,一起探讨。(QQ群465082844)
同时,作者也是U Sparkle活动参与者哦,UWA欢迎更多开发朋友加入
U Sparkle开发者计划,这个舞台有你更精彩!
◆◆◆
简单介绍
Lua作为一个极为精简的嵌入型脚本语言,已经广泛地用在了游戏业。Lua的存在一般是两种场合,一种用于实现游戏上层业务逻辑,一种则利用了Lua语言本身灵活简单的数据表达能力而被广大程序员用于数据的存储,也就是常说的配置文件。
一般来说配置文件的初始来源是策划维护着的、有着一定格式约束的Excel表格,经由程序员提供的导出工具,把Excel的表格数据导出成为游戏能直接读取使用的Lua源码。
这些源码文件以Lua table的形式存储与Excel等价的数据,通常可以简单把这个配置表看成是一组2维数组,转换成配置就是一个key(Excel第一列)对应一组子数据(Excel中一行),那么整个配置数据就是一个大表包含着若干小表,如下:
原始配置表:
转换成Lua后大致是这样:
如果配置文件中的数据过大,或着是有冗余的无用数据,那么势必会导致输出的Lua文件过大,这将严重影响加载速度和增大内存占用量。本文将介绍一种消除配置文件中冗余数据,达到压缩和优化数据存储的方法。
◆◆◆
优化思路
介绍该方法前,大家先来看看一般数据在哪些地方会有冗余。由上面的示例图我们可以清晰地看到数据的冗余点:
大量的数据是重复的,或着是代表没有意义的空值(比如0,[]等);
大量的中文字符串是需要游戏后期做本地化处理的;
很多复合型数据(子表,数组)内容是一样的;
搞清楚了数据冗余的原因,我们就可以制定优化方案:
对于Excel中的一列,出现次数最多的值认定为默认值,然后把它从Lua表中剔除掉,然后利用metatable机制实现全局默认值存储;
对于中文字符串,替换为一个唯一的id标识,写回到Lua表中,读取的时候加上相应的查找替换;
对于一些复杂的子表或着数组,做唯一化替换处理,替换后写回到原始数据中;
能看出来,上面的操作其实做的都是唯一化处理,所以有个要求就是整个Lua表的数据必须是只读的,如果不满足以上条件,一切优化都是错误的。所以在最后我们需要把整个表改为只读,以保护数据不被错误篡改。
优化后变成了下面这样:
看上去可读性没有原始的那么高了,一些被多处引用的子表已经被替换成了一个变量,这些变量是以local的形式存储在作用域的。由于Lua本身的一些限制,一个作用域内能够存放的最大local变量的个数是200个(lparser.c #define MAXVARS 200),所以超过个数限制的多余部分表会被放入一个临时数组中,初始化的时候需要额外的查表,稍微多一些开销,但可以接受。
◆◆◆
归纳总结
一般来说配置表文件优化后只有之前不到一半的大小,大量的重复数据被优化掉了,极大地提升了加载时间和内存占用。
前后文件对比如下:
最后说下宿主语言环境中,如果需要读取Lua表中的子表数据,我们可以把该表的pointer拿出来作为键值,存放于查找表中,这样宿主环境中也能读取到一个唯一的数组,节约内存。
本文中使用的配置表优化工具源码已经放在github,需要的朋友可以自取,喜欢的朋友别忘了给个小星星哟:)
https://github.com/lujian101/LuaTableOptimizer
文末,再次感谢卢建的分享,如果您有任何独到的见解或者发现也欢迎联系我们,一起探讨。(QQ群465082844)。
也欢迎大家来积极参与U Sparkle开发者计划,简称"US",代表你和我,代表UWA和开发者在一起!
这是侑虎科技第183篇原创文章,感谢作者卢建供稿。欢迎转发分享,未经作者授权请勿转载。作者QQ:345005607。如果您有任何独到的见解或者发现也欢迎联系我们,一起探讨。(QQ群465082844)
同时,作者也是U Sparkle活动参与者哦,UWA欢迎更多开发朋友加入
U Sparkle开发者计划,这个舞台有你更精彩!
◆◆◆
简单介绍
Lua作为一个极为精简的嵌入型脚本语言,已经广泛地用在了游戏业。Lua的存在一般是两种场合,一种用于实现游戏上层业务逻辑,一种则利用了Lua语言本身灵活简单的数据表达能力而被广大程序员用于数据的存储,也就是常说的配置文件。
一般来说配置文件的初始来源是策划维护着的、有着一定格式约束的Excel表格,经由程序员提供的导出工具,把Excel的表格数据导出成为游戏能直接读取使用的Lua源码。
这些源码文件以Lua table的形式存储与Excel等价的数据,通常可以简单把这个配置表看成是一组2维数组,转换成配置就是一个key(Excel第一列)对应一组子数据(Excel中一行),那么整个配置数据就是一个大表包含着若干小表,如下:
原始配置表:
转换成Lua后大致是这样:
local mapsetting = { [1] = { name = "map1", useminimap = "map1000", show_name = "十里桃林", cam_pitch = 40, cam_yaw = 230, cam_dis = 10, pkflag_support = 1, pkflag_punish = 1, maxnum = 50, scheduleConf = {}, scriptId = {}, ... }, [2] = { name = "map2", useminimap = "map1000", show_name = "碧波潭", cam_pitch = 40, cam_yaw = 230, cam_dis = 10, pkflag_support = 1, pkflag_punish = 1, maxnum = 50, scheduleConf = {}, scriptId = {10002}, ... }, [3] = { name = "map3", useminimap = "map1000", show_name = "梵音谷", cam_pitch = 40, cam_yaw = 230, cam_dis = 10, pkflag_support = 1, pkflag_punish = 1, maxnum = 50, scheduleConf = {}, scriptId = {}, ... } ... } return mapsetting
如果配置文件中的数据过大,或着是有冗余的无用数据,那么势必会导致输出的Lua文件过大,这将严重影响加载速度和增大内存占用量。本文将介绍一种消除配置文件中冗余数据,达到压缩和优化数据存储的方法。
◆◆◆
优化思路
介绍该方法前,大家先来看看一般数据在哪些地方会有冗余。由上面的示例图我们可以清晰地看到数据的冗余点:
大量的数据是重复的,或着是代表没有意义的空值(比如0,[]等);
大量的中文字符串是需要游戏后期做本地化处理的;
很多复合型数据(子表,数组)内容是一样的;
搞清楚了数据冗余的原因,我们就可以制定优化方案:
对于Excel中的一列,出现次数最多的值认定为默认值,然后把它从Lua表中剔除掉,然后利用metatable机制实现全局默认值存储;
对于中文字符串,替换为一个唯一的id标识,写回到Lua表中,读取的时候加上相应的查找替换;
对于一些复杂的子表或着数组,做唯一化替换处理,替换后写回到原始数据中;
能看出来,上面的操作其实做的都是唯一化处理,所以有个要求就是整个Lua表的数据必须是只读的,如果不满足以上条件,一切优化都是错误的。所以在最后我们需要把整个表改为只读,以保护数据不被错误篡改。
优化后变成了下面这样:
local __rt_1 = { 0 } local __rt_2 = { } local __rt_3 = { 10004 } ... local __rt_34 = { desc = "@40313", drop_show = __rt_33, scriptId = { 10001, 10011 }, shield = 6, show_name = "@470882", subtype = 4 } ... --超出Lua局部变量个数,则需要另外单独存储和引用 --createtable是把c中创建表的函数导入到lua中,这样通过创建一个预分配 --的表,能有效的避免table re-hash,有效利用内存 --下面创建一个预分配919个数组array-part、0个元素的空hash-part local __rt = createtable and createtable( 919, 0 ) or {} __rt[1] = { 2861, 1, 46, 8 } __rt[2] = { 2861, 1, 55, 12 } __rt[3] = { 2861, 1, 50, 7 } ... local mapsetting = { { name = "map1", scriptId = __rt_2, shield = 0, show_name = "@384651", type = 0, ... }, { desc = "@424100", music = "map2_r", name = "map2", scriptId = { 10002 }, shield = 0, show_name = "@424100", ... }, { bloomtype = 1, scriptId = __rt_2, shield = 0, show_name = "@491800", type = 0, ... }, { cam_yaw = 1, scriptId = __rt_2, shield = 0, show_name = "@499116", type = 0, ... } } ... --大量的默认值存储在这里 local __default_values = { Belongto = 0, Instance_Show = 0, Instance_group = 0, daily_getcount = 9, damage_modify = 1, desc = "@282313", difficult = 0, pkflag_support = 1, scheduleConf = __rt_2, scriptId = __rt_32, ... } ... do local base = { __index = __default_values, --基类,默认值存取 __newindex = function() --禁止写入新的键值 error( "Attempt to modify read-only table" ) end } for k, v in pairs( mapsetting ) do setmetatable( v, base ) end base.__metatable = false --不让外面获取到元表,防止被无意修改 end return mapsetting
看上去可读性没有原始的那么高了,一些被多处引用的子表已经被替换成了一个变量,这些变量是以local的形式存储在作用域的。由于Lua本身的一些限制,一个作用域内能够存放的最大local变量的个数是200个(lparser.c #define MAXVARS 200),所以超过个数限制的多余部分表会被放入一个临时数组中,初始化的时候需要额外的查表,稍微多一些开销,但可以接受。
◆◆◆
归纳总结
一般来说配置表文件优化后只有之前不到一半的大小,大量的重复数据被优化掉了,极大地提升了加载时间和内存占用。
前后文件对比如下:
最后说下宿主语言环境中,如果需要读取Lua表中的子表数据,我们可以把该表的pointer拿出来作为键值,存放于查找表中,这样宿主环境中也能读取到一个唯一的数组,节约内存。
本文中使用的配置表优化工具源码已经放在github,需要的朋友可以自取,喜欢的朋友别忘了给个小星星哟:)
https://github.com/lujian101/LuaTableOptimizer
文末,再次感谢卢建的分享,如果您有任何独到的见解或者发现也欢迎联系我们,一起探讨。(QQ群465082844)。
也欢迎大家来积极参与U Sparkle开发者计划,简称"US",代表你和我,代表UWA和开发者在一起!
相关文章推荐
- ANALYSIS SERVER配置参数优化方案
- 案例分析:无锡移动采用“戴尔虚拟化方案+EqualLogic™存储+ICS服务”优化IT系统
- Innodb IO优化与配置方案
- android sdcard存储方案优化(基于wrapfs文件系统):之三
- 项目重构之数据源配置与优化:log4j 配置数据库连接池Druid,并实现日志存储到数据库
- 使用weblogic连接池来得到数据库连接(通过配置文件进行读取的优化方案)
- Android布尔型配置存储优化
- 如何优化Urchin配置文件每月数据库的磁盘存储空间
- HPL环境安装、配置及初步优化方案(报告)
- Tomcat 配置详解/优化方案
- Apache for windows虚拟主机配置方案(安全优化)
- 配置文件优化方案(二)
- 项目中配置文件优化方案
- Innodb IO优化与配置方案
- Mysql5.5 InnoDB存储引擎配置和优化
- apache配置文件“httpd.conf”优化方案
- 数据量10亿级别的数据库表,多行存储成一行、一列扩展成多列之数据优化及迁移方案(三)
- Google Protocol Buffer用于配置文件存储的方案与兼容性分析
- Tomcat6优化配置方案
- lua gc 优化方案