Lua性能优化

原文:Lua Performance Tips

  偶然找到《Lua Performance
Tips》这篇有关Lua的优化文章,个人认为相较于多数浅而出言要好不丢掉。尽管Lua已经到5.2本了,但里面的技巧还是会就此到,通过翻译自己为能够重新深刻之错过探听文中提到技巧。第一浅翻译,错误以及不当之处自然非会见掉,欢迎指正,谢谢……以下为正文

  • 基础实例
  • 关于表
  • 至于字符串
  • 简化,复用,再生
  • 后记

 

比如说另任何编程语言一样,在Lua中,我们吧如遵守以下简单久优化程序的规则:

规则1:不要优化。

规则2:仍然不要优化(专家除外)

  当用Lua编程时,这片漫漫规则显得尤其关键。Lua以性著称,而且以脚本语言中吗坐这个要值得称道。

  然而,我们还懂得性能是编程的一个关键因素。具有复杂性指数日之问题让称之为疑难问题并无是偶然发生。太迟之结果是无济于事的结果。因此,每个精彩的程序员应该总是以花费资源去优化一截代码的代价和当下段代码在运作代码时节约资源的进项相抵消。一个美好之程序员关于优化的率先个问题总是会问:“程序需要优化吗?”如果答案是迟早的(仅当这),第二个问题应是:“哪地方?”

  为了对这有限个问题我们需要几手段。我们不应有于尚未适度的测时尝试优化软件。大牛和菜鸟之前的例外不是起更的程序员更好的指出程序的一个地方或耗时:不同之处是大牛知道他们连无擅长那起任务。

  最近几年,Noemi Rodriguez和自我之所以Lua开发了一个CORBA ORB(Object
Request Broker)原型,后来上扬成OiL(Orb in
Lua)。作为第一独原型,以行简明为目标。为了避免引用额外的C语言库,这个原型用有些划算操作分离每个字节(转化成为256之基数)。不支持浮点数。因为CORBA把字符串作为字符序列处理,我们的ORB第一破将Lua的字符串转化成字符序列(是Lua中的table),然后如任何队那样处理结果。

  当我们好第一个原型,我们以及用C++实现的标准的ORB的性相较。我们预料我们的ORB会稍微放缓点,因为它们是为此Lua实现之,但是,慢的极度让我们失望了。开始时,我们只是归咎为Lua。最后,我们猜测原因想必是每个数字序列化所需要的那些操作。因此,我们决定于分析器下下运作程序。我们所以了一个非常简单的分析器,像《Programming
in
Lua》第23回描述的那样。分析器的结果大吃一惊到我们。和咱们的直觉不同,数字序列化对性能的影响不坏,因为从没最好多的数字序列化。然而,字符串序列化占用总时之怪非常片段。实际上每个CORBA消息还产生几个字符串,即使我们无明确地操作字符串:对象引用,方法名字与外的一点整数值都于编码成字符串。并且每个字符串序列化需要昂贵之代价去操作,因为就需要创造新表,用每个独立的字符填充,然后序列化这些结果的各个,这涉及到一个连一个序列化每个字符。一旦我们再实现字符串序列化作为特种之风波(替换下相似的排代码),我们就是会获得高度之速度提升。仅仅用额外的几乎履代码,你的履效率就能比较得及C++的尽(当然,我们的行还缓慢,但切莫是一个多少级)。

  因此,当优化程序性能时,我们承诺总是去测量。测量前,知道优化哪里。测量后,知道所谓的“优化”是否真的增进了俺们的代码。

  一旦而说了算委要优化你的Lua代码,本文或许帮助您怎么样去优化,主要通过展示在Lua中如何会慢和怎样会赶紧。在这里自己非会见谈论优化的形似技术,比如更好的算法。当然,你当明了以会就此这些技能,但是,你能够起另外的地方上及那些一般的优化技术。在及时首稿子里本身就讲解Lua特有的技巧。整篇文章,我以见面不时的测量小序的年月以及空间。除非其他起证实,我有所的测是以Pentium
IV 2.9 GHz和主存1GB,运行在Ubuntu 7.10, Lua
5.1.1。我会频繁地叫来实际的测量结果(例如,7秒),但是会靠让不同测量方法。当自己说一个先后于其它一样底“快X%”的意是运行时刻不见“X%”。(程序快100%意味着运行不花时间。)当自己说一个次于其他一个“慢X%”的意是其它一个快X%。(程序慢50%之意思是运作费两倍时间。)

 

