Lua高级应用

2023-05-16

一、lua数据结构及内存占用分析

1.基础数据结构

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 */
	long long i;     /* lua_Integer  integer numbers */
	double n;       /* lua_Number  float numbers */
} Value;

struct lua_TValue {
	Value value_; 
	int tt_;
} TValue;

lua的table占用内存明显高于C/C++,主要有以下几个原因:

1、lua table支持动态插入,所以为了性能必须要分配更大内存,减少因为每次的插入而导致的重新分配。这样极端情况就会多消耗掉一倍的内存。

2、lua table的节点使用的是Node,为了追求通用性,对应kv字段基本都是TValue类型,而这个类型占用16字节,kv消耗加起来就是32字节,明显高于C/C++里面简单字段类型的消耗。当然lua table引入array可以不需要key字段,内存接近省一半。

3、lua table本身的管理数据有56字节,如果是一个很大的表,这个占用比率并不明显。但如果是多个小表,占用比例就会很大。

4、在实际开发过程中,一般都会表嵌套表,很多层,由于每层都有内存冗余和浪费,这样嵌套下来消耗就会叠加的更明显。

collectgarbage("stop");--先停止GC
local gc1 = collectgarbage("count");
local nullTable = {};
local gc2 = collectgarbage("count");
print( gc2 - gc1 );--一个空表是56B
local gc3_1 = collectgarbage("count");
nullTable["a"] = 0;
local gc3_2 = collectgarbage("count");
print( gc3_2 - gc3_1 );--一个kv是32B
local gc4_1 = collectgarbage("count");
nullTable[1] = 0;
local gc4_2 = collectgarbage("count");
print( gc4_2 - gc4_1 );--一个v是16B
collectgarbage("restart");--重新唤起GC

2.lua内存占用分析

Lua中table类型,每条记录对外都是key-value的方式读写,底层是用array+hashtable的方式管理数据的,但对外是透明的。

不论array还是hashtable都是连续的内存分布。在查找时:

1. 如果key是整型, 并且 key > 1 and key < max_array_size, 直接取array[key]数据

2. 其他情况,默认读取hashtable。Hashtable的管理方有些特别,当不同key hash到同一个node时,用链表来维护这些冲突节点。与stringtable 链表节点动态分配的方法不同, HashTable使用空闲链表来维护冲突节点。

