和lua的效率对比测试_如何深入理解Lua数据结构和内存占用?

本文详细探讨了Lua中的数据结构,尤其是string和table的内存占用情况。通过对比Lua 5.1和5.3的差异,分析了字符串的内存消耗,特别是长字符串在不同场景下的内存占用。此外,还研究了table的内存管理和rehash过程,提供了内存估算方法,并给出了内存优化建议。
摘要由CSDN通过智能技术生成

导语 Lua底层数据结构的内存动态分配,一般情况下不太好估算内存占用。腾讯游戏学院专家Ross在本文剖析lua常见数据结构string和table的底层实现原理,并从中找到一般性的内存占用估算方法。

由于lua是一个跨平台的脚本语言,会根据平台位数(16bitbit)、平台类型(linuxwindows)、语言标准(C89C99)、以及编译参数等开启预编译选项,导致基本数据结构的字长和类型会动态变化,以Tlinux2.2 x86_64 进行编译为基础进行分析介绍, lua版本5.3.4。并根据我们开发过程中一些常见的情景进行分析:

基础数据结构

Lua的基本数据表示方式是type + union的方式,根据不同类型映射到union的不同结构上面, 统一的表示结构lua_TValue:

typedef union Value {
  GCObject *gc;    /* collectable objects */
  void *p;         /* light userdata */
  int b;           /* booleans */
  lua_CFunction f; /* light C functions */
  lua_Integer i;   /* integer numbers */
  lua_Number n;    /* float numbers */
} Value;
​
struct lua_TValue {
  Value value_; 
  int tt_;
} TValue;

但由于lua_Integer和lua_Number在当前平台预处理后,分别定义成long long 和 double类型,所以为方便理解,上述结构经过转义:

typedef union Value {
  GCObject *gc;    /* collectable objects */
  void *p;         /* light userdata */
  int b;           /* booleans */
  lua_CFunction f; /* light C functions */
  long long i;     /* integer numbers */
  double n;       /* float numbers */
} Value;
​
struct lua_TValue {
  Value value_; 
  int tt_;
} TValue;

在lua5.1版本中,统一使用lua_Number来表示整数和浮点数,而double能够表示的整数大小有限,大概2^52的长度,所以用lua_Number表示一些类型为int64_t的全局唯一id长度不够,类似物品id、角色id。在lua5.3中,上述问题不再存在。

lua简单数据类型bool、整型、浮点型都是统一用lua_TValue来表示,消耗内存为sizeof(lua_TValue) = 16字节。因此相对CC++表示整型的charshortintint64_t来说,这种数据结构的表示法虽然比较统一,但比较消耗内存。还有一些复杂的数据结构,统一封装在GCObject中。由于5.1和5.3表示法略有差异,5.1方便理解,如下:

union GCObject {
  GCheader gch;
  union TString ts;
  union Udata u;
  union Closure cl;
  struct Table h;
  struct Proto p;
  struct UpVal uv;
  struct lua_State th;  /* thread */
};

数据结构的内存占用

在开发过程中,最常用的数据结构是sting和table类型,那么现在主要分析这两种数据结构的内存占用。

2.1 string 类型

String又细分为短字符串LUA_TSHRSTR和长字符串LUA_TLNGSTR两种,默认长度小于40的为LUA_TSHRSTR,使用全局stringtable进行管理。即所有短字符串都在stringtable中存放,相同字符串只会有一份实际数据拷贝,每份相同的TString对象只是存放一个hash值,用来索引stringtable。而长字符串则跟普通的GCObject没有差别,相同字符串在内存都是单独一份数据拷贝。在Lua5.1中,没有区分长短字符串,所有的字符串统一在stringtable中存在唯一拷贝。猜想这种改变一是因为长字符串出现相同的情况比较少,二是lua5.1的方式长字符串TString计算Hash是抽取部分字符进行运算,这样的计算方式可能被伪造导致不同字符串的hash值一样,但要是所有字符全用来计算hash又比较耗时。

下面是一个string类型的GCObject的内存表示,根据长短字符串表示方式不一样:

0c750170bf3a4d3ad09d5329ab0a92fe.png

Ø 内存占用分析实践

下面分别对比四种情景来分析内存占用的不同。因为lua在使用 .. 连接字符串时,底层会调用luaV_concat -> luaS_newStr新建字符串,这里对比下在创建短字符串和长字符串后的内存消耗:

b831f63f9999da879ac55c4866c40ab5.png

通过对比:在创建相同字符串时,因为短字符串在StringTable只有一份拷贝,而长字符串每次都会产生新的数据拷贝,所以消耗内存差异明显。 0.58K VS 771.48K

df6d84697885148404de5874f02bf3a9.png

通过对比:在创建不同字符串时,不论长短字符串,都会有一份不同字符串的拷贝,所以都需要消耗大量内存。1052K VS 1145K

问题一:在对比情景2和情景4时,长字符串都是一份拷贝

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值