[TOPIC: topicViewTemplate]
[GLOBAL: userSmallPhoto]

## [Tips] Optimization 101 Started by Danny Dec 02 2011 06:45 PM

87 replies to this topic
[TOPIC CONTROLS]
[/TOPIC CONTROLS]
[modOptionsDropdown]
[/modOptionsDropdown]
[reputationFilter]
[TOPIC: post.html]
#1

### Danny

[GLOBAL: userInfoPane.html]
Danny
• Corona Geek

• 2,597 posts
• Corona Staff

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 :

```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 :

```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
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.

```
score = score + 1
score.text = "score " .. score
end

```

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-%E2%80%94-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.

```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!
uid: 84637 topic_id: 18550 reply_id: 318550

• carlospalma_sistemas likes this

[TOPIC: post.html]
#2

### PixelEnvision

[GLOBAL: userInfoPane.html]
PixelEnvision
• Contributor

• 305 posts
• Corona SDK

Nice post, thank you!

Specially #2, I wasn't aware of that till now...
uid: 10478 topic_id: 18550 reply_id: 71250

[TOPIC: post.html]
#3

### darkconsoles

[GLOBAL: userInfoPane.html]
darkconsoles
• Contributor

• 795 posts
• Guests

very cool examples, thanks
uid: 16142 topic_id: 18550 reply_id: 71266

[TOPIC: post.html]
#4

### Danny

[GLOBAL: userInfoPane.html]
Danny
• Corona Geek

• 2,597 posts
• Corona Staff

Just added #6, functions. Enjoy :)
uid: 84637 topic_id: 18550 reply_id: 71269

[TOPIC: post.html]
#5

### naveen_pcs

[GLOBAL: userInfoPane.html]
naveen_pcs
• Contributor

• 267 posts
• Corona SDK

For #2 would it still be better to use translate if your core game loop is inside an enterFrame listener (I currently increment x and y values within enterFrame).

Would it actually be a noticeable performance difference?
uid: 51654 topic_id: 18550 reply_id: 71271

[TOPIC: post.html]
#6

### Danny

[GLOBAL: userInfoPane.html]
Danny
• Corona Geek

• 2,597 posts
• Corona Staff

Yeah it would be better to use translate and it would be faster yeah.
uid: 84637 topic_id: 18550 reply_id: 71272

[TOPIC: post.html]
#7

### don

[GLOBAL: userInfoPane.html]
don
• Contributor

• 638 posts
• Corona SDK

I've done tests. #2 works even better by caching the access of x and y. It's the accessing that's expensive not the modification.

My test was to use the uma horse sprite. Display 225 of them and have them loop the animation.
Looping over all the sprites ever frame with this code produced 40-45fps*
```d.x = 0 -- just this access will slow things down significantly
d.y = 0
```

This however produced 56-60fps*
```d:translate(0, 0)
```
In other words, when you construct your display objects:
```
local d = display.newGroup()
d.cx = d.x
d.cy = d.y

-- .. somewhere modify cx, cy
local dx = d.cx + 1
local dy = d.cy + 1

