博文

LuaJIT

图片
源码剖析 编译 词法分析+语法分析+字节码生成 Lua这三个阶段不是分开进行的,一个statement先进行词法分析( lex_scan )得到当前LexToken,然后根据当前LexToken和前置LexToken进行语法分析和字节码生成,这两步是同时进行的。所以语法分析一部分(例如函数的参数)代码的同时会将该部分的机器码emit到寄存器,不是语法分析一整个statment结束之后再统一emit字节码。 注释及长注释在词法分析( lex_scan )获得LexToken时跳过的。 需求:屏蔽print函数调用 屏蔽程序代码中的print函数调用,想到了四个方案: 在静态代码检查时做一个print函数的检测,如果发现有print函数调用,则自动开单给责任人处理。 未采用原因:感觉这个方案治标不治本,而且开单给别人处理比较麻烦。 在词法分析时,如果检测到TK_name的值是"print",做一个词法分析的特殊逻辑,将整个函数调用的LexToken跳过。 未采用原因:该方案要对所有的TK_name进行 memcmp ,不太合理。而且分析函数调用的LexToken范围,属于语法分析的逻辑, 不应该在词法分析阶段做。 在语法分析+字节码生成时,在 parse_call_assign 中如果检测到函数调用的函数名是"print",将 expr_primary 替换成新写的语法分析逻辑,只保留语法分析部分,不生成函数调用的字节码。 未采用原因:由于LuaJIT的 parse_call_assign 源码实现,该方案需要修改主表达式解析 expr_primary 以及将函数调用是否为"print"的结果从 expr_primary 透传到 parse_call_assign 进行相关字节码处理,改变了luaJIT现有的函数定义、代码结构的一贯性。又考虑到保留 print() 的字节码性能影响很小,因此没有采用该方方案。 在语法分析+字节码生成时,如果检测到函数调用的函数名是"print",将 parse_args 替换成新写的语法分析逻辑,只保留语法分析部分,不生成参数的字节码(即最终 print(...) 生成的字节码实际上是函数 print() )。 ...

朝花夕拾

