库文件地址
lua官方地址
luajit
执行原理
这里先卖个关子,有一次面试,面试官问我, objc_msgsend 可以使用C语言写吗? 当时没回答好,理论上是可以的。
我们都知道lua是解释执行语言,C是编译执行语言,最大的区别,就是C可以充分利用编译器,制作成汇编指令,效率最高。
lua解释性语言只能解释成字节码,模拟汇编,进行操作。
lua是C写的,所以 objc_msgsend 理论上也可以C实现,动态的执行方法(不同参数,不同返回值),但是效率就降低为解释执行语言的效率了。
待补充。。。。。。。。。
lua指令集
参看lopcodes.h
文件,里面定义了很多指令,
还有一个重要函数,我就不贴代码了,一个方法400多行,在lvm.C
文件中的luaV_execute
,可以看下
1 | /* main loop of interpreter */ |
一些零散的源码阅读
Lua虚拟机并不能执行我们的ifStateMent这种东西。Lua源码里的实现也是类似这种TokenType 和 结构化的 if Statement whileStatement等等,并且Lua没有生成完整的语法树。Lua源码的实现里面,它是解析一些语句,生成临时的语法树,然后翻译成指令集的。并不会等所有的语句都解析完了再翻译的。语义解析和翻译成指令集是并行的一个过程。贴一个源码里面关于语义解析的部分实现:
1 | static void body (LexState *ls, expdesc *e, int needself, int line) { |
LuaJit
iOS的luajit
- LuaJIT分为JIT模式和Interpreter模式
- JIT模式: 高效的机器码
级别
执行, 然而不幸的是这个模式在iOS下是无法开启的
,因为iOS为了安全,从系统设计上禁止了用户进程自行申请有执行权限的内存空间
,因此你没有办法在运行时编译出一段代码到内存然后执行,所以JIT模式在iOS以及其他有权限管制的平台(例如PS4,XBox)都不能使用。 - Interpreter模式:编译成中间态的字节码(bytecode), 相比之下当然比JIT慢。但好处是这个模式不需要
运行时生成可执行机器码
(字节码是不需要申请可执行内存空间的
)
- JIT模式一定更快?不一定!
参考下图,这里可以看到,第一,Interpreter模式是必须的,无论平台是否允许JIT,都必须先使用Interpreter执行;第二,并非所有代码都会JIT执行,仅仅是部分代码会这样,并且是运行过程中决定的。
优化
- Reduce number of unbiased/unpredictable branches.减少不可预测的分支代码
- Use FFI data structures. 如果可以,将你的数据结构用ffi实现,而不是用lua table实现
- Call C functions only via the FFI.尽可能用ffi来调用c函数。
- Use plain ‘for i=start,stop,step do … end’ loops.实现循环时,最好使用简单的for i = start, stop, step do这样的写法,或者使用ipairs,而尽量避免使用for k,v in pairs(x) do
- Find the right balance for unrolling.循环展开,有利有弊,需要自己去平衡
- Define and call only ‘local’ (!) functions within a module.
- Cache often-used functions from other modules in upvalues.
- Avoid inventing your own dispatch mechanisms.避免使用你自己实现的分发调用机制,而尽量使用內建的例如metatable这样的机制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17-- 编程的时候为了结构优雅,常常会引入像消息分发这样的机制,然后在消息来的时候根据我们给消息定义的枚举来调用对应的实现,过去我们也习惯写成:
if opcode == OP_1 then
elesif opcode == OP_2 then
...
-- 但在luajit下,更建议将上面实现成table或者metatable
local callbacks = {}
callbacks[OP_1] = function() ... end
callbacks[OP_2] = function() ... end
-- 这是因为表查找和metatable查找都是可以参与jit优化的,而自行实现的消息分发机制,往往会用到分支代码或者其他更复杂的代码结构,性能上反而不如纯粹的表查找+jit优化来得快 - Do not try to second-guess the JIT compiler.无需过多去帮jit编译器做手工优化。
- Be careful with aliasing, esp. when using multiple arrays.变量的别名可能会阻止jit优化掉子表达式,尤其是在使用多个数组的时候。
- Reduce the number of live temporary variables.减少存活着的临时变量的数量
- Do not intersperse expensive or uncompiled operations.减少使用高消耗或者不支持jit的操作
Lua的API
参考api文档
#define lua_open() luaL_newstate() 开启一个lua状态机
1 | void lua_settable (lua_State *L, int index); |
lua_pop(L,num)函数从栈顶开始移除。
当num>0时从栈顶移除指定个数 。
当num=0时栈不受影响
当num=-1时栈中元素全部移除lua_settop函数说明
该函数用于指定栈的高度,栈只能从栈顶压栈,不能从栈底添加数据。所以栈底的数据会保持不变。
当新的高度大于原来的高度时,会从栈顶压入数据,压入的数据不可用(因为是随机的)。
当新的高度小于原来的高度时,会从栈顶移除多余的元素。
当输入参数为负数时,表示从栈顶开始的索引(最栈顶元素为-1)。该函数会移除栈顶到该元素之间的所以元素。-1则无,-2 则移除-1 。-3则移除-1,-2。以此类推。但是负数编号不能超出栈底的负数索引,超出会抛出异常。lua_pop函数及是使用了该特性。int luaL_getmetatable (lua_State L,constchartname);
将注册表中 tname 对应的元表(参见 luaL_newmetatable)压栈。如果没有 tname 对应的元表,则将 nil 压栈并返回假。
参考
https://blog.uwa4d.com/archives/usparkle_luajit.html
http://wiki.luajit.org/Numerical-Computing-Performance-Guide
深入浅出