首先说一种典型的情况,调用table.insert 或者table[#table + 1] 按序插入列表的,就是存放在array里面。

使用预填充方式创建table会省CPU消耗,否则每次动态扩容都会新建数据表,把原来的数据重新hash到新分配的内存中,并且每次扩容都是上一次的2倍。

rehash数据重新分布

上面只是描述节点不够用时触发内存扩容,数据重新进行hash分布。但如果既有Array数据,又有HashTable数据时,会怎么进行rehash呢?

  系统会把所有的key为整数的节点进行统计(包含在array和HashTable的),同时数组最大内存Max_Aarry_Size按2的指数倍增长,然后计算满足条件Key <=Max_Aarry_Size的整数节点数量,当数量 > Max_Aarry_Size/2 就认为array内存能充分利用,使用率超过50%,直到系统找到一个最大的符合条件的Max_Aarry_Size为止。剩下的节点数量进入HashTable, HashTable也是按2的指数倍增长,直到能够装下剩余节点数为止。

因此,就可知道下方的表为什么使用的HashTable存储的原因了。

for i=1, 10000 do

    tab[10000+i] = 10000+i

end

当Max_Aarry_Size = 2^13=8196时,没有Key落在这个[1, 8196]区间。

当Max_Aarry_Size= 2^14=16392时,只有6392条数据落入区间[1, 16392], 区间利用率小于50%。

当Max_Aarry_Size= 2^15=32784时,只有10000落入区间[1, 32784], 也不满足利用率的要求。

二、Lua高级应用

1.Lua协程

---唤起协程
function receive (prod)
    local status, value = coroutine.resume(prod)
    return value
end
---挂起等待
function send (x)
    coroutine.yield(x)
end
---生产者:生产一个产品
function producer ()
    return coroutine.create(function ()
        while true do
            local x = math.random( 1, 1000 ); -- produce new value
            send(x)
        end
    end)
end
---解释器:对产品进行包装操作
function filter (prod)
    return coroutine.create(function ()
        local line = 1
        while true do
            local x = receive(prod) -- get new value
            x = string.format("Line: %5d Value: %s", line, x)
            send(x)  -- send it to consumer
            line = line + 1
        end
    end)
end
---消费者:消费者有需求,则通知解释器,解释器先通知生产者生产,再进行包装,交给消费者
function consumer (prod)
    return receive(prod) -- get new value
end


local product = filter( producer());
function TestPanel.secondUpdate()
   print_green("-----",consumer( product ));
end

2.非抢占式多线程:LuaSocket库

require "luasocket"; ---加载LuaSocket库
 ---接收数据
function receive (connection)
    connection:timeout(0) -- timeout(0)使得对连接的任何操作都不会阻塞
    local s, status = connection:receive(2^10)
    --当操作返回的状态为 timeout 时意味着操作未完成就返回了
    if status == "timeout" then --timeout的时候挂起,挂起后s和status还是在被不断推送的。
        coroutine.yield(connection)
    end
    return s, status
end
---下载数据
function download (host, file)
    local c = assert(socket.connect(host, 80)) --80主机端口
    local count = 0  -- counts number of bytes read
    c:send("GET " .. file .. " HTTP/1.0\r\n\r\n")
    while true do
        local s, status = receive
        count = count + string.len(s)
        if status == "closed" then break end
    end --上述方法只是计算接收到的文件大小(字节数)
    c:close() --关闭连接
    print(file, count)
end
---获取文件
threads = {}  -- list of all live threads
function get (host, file)  -- create coroutine
    local co = coroutine.create(function ()
        download(host, file)
    end)
    -- insert it in the list
    table.insert(threads, co)
end
---分配器( 对所有线程循环,移除掉已经完成任务的线程)
function dispatcher ()
    while true do
        local n = #threads
        if n == 0 then break end  -- no more threads to run
        local connections = {}
        for i=1,n do
            local status, res = coroutine.resume(threads[i])
            if not res then -- thread finished its task?
                table.remove(threads, i)
                break
            else -- timeout
                table.insert(connections, res)
            end
        end
        if #connections == n then  --当所有的连接都 timeout 分配器调用 select 等待任一连接状态的改变,此方法不会发生忙等待,否则可能会引起程序阻塞。
            socket.select(connections)
        end
    end
 end
---实例调用
function GetSocketData()
    local host = "www.w3c.org";
    get(host, "/TR/html401/html40.txt");
    get(host, "/TR/2002/REC-xhtml1-20020801/xhtml1.pdf");
    get(host, "/TR/REC-html32.html");
    get(host, "/TR/2000/REC-DOM-Level-2-Core-20001113/DOM2-Core.txt");
    dispatcher() --main loop;
end

3.设置只读表

function readOnly( t )
    local proxy = {};
    local mt = {
       __index = t,
       __newindex = function( t, k , v )
          print_e( "attempt to update a read-only table"  );
       end
    }
    setmetatable( proxy , mt );
    return proxy;
end

4.使用动态名字访问全局变量

--获取值
function getfield(f )
    local v = _G; --  lua会把所有全局表存到_G里
    for w in string.gmatch(f, "[%w_]+") do
        v = v[w];--层层查找
    end
    return v;--返回最后一层找到的值
end
--设置值
function setfield(f, v )
    local t = _G
    for w,d in string.gmatch(f, "([%w_]+)(.?)") do
        if d == "." then
            t[w] = t[w] or {}
            t = t[w]
        else
            t[w] = v
        end
    end
end

setfield("TestManager.test.value", 10 );
print( tostring( getfield( "TestManager.test.value" ) ) );--10
local a = TestManager.test.value; --10

5.使用二进制节省内存

一个空的table ({})就会占用56字节,另外每个键占用16字节,每个值占用16字节,加起来32字节,如果是连续的键(1,2,3...) , 那样就只有值占用16字节。 所以应该尽量避免频繁的创建table,尤其是在update里。另外假如想弄一个字段,但是一个键对应两个值,一般写法是: table = { [key] = { value1 = 1 , value2 = 2 } }; 可以看出每个key都要创建个table但是里面就存了2个值,性价比太低,可以优化成 table = { [key] = ( 1 << 16 ) + 2 };(因为位运算优先级较低,所以需要加个括号)这样每个key就对应一个数字,内存比table小得多。
值的取法:取 value里的1 : value >> 16 (因为1左移了16位,取的时候只要右移16位就行了) 。 取value里的2 : value & 0xFFFF。 ( 0xFFFF 是16进制写法,0x是固定格式,FFFF对应二进制的 1111 1111 1111 1111,当value和 0xFFFF进行按位与运算后,得出的就是后16位的值,即为2,(因为1左移了16位,与不到) )
(当然16位不够也可以32 , 但是不要超过32,lua的number使用double类型,故有53个有效位,外加1个隐含位和1个符号位 , 左移的时候注意位数 )

简单说一下二进制与16进制转换:以二进制的每4位为一个单位,1111 , 分别对应 8 4 2 1 ,即2^3  2^2  2^1  2^0   。转化为十进制就是 1+2+4+8 = 15 , 即十六进制的F。 1111 1111 1111 1111就是十六进制的FFFF。同理 二进制1100 => 8 + 4 + 0 + 0 = 十进制12 =》 十六进制C   0101 => 0+4+0+1 =》 十进制5 =》十六进制5 。 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Lua高级应用 的相关文章

  • 了解静态链接嵌入式lua环境中lua扩展dll的构建/加载

    我有一个相对复杂的 lua 环境 我试图了解以下内容如何工作 起始设置包括以下两个模块 主要应用 无lua环境 DLL 静态链接到lua lib 包括解释器 该 dll 被加载到主应用程序中 并运行 lua 控制台解释器和可从控制台访问的
  • 我应该用 C++ 封装 Lua 代码的哪些代码?

    我终于将 LuaBind 配置为与我的 C 项目一起使用 我最近发现 Tiled 地图编辑器可以选择将文件导出为 lua 所以我想尝试一下 我将使用什么代码来封装下面粘贴的代码以使其与我的 C RPG 项目一起使用 return versi
  • Lua 中的内联条件(a == b ? "yes" : "no")?

    无论如何 Lua 中可以使用内联条件吗 Such as print blah a true blah nahblah Sure print blah a and blah or nahblah
  • 在 Corona sdk 上保存高分?

    我想保存游戏中创建的高分 并且当玩家点击高分按钮时可以在主菜单中看到 有人可以帮助我吗 您可以使用SQLITE https docs coronalabs com api library sqlite3 index html将高分保存到数据
  • 如何解密Lua字节码?

    早上好 我正在尝试破译 Moon 字节码 但我无法以任何方式 有人可以帮助我吗 我有这个 例如 code 27 76 117 97 81 0 1 4 4 4 8 0 如何将此字节码解密为文本 我已经在这里搜索 http www asciit
  • Redis 排序集和解决关系

    我正在使用 Redis 排序集来存储我正在处理的项目的排名 我们没有预料到 我们想要如何处理关系 Redis 按字典顺序对具有相同分数的条目进行排序 但我们想要做的是对具有相同分数的所有条目给予相同的排名 例如在以下情况 redis 127
  • lua http套接字超时

    LuaSocket HTTP 模块文档说可以在 HTTP 连接上设置超时 可以设置以下常量来控制 HTTP 模块的默认行为 PORT 用于连接的默认端口 PROXY 用于连接的默认代理 TIMEOUT 设置所有I O操作的超时时间 USER
  • 如何在aerospike中获取ttl为-1的记录集?

    我在aerospike中有很多记录 我想获取ttl为 1的记录 请提供解决方案 只是为了澄清 设置TTL 为 1 https github com aerospike aerospike client go blob master docs
  • Kong - 验证上游 ssl(ssl_proxy 打开)

    我已经成功为 API 安装了 kong 网关 该 API 通过上游负载平衡到多个目标 应用程序服务器 现在 我有一个我的应用程序服务器的自签名证书 kong 和目标之间的 ssl 握手应该失败 我推断 kong 不验证上游证书 经过一些研究
  • lua中的权限问题

    是否需要在 corona build settings 中设置一些特定权限才能将高分永久保存在文件中 每次运行代码时都会出现 权限被拒绝 的错误 如何纠正这个错误 这是我尝试过的代码 function read score local f1
  • Lua 访问表的键和值

    我想在关卡编辑器中读取 Lua 文件 这样我就可以以可视化格式显示其数据供用户编辑 如果我有一个像这样的 Lua 表 properties Speed 10 TurnSpeed 5 Speed显然是关键并且10价值 我知道如果我知道像这样的
  • 如何通过 C API 在自己的环境中执行不受信任的 Lua 文件

    我想通过调用在其自己的环境中执行不受信任的 lua 文件lua setfenv http pgl yoyo org luai i lua setfenv这样它就不会影响我的任何代码 该函数的文档仅解释了如何调用函数 而不解释如何执行文件 目
  • 在Luasocket中,在什么条件下,即使在select告诉它可以安全读取之后,accept调用也可以阻塞?

    卢阿索基特select http w3 impa br diego software luasocket socket html select函数应该告诉何时可以在不阻塞的情况下读取套接字 它显然也可以用来告诉服务器套接字何时准备好接受新连
  • LuaJ 导入 Lua 方法

    我正在使用 LuaJ 并且我有一个 lua文件充满了一堆函数 如何通过 LuaJ 导入这些函数以在 Java 中使用 一种选择是将文件编译为 Java 代码并导入它 另一种方法是使用可嵌入解释器直接从 Java 代码中调用 Lua 文件 E
  • 如何使用 Lua 运行可执行文件?

    我有一个可执行文件想要使用 Lua 运行 我该怎么做 似乎无法在任何地方找到有关此的任何文档 您可以使用 Lua 原生的 执行 命令 Example os execute c temp program exe 资料来源 Lua 指南 os
  • Lua 的标准(或最好支持的)大数(任意精度)库是什么?

    我正在处理大量无法四舍五入的数字 使用 Lua 的标准数学库 似乎没有方便的方法来保持精度超过某些内部限制 我还看到有几个库可以加载以处理大数字 http oss digirati com br luabignum http oss dig
  • Tarantool 中的冲突解决(如何在发生冲突时修复主-主模式下的复制)

    在多master场景下使用Tarantool如何实现冲突解决 我正在开发一项应该具有高可用性的服务 因此决定使用 nginx 作为 tarantool 两个节点 禁用只读选项 的负载均衡器 带有备份指令 它会重试对其他节点的失败请求 但如果
  • 如何从表成员中引用lua表成员?

    我在 lua 有一张表 enUS LOCALE STHOUSANDS Thousands separator e g comma patNumber d LOCALE STHOUSANDS d regex to find a number
  • 在循环内部或外部声明本地更好吗? [复制]

    这个问题在这里已经有答案了 我习惯这样做 do local a for i 1 1000000 do a
  • 如何将 Lua 粘合到 C++ 代码?

    您使用 Luabind toLua 或其他库 如果使用 是哪一个 还是根本不使用 每种方法都有哪些优点和缺点 我不太同意 自己投票 将基本类型和静态 C 函数绑定到 Lua 是微不足道的 是的 但是当你开始处理表和元表时 情况就会发生变化

随机推荐

  • Disabled PicPipeline: ImagesPipeline requires installing Pillow 4.0.0 or later

    目录 一 scrapy是什么 二 问题以及原因 三 解决办法 1 确保系统已经安装了 Pillow 库 2 安装 Pillow 库 3 在项目根目录中添加 Pillow 的 pth 文件 一 scrapy是什么 Scrapy是一个用于从网站
  • ERROR 1129 (HY000): Host ‘192.168.0.1‘ is blocked because of many connection errors; unblock with ‘m

    MySql远程链接报错 问题 xff1a mysql u root h 192 168 0 1 p Enter password ERROR 1129 HY000 Host 39 192 168 0 1 39 is blocked beca
  • JVM虚拟机详解

    一 JVM简介 JVM是Java Virtual Machine Java虚拟机 的缩写 简单来说JVM是用来解析和运行Java程序的 虚拟机是一种抽象化的计算机 通过在实际的计算机上仿真模拟各种计算机功能来实现的 Java虚拟机有自己完善
  • 云计算OpenStack详解

    一 OpenStack简介 1 OpenStack发展历程 2002年 美国著名的电商公司亚马逊 Amazon 干了一件 不务正业 的事 他们向客户推出了一项全新的业务 包括存储空间 计算能力等资源服务的Web nbsp Service 这
  • Linux虚拟化网络之vlan配置

    问题描述 Linux主机划分两个vlan 服务器server1的物理网卡的IP地址为1 1 1 1 24 服务器server2的物理网卡的IP地址为1 1 1 2 24 物理网卡下要虚拟化出来两个Vlan子接口 vlan10中主机的IP地址
  • 部署SDN控制器对接OVS网元实现转控分离实战 附ODL控制器

    1 云计算对网络的需求 1 多租户网络隔离 云中包含多个租户 不同租户可以自己设计自己的内部网络 172 16 0 0 10 0 0 192 168 0 存在安全隐患 要让不同租户网络要分隔开 nbsp 不同租户的网络需要互相二层隔离 三层
  • Python 数据采集、清洗、整理、分析以及可视化实战

    一 数据分析思路 大概可以分为下面这几个步骤 数据采集 原始数据完整性检查 数据清洗 整理 从不同角度对数据进行分析 数据可视化 总结 主要使用 Python 来进行分析 数据采集 主要涉及的 python 库包括 requests Bea
  • ERROR: torch-1.6.0+cu101-cp37-cp37m-win_amd64.whl is not a supported wheel on this platform.

    目的 xff1a 使用混合精度训练模型 方法 xff1a 最新版pytorch1 6已封装进混合量化的模块 xff0c 只需几句代码就可以提高batch size 速度会有非常大的提升 安装pytorch xff1a pip install
  • Clash开启系统代理System proxy后无效,无流量

    在注册表目录 xff1a HKEY LOCAL MACHINE SOFTWARE Policies Microsoft Windows CurrentVersion Internet Settings 设置ProxySettingsPerU
  • Proximal Policy Optimization(PPO)算法原理及实现!

    Proximal Policy Optimization PPO 算法原理及实现 xff01 这两天看了一下李宏毅老师的强化学习课程的前两讲 xff0c 主要介绍了Policy Gradient算法和Proximal Policy Opti
  • 树莓派4b ubuntu 20 设置阿里源

    前言 设置国内源其实很简单 xff0c 但是由于我是下载的 64位 操作系统 xff0c 并且树莓派是arm架构 xff0c 所以有一点不同 执行 lsb release a 查看发行版本 ubuntu 64 ubuntu etc netp
  • CAS登录流程

    访问 http localhost analysis 请求会被ngixn如下配置拦截 location analysis root D work dist index index html index htm try files uri u
  • 解决centos7 sshd端口修改后,重启服务器sshd无法连接的问题

    针对centos7的变化 xff0c 修改sshd端口需要修改 etc ssh sshd config selinux 防火墙 1 修改 etc ssh sshd config 执行命令 xff1a vi etc ssh sshd conf
  • osmWebWizard.py: error: typemap file “E:\SUMO\tools\data\typemap\osmPolyconvert.typ.xml“ not found

    在使用sumo的时候 xff0c 根据官方文档 xff08 OSMWebWizard SUMO Documentation dlr de xff09 显示 xff0c 在tools目录下使用python osmWebWizard py 来生
  • 内核与驱动_08_键盘驱动原理及代码

    文章目录 技术原理Windows中从击键到内核流程 键盘硬件原理键盘过滤的框架搭建应用设备扩展键盘过滤模块的动态卸载键盘过滤的请求处理通常的处理 PNP的处理读的处理读完成的处理从请求中打印出按键信息从缓冲区中获得KEYBOARD INPU
  • LCD 12864B V2.0的使用

    内置ST7920控制器和中文字库的LCD12864的使用 前言 大家好 xff0c 我是小灬贱 今天我给大家带来LCD12864的使用方法以及我的一些经验 文章里面如有不妥之处或者表达不清晰的地方还请各位多多指教 可以在文下评论或者私信我
  • Unity:内存管理、GC优化

    目录 一 GC简介 1 堆内存分配和回收机制 2 垃圾回收时的操作 3 何时会触发垃圾回收 xff1f 4 GC操作带来的问题 二 GC优化 1 降低GC影响的方法 2 减少内存垃圾的数量 3 造成不必要的堆内存分配的因素 1 字符串 2
  • C# 常用的文件IO操作

    目录 一 IO流 1 文件夹操作 Directory类 2 文件操作 File类 3 路径操作 Path类 4 读取文件 StreamReader类 5 写入文件 StreamWriter类 二 动态链接库kernel32 1 写入文件 2
  • OutLine源码解析 -- 为什么要尽量避免使用OutLine

    相信很多人在刚入职Unity的时候都被告诫过尽量避免使用OutLine xff0c 只知道它很费性能 xff0c 但是很多人并不知道它为什么很费性能 今天通过源码来探索一下 首先看一下OutLine cs里的源码 public overri
  • Lua高级应用

    一 lua数据结构及内存占用分析 1 基础数据结构 lua的基本数据表示是type 43 union 的方式 xff0c 根据不同类型映射到union的不同结构上面 xff0c 统一的表示结构lua TValue xff1a typedef