基础实例

运作任何代码前,Lua会拿源码转化(预编译)成中格式。这种格式是虚拟机指令的行,类似于真正CPU的机器码。这种中间格式然后叫得中生一个每个指令是平等栽状况特别之switch的while循环的C语言解释。

  可能当好几地方你就读了起5.0版本本Lua使用基于寄存器的虚拟机。这个虚拟机的“寄存器”和诚CPU的寄存器不符合,因为这种可是勿能够移植而十份限可用寄存器的数据。取而代之的凡,Lua使用堆(一个数组加上些索引来实现)容纳寄存器。每个移动函数有一个活动记录,那是个函数在里面存储其寄存器的堆片段。因此,每个函数有他好之寄存器(这仿佛于以windows某些CPU创建的寄存器)。每个函数可能使用过250单寄存器,因此每个指令就发生8个引用寄存器。

  提供了大量的寄存器,Lua预编译能够当寄存器储存剩余的一些变量。结果是以Lua中做客一些变量非常快。举个例子,如果a和b都是有些变量,像a
= a + b这种话生成单条指令:ADD 0 0
1(假设a及b中分别储存0和1)。作为比,如果a和b都是全局变量,增加的代码会如这样:

GETGLOBAL 0 0 ; a

GETGLOBAL 1 1 ; b

ADD 0 0 1

SETGLOBAL 0 0 ; a

 

为此,这生轻证明优化Lua程序的一个重要规则:使用部分变量!

  如果您要进一步提高你程序的性质,除了显而易见的那些,这里还有你会采用一些变量的地方。例如,如果您以长循环中调用函数,你得用部分变量引用这函数。举个例子,代码

for i = 1, 1000000 do

   local x = math.sin(i)

end

 

于下这个慢30%:

local sin = math.sin

for i = 1, 1000000 do

   local x = sin(i)

end

 

  访问外部的组成部分变量(也就是是,闭包函数中的变量)不见面及做客一些变量那样抢,但照样比较看全局变量快。考虑下的代码有:

function foo (x)

  for i = 1, 1000000 do

    x = x + math.sin(i)

  end

  return x

end

print(foo(10))

咱得以经过以foo函数外声明一个sin变量来优化: 

local sin = math.sin

function foo (x)

  for i = 1, 1000000 do

    x = x + sin(i)

  end

  return x

end

print(foo(10))

 

其次截代码运行比原先那个快30%。

  尽管跟另语言的编辑器比,Lua编译器的效率非常强,编译是桩繁重的天职。因此,你应该尽可能避免在程序中编译(例如,函数loadstring)。除非您得运行动态的代码,像经过终端输入的代码,你充分少要编译动态代码。

  作为例子,考虑下的代码,创建一个回去1至10000常常数值的函数的发明:

local lim = 10000

local a = {}

for i = 1, lim do

  a[i] = loadstring(string.format("return %d", i))

end

print(a[10]()) --> 10

 

及时段代码运行需要1.4秒。

  使用闭包,我们不管需动态编译。下面的代码用1/10的工夫(0.14秒)创建同的100000只函数。

function fk (k)

  return function () return k end

end

local lim = 100000

local a = {}

for i = 1, lim do

  a[i] = fk(i)

end

print(a[10]()) --> 10

 

 

关于表

普普通通,你莫需呢使说明而了解Lua是什么执行表的别样事。实际上,Lua竭尽全力确保兑现细节无露于用户。然而,这些细节经过表操作的属性显出。因此,要优化使用表的顺序(这几乎是任何Lua程序),还是掌握Lua是什么样执行表的会面比好。

  在Lua中表的执行涉及有聪明的算法。Lua中之表有两有些:数组和哈希。对少数特殊的n,数组存储于1届n的整数键的条目。(稍后我们以见面讲课这个n是安算的。)所来外的条文(包括限制外的整数键)转至哈希有的。

  顾名思义,哈希部分行使哈希计算存储和寻找他们的键。使用让喻为开发地址之阐发,意思是负有的条款被储存在它和谐之哈希数组中。哈希函数给出键的机要索引;如果存在冲突(即什么两只键被哈希及同一个职),这些键被连续到每个元素占用一个数组条目的列表中。

  当Lua在表中插入一个新键,并且哈希数组一度满之时段,Lua会重新哈希。重新哈希第一步是控制新数组部分以及初哈希部分的高低。因此,Lua遍历所有因素,并对准那计数,分类,然后选取数组最老尺寸的2底幂次方的长短,以便超过一半的数组元素被填。哈希大小是不过小尺寸的2底幂次方,能够容纳剩余的素(即那些在反复组有不吻合的)。

  当Lua创建空表时,数组和哈希这简单组成部分的分寸都为0,因此,也没吗她们分配数组。当运行下面代码让咱省啊会出:

