What are your personal performance tips/tricks?

1 favourites
  • 15 posts
From the Asset Store
Firebase: Analytics, Dynamic Links, Remote Config, Performance, Crashlytics on Android, iOS & Web Browser
  • Hello all,

    I tend to find new ways to approach problems after discussing with others. I'm interested to know if anyone has unique approaches for performance, especially as the project grows; anything from tiny/specific situations to general coding principles.

    This serves as a good general guide: construct.net/en/make-games/manuals/construct-3/tips-and-guides/performance-tips, but perhaps you've encountered scenarios where you had to approach a performance issue in a less obvious way.

    Thanks for any response,

    1Step

  • look in there like a bunch of posts down construct.net/en/forum/construct-3/general-discussion-7/eleanors-patreon-the-best-142183

    it'S pretty decent if you can make sense of it all

    I'm so sorry for the way I was talking

    I was like, very angry

    so it probably is cringe and edgy

    but there's some good info !

  • If I can add some reccomendations,

    try to keep your events refactorable,

    like, if you want your gravity to chance

    it'S not fun to change a hardcoded gravity numbers in 10 different events

    it's much easyer if those 10 events reference a gravity variable.

    if you don'T want to pollute your gloval scope with global variables

    you can put them and the events referencing thme in a group so it becomes a local variable

    make your events small and easy to read, don't like

    write events that do the same thing 10 times

    because it'S 10 different objects

    reuse the code with like containers or families

    so if you have a bug or want to change the code later

    you don'T have to fix it or change it 10 times

    the easier to read the easier it is to fix bugs

    comment your code and explain what it does to your future tired self

    like, if you spent a month

    working on a different part of your code

    and you come back here

    you'll have no idea what any of it does

    but if your past self explained it good

    then you can like, understand it better and its all good

    use pick by evaluate

    it'S like hands down the best of all conditions by far

    it lets you put tons of conditions into a single one and saves tons of CPU if you do it right, because you can use a ternary operator to just nope out of the condition and not test the rest of them on a per instance basis

    understand picking

    it'S like fundamentally the most important thing in construct

    i think!!

    make a ton of experimental stuff and learn tons from your mistakes!!

    don'T be afraid to not use behaviors and instead make your own mechanics

    that makes your game stand out a lot more and you learn a ton and become an awesome developer

    and you don'T have to worry about like

    special cases where the behaviors don't work, because they'Re like

    kind of basic catch-all

    master of none

    type of things

    and a lot of people tend to use a ton of "band aid fixes" to implement mechanics that aren'T built in behaviors

    while using behaviors

    when they could make their own specialised mechanics from the ground up instead.

    anyways, that'S what i got for now

  • Thank you Eleanor. That was a lot of intriguing info you provided.

    Notes/Questions

    1. Use self-referencing as much as possible.

    2. Arrays are slow (I generally agree here, would this be irrelevant for direct lookups (Array.At(0,0))?

    3. Should the storage of data in arrays be preserved solely for when there is a need to sort data? If so, what is your preferred method of storing data when performance is concerned? (You ended up answering this in your collision method explanation: Dictionaries, referring to the unused space of an array as another pitfall)

    4. Static variables, I’ve been down this road in another project, but stopped due to some posts I read, such as: construct.net/en/forum/construct-2/general-discussion-17/difference-constant-static-108524, Toby refers directly to constants in this case, but they would theoretically have even less overhead than static as they would not be accessible during run-time. If constants hold no performance benefit, I am curious how static variables would.

    5. You prefer to use ternary operators rather than the if else condition blocks due to overhead, this is interesting. I need to test this out.

    6. If a calculation needs to be completed more than once in an event chain, set it in a variable so the calculation is performed only once.

    7. Can you explain your collision method a bit more? Are you stating that you check if a coordinate of a player, stored as a dictionary key, is already in the dictionary (has key) to determine if a collision check has occurred?

    8. Speaking of collision checks, do you have any experience with relying solely on distance checks rather than collision polygons?

    9. Use containers when possible, I investigated this earlier, but I did not feel in “control” of picking the objects in events due to the automatic nature of referencing all objects in the container when one is selected. I will look back into this.

    10. Pick by UID is a preferred pick method.

    11. Using pins created big slowdowns for you, this one was surprising to me. What is your take on the set as a child manner of positioning vs set position vs Pin? (https://www.construct.net/en/forum/construct-2/general-discussion-17/pin-behavior-set-position-72028)

    12. You mention using events in favor of behaviors however: construct.net/en/forum/construct-3/general-discussion-7/picking-performance-132709 (scrolling down a bit refers to behaviors running faster than events due to the overhead, nonetheless I think I understand your point, behaviors can sometimes be general purpose, with a portion of its cost not being utilized and thus essentially serving as a form of overhead. So perhaps this is a case-by-case basis.

    13. Pick by evaluation is one of your preferred methods of picking, I used this quite often in a previous project, but I came across a post (cannot find it now) that mentioned directly checking an instance variable through the object is faster than checks through the system (compare two values, pick by comparison, evaluate), now this does not consider the benefits of ternary operators, so this might be moot.

    General questions for you:

    1) What is your take on physics? I tend to disable most collision-based physics and apply physics forces on collision instead, perhaps it is better to work with a more custom system for physics like outcomes?

    2) What is your current main bottleneck in performance? (I have watched gameplay of your project, several sprites running around, the main thing that impresses me is the multiple accessories that are also present on the characters, I have made a project with this feature in the past and that tended to be one of the major consumptions of CPU.)

    3) Is your target FPS 60? If so, how many characters are you able to have running around until you start to drop from that target.

    4) What is true scale pixel size of your characters? (16 x 16 for example)

  • Thank you Ele, (if I can call you Ele).

    You can not. I'm Eleanor

    Notes/Questions

    1. Use self-referencing as much as possible.

    I mean yeah but not for the sake of it, like, use self referencing because it plays a huge part in the rest of what im saying, you know ?

    2. Arrays are slow (I generally agree here, would this be irrelevant for direct lookups (Array.At(0,0))?

    Now that I am reading it, this advice sounds way too convoluted.

    It takes into account that, you might be looking for a way to implement a data structure to control your game objects, and iterate on them, and it says that instead of iterating your objects by going into the array to apply things like velocity or HP to them through a loop like this to structure your code and events in an easier to manage way than the "basic stuff everyone does", by manipulating data instead of objects, you can realise that, when you do an action, that action is applied to objects in sequence, like, if you do, set self.x to self.x + self.vx, it will go through every single object of that object type and one by one sequentially apply their self velocity to themselves to change their position. IF you start thinking this way, then you can realise that, instead of going to an array sequentially to apply forces and velocities on objects, you can instead create empty arrays with no data in them, give them instance variables, and then array.iid becomes kind of like your x axis in the array, and instance variables become your Y axis, and your objects become data to iterate over so you can separate your logic from the rendering part of the game, and this paragraph is getting long and i have a lot of questions to answer so let'S move on

    3. Should the storage of data in arrays be preserved solely for when there is a need to sort data? If so, what is your preferred method of storing data when performance is concerned? (You ended up answering this in your collision method explanation: Dictionaries, referring to the unused space of an array as another pitfall)

    I don't know what this means. What kind of data needs to be sorted?

    4. Static variables, I’ve been down this road in another project, but stopped due to some posts I read, such as: construct.net/en/forum/construct-2/general-discussion-17/difference-constant-static-108524, Toby refers directly to constants in this case, but they would theoretically have even less overhead than static as they would not be accessible during run-time. If constants hold no performance benefit, I am curious how static variables would.

    this basically just means, that if you need a number for "player max velocity", you don'T just give every single player a velocity instance variable, you make one big solid static constant variable and access that instead. They'Re faster to access than instance variables anyway

    5. You prefer to use ternary operators rather than the if else condition blocks due to overhead, this is interesting. I need to test this out.

    I have no idea how javascript works, but I'm just hard guessing that in the engine, every single time you add a condition, the engine just calls a function to loop through every single object of that type to compare values to put them on a "picked" data structure or something, and then the next condition loops though the whole "picked" data structure to compare more values to build another data structure (or re-use the same one) and once it'S done with conditions, it calls a function to iterate through the whole thing again to apply changes to the variables or whatever. What if like, while you'Re looping through your whole data structure of objects like explained in the first giant paragraph, and you have your "object" in memory, just change it'S variable, right there and now, instead of waiting for multiple loops. it's like, if you're choosing clothes, and you go through all of your clothes, choose a piece of clothing from each drawer but leave them there and close the drawers until you got the idea of what your outfit will be, and then re-open the drawers where you remember seeing the pieces one by one again to put them on, just fucking take the socks and put them on as soon as you see them, it'S not rocket science lmfao

    6. If a calculation needs to be completed more than once in an event chain, set it in a variable so the calculation is performed only once.

    Like if you do the thing i just described above, but, if every single time you do it, you do a variable check for the same thing, like, is object active, just do a "is object active" condition and get rid of it instead of looping through the objects 5 times and every single time checking if it's active, just check once, even though I just told you to not use conditions, it'S not like, ALWAYS DO IT FOR EVERYTHING, use your judgement, so in the same way, if you need to calculate the velocity of your character every time you have an action that reference it'S velocity, just make a velocity instance variable and calculate it once

    7. Can you explain your collision method a bit more? Are you stating that you check if a coordinate of a player, stored as a dictionary key, is already in the dictionary (has key) to determine if a collision check has occurred?

    8. Speaking of collision checks, do you have any experience with relying solely on distance checks rather than collision polygons?

    respond to two questions at once !!!!

    so like, academics are going to tell you this is called big O notation or something something, but we'Re just going to call it common sense here, like, if you start doing your own collision detection algorithm, you'll obviously start with, every object checks every other object for distance, and that'S like, a fuckton of checks, you know? it'S like, the more objects you have, the slower it gets, some exponential stuff, exponential stuff is like the U thing on graphic calculators, whereas, you want something more like a V, where, for each object you have, you only do a collision check x times per objects, not object * object * x times. I wanted to have thousands of entities moving in my game level at once, so like, you start trying to figure out ways to optimise, like, make some entities that are far away sleep so you don'T run collision checks on those or seomthing, but then, how do you know which object is sleeping to which object? and you start looking on youtube for spatial partitionning and quadtrees and that fancy stuff but you didn'T do well at school so you don'T really understand most of it, then you'Re like, what if, minecraft chunks?, player position, rounded to a grid, objects only test distance against object close by in the grid, then no matter if there's like a billion objects far away, you don'T check distance on every object anymore, that'S cool i guess!!

    also, distance checks are awful, they'Re like square root something something blehh, instead, you can do math like a little baby, just like me!! If you have a character with like and X and a Y, you don'T distance check everything, you separate X from Y, then you're like, is the lowest X of this object bigger than the highest X of this object, is the highest X part of this object Bigger than the lowest part of this object, if both true, it will equal 1 with binary operators or you can use a ternary operator, and you can go through a huge list objects with that baby math it'S great, then you can youtube like, is point in a circle, is circle intersecting circle, is a point in a polygon, AABB, whatever it is, you think it, youtube has it !!! Learning all this weird stuff makes you a super awesome programmer !!! keep it up !!

    9. Use containers when possible, I investigated this earlier, but I did not feel in “control” of picking the objects in events due to the automatic nature of referencing all objects in the container when one is selected. I will look back into this.

    containers are kinda weird to figure out but once you get it, you "get it".

    it lets you iterate through objects like I just said a million times, and then use other object references without picking or anything, because you'Re already in a "sex value" not loop object loop loop you know? and it'S sequential so it'S picked so you can do self.x but also shoes.size or whatever, and it will work, the correct shoes size for your character, like magic

    10. Pick by UID is a preferred pick method.

    instead of going though the whole data structure of object to compare variables to pick one object, I think in the engine objects are stored in a javascript map() so it's like, a dictionary, but the object key is the UID, so you can just pick the correct object by key instead of looking though all of them because it'S like, indexed or something something probably

    11. Using pins created big slowdowns for you, this one was surprising to me. What is your take on the set as a child manner of positioning vs set position vs Pin? (https://www.construct.net/en/forum/construct-2/general-discussion-17/pin-behavior-set-position-72028)

    Honestly I kinda just have a bias and dislike most behaviors because you can'T control when the "change" the behavior does happens, and you have to like, test it, and work around it, and it'S really annoying, so, set position baybee!!! (also yes when i was like, doing performance tests with the project example, "quadisperf" or whatchumacall it, pin was causing major slowdowns for some reason when creating and deleting objects, like if something is offscreen, the engine will keep pining it and calculating it'S quad thing and like, i don'T want that

    12. You mention using events in favor of behaviors however: construct.net/en/forum/construct-3/general-discussion-7/picking-performance-132709 (scrolling down a bit refers to behaviors running faster than events due to the overhead, nonetheless I think I understand your point, behaviors can sometimes be general purpose, with a portion of its cost not being utilized and thus essentially serving as a form of overhead. So perhaps this is a case-by-case basis.

    if you don'T do picking and structure your project right, it's like, very fast, you'll be suprised.

    13. Pick by evaluation is one of your preferred methods of picking, I used this quite often in a previous project, but I came across a post (cannot find it now) that mentioned directly checking an instance variable through the object is faster than checks through the system (compare two values, pick by comparison, evaluate), now this does not consider the benefits of ternary operators, so this might be moot.

    sure but you can checks multiple variables in a single shot, while the instance is being accessed something something I don't know what im talking about so like, it's much faster than multiple "maybe faster" sequential directly checking variable picking

    General questions for you:

    1) What is your take on physics? I tend to disable most collision-based physics and apply physics forces on collision instead, perhaps it is better to work with a more custom system for physics like outcomes?

    I'm not messing around with physics, I don'T have math education and that's like, way above me

    2) What is your current main bottleneck in performance? (I have watched gameplay of your project, several sprites running around, the main thing that impresses me is the multiple accessories that are also present on the characters, I have made a project with this feature in the past and that tended to be one of the major consumptions of CPU.)

    the most single biggest bottleneck is Zordering and instance counts, the engine does not like to have a ton of sprites being used and z ordered at the same time

    3) Is your target FPS 60? If so, how many characters are you able to have running around until you start to drop from that target.

    I made this super fancy schmancy fixed timestep thingy where the game runs internally at 30 FPS ( could be lower) but the gameplay is smooth as hell even at 240hz refresh rate because separate from the game tick, i have a rendering tick that runs on every tick, and makes movement buttery smooth and it'S super simple and i'm super proud of it ( it hasn't been shown to the public yet)

    It has to be done this way because i t's like online multiplayer, so 1 frame of data has to be the same for everyone, you can'T multiply speed by deltatime, because you get different results, like, if the game hangs for a second, your character is just going to fall through the floor or walk through walls because Deltatime just becomes so big

    4) What is true scale pixel size of your characters? (16 x 16 for example)

    I have no idea what this means. THey take the space that they take?? Like, we live in the future, we don'T have to abide by retro rules, pixel art isn'T retro, it'S a stylistic choice ! If for one animation frame your character is 100 pixels big and does a big explosion punch, then she is 100 pixels big !! BIG LAZERS !!! I don'T care !!! do whatever you want!!!

    I hope that helps. Join my discord or whatever, support my game, buy my assets on the asset store, bla bla bla. I NEED TO EAT !!! I AM A HUMAN BEAN!!!

    construct.net/en/game-assets/game-templates/pixel-perfect-resolution-554

  • Thank you for the explanations Eleanor. 👍

    I don't know what this means. What kind of data needs to be sorted?

    As for the array sorting: I was referring to if you need to pick the highest or lowest value in the data series, or loop from the highest to the lowest/lowest to highest.

  • if you want the highest you don'T sort, you store 0 in N, go through your whole list of object, if object.var is greater than N, set N to object.var, optionally save object UID, if you want to use that object for something, keep iterating

    Optionally, if object.var is static, only compare when object.var is created, each time it'S created, or, check every time an object.var changes, if not every object.var changes every frame, you can like, test your own performance and pick the best way to do it

  • Try Construct 3

    Develop games in your browser. Powerful, performant & highly capable.

    Try Now Construct 3 users don't see these ads
  • Don'T take anything I'Ve just said at face value, run your own tests, verify the numbers, find out for which case, what, has the best performance, that gets you more experience with using the engine like this, and get a better understanding of what im saying and then *slaps you in the back* you'll be good to go

  • Well said.

  • Some recommendations for cpu:

    Any event that can be triggered, should be triggered.

    Avoid running anything every tick, even though it seems like it might need to. For example, setting something to the mouse position every tick. This can be optimized by setting it to the mouse position only when the mouse moved from it's previous position instead.

    Narrow the scope of every event as much as possible, especially in loops or for each element, to act upon only what it needs to act upon.

    As for memory usage, that memory article scirra.com/blog/112/remember-not-to-waste-your-memory covers it pretty well. Generally speaking you don't want to even get near the limit of available vram of your target system. If you need to exceed that amount, use layouts to break up and manage memory usage.

  • Thanks for the input oosyrag.

  • Some recommendations for cpu:

    Any event that can be triggered, should be triggered.

    Avoid running anything every tick, even though it seems like it might need to. For example, setting something to the mouse position every tick. This can be optimized by setting it to the mouse position only when the mouse moved from it's previous position instead.

    This seems like general good advice but I'm not sure if I'm doing it enough or not.

    For instance with your mouse example, it feels like checking whether the mouse has moved would be slower to figure out than just setting the object to mouse position? Or barely a difference?

  • For instance with your mouse example, it feels like checking whether the mouse has moved would be slower to figure out than just setting the object to mouse position? Or barely a difference?

    That was an overly simplified example, and in that case there wouldn't really be a difference that mattered.

    For a more practical example, see this project I put together for another thread: dropbox.com/s/v56jw51ktwd70mj/bfsfloodfillexample.c3p

    If event 2 ran every tick (following the mouse, since its a highlight) with the heavy flood fill algorithm behind it, it would be significantly inferior to only running upon changing tiles.

    Basically I try to avoid running actions at all if not required (they won't actually do anything). If it's just one action, then whatever. But if its more, it can make a massive difference. It's very easy

    normally to overlook this, because of the fact that there's no discernable difference between "Event runs, nothing changes" and "Event doesn't run, nothing changes" (unless you start losing significant framerate, as would be the case in the flood fill example). But in the first scenario, the cpu could be doing x amount of work in the background, even though there are no visible changes.

  • Honestly, the best performance advice is: measure performance, and only change things that improve the measurements.

  • Yes, this is true

    but

    to get a result like this

    Subscribe to Construct videos now

    I had to fundamentally change my event structure to what I described, and it took years of measuring performance and experimenting.

    If I can spare someone else those years

    then that'S good

    I wish someone else would have done it for me

    So I'm doing it

Jump to:
Active Users
There are 1 visitors browsing this topic (0 users and 1 guests)