[Tips] Optimization 101

Hello Danny ,

Would like to say just a big THANK YOU!

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

PS: Tip #2 and #6 are wonderful.

Please, let it come even more. haha

Cheers,
Rodrigo. [import]uid: 89165 topic_id: 18550 reply_id: 71325[/import]

This should be at the top of the post too:

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

I agree, sure it should be at the TOP!

+1.

[import]uid: 89165 topic_id: 18550 reply_id: 71332[/import]

@Don, @Rodrigo: Your wish is my command :slight_smile: [import]uid: 84637 topic_id: 18550 reply_id: 71337[/import]

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! :slight_smile:

Thanks!

Cheers ,
Rodrigo.
Twitter: @RSCdev [import]uid: 89165 topic_id: 18550 reply_id: 71338[/import]

I’d like to know what might be the best practice to load and play short sound clips.

I have almost two dozen sound files. Majority of them are 8KB in size. Only three of them are 12KB and 16KB. Under the audio.loadSound() section of the audio API, it says You should use this to load all your short sounds, especially ones you may play frequently. For best results, load all the sounds at the launch of your app or the start of a new level.

So I do the following, but I’m not entirely sure if this is the right way to go:

My main.lua
[lua]-- load all sound effects when game app launches
– most of these sounds are played often in each game modules
_G.mySound1 = audio.loadSound(“mySound1.aac”)
_G.mySound2 = audio.loadSound(“mySound2.aac”)
_G.mySound3 = audio.loadSound(“mySound3.aac”)
_G.mySound4 = audio.loadSound(“mySound4.aac”)

– I continue to add more, but you get the idea[/lua]

In my game module where sounds need to play:
[lua]-- localize globally loaded sound
– I include ones that are played in this module
– in other words, I don’t localize all globally loaded sound in each module
local mySound2 = _G.mySound2
local mySound3 = _G.mySound3
local mySound4 = _G.mySound4

–when I need to play the sound
audio.play(mySound2)
audio.play(mySound3)
audio.play(mySound4)[/lua]

I localized the globally loaded sounds because I hear localizing is important for performance (and tip #1 says I should too). In addition, I remember reading something about table-look-up impacting performance, and _G is a table, isn’t it?

So, instead of doing audio.play(_G.mySound1), I do audio.play(mySound1) by localizing the globally loaded sound. By doing this, I don’t need to load & dispose and then load the same sound again when I play one level after another (or when I reload a same level to play again.)

The thing is, currently, I don’t dispose the local variables such as mySound1 when I unload the module even though the audio.loadSound() section of the audio API says: Please note that you are responsible for unloading (cleaning up) any audio files you load with this API. Use the audio.dispose() API to clean up audio handles you are completely done with them and want to unload from memory to get back more RAM.

When I try disposing localized version of the globally loaded sound like audio.dispose(mySound1), it gave me an error, so I decided not to dispose these local variables at all. And, I’ve been monitoring if there’s any memory leak associated with this practice – but so far, I haven’t seen any issue.

However, I’m not entirely sure how I set up my sound load/play is a good practice. Is it better if I load the sound at the start of each level (and not use _G table at all)?

But then, if I don’t use the _G table and load/dispose sound at start/end of each game level, I will essentially be unloading a dozen sound files and then reloading the same dozen sound files immediately after disposing them. So this feels wrong too.

Any thoughts? I’d like to hear how others are handling a case like this, and if there’s a better approach I should use.

Naomi

[import]uid: 67217 topic_id: 18550 reply_id: 71362[/import]

Great post! very useful!
I’ll apply these tips to the next update of my game.

Thanks [import]uid: 74676 topic_id: 18550 reply_id: 71544[/import]

Fantastic!! Thank you! [import]uid: 40033 topic_id: 18550 reply_id: 71910[/import]

I want to develop application for enterprise level,for instance,a software for hotel in which a customer will navigated through tab(application software) for accessing the facilities available which i’m developing with the help of android which i find flexible for development but this same scenario i want to repeat for iphone i find corona SDK for cross platform development which uses Lua Scripting but its syntax is basically for game(as i saw)and i thing it will lead me to complexity in future while development so which one will be suitable language (which i can use)? [import]uid: 108555 topic_id: 18550 reply_id: 73903[/import]

@Danny,

Some nice tips in your post. There is one issue with tip #6. You can’t add event listeners to the table objects like you’re doing because you created an associate array instead of an indexed array. #myButtons will not return the number of elements in an associate array. The “for” loop needs to be modified as follows:

--Add event listeners for all the buttons for i,v in pairs(myButtons) do myButtons[i]:addEventListener("tap", handleButtons) end [import]uid: 7559 topic_id: 18550 reply_id: 87770[/import]

Performance tip: If you’re sure that you’re not leaking memory. Disable the garbage collector in critical performance code. Reenable it when you need to reclaim memory.

[code]

collectgarbage(‘stop’) – prevents the gc from running

– in a level doing stuff

– exiting level
– clean up and nil no longer used variables

collectgarbage(‘restart’) – restart the gc
collectgarbage(‘collect’) – reclaim memory
[/code] [import]uid: 27183 topic_id: 18550 reply_id: 96042[/import]

For number 5, couldn’t you optimise it using the previous hints too?

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:

local newSprite =sprite.newSpriteSheetFromData local mySheets = { ["Sheet1"] = newSprite("mySheet1.png", require("mySheet1").getSpriteSheetData()) ["Sheet2"] = newSprite("mySheet2.png", require("mySheet2").getSpriteSheetData()) ["Sheet3"] = newSprite("mySheet3.png", require("mySheet3").getSpriteSheetData()) } [import]uid: 108660 topic_id: 18550 reply_id: 96043[/import]

Be careful here. There is a famous quote: “Premature optimization is the root of all evil.” (I encourage you all to research it.) Quite often you’ll do the wrong thing. Benchmark and verify.

#1) People are too worried about local vs. global. Local is faster, but you’ll never know the difference unless you are in a tight inner loop that is being called many times. You may (or may not) also be trading off other things (like memory) in return for speed. There are good reasons to use local variables other than speed, so use them for that. But if global variables make sense, don’t be afraid of them.

#2) I think this is correct, though the reasons are mired in detailed. ‘Property’ accesses are implemented as the ultimate fallback and then we must sort out what to do on the C-side whereas calling a (translate) function avoids this. Batch operations (e.g. setting both x and y in one call) will also be faster since you run fewer instructions.

#3) This is wrong. In Lua, string comparisons are pointer comparisons which are as fast as you can get. In the suggested work-around, you will incur table look ups for each comparison and then number comparisons which will be slower. Not to mention that the table look ups by string key will essentially be doing the exact string comparison behind the scenes you were trying to avoid in the first place. (Ahem, premature optimization is evil.)

#4) A totally different reason to not use ipairs: It has been deprecated in Lua 5.2.

#5) This is more coding style than optimization. They are reasonable suggestions to consider though.

#6) This will really depend on the situation. Sometimes having more functions will be faster. Sometimes it will not.
[import]uid: 7563 topic_id: 18550 reply_id: 96046[/import]

Another performance tip, I used in Cannon Cat. Use simple physics shapes over complex ones.

Unless you really absolutely need the accuracy swap out your complex multibody physics shapes for rectangles and circles. This is useful for hit detection. If your objects are moving at high speed and the collision response doesn’t require accurate physics this can be a good thing to try if you have lots of objects. [import]uid: 27183 topic_id: 18550 reply_id: 96055[/import]

Added two more sections to the first post

7) Updating Objects : Only do it when you have to.