local a = {}

for i = 1, 3 do

  a[i] = true

end

 

 

从创立空表开始。在首先次于巡回中,a[1] =

true赋值时接触重新哈希;Lua设置表的数组部分的分寸为1并且于哈希部分也空。在次软巡回中,a[2]

true赋值时更同糟沾重新哈希。因此现在表的数组部分的大小也2。最后,第三潮还接触重新哈希,数组部分的大大小小增长及4。

例如这样的代码

a = {}

a.x = 1; a.y = 2; a.z = 3

 

啊召开类似之 操作,除了说明的哈希部分提高外。

  对于特别要命的阐发,初始化的开销会分摊到全体经过的创:虽然有三单元素的表如要三坏重复哈希,但有一百万只因素的申止需要20差。但是当您创造上千个稍的表时,总的吃会大特别。

  旧本子的Lua创建空表时见面预分配几独岗位(4独,如果我尚未记错的说话),以避免这种初始化小表时的开支。然而,这种办法会浪费内存。举个例子,如果你创造一百万独坐标点(表现为单纯出少个因素的申)而每个使用实际用之有数加倍内存,你用会面提交高昂的代价。这吗是本Lua创建空表不见面预分配的故。

  如果您用C语言编程,你可以通过Lua的API中lua_createtable函数避免那些更哈希。他在无处不在的lua_State后承受两独参数:新表数组部分的初始大小以及哈希部分的始发大小。(虽然再度哈希的运算法则总会用反复组的尺寸设置也2的幂次方,数组的深浅可以是任意值。然而,哈希的轻重要是2底幂次方,因此,第二单参数总是取整为不比较原值小之比较小的2之幂次方)通过吃有新表合适的大大小小,这万分容易避免那些初始的更哈希。当心,无论如何,Lua只会当更哈希时候才能够收缩表。因此,如果您从头大小比较待的好,Lua可能永远不见面改你浪费之长空。

  当用Lua编程时,你得用构造器避免那些初始再哈希。当您勾勒下{true,
true,
true}时,Lua会预先知道表的数组部分以会见要达成三只空位,因此Lua用此分寸创建表。同样地,如果您勾勒下{x
= 1, y = 2, z =
3},Lua会创建4单空位的哈希表。举个例子,下面的轮回运行需要2.0秒:

for i = 1, 1000000 do

  local a = {}

  a[1] = 1; a[2] = 2; a[3] = 3

end

 

要是我们创建是大小的阐明,我们见面以运行时压缩到0.7秒:

for i = 1, 1000000 do

  local a = {true, true, true}

  a[1] = 1; a[2] = 2; a[3] = 3

end

 

  如果我们刻画如{[1] = true, [2] = true, [3] =
true},然而,Lua不会见够智能到检测为出底表达式(本例中凡是文字数字)指的凡屡组索引,因此会面创造4只空位的哈希表,浪费了内存和CPU时间。

  仅来当表重新哈希时,表的数组和哈希部分的大小才会重新计算,只有在表完全满且Lua需要插入新的素时候有。如果您整历表清除所有的字段(即设置他们也空),结果是表明不见面收缩。然而,如果你插入一些初的素,最后表不得不重新调整大小。通常就不是只问题:如果您直接排元素与插新的(在不少主次中都是发生代表性的),表的分寸保持无变换。然而,你该不指望通过清除大的发明的字段来过来内存:最好是释放表本身。

  一个劫持重新哈希的花样是插足够多凡是空值到表中。看接下来的例子:

a = {}
lim = 10000000
for i = 1, lim do a[i] = i end -- create a huge table
print(collectgarbage("count")) --> 196626
for i = 1, lim do a[i] = nil end -- erase all its elements
print(collectgarbage("count")) --> 196626
for i = lim + 1, 2*lim do a[i] = nil end -- create many nil elements
print(collectgarbage("count")) --> 17

