http://developer.anscamobile.com/content/performance-and-optimization
Hey guys, just want to share some optimization techniques here with you. I will update this list when I can or add any techniques you guys may suggest.
1) Localization : Localizing Lua functions provides a decent > massive speed difference. How do you do this ? Like so :
The slower way (Bad)
local myRand = math.random(1, 10)
The faster way (Good)
local mRand = math.random
local myRand = mRand(1, 10)
Other examples of this
local mRandSeed = math.randomseed
--Call it
mRandSeed(os.time())
local sFormat = string.format
--Call it
sFormat("%02d", myVar)
local tRemove = table.remove
--Call it
tRemove(myTable)
2) Avoid moving objects manually. Moving objects by incrementing it’s x,y values each frame is slower than using translate.
Example :
The bad way
obj.x = obj.x + 1
obj.y = obj.y + 1
The good way
obj:translate(1, 1)
You can also use translate even if you only need to just increase the x or y and not both as shown below :
--Only increment the x value
obj:translate(1, 0)
--Only increment the y value
obj:translate(0, 1)
3) Avoid heavy string comparison. If you are doing a lot of string comparison checks like below :
local myString = "none"
if myString == "hi" or myString == "do" or myString == "lol" then
It is faster to use a table of enums. That way you can still know the values but you are comparing numbers which is much faster than comparing strings.
local myString = 1
local enumTable = {
["hi"] = 1,
["do"] = 2,
["lol"] = 3,
}
if myString == enumTable["hi"] or myString == enumTable["do"] or myString == enumTable["lol"] then
4) For loops. Certain types of for loop execute faster than others.
Fastest :
for i = 1, #myTable do
--
end
Second fastest
local fpairs = ipairs
for i, v in fpairs(myTable) do
end
The difference between the two is roughly 120ms.
5) Make use of tables to cut down on local variable use and organise your code.
Bad practice for a group of data.
local mySheet1 = sprite.newSpriteSheetFromData("mySheet1.png", require("mySheet1").getSpriteSheetData())
local mySheet2 = sprite.newSpriteSheetFromData("mySheet2.png", require("mySheet2").getSpriteSheetData())
local mySheet3 = sprite.newSpriteSheetFromData("mySheet3.png", require("mySheet3").getSpriteSheetData())
Better practice for group of data.
local mySheets = {
["Sheet1"] = sprite.newSpriteSheetFromData("mySheet1.png", require("mySheet1").getSpriteSheetData())
["Sheet2"] = sprite.newSpriteSheetFromData("mySheet2.png", require("mySheet2").getSpriteSheetData())
["Sheet3"] = sprite.newSpriteSheetFromData("mySheet3.png", require("mySheet3").getSpriteSheetData())
}
To give an advantage of the benefits of this, despite the obvious ones of memory savings and organized blocks of code, take this scenario.
Attempting to dispose of the 3 sprite sheets and nil the variables using method 1 (the bad practice)
mySheet1:dispose()
mySheet2:dispose()
mySheet3:dispose()
mySheet1 = nil
mySheet2 = nil
mySheet3 = nil
Now disposing of the sprite sheets and nil the table. (the better practice)
local tRemove = table.remove
for i, v in pairs(mySheets) do
mySheets[i]:dispose()
mySheets[i] = nil
end
tRemove(mySheets)
See how much easier that is to manage and remove? Thats one of the other countless benefits of using tables.
Note: Overuse of tables will consume more lua memory. So use appropriately.
6) Functions : Use less of them.
Say for instance you have 4 buttons on screen that each have a tap event. You don’t need four functions (one for each), just the one will suffice.
--Table to store the buttons
local myButtons = {}
myButtons["Pause"] = display.newImage("pause.png")
myButtons["Pause"].myId = "Pause" --Set the buttons id
myButtons["Shoot"] = display.newImage("shoot.png")
myButtons["Shoot"].myId = "Shoot" --Set the buttons id
myButtons["Move"] = display.newImage("move.png")
myButtons["Move"].myId = "Move" --Set the buttons id
myButtons["Retry"] = display.newImage("retry.png")
myButtons["Retry"].myId = "Retry" --Set the buttons id
--Function to handle our buttons
local function handleButtons(event)
local target = event.target
--Handle action for each different button
if target.myId == "Pause" then
--Pause
elseif target.myId == "Shoot" then
--Shoot
elseif target.myId == "Move" then
--Move
elseif target.myId == "Retry" then
---Retry
end
return true
end
--Add event listeners for all the buttons
for i = 1, #myButtons do
myButtons[i]:addEventListener("tap", handleButtons)
end
See one function that handles all your buttons. This saves using 4 separate functions for something that can be achieved in one
7) Updating Objects : Only do it when you have to.
Say for instance you are creating a space shooter and have a text object on screen to display your current score. You obviously want to update it to reflect the current score, but you don’t need to do this every frame.
The bad way
local function updateScore(event)
score = score + 1
score.text = "score " .. score
end
Runtime:addEventListener("enterFrame", updateScore)
The good way:
--In your collision handler (when enemy gets hit with a bullet you fired
local function onCollision(event)
--Where Bullet collides with enemy
score = score + 1
score.text = "score" .. score
end
That way your only updating an object when you need to, rather than wasting cpu cycles updating something when it doesn’t need updating.
8) Creating objects : Things to always remember.
This is covered in greater depth here: http://blog.anscamobile.com/2011/09/how-to-spawn-objects-—-the-right-way/
When you have a function to create an object (or series of objects) you must return the object at the end of the function otherwise you have no way to clear the object from memory. This is also good programming practice.
Bad example:
local function spawnAGuy(amount)
local guy = display.newImage("guy.png")
guy.x = 100
guy.y = 100
end
--Create the guy (note in this case your local reference is only creating a reference the the function not the created "guy" object)
local spawnedGuy = spawnAGuy
--Remove it
you cant
Better example:
local function spawnAGuy(group)
local guy = display.newImage("guy.png")
guy.x = 100
guy.y = 100
--Insert the guy into the specified group if it exists (then you can easily clear it from display
if group then
group:insert(guy)
end
return guy
end
--Create the guy
local spawnedGuy = spawnAGuy(localGroup)
--Remove it
display.remove(spawnedGuy)
spawnedGuy = nil
Best example (use parameters)
local function spawnAGuy(params)
local guy = display.newImage("guy.png")
guy.x = params.x or 100
guy.y = params.y or 100
--Insert the guy into the specified group if it exists (then you can easily clear it from display
if params.group then
prams.group:insert(guy)
end
return guy
end
--Create the guy
local spawnedGuy = spawnAGuy({x = 20, y = 130, group = localGroup})
--Remove it
display.remove(spawnedGuy)
spawnedGuy = nil
Using paramater passing is a hugely powerful feature, you can even create a function in the spawning function to destroy/remove the guy.
local function spawnAGuy(params)
local guy = display.newImage("guy.png")
guy.x = params.x or 100
guy.y = params.y or 100
--Insert the guy into the specified group if it exists (then you can easily clear it from display
if params.group then
prams.group:insert(guy)
end
--Create a function to remove the guy
function guy:destroy()
display.remove(self)
self = nil
end
return guy
end
--Create the guy
local spawnedGuy = spawnAGuy({x = 20, y = 130, group = localGroup})
--Remove it
spawnedGuy:destroy()
More coming soon!, enjoy!
[import]uid: 84637 topic_id: 18550 reply_id: 318550[/import]