图片
架构相关 playcenter 主从模式 注意,从playcenter也会监听MessageHub的消息Emit,所以不应该在从playcenter上的Listen处理函数应该判断是否是主playcenter: if not g_ServiceMgr:IsMasterScheduler() then return end 模块相关 DBProxy的使用 Define 自定义Query MessgeHub Define game\common\lua\messagehub\MessageDefine.lua Use Important 伏羲服务以及伏羲长连接 Use CStore框架 时序 CompetitionFlow Joiner Node 与gameplay的数据交互 体素化 什么是体素化 类似2D格子/像素点,体素数据是3D划分后的每个小格子里的体素数据,可以有坐标、障碍、材质、标记等信息,用于上层应用(运动校验、进出区域等) 为什么要体素化 客户端的刚体碰撞体信息服务器没有。客户端需要将体素化数据同步给服务器 不做体素化是不是也可以,直接把刚体信息告诉服务器也可以 客户端对象(RenderObject、Lua对象、引擎对象);服务器对象(Lua对象、引擎对象) 运动碰撞校验;进出区域(公共场景、玩法场景如禁马区); 体素化数据结构(TCoreGrid) 如何获得体素化数据 制作美术(美术):场景搭建,模型prefeb,添加刚体 导出刚体(C#):三角网格数据的导出 场景体素化(静态)(C++)由刚体生成体素化数据:读取,PxPhysicsAPI重建场景,射线采样,解析, 静态体素数据应用中解决的问题:洞穴问题;海水问题;河流湖泊问题(复杂不规则的河流湖泊水域,模拟刚体,利用水碰撞和下方的碰撞的高度差决定当前坐标下水的深度);瀑布水帘洞问题(河流切片,给瀑布打上不一样的标记,不做抬高) 动态体素化数据:庄园、动态平台、化学物品 体素数据相关的优化: 内存:调色盘数据结构(RnRIndex) 存储:边界:小副本支持给体素化数据刷边界信息,无需加载玩家不可达的边界区域的体素数据 网络: 踩...

Programming in Lua First Edition

Part I · The Language 1 - Getting Started 1.1 Chunks 在lua命令中 -l 选项会调用 require 函数 e.g. prompt> lua -i -la -lb -i :Lua运行指定代码段后进入交互模式 另一个link外部代码段的方式是使用 dofile 函数 在调试/测试时使用 -i 和 dofile 非常方便 1.2 Global Variables 1.3 Some Lexical Conventions(词法约定) Lua大小写敏感 以下划线开头的大写字母标识符是Lua保留字的命名风格,因此要避免使用 1.4 The Stand-Alone Interpreter lua [options] [script [args]] -e: 直接将命令传入Lua(e.g. prompt> lua -e "print(math.sin(12))" ) -l: 使用require加载一个文件 -i: 进入交互模式 全局变量arg存放Lua的命令行参数,脚本名索引为0: prompt> lua -e "sin=math.sin" script a b args[-3] = "lua" args[-2] = "-e" args[-1] = "sin=math.sin" args[0] = "script" args[1] = "a" args[2] = "b" 2 - Types and Values Lua的8个基本类型:nil, boolean, number, string, userdata, function, thread, table type()以字符串形式输出参数的类型 2.1 Nil 2.2 Booleans 在控制结构的条件中,nil和false为假,其他值都为真 2.3 Numbers 表示实数。一般有个错误的看法 CPU 运算浮点数比整数慢。事实不是如此,用 实数代替整数不会有什么误差(除非数字大于 100,00...

Lua源码剖析

reference https://ty-chen.github.io/lua-linux/

Lua Gems

性能优化 关于性能优化的两条格言: 不要优化 还是不要优化(仅限专家) 不要在缺乏恰当度量( measurements )时试图去优化软件。编程老手和菜鸟之间的区别不是说老手更善于洞察程序的性能瓶颈,而是老手知道他们并不善于此。 做性能优化离不开度量。优化前度量,可知何处需要优化。优化后度量,可知「优化」是否确实改进了代码。 basic lua使用基于寄存器的虚拟机,用一个栈来存放它的寄存器。lua中每个active的函数都有一个activation record放在栈里,记录了这个函数使用的寄存器。因此,每个函数都有其自己的寄存器。由于每条指令只有 8 个 bit 用来指定寄存器,每个函数便可以使用多至 250 个寄存器。Lua 的寄存器如此之多,预编译时便能将所有的局部变量存到寄存器中。所以,在 Lua 中访问局部变量是很快的。 因此,可以是用局部变量提高性能。例如给多次引用的外部变量(e.g. tbl.data )赋予别名。但是这在 LuaJIT 上几乎已经没有了优势。 变量创建问题考虑比较跨域性能消耗和创建消耗:循环内创建简单变量比循环外创建多次覆写效率高;循环外创建长table比循环内创建效率高。 避免动态编译( loadfromstring() 等),可以使用闭包等来避免。 Short inline expressions can be faster than function calls. t[#t+1] = 0  is faster than  table.insert(t, 0) . table Lua 实现表的算法颇为巧妙。每个表包含两部分:数组(array)部分和哈希(hash)部分,数组部分保存的项(entry)以整数为键(key),从 1 到某个特定的 n,(稍后会讨论 n 是怎么计算的。)所有其他的项(包括整数键超出范围的)则保存在哈希部分。 顾名思义,哈希部分使用哈希算法来保存和查找键值。它使用的是开放寻址(open address)的表,意味着所有的项都直接存在哈希数组里。键值的主索引由哈希函数给出;如果发生冲突(两个键值哈希到相同的位置),这些键值就串成一个链表,链表的每个元素占用数组的一项。 当 Lua 想在表中插入一个新的键值而哈希数组已满时,Lua 会做...