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中的运算符是什么意思 例如 在以下代码中 if x params then the is not equals 这在其他语言中是等价的
  • 访问 Lua 类型元表

    显然 getmetatable 可以访问几种类型的元表 getmetatable getmetatable getmetatable newproxy true 然而 似乎您无法获取其他类型的元表 除了函数 似乎无法访问数字 布尔值或 ni
  • Lua:“拖动”数组中的元素序列

    我正在尝试创建一个函数 将连续数量的元素 拖动 到数组中的新位置 并限制为数组的当前大小 其他项目应该围绕 拖动 的项目晃动 例如 如果我的数组有 7 个元素 并且我想拖动中间的三个 1 2 3 4 5 6 7 lt keys a b C
  • Lua - 尝试调用全局(零值)

    执行此代码时 出现错误 尝试调用全局 forId 零值 function execute args local itemid 526 local bone forId itemid this is where the error occur
  • 如何访问废弃的函数参数?

    在 Lua 中 调用带有多余参数的函数将简单地丢弃这些参数 有没有可能与debug库来访问这些被丢弃的参数 我不是在寻找可变参数函数 function test local info debug getinfo 1 u print info
  • 什么更快?循环或多个 if 条件

    我想知道什么更快 是只用一条指令 即 1 1 执行 9 次 for 循环还是执行 9 个 if 条件时 我认为 if 更快 因为您不需要检查循环中的指令 它应该几乎相同 因为for循环本质上是检查if条件为真并运行一段代码 非常类似于if声
  • 我应该用 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 验证电子邮件地址

    在我的项目中 有一个供用户填写详细信息的表单 其中有一个文本字段用于输入用户的电子邮件 ID 所以我需要在 corona 项目中验证该文本字段中的电子邮件 试试这个正则表达式 local email email protected cdn
  • C 的哪些部分最可移植?

    我最近读到了 Lua 联合创始人 Luiz H de Figueredo 和 Roberto Ierusalimschy 的采访 他们讨论了 Lua 的设计和实现 至少可以说这是非常有趣的 然而 讨论的一部分让我想起了一些事情 Robert
  • Redis 排序集和解决关系

    我正在使用 Redis 排序集来存储我正在处理的项目的排名 我们没有预料到 我们想要如何处理关系 Redis 按字典顺序对具有相同分数的条目进行排序 但我们想要做的是对具有相同分数的所有条目给予相同的排名 例如在以下情况 redis 127
  • 用于嵌入式服务器的 Web 技术

    我最近开始了一个针对嵌入式设备的新 Web 开发项目 并希望征求一些有关使用技术的建议 该设备将提供 HTML 页面 其中包括用于从 JSON 服务器检索数据的 AJAX 代码 我们暂时使用 Cherokee 作为 Web 服务器 但我们并
  • Lua中如何获取表中的最大整数?

    Lua中如何获取表中的最大整数 在Lua 5 1及更早版本中 你可以使用 math max unpack 1 2 3 4 5 这受到Lua堆栈大小的限制 在 PUC Lua 5 1 上 该值的最大值可达 ca 8000 个数字 如果堆栈空闲
  • 为什么 LuaJIT 这么好?

    编辑 不幸的是 LuaJIT 已从下面链接的比较中删除 This 比较 http shootout alioth debian org u64 which programming languages are fastest php编程语言的
  • Lua 从 5.1 更新 - LUA_GLOBALSINDEX 问题

    我最近将旧的 Lua 5 1 项目更新到了该库的最新版本 但遇到了问题LUA GLOBALSINDEX 它变得不确定 我只用过它lua getfield函数 像这样 void luastartgame void if startgamefu
  • 去掉尾随零和小数点

    使用 Lua 我将数字格式化为可变位数并去掉尾随零 小数点 例如 string format precision f value gsub 0 1 gsub 值的类型为数字 正数 负数 整数 小数 所以任务已经解决了 但出于美学 教育和性能
  • Lua 将字符串转换为数字 - 取决于语言环境

    刚刚注意到字符串 and tonumber 在 Lua 中是依赖于语言环境的 知道如何在不使用的情况下将字符串转换为数字tonumber 谢谢 例如将字符串 58 5 转换为 58 5 另外 当我尝试将带点的数字传递给函数时 该函数会转换
  • 如何在 emacs lua-mode 中配置缩进?

    完整的 emacs 新手在这里 我在 Ubuntu 上使用 emacs 23 1 1emacs 入门套件 https github com technomancy emacs starter kit 我主要在 lua 模式下工作 安装了pa
  • 在 Awesome-wm 中为特定应用程序设置窗口布局

    如何配置很棒 以便它可以启动两个窗口对齐的新应用程序 如下所示 xxxxxxxxxx xxxxxxxxxx xxxxxxxxxx xxxxxxxxxx 其中 x 是 pidgin 中的对话窗口 是好友列表窗口 一般来说 我想指定右窗口的宽度
  • 推送 Lua 表

    我已经创建了一个Lua表C 但我不知道如何将该表推入堆栈顶部 以便我可以将其传递给 Lua 函数 有谁知道如何做到这一点 这是我当前的代码 lua createtable state libraries size 0 int table i

随机推荐

  • 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