d:translate(dx - d.cx, dx - d.cy)
d.cx = dx
d.cy = dy
```

Is much faster for large numbers of display objects over x, y, rotation, xScale, yScale. The alpha and isVisible properties however are not affected.

This has been confirmed by yanuar and I. But don't take my word for it, test it yourself.

"We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil" -Donald Knuth

* numbers reported testingon 4th gen ipod touch, your will be different if you use a different sprite or device due to fillrate or other device dependent characteristics
uid: 27183 topic_id: 18550 reply_id: 71273

[TOPIC: post.html]
#8

### Puzzle Runner

[GLOBAL: userInfoPane.html]
Puzzle Runner
• Contributor

• 394 posts
• Corona SDK

I do agree with Don. I avoid doing stuff I know is horrible, but beyond that I will only start optimizing if my iPhone 3GS has significant performance issues. (iPhone 3GS is what I consider the "bare minimum" right now).

@Danny I'd be interested to see the performance comparison between using the transition functions versus other possible solutions. (example, trying to transition with timers instead) Also, the information you provided above should be added to the existing optimization document in the docs section so that it remains easily accessible.
uid: 36054 topic_id: 18550 reply_id: 71277

[TOPIC: post.html]
#9

### jose2

[GLOBAL: userInfoPane.html]
jose2
• Contributor

• 108 posts
• Corona SDK

This post need to be sticky !!

EDIT: Ok, sticked !! XD
uid: 55808 topic_id: 18550 reply_id: 71276

[TOPIC: post.html]
#10

### jose2

[GLOBAL: userInfoPane.html]
jose2
• Contributor

• 108 posts
• Corona SDK

+1 @blasterv , i used several times transition.to in enterframe and i need to get better perfomance.
uid: 55808 topic_id: 18550 reply_id: 71278

[TOPIC: post.html]
#11

### Danny

[GLOBAL: userInfoPane.html]
Danny
• Corona Geek

• 2,597 posts
• Corona Staff

@jose2 : yeah i made it a sticky for convenience ;)

@blasterv, sure. Plenty of other things will be covered too. Once i feel the list is extensive enough I will make a blog post about it and add it to the official api optimization listing.
uid: 84637 topic_id: 18550 reply_id: 71279

[TOPIC: post.html]
#12

### Puzzle Runner

[GLOBAL: userInfoPane.html]
Puzzle Runner
• Contributor

• 394 posts
• Corona SDK

@jose2 I try to keep enterframe fairly empty. Typically I use timers and transition API for Artifical Intelligence (when enemies must take/not take action based on actions of the player)
uid: 36054 topic_id: 18550 reply_id: 71280

[TOPIC: post.html]
#13

### jose2

[GLOBAL: userInfoPane.html]
jose2
• Contributor

• 108 posts
• Corona SDK

@danny, about the 4th. Is more faster user the second or the first ???. Is not clearly explained.

I read somewhere (a blog about lua optimization) about the for..do perfomance vs ipairs and tolds is more faster use for..do if you know the table length, is true ?

uid: 55808 topic_id: 18550 reply_id: 71282

[TOPIC: post.html]
#14

### Naomi

[GLOBAL: userInfoPane.html]
Naomi
• Corona Geek

• 2,303 posts
• Corona SDK

This is awesome! Thank you, Danny!

Naomi
uid: 67217 topic_id: 18550 reply_id: 71284

[TOPIC: post.html]
#15

### Danny

[GLOBAL: userInfoPane.html]
Danny
• Corona Geek

• 2,597 posts
• Corona Staff

@jose2: I stated above the code snippets. The first one is the fastest :)
uid: 84637 topic_id: 18550 reply_id: 71287

[TOPIC: post.html]
#16

### jose2

[GLOBAL: userInfoPane.html]
jose2
• Contributor

• 108 posts
• Corona SDK

@blasterv yes but i need to use enterframe loop to know some positions of my sprites to add new graphics.

For example, a sprite who needs a "glasses" sprite i do this:

```for i = eyeeffectsGroup.numChildren,1,-1 do

if (eyeeffectsGroup[i]~= nil) then

end
end