8) Creating objects : Things to always remember. [import]uid: 84637 topic_id: 18550 reply_id: 105892[/import]

Thank you, Danny! I love the tip on creating objects, especially the one with passing the param plus destroy function. Wow.

I have a question, though. Is the guy:destroy() function inside the spawnAGuy function local function? I mean, if there’s a function B inside a local function A, does it automatically make the function B local to the function A?

Naomi [import]uid: 67217 topic_id: 18550 reply_id: 105923[/import]

Naomi,

It’s roughly equivalent to

[lua]local guy = {}

guy.stuff = “Some stuff.”

guy.thing = function(param) return param end

function guy:destroy() self:removeSelf(); self=nil; return true end[/lua]

“Guy” is a local variable that happens to be a table. The key/value pairs you add to that table, even if the value happens to be a function, don’t get scoped differently. The ‘guy’ table is local to its scope regardless of its contents.

Likewise

[lua]local function fA(param)

local function fB(param)
return param
end

return fB(param)
end[/lua]

fA is local to whatever scope in which its defined and fB is local to fA (and therefore meaningless outside of fA(). [import]uid: 44647 topic_id: 18550 reply_id: 105934[/import]

Hey, @toby2, thank you for clarifying it. The reason why I asked is because Danny’s example looks like this:

[lua]local function fA(param)
function fB(param)
return param
end
return fB(param)
end[/lua]

I suppose function fB doesn’t need to be declared local to be local to function fA.

Thanks again!
Naomi [import]uid: 67217 topic_id: 18550 reply_id: 105937[/import]

Late to the party and just wanna give a big thank you to Danny for this great post!!! DEFINITELY going in my bookmarks for sure!!!

-Mario [import]uid: 11636 topic_id: 18550 reply_id: 105939[/import]

@Naomi,

Danny’s example is actually more like the first example I posted. Stripped down it looks like this:

[lua]local function spawnAGuy(params)
local guy = display.newImage(“guy.png”)

function guy:destroy()
display.remove(guy)
guy = nil
end

return guy
end[/lua]

The function guy:destroy() function declaration is equivalent to guy.destroy = function(self). “Guy” is already a local variable and you’re adding a key/value pair named “destroy” with a function value to a table.

In the example you just posted, fB ends up being a global function when you remove ‘local’ from the declaration. I hope you can see the difference despite my less-than-stellar explanation. [import]uid: 44647 topic_id: 18550 reply_id: 105942[/import]