自己未引进这种鬼把戏,除非在奇特情形下:这会很缓慢而没有爱之办法指导“足够”是借助多少元素。

  你或许会见怪怎当插入空值时Lua不见面收缩表。首先,要避测试插入表的是呀;检测赋空值会造成有的赋值变慢。其次,更关键之是,当遍历表时允许赋空值。思考接下去的斯轮回:

for k, v in pairs(t) do
    if some_property(v) then
        t[k] = nil -- erase that element
    end
end

假使赋空值后Lua对发明还哈希,这拨破坏本次遍历。

  如果您想清空表中拥有的要素,一个简短的遍历是贯彻他的正确方法:

for k in pairs(t) do
    t[k] = nil
end

“聪明”的选择是这轮回

while true do
    local k = next(t)
    if not k then break end
    t[k] = nil
end

而,对于生老之表这循环会非常慢。函数next,当不带来前一个键调用时,返回表的“第一单”元素(以某种自由顺序)。这样做,next函数开始全方位历表的多次组,查找无呢空的素。当循环设置第一单要素呢空时,next函数花更丰富之时日查找第一独非空元素。结果是,“聪明”的循环花费20秒清除出100,000单元素的发明;使用pairs遍历循环花费0.04秒。

 

关于字符串

和表一样,为了重新高速之运用字符串,最好理解Lua是怎样处理字符串的。

  不同为多数之脚本语言,Lua实现字符串的方式展现在有限独重要的点。第一,Lua中拥有的字符串都是内化的。意思是Lua对无一许符串只保留一份拷贝。无论何时出现新字符串,Lua会检测是字符串是否早已存在备份,如果是,重用拷贝。内化使像字符串的较和表索引操作十分急匆匆,但是字符串的创导会慢。

  第二,Lua中之变量从不持有字符串,仅是引用他们。这种实现方式加速了几乎单字符串的操作。举个例子,在Perl语言中,当您勾勒下类似于$x

$y,$y含有一个字符串,赋值会自$y缓冲中字符串内容复制到$x的缓冲。如果字符串很丰富的说话,这就见面变成昂贵的操作。在Lua中,这种赋值只待复制指于字符串的指针。

  然而,这种富含引用实现减慢了字符串连接的这种特定形式。在Perl中,$s =
$s . “x”和$s . =
“x”操作而了无均等的。在首先单被,你沾的一个$s的正片,并于她的结尾加上“x”。在亚独遭,“x”简单地附加到由$s变量保存的中间缓冲上。因此,第二种植形式与字符串的轻重缓急不相干(假设缓冲区有剩余文本的空间)。如果你在循环里用这些命令,他们之区别是线性和亚蹩脚方算法的分别。举个例子,下面的巡回读一个5M之文件花费了盖5分钟。

$x = "";
while (<>) {
    $x = $x . $_;
}

要是我们将 $x = $x . $_ 变成 $x .= $_, 这次时间跌至0.1秒!

  Lua不支持第二独,更快之可怜,这是盖它的变量没有缓冲和它们相互关联。因此,我们必须用显示的缓冲:字符串表做这项工作。下面的大循环0.28秒读取同样的5M文件。虽然不如Perl快,但为特别是了。