```

I put the glasses sprite in the eyeeffects display group and call the previous "padre" (parent) graphic to know the exactly x, y from parent and made the rotation. It's ok?
uid: 55808 topic_id: 18550 reply_id: 71283

[TOPIC: post.html]
#17

### jose2

[GLOBAL: userInfoPane.html]
jose2
• Contributor

• 108 posts
• Corona SDK

Thanks @danny !!

So, for example in the code in #13 what's the best way to optimize this ??

This code execute on enterframe
uid: 55808 topic_id: 18550 reply_id: 71289

[TOPIC: post.html]
#18

### Naomi

[GLOBAL: userInfoPane.html]
Naomi
• Corona Geek

• 2,303 posts
• Corona SDK

[deleting the double post]
uid: 67217 topic_id: 18550 reply_id: 71285

[TOPIC: post.html]
#19

### don

[GLOBAL: userInfoPane.html]
don
• Contributor

• 638 posts
• Corona SDK

@Danny

What do you think about creating a series of tests so we can measure this stuff and have everyone report back? I created 65 tests just to figure out what's slowing stuff down. It would be cool if there was a place like on github to share with everyone and collate results so that we're not all coding in the dark.
Here are my findings based on those tests. Stuff that kills sprite performance.
1. fill rate! large graphics that need to be updated use smaller sprites (scaling up affects fill rate, scaling down improves perfromance)
2. resorting of sprites (create / destroy, toBack, toFront) do this a lot and it will slow things down a lot
3. accessing x, y, rotation, xScale, yScale is very slow (use translate, rotate and scale and cache the properties for update, see my earlier post for an example)
4. avoid destroying your sprites, instead make them invisible (isVisible = false or alpha = 0) Toggling sprite visibility has some overhead, but may really improve performance.
5. if your sprites are hidden, pause their playback if you can. (you can have 10k hidden sprites not playing and get 60fps, but only 400 hidden sprites playing at 60fps)
6. if the 300 uma sprites are visible when in the viewport, what is performance like when they are out of the view port are they culled? performance improves by about 10 fps when they are offscreen (would doing manual culling help? needs to tested)

All my sprite tests were done with the uma sprite sheet tested on 4th gen ipod touch.

I assume this will extend to any display object, but I haven't confirmed this since my entire game is using only sprites.

Further useful test should be done with physics, timers, variable scoping (globals are slow, but how local is local? what if you access a variable local to a file from inside a function?)
uid: 27183 topic_id: 18550 reply_id: 71294

• stefano.figurelli likes this

[TOPIC: post.html]
#20

### jose2

[GLOBAL: userInfoPane.html]
jose2
• Contributor

• 108 posts
• Corona SDK

I made a mistake. I don't use transition.to in enterframe but i have several loops using it:

Normally in my game swaps around 40 sprites using transition.to loops:

```local a1
local a2

function a1()
transition.to (ball, {xScale =0.9, yScale=1.1, onComplete=a2, time=200})
end

function a2()
transition.to (ball, {xScale =1.1, yScale=0.9, onComplete=a1, time=200})
end

a1()

```

I do this to achieve a bounce effect in balls and every ball loops it.

A better way to optimize this ?
uid: 55808 topic_id: 18550 reply_id: 71299

[TOPIC: post.html]
#21

### @RSCdev

[GLOBAL: userInfoPane.html]
@RSCdev
• Corona Geek

• 1,489 posts
• Corona SDK

Hello Danny,

Would like to say just a big THANK YOU!

Those TIPS are just A.w.e.s.o.m.E! :)

PS: Tip #2 and #6 are wonderful.

Please, let it come even more. haha

Cheers,
Rodrigo.
uid: 89165 topic_id: 18550 reply_id: 71325

[TOPIC: post.html]
#22

### don

[GLOBAL: userInfoPane.html]
don
• Contributor

• 638 posts
• Corona SDK

This should be at the top of the post too:

http://developer.anscamobile.com/content/performance-and-optimization
uid: 27183 topic_id: 18550 reply_id: 71329

[TOPIC: post.html]
#23

### @RSCdev

[GLOBAL: userInfoPane.html]
@RSCdev
• Corona Geek

• 1,489 posts
• Corona SDK

I agree, sure it should be at the TOP!

+1.

uid: 89165 topic_id: 18550 reply_id: 71332

[TOPIC: post.html]
#24

### Danny

[GLOBAL: userInfoPane.html]
Danny
• Corona Geek

• 2,597 posts
• Corona Staff

@Don, @Rodrigo: Your wish is my command :)
uid: 84637 topic_id: 18550 reply_id: 71337

[TOPIC: post.html]
#25

### @RSCdev

[GLOBAL: userInfoPane.html]
@RSCdev
• Corona Geek

• 1,489 posts
• Corona SDK

wOw!

@Danny, very kind of you doing so as it can be even more helpful (besides your own Tips) for many others Corona Members as well as it`s for myself! :)

Thanks!

Cheers,
Rodrigo.