Jump to content

[TOPIC: topicViewTemplate]
[GLOBAL: userSmallPhoto]
Photo

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

* * * * * 8 votes
87 replies to this topic
[TOPIC CONTROLS]
Page 2 of 4 1 2 3 4
[/TOPIC CONTROLS]
[modOptionsDropdown]
[/modOptionsDropdown]
[reputationFilter]
[TOPIC: post.html]
#26

Naomi

[GLOBAL: userInfoPane.html]
Naomi
  • Corona Geek

  • 2,303 posts
  • Enterprise

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


In my game module where sounds need to play:
-- 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 modulelocal mySound2 = _G.mySound2local mySound3 = _G.mySound3local mySound4 = _G.mySound4--when I need to play the soundaudio.play(mySound2)audio.play(mySound3)audio.play(mySound4)


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

uid: 67217 topic_id: 18550 reply_id: 71362


[TOPIC: post.html]
#27

Jadynut

[GLOBAL: userInfoPane.html]
Jadynut
  • Contributor

  • 105 posts
  • Corona SDK

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

Thanks
uid: 74676 topic_id: 18550 reply_id: 71544


[TOPIC: post.html]
#28

Pixin

[GLOBAL: userInfoPane.html]
Pixin
  • Contributor

  • 269 posts
  • Corona SDK

Fantastic!! Thank you!
uid: 40033 topic_id: 18550 reply_id: 71910


[TOPIC: post.html]
#29

vishwas.zambre

[GLOBAL: userInfoPane.html]
vishwas.zambre
  • Observer

  • 5 posts
  • Corona SDK

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)?
uid: 108555 topic_id: 18550 reply_id: 73903


[TOPIC: post.html]
#30

Tom

[GLOBAL: userInfoPane.html]
Tom
  • Moderator

  • 1,480 posts
  • Corona Staff

@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
uid: 7559 topic_id: 18550 reply_id: 87770


[TOPIC: post.html]
#31

don

[GLOBAL: userInfoPane.html]
don
  • Contributor

  • 638 posts
  • Corona SDK

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.


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
uid: 27183 topic_id: 18550 reply_id: 96042


[TOPIC: post.html]
#32

jeff472

[GLOBAL: userInfoPane.html]
jeff472
  • Contributor

  • 391 posts
  • Corona SDK

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())
}
uid: 108660 topic_id: 18550 reply_id: 96043


[TOPIC: post.html]
#33

ewing

[GLOBAL: userInfoPane.html]
ewing
  • Corona Geek

  • 1,138 posts
  • Alumni

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.
uid: 7563 topic_id: 18550 reply_id: 96046


[TOPIC: post.html]
#34

don

[GLOBAL: userInfoPane.html]
don
  • Contributor

  • 638 posts
  • Corona SDK

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.
uid: 27183 topic_id: 18550 reply_id: 96055


[TOPIC: post.html]
#35

Danny

[GLOBAL: userInfoPane.html]
Danny
  • Corona Geek

  • 2,597 posts
  • Corona Staff

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.
uid: 84637 topic_id: 18550 reply_id: 105892


[TOPIC: post.html]
#36

Naomi

[GLOBAL: userInfoPane.html]
Naomi
  • Corona Geek

  • 2,303 posts
  • Enterprise

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
uid: 67217 topic_id: 18550 reply_id: 105923


[TOPIC: post.html]
#37

toby2

[GLOBAL: userInfoPane.html]
toby2
  • Contributor

  • 166 posts
  • Corona SDK

Naomi,

It's roughly equivalent to

local guy = {}guy.stuff = "Some stuff."guy.thing = function(param) return param endfunction guy:destroy() self:removeSelf(); self=nil; return true end


"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

local function fA(param)    local function fB(param)        return param    end    return fB(param)end


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


[TOPIC: post.html]
#38

Naomi

[GLOBAL: userInfoPane.html]
Naomi
  • Corona Geek

  • 2,303 posts
  • Enterprise

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

local function fA(param)    function fB(param)        return param    end    return fB(param)end


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

Thanks again!
Naomi
uid: 67217 topic_id: 18550 reply_id: 105937


[TOPIC: post.html]
#39

mroberti

[GLOBAL: userInfoPane.html]
mroberti
  • Contributor

  • 308 posts
  • Corona SDK

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
uid: 11636 topic_id: 18550 reply_id: 105939


[TOPIC: post.html]
#40

toby2