local t = {}
for line in io.lines() do
    t[#t + 1] = line
end
s = table.concat(t, "\n")

 

简化,复用,再生

当处理Lua资源时,我们相应同等用促进地球资源的3R发起。

  简化是立三个挑选中极其简易的。有几乎种方法可免对新目标的需要。举个例子,如果你的次第采取了成百上千之阐发,可以设想数据见的变动。举个简单的事例,考虑次操作折线。在Lua中尽当的代表折线是一律组点的列表,像这样:

polyline = { { x = 10.3, y = 98.5 },
             { x = 10.3, y = 18.3 },
             { x = 15.0, y = 98.5 },
              ...
}    

尽管自,但代表十分死之折线并无殊合算,因为各个一个单身的接触都要一个表。第一独做法是反为当频繁组被著录,这会以重复不见的内存:

polyline = { {10.3, 98.5 },
             {10.3, 18.3 },
             {15.0, 98.5 },
              ...
} 

于有百万独点的折线,这种转移会把利用的内存从95KB减少至65KB。当然,你付了易读性的代价:p[i].x比p[i][1]更便于掌握。

  另一个重经济之做法是一个列表存放坐标的x,另一个存坐标的y:

polyline = { x = { 10.3, 10.3, 15.0, ...},
           y = { 98.5, 18.3, 98.5, ...}
}

原来的p[i].x 变成现在底
p.x[i]。通过动用这种做法,一百万独点的折线仅仅用了24KB的内存。

  查找减少生成垃圾的好地方是以循环中。举个例子,如果当循环中不断的缔造表,你可于循环中把其换出,甚至当外边包裹创建函数。比较:

function foo (...)
  for i = 1, n do
    local t = {1, 2, 3, "hi"}
    -- do something without changing ’t’
    ...
  end
end
local t = {1, 2, 3, "hi"} -- create ’t’ once and for all
function foo (...)
  for i = 1, n do
    -- do something without changing ’t’
    ...
  end
end

闭包可以据此同的技能,只要你莫把其移出它们所急需之变量的作用域。举个例子,考虑接下去的函数:

function changenumbers (limit, delta)
  for line in io.lines() do
    line = string.gsub(line, "%d+", function (num)
          num = tonumber(num)
          if num >= limit then return tostring(num + delta) end
          -- else return nothing, keeping the original number
         end)
    io.write(line, "\n")
  end
end

我们透过把内部的函数移到循环的以外来避免吗各国行创建一个新的闭包:

function changenumbers (limit, delta)
  local function aux (num)
    num = tonumber(num)
    if num >= limit then return tostring(num + delta) end
  end
  for line in io.lines() do
    line = string.gsub(line, "%d+", aux)
    io.write(line, "\n")
  end
end

可,我们不能够管aux已至changenumbers函数外面,因为那样aux不可知顾到limit和delta。

  对于广大种字符串处理,我们好透过操作现存字符串的目录来减少对新字符串的消。举个例子,string,find函数返回他找到模式之位置,代替了相当。通过返回索引,对于每次成功匹配可以避创建一个初(子)的字符串。当必要时,程序员可以由此调用string.sub得到匹配的子字符串。(标准库有一个较子字符串的效益是个好主意,以便我们不必从字符串提取出大值(因而创建了一个新字符串))

  当我们不可避免用初目标时,通过录取我们任然可以免创建那些新目标。对于字符串的用是从来不必要的,因为Lua为我们搞好了:它总是内化用到的备字符串,因此,尽可能用它们。然而,对于表来说,重用可能坏实惠。作为一个科普的事例,让我回在循环中开创建表的气象。然而,这次表里的情节未是常量。尽管如此,我们依旧可以屡屡之以装有迭代中收录同一个说明,仅仅改变它的情节。考虑这个代码块:

local t = {}
for i = 1970, 2000 do
    t[i] = os.time({year = i, month = 6, day = 14})
end

下面这个是一律的,但是它们用了发明:

local t = {}
local aux = {year = nil, month = 6, day = 14}
for i = 1970, 2000 do
    aux.year = i
    t[i] = os.time(aux)
end

一个专程实用的方式来实现复用的方法是通过memoizing.。基本思想非常简单:储存输入的一点计算的结果,以便当再度出一致的输入时,程序只待复用之前的结果。

  LPeg,一个Lua中初的模式匹配包,对memoizing的施用大有趣。LPeg把每个模式编译成内在的形式,一个用以解析机器执行匹配的“程序”。这种编译和配合自己比代价十分高昂。因此,LPeg记下它们的编译结果连复用。一个概括的表将描述模式之字符串与相应的里表示相关联。

  memoizing的通常问题是储存以前结果花的空间或跨越复用这些结果的纯收入。Lua为了化解这题目,我们好为此弱表来保存结果,以便没有因此了之结果最后能起表里移除。

  Lua中,用高阶函数我们得以定义个通用的memoization函数:

function memoize (f)
  local mem = {} -- memoizing table
  setmetatable(mem, {__mode = "kv"}) -- make it weak
  return function (x) -- new version of ’f’, with memoizing
    local r = mem[x]
    if r == nil then -- no previous result?
      r = f(x) -- calls original function
      mem[x] = r -- store result for reuse
    end
    return r
  end
end

于出任意的函数f,,
memoize(f)返回一个初的和f返回相同结果的函数,并且记下其。举个例子,我们得再定义带memoizing版本的loadstring:

loadstring = memoize(loadstring)

我们完全像之前的不胜那样采用初函数,但是要我们加载的字符串中起众多再的,我们能够得到惊人之进项。

  如果你的顺序创建与刑释解教无限多之协程,回收再生可能是单提高性的挑三拣四。当前底协程API不提供直接支持复用协程,但是咱好突破这个范围。考虑下的协程

co = coroutine.create(function (f)
    while f do
      f = coroutine.yield(f())
    end
   end

这个协程接受一个学业(运行一个函数),返回其,并且形成后等下一个学业。

  Lua中多数之复苏由垃圾回收器自动执行。Lua用一个增量的渣回收器。这意味回收器表现吗为比小的步骤(逐步地)与程序执行交错执行任务。这些步骤的点子正比于内存分配:Lua每分配简单的内存,垃圾收集器就会举行同比例之办事。程序消耗内存越快,收集器回收的越快。

  如果我们针对先后采取简化和复用原则,通常收集器没有最多之干活可做。但是有时我们不能够幸免大量垃圾堆的出,此时收集器就变的笨重了。Lua中杂质收集器为一般程序召开了调整,因此于大多数软件中见的一对一不错。然而,有时对特别的状通过调整收集器我们得提高程序的特性。

  我们可由此Lua中collectgarbage函数或C中之lua_gc控制污染源收集器。尽管接口不同,但双方都提供的成效基本一样。我会见因此Lua的接口来谈谈,但是,通常这种操作用C比较好。

  collectgarbage函数提供了几只效益:它可歇与重启收集器,强制完整的征集循环,强制收集的同等步,获得Lua使用的总内存,并且改变影响收集器步幅的有数个参数。当调整内存不足的先后时她每有用途。

  对于一些种类的批处理程序,“永远”停止收集器是独选项,它们创建几独数据结构,基于这些数据结构产生输出,然后退出(例如编辑器)。对于这些程序,试图回收垃圾或浪费时间,因为就生那个少的污物为回收,并且当次结束时享有的内存会被放。

  对于非批处理的顺序,永远停止收集器并非是个选择。尽管如此,这些程序可能会见收益为以一些关键时期停止收集器。如果出必要,程序可以完全控制污染源收集器,做法是一直维系它已,只有明确地强制一个手续或一致涂鸦完整收集来运行它们运行。举个例子,有些事件驱动平台供设置idle函数选项,当没其他的事件处理时才会于调用。这是废物回收的绝佳时间。(Lua5.1中,每次当收集器停止时,强制执行某些收集。因此,强制某些收集后而必须立刻调用collectgarbage(“stop”)来保障他们打住。)

  最后,作为最终一个手段,你可以品味更改收集器的参数。收集器有些许独参数控制它们的涨幅。第一个名叫pause,控制收集器在好一个收集周期与起来产一个等候多长时间。第二独参数叫做stepmul(来自step
multiplier),控制每一个手续收集器收集多少。简言之,较小的中断与比生之幅度能增进收集器的速。

  这些参数对先后的圆性能影响是蛮为难料的。更快之收集器明显浪费更多的CPU周期;然而,它能抽程序用的总的内存,从而减少分页。只有仔细的品尝才会让你这些参数的无限佳值。

 

后记

正巧使我辈介绍中讨论的那样,优化是出技巧的。这里发出几乎点用小心,首先程序是否需要优化。如果它起实在的性问题,那么我们不能不稳定到谁地方同哪些优化。

  这里我们讨论的技术既无是唯一也未是最好要紧之一个。我们关心的凡Lua特有的技艺C语言,因为来重多之针对性通用技术的素材。

  于我们收前,我眷恋取鲜只在提升Lua程序性能边缘的选项。因为这点儿单还涉及到Lua代码之外的扭转。第一只凡是运LUaJIT,Mike
Pall开发的Lua即使编译器。他已经做了精的劳作,并且LuaJIT可能是当下动态语言极其抢之JIT。缺点是,他只能运行在x86绑架构上,而且,你要不标准的Lua解释器(LuaJIT)来运作程序。优点是于一些啊非移代码的情事下能快5倍之速度运行而的次序。

  第二单选择是把有些代码放到C中。毕竟,Lua的性状有是与C代码结合的力量。这种状态下,最重点的少数是也C代码选择正确的粒度级别。一方面,如果你只有将非常简单的函数移到C中,Lua和C通信的支付可能逾那些函数对性提升的低收入。另一方面,如果你管最好要命的函数移到C中,又会失去浑圆。

  最后,谨记,这点儿独挑选有点不兼容。程序中重复多的C代码,LuaJIT能优化代码就见面重复少。

简化,复用,再生