Perfomance Optimisation on Large Projects

So my thing is getting kinda large now and my biggest worry is that I can’t get it optimized properly, or that GDevelop actually doesn’t really support large projects in the first place. For reference the game now features dozens of enemies with complex behaviours, as well as a lot of event dealing with the environment and player behaviour, including events that manage the hundreds of distinct items the player can have.

Some stuff I’m wondering about:

  • How much does object count matter for performance? Can “for each object x, do” events be quantified in terms of how much more they impact performance?

  • What are the most taxing event actions/checks? To me it feels like constant distance checks and collisions seem to be high up. “Pick closest object” also seems to cause issues if used a lot simultaneously. Can someone elaborate on what event actions should best be used sparsely to improve performance.

  • How well does GDevelop deal with string checks? Does continuously checking for a string instead of a value impact performance significantly?

  • Do events for stuff that does not exist matter as far as performance is concerned? Right now the game takes place in one big combat scene, that links to events for each enemy type, terrain type and so on. If events that check for enemies that currently do not exist are linked, does that creates background processes that impact performance? If so how can one get around that? Can external events be unlinked again somehow?

  • Particles seem to cause a lot of performance stress, even though the game doesn’t create that many particles. From what I have learned, particles in Unity /Game-maker are more economic than using individual objects. Is that actually the case in GDevelop or might objects be more economic after all?

  • Conditions are checked in order of appearance within events right? If a condition of an event isn’t met, are subsequent conditions still checked fore? Can event checks be optimized by placing the most commonly false conditions first?

1 Like

Bump, I want to know all these questions too.

1 Like

Thanks for the bump, those are good questions and it’s a shame they were unanswered.

In normal events, each instruction (condition or action) that accepts an object as a parameter will be repeatedly executed for each currently picked instance. In a for each event, all instructions (even ones that do not use objects) are called for each instance of an object.

That’s kind of hard to tell, the best way is to look at used objects, then think about the instruction and try to imagine how it could be implemented.

For example, the “Mouse released” event must be rather simple, it does not use any object so it is not being repeatedly executed, and internally it must simply perform a check against a boolean value (the button can either be released or not), one of the (if not the) most basic operations a computer can make. Therefore, this condition is very very fast to execute.

On the other hand, a collision check uses two objects, so the times the condition is run is multiplied once by the number of instances of the first object, and again by the number of instances of the second object. Additionally, each check needs to be made for every polygon of the collision mask of the objects, so it gets multiplied again if you use masks with multiple polygons. And finally, it is hard to think of a trivial implementation, such a condition will have to use some complex maths. All those factors allow guessing that the condition must be very bad for performance.

In addition to that, it’s always good to run some benchmarking by putting some test events inside of groups and looking in the profiler which are the shortest to execute.

Since this question was asked before GDevelop 5, I will first answer in regards to GDevelop 4’s native platform:

Most of the time, yes. If two strings are not of the same length, the comparison will check after comparing length and the performance will therefore equal comparing two numbers. If they are all of the same length, then it will have to check character by character for differences, so it will add one check for each same character. If the strings are equal, it will still take about the number of characters of the string times more time than number comparisons, since the check has to run for each character before equality is certain.

For example, "state_a" and "state_b" will usually take about 7 times more time to run than a number comparison, since they have the same length (amount of characters, 1st operation) and then will check the first 6 characters ("state_"), which equals to one number comparison each, before finding the difference between the two strings.
If you have strings "walk" and "running", it will have the same performance as checking two numbers, as their length differ.
If you compare “hello, my friend :)” with another occurrence of that string, it will take 20 times as much as a number comparison, as it has to check length, then every 19 characters of the string.

In GDevelop 5 and the JavaScript platform generally, though, there is an additional optimization (that is not guaranteed but practically present in pretty much all JavaScript compilers): interning. This allows to make most checks as long as a number comparison, so you should be fine with strings there most of the time.

External events are basically copy-pasted in place of link events at compilation time, so they cannot be “unlinked”, but you can disable all of its events by putting the link event under an event that checks for a variable that you can modify to toggle the execution of the external events on and off.

Not really. If your events use conditions on the unexisting objects, the condition will not even run and directly return as not met, not running the rest. If you are using some events without object conditions that are not composed exclusively of object actions (object instruction == an instruction that takes an object as a parameter), then the free actions (actions that do not take an object as a parameter) may still run. In that case or if you want to be extra sure, you can add a parent event to all of your events for an object comparing the number of instances of the scene to make sure there is at least 1 before executing its events.

Exactly :slight_smile:. Always put free conditions, performant conditions, and conditions that are likely to be false first. An unmet condition will always skip over the next conditions, actions, and subevents.
Also use subevents to run conditions only once instead of multiple times, like this:
image image

I sadly cannot really answer that one as I lack knowledge there, though I assume that particles are more performant than sprites if you do the same operations on a sprite than on the particle, like, particles have pretty much all their properties changed every frame (opacity, position, existence, color, …), so sprites can be more performant if you do not do all of this. But then again, I have no real knowledge there, so take this with a grain of salt.

6 Likes

Thank you so much, this information is very valuable to me. I only noticed the reply now because of no notifications, but know that I appreciate taking the time to answer those questions.

1 Like