[转]Lua-wiki Optimisation Tips

原文:http://lua-users.org/wiki/OptimisationTips

 

原文:http://lua-users.org/wiki/OptimisingGarbageCollection

Optimising Garbage Collection

lua-users home
wiki

 

Introduction

Garbage collection of your system can cause performance problems if a large number of objects have been generated. This page discusses the issues involved and ideas for avoiding the problem.

 

Minimising objects created

Question: Basically I want to minimise the amount of temporary memory being allocated, so that I can reduce the frequency GC is called (and maybe reduce the hit of each GC?). Can anyone give some hints on this topic? For instance, if I have local variables declared inside function A, will the local variables allocate memory each time A is called?

(ET) No. Local variables are kept on a stack and will not affect the GC. These constructs will create a new garbage collected object:

 

  • a..b (example only; any string generating construct i.e. strsub() [*1] or lua_pushstring().)
Each new string creates a new object. Lua has unique strings, that means that each possible string exists only once within Lua. If a new string occurs Lua first checks if it already exists within the string pool. If it does, a reference to that older string is used. If it does not exist, a new string object is created and put into the string pool. The GC checks the string pool for unused strings and frees them. So creating a lot of unique strings will trash the GC. I.e. this is bad GC-wise:
for i=1,100 do  x="foo"..tostring(i)  end  -- 100 new strings
Note: Code segments hold references to strings constants (a la "foo" above). So the string will exist in the string pool as long as the function exists; it does not have to be created each time the function is executed.

 

  • { ... }
Each time a table constructor is executed a new table is created. Try to reuse tables if possible. You could even go as far as creating and using newtable/freetable functions that manage a pool of unused tables. That way you get old style memory management for tables but you'll also get the old problems too ;-)

 

  • function() ... end
Executing a function statement creates a closure (note: not calling the function but executing the function statement!). Normally no big impact on the GC but if you have function statements within a loop you may create a lot of garbage. I.e.:
for i=1,100 do  foo(function() end)  end  -- bad for GC
creates 100 function closures.

 

  • function(foo, ...) end
Vararg functions create a table for the ellipsis (stored in 'arg') each time the function is called.

 

  • userdata objects
In Lua 4.0 (and 3.x) userdata objects are handled similar to strings. They are equal if the pointer value and the tag are equal. What about Lua5?

 

  • dofile/dostring
Well, these load/compile new code and will create strings and (GCed) code segments.

 

Freeing local variables

Question (continuation of above): So any local string or table will be freed when you exit a function?

Variables are a place to store objects (In fact, except for numbers it only holds pointers to the objects). Whether the stored object is a number or a table or anything else doesn't bother the variable. And local variables are not garbage collected objects themselves. So you may use them without creating intermediate objects. I think, that was what the original poster meant.

 

"So you may use them without creating intermediate objects." Does this need clarification? The variable itself doesnt create a GC object but its value could. You can use local variables which reference other existing objects without creating objects that need to be collected. But, you can't create new objects which are locals without creating an object that needs to be collected later.

About the objects stored within local variables: at the moment the local variable is no longer active the GC may collect the object that was stored there. The actual freeing of the object has nothing to do with function scopes or variable live times. The GC runs at certain times and frees all objects that are no longer accessible by any L

[*1] strsub exists in Lua 4, not Lua 5.

 

 

 

原文:http://lua-users.org/wiki/OptimisingUsingLocalVariables

Optimising Using Local Variables

lua-users home
wiki

 

Overview

Local variables are very quick, since they are accessed by index.

Local variables are very fast as they reside in virtual machine registers, and are accessed directly by index. Global variables on the other hand, reside in a lua table and as such are accessed by a hash lookup. -- Thomas Johansson

 

Making global variables local

Local variables are very quick, since they are accessed by index. If possible, make global variables local (weird, eh?). Seriously, it works great and indexed access is always going to be faster than a hash lookup. If a variable, say GameState, needs global scope for access from C, make a secondary variable that looks like 'local GSLocal = GameState' and use GSLocal within the module. This technique can also be used for functions that are called repetitively, too. eg.