[GLOBAL: userInfoPane.html]
toby2
  • Contributor

  • 166 posts
  • Corona SDK

@Naomi,

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

local function spawnAGuy(params)
    local guy = display.newImage("guy.png")

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


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.
uid: 44647 topic_id: 18550 reply_id: 105942


[TOPIC: post.html]
#41

Naomi

[GLOBAL: userInfoPane.html]
Naomi
  • Corona Geek

  • 2,303 posts
  • Enterprise

@toby2, ah, of course. I didn't see (or rather my brain didn't quite register) the guy+colon there in Danny's example. Yes, now you've made it super clear to me. Thank you so much!

Naomi
uid: 67217 topic_id: 18550 reply_id: 105943


[TOPIC: post.html]
#42

Danny

[GLOBAL: userInfoPane.html]
Danny
  • Corona Geek

  • 2,597 posts
  • Corona Staff

Updated the destroy part of the spawn example as you can simply pass self to display.remove and nil out self as self is passed in the declaration.
uid: 84637 topic_id: 18550 reply_id: 106049


[TOPIC: post.html]
#43

Beloudest

[GLOBAL: userInfoPane.html]
Beloudest
  • Contributor

  • 189 posts
  • Corona SDK

Number 2 is not totally correct from my tests, if you cache obstacle.x in a local variable within a local function then update the cahced variable and update the objetc.x later it seems to be quicker than translate from my tests with parralax scrolling.
uid: 118379 topic_id: 18550 reply_id: 106060


[TOPIC: post.html]
#44

Beloudest

[GLOBAL: userInfoPane.html]
Beloudest
  • Contributor

  • 189 posts
  • Corona SDK

Opps just read the previous posts, Don already mentioned it in a sense.
uid: 118379 topic_id: 18550 reply_id: 106062


[TOPIC: post.html]
#45

jeff472

[GLOBAL: userInfoPane.html]
jeff472
  • Contributor

  • 391 posts
  • Corona SDK

I thought that transition.to would give me the same kind of boost
so I turned this:

theActual.xScale = _G.currentScale;
theActual.yScale = _G.currentScale;
theActual.x = x*(40 * _G.currentScale)+(20 * _G.currentScale)
theActual.y = y* (40 * _G.currentScale)+(20 * _G.currentScale)
into this:
transition.to(theActual,{time=0,xScale = _G.currentScale,yScale = _G.currentScale, x = x*(40 * _G.currentScale)+(20 * _G.currentScale), y= y* (40 * _G.currentScale)+(20 * _G.currentScale) })
And it slowed down
Is that to be expected?

(I am about to reduce the calcs independently of this.. this issue is that I was expecting transition.to to produce an improvement in the same way as the translate() tip)

Any comments?
uid: 108660 topic_id: 18550 reply_id: 110711


[TOPIC: post.html]
#46

OwenYang

[GLOBAL: userInfoPane.html]
OwenYang
  • Contributor

  • 163 posts
  • Corona SDK

wow....damn good!!!
uid: 25057 topic_id: 18550 reply_id: 119184


[TOPIC: post.html]
#47

gtt

[GLOBAL: userInfoPane.html]
gtt
  • Contributor

  • 164 posts
  • Corona SDK

Great post! thanks @danny, a few of these optimizations are new to me and we're going to take them into consideratio in our next game :)
uid: 80469 topic_id: 18550 reply_id: 119196


[TOPIC: post.html]
#48

apilling63

[GLOBAL: userInfoPane.html]
apilling63
  • Enthusiast

  • 34 posts
  • Corona SDK

Awesome post.  V helpful.  :)

 

Does anyone know if there is any penalty for registering multiple event listeners.  For example if one was to add three "enterFrame" listeners that perform separate distinct functions is that more costly than registering a single listener that performs all three tasks.

 

The former would be preferable from a code clarity P.O.V.



[TOPIC: post.html]
#49

dssi3332

[GLOBAL: userInfoPane.html]
dssi3332
  • Observer

  • 23 posts
  • Corona SDK

 would the first tip be applied when i will just use the math.rand function for just once for the whole game?



[TOPIC: post.html]
#50

Hendrix000007

[GLOBAL: userInfoPane.html]
Hendrix000007
  • Contributor

  • 177 posts
  • Corona SDK

this is realy useful info Danny one q I would like to get an answer on -and its been talked about some times now, is what is most efficient: local myFunc = function( ) or local function myFunc( ) and why?


[topic_controls]
Page 2 of 4 1 2 3 4
 
[/topic_controls]