C++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特有的技能,因为起重复多的对通用技术的素材。

  于我们收前,我眷恋取鲜只在提升Lua程序性能边缘的取舍项。因为马上片个都事关到Lua代码之外的变化。第一单凡是运LUaJIT,Mike
Pall开发之Lua即使编译器。他都开了可观的行事,并且LuaJIT可能是眼下动态语言极其抢之JIT。缺点是,他只得运行在x86劫持构上,而且,你用不标准的Lua解释器(LuaJIT)来运转程序。优点是以某些呢未转移代码的状下会尽快5倍增之速运行而的次。

  第二独选项是拿有代码放到C中。毕竟,Lua的特色有是跟C代码结合的力。这种情形下,最紧要的一些凡是吧C代码选择对的粒度级别。一方面,如果你偏偏拿非常简单的函数移到C中,Lua和C通信的出可能过那些函数对性提升的纯收入。另一方面,如果你把最特别之函数移到C中,又见面去浑圆。

  最后,谨记,这半单选择有点不匹配。程序中还多的C代码,LuaJIT能优化代码就会见再度少。

简化,复用,再生