x = { a=1,b=2 }
function foo()
  local y=x
  print( x.a )
  print( y.b )  -- faster than the print above since y is a local table
end

Note, this will also work with global functions (including standard library functions), eg.

(Steve Dekorte) I just got around to playing with this and it works great. For example this code:

local i, v = next(t, nil)
while i do i, v = next(t, i) end

Is 10% faster if you make next a local:

local next = next
local i, v = next(t, nil)
while i do i, v = next(t, i) end

I also did some other tests and found that foreach() is ~20% faster than the equivalent while loop, while foreachi() was ~20% slower than a while loop.

for i, v in t do end   -- about 5x as fast as a while

Keep in mind that what Steve is measuring in his tests is loop overhead (the loop body is empty). In reality there are some statments in the body so the overhead is not so significant. -- John Belmonte

 

 

 

原文:http://lua-users.org/wiki/OptimisationCodingTips

Optimisation Coding Tips

lua-users home
wiki

 

Lua 5.1 Notes

 

 

  • Memory allocation from the heap--e.g. repeatedly creating tables or closures--can slow things down.

 

  • Short inline expressions can be faster than function calls. t[#t+1] = 0 is faster than table.insert(t, 0).

 

  • Constant folding: x * (1/3) is just as fast as x * 0.33333333333333333 and is generally faster than x/3 on most CPUs (see multiplication note below). 1 + 2 + x, which is the same as (1+2) + x should be just as fast as 3 + x or x + (1 + 2) but faster than x + 1 + 2, which is the same as (x + 1) + 2 as is not necessary equivalent to the former. Note that addition of numbers on computers is generally not associative when overflow occurs, and the compiler doesn't even know whether x is a number or some other type with a non-associative __add metamethod. - LuaList:2006-03/msg00363.html . It's been reported that Roberto is seriously thinking about removing constant folding from Lua 5.2 since constant folding has been a source of bugs in Lua (though some of us really like constant folding -- DavidManura).

 

  • Multiplication x*0.5 is faster than division x/2.

 

  • x*x is faster than x^2

 

  • Factoring expressions: x*y+x*z+y*z --> x*(y+z) + y*z. Lua will not do this for you, particularly since it can't assume distributive and other common algebraic properties hold during numerical overflow.

--DavidManura

Note that Roberto Ierusalimschy's article Lua Performance Tips from the excellent [Lua Programming Gems] book is [available online].

 

Lua 4 Notes

The following information concerns optimization of Lua 4 and is kept here for historical reference.

 

General tips on coding

(Joshua Jensen) These are some optimization strategies I use (off the top of my head):

 

  • Local variables are very quick, since they are accessed by index. If possible, make global variables local (weird, eh?). Seriously, it works great and indexed access is always going to be faster than a hash lookup. If a variable, sayGameState, needs global scope for access from C, make a secondary variable that looks like 'local GSLocal = GameState' and use GSLocal within the module. This technique can also be used for functions that are called repetitively, too. (seeOptimisingUsingLocalVariables)
  • for loops are quite a bit faster than while loops, since they have specialized virtual machine instructions.
  • In your C callback functions, use lua_rawcall() to call other functions. The overhead of a setjmp() call for exceptions (and a few other things) is avoided. I would not recommend using lua_rawcall() outside of a callback in case something goes wrong during execution. Without the setjmp() call, the error handler that exits the application is called.
  • If possible, in your C functions, try and use lua_rawget() and lua_rawgeti() for table access, since it avoids the tag method checks. Be sure to use lua_rawgeti() for indexed access. It's still a hash lookup, but it's probably the fastest way to get there by index.
  • In C, use lua_ref() wherever possible. lua_ref() behaves similarly to a local variable in terms of speed.
  • Know that C strings passed into a Lua function (such as lua_getglobal()) from C are translated to a Lua string on entry. If a string is to be reused across multiple frames of the game, do a lua_ref() operation on it, too.

This information was written for Lua, pre v4.0 -- Nick Trout

 

 

Assertions

Using the standard assert function with a non-trivial message expression will negatively impact script performance. The reason is that the message expression is evaluated even when the assertion is true. For example in

assert(x <= x_max, "exceeded maximum ("..x_max..")")

regardless of the condition (which usually will be true), a float to string conversion and two concatenations will be performed. The following replacement uses printf-style message formatting and does not generate the message unless it is used:

function fast_assert(condition, ...)
    if not condition then
        if getn(arg) > 0 then
            assert(condition, call(format, arg))
        else
            assert(condition)
        end
    end
end

Now the example becomes:

fast_assert(x <= x_max, "exceeded maximum (%d)", x_max)

This is the VM code generated:

assert(x <= x_max, "exceeded maximum ("..x_max..")")
        GETGLOBAL  	0	; assert
        GETGLOBAL  	1	; x
        GETGLOBAL  	2	; x_max
        JMPLE      	1	; to 6
        PUSHNILJMP 	
        PUSHINT    	1
        PUSHSTRING 	3	; "exceeded maximum ("
        GETGLOBAL  	2	; x_max
        PUSHSTRING 	4	; ")"
        CONCAT     	3
        CALL       	0 0
fast_assert(x <= x_max, "exceeded maximum (%d)", x_max)
        GETGLOBAL  	5	; fast_assert
        GETGLOBAL  	1	; x
        GETGLOBAL  	2	; x_max
        JMPLE      	1	; to 17
        PUSHNILJMP 	
        PUSHINT    	1
        PUSHSTRING 	6	; "exceeded maximum (%d)"
        GETGLOBAL  	2	; x_max
        CALL       	0 0

 

Edit: April 23, 2012 By Sirmabus
The code above will not actually work with 5.1
Also added some enhancements like pointing back to the actual assert line number,
and a fall through in case the assertion msg arguments are wrong (using a "pcall()").

 

function fast_assert(condition, ...)
   if not condition then
      if next({...}) then
         local s,r = pcall(function (...) return(string.format(...)) end, ...)
         if s then
            error("assertion failed!: " .. r, 2)
         end
      end
      error("assertion failed!", 2)
   end
end

 

Fast Unordered List Iteration

Frequently in Lua we build a table of elements such as:

table = { "harold", "victoria", "margaret", "guthrie" } 

The "proper" way to iterate over this table is as follows:

for i=1, getn(table) do
    -- do something with table[i]
end

However if we aren't concerned about element order, the above iteration is slow. The first problem is that it calls getn(), which has order O(n) assuming as above that the "n" field has not been set. The second problem is that bytecode must be executed and a table lookup performed to access each element (that is, "table[i]").

A solution is to use a table iterator instead:

for x, element in pairs(table) do
    -- do something with element
end

The getn() call is eliminated as is the table lookup. The "x" is a dummy variable as the element index is normally not used in this case.

There is a caveat with this solution. If library functions tinsert() or tremove() are used on the table they will set the "n" field which would show up in our iteration.

An alternative is to employ the list iteration patch listed in LuaPowerPatches.

 

Table Access

Question: It's not the performance of creating the tables that I'm worried about, but rather all the accesses to the table contents.

(lhf) Tables are the central data structure in Lua. You shouldn't have to worry about table performance. A lot of effort is spent trying to make tables fast. For instance, there is a special opcode for a.x. See the difference between a.x and a[x] ... but, like you said, the difference here is essentially an extra GETGLOBAL.

 

a,c = {},"x"
        CREATETABLE	0
        PUSHSTRING 	2	; "x"
        SETGLOBAL  	1	; c
        SETGLOBAL  	0	; a
b=a.x
        GETGLOBAL  	0	; a
        GETDOTTED  	2	; x
        SETGLOBAL  	3	; b
b=a["x"]
        GETGLOBAL  	0	; a
        GETDOTTED  	2	; x
        SETGLOBAL  	3	; b
b=a[c]
        GETGLOBAL  	0	; a
        GETGLOBAL  	1	; c
        GETTABLE   	
        SETGLOBAL  	3	; b
        END


posted @ 2013-09-10 11:30  Scan.  阅读(589)  评论(0)    收藏  举报