Je Fawk's Forum Posts

  • The CPU profiler is great to see usage side by side, even though it might be wrong.

    When my game stutters on my M1 Mac that's when it needs optimizing, because if it has problems there, it will be worse on weaker computers.

    Hard to avoid looking at this thinking I'm testing wrong though:

    However Ashley you made some good points and provided interesting insight, thanks!

    > Ashley: So for maximum performance, avoid "for each" unless you really need it.

    Tips like this are super valuable. I think people really need something like a Construct Cookbook or CheatSheet that shows the best practices for doing things the right way.

    It's a good tip, for code that can go without it indeed. I first test without and then if it doesn't work after some changes around I sadly have to resort to For Each.

    I bet I could avoid it more often if I had more time to change things around more, but I need to balance finishing the project vs making it better and not having it published for some time.

    > ...in the case of "has tags", in one case you just compare a string which is a very simple and quick operation for a CPU. On the other hand "has tags" has to split the given string by spaces to extract individual tags, and then verify that all the provided individual tags are in the set of tags for the given instance. It's probably at least 10x as much work as just comparing a string. It's not that it's slow...It's just that you've used a feature which necessarily includes more complex steps. So if you make a benchmark that absolutely hammers that specific feature, you will probably see something like a 10x difference.

    I don't understand why it splits when it has 1 tag and no spaces. Feels like if there's a check "does it have any white spaces" it would not be so bad in performance for this scenario, but to be fair, I'd still use the instance var check.

    — just like Jase00 said, “This is a good example of understanding ‘under the hood’ a bit more so that we can make decisions on what to use and how to structure things.”

    It's what I've been saying all this time, every dev wants to understand more of what's happening under the hood so that we can avoid wasting time guess-testing.

  • Ashley thank you for the reply.

    TLDR

    It's acceptable to have different ways of doing 1 thing, and some being worse than others, but a lot of devs were surprised about very basic things, that if done differently result in high CPU gains, for example the Sprite.instancevar vs compare two values, or using an instance var rather than Tags if you have only 1 tag per sprite and not multiple: construct.net/en/forum/construct-3/general-discussion-7/stop-using-tags-tag-187031

    I would like to see more info in the official docs about these quirks. Also there are some useful blog posts from you that are very hard to find, if they are still relevant today.

    Real world projects

    We've been struggling with performance issues for a long time now, started with our multiplayer game Nightwalkers.io which could not have more than ~ 100 zombies on the screen. You could argue the architecture was wrong to begin with, which might be true, but also optimizations were always needed and took a lot of dev time to achieve, which did improve the game until a point.

    We're not making simple games, we're making very complex ones.

    This forum post started from the following issue

    This is CPU intensive

    This is not

    And these are my measurements, both the CPU profiler as well as my laptop's CPU usage. Can't get any more real than this I believe.

    CPU intensive: C3 CPU profile 19.3%, system CPU usage user 14%, Chrome 48.9%

    Not so CPU intensive: C3 CPU profile 5.8%, system CPU usage user 10%, Chrome 44%

    Bear in mind that I was not doing anything on the screen apart from having the panels opened, so most mechanics were on pause.

    Regarding the CPU power

    I don't think it's related to this in this case because people have tested with the same groups active at all times, and it shows differences.

    If the CPU would be throttled because it's not used at max, then all the groups would suffer and show reliable differences.

    BeatsByZann I'm sure he could provide the c3p without any addons if it would be helpful.

    He posted these screenshots:

    Also here alastair did the same: construct.net/en/forum/construct-3/general-discussion-7/stop-using-tags-tag-187031

    Construct 3 is very fast

    Also regarding how fast C3 is, I have to agree with you, this would have to be tested using real life projects because at the end of the day, that's what we make and that's what people play.

    Why make forum posts about small tests that might not appear in real projects?

    It would be ideal to publicly show what's wrong in our project but it's not realistic. So we make small projects in an attempt to replicate an already existing theory. And we're very happy that other devs pitched in on this, it was a very useful community-driven test. As far as I understood, a lot of devs gained some valuable knowledge from these.

  • thanks for testing it out!

    Also can do without disabling groups as you can compare each Group's performance in the CPU tab while debugger is open:

    True, but I don't trust cross comparison on the same run of a project for some reason 😁, so I prefer to run each group separate.

  • Try Construct 3

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

    Try Now Construct 3 users don't see these ads
  • R0J0hound Great stuff, thank you for sharing!

    Would have been great to see Compare two values as well if you have the chance.

    Sprite.a = 1

    Sprite.b = 0

  • Thanks to this thread, I will be hunting down those "for each" blocks, seeing if I do indeed have some subevents with a quick "Sprite Inst Var = 5", and change this to "Evaluate Expression", measure, see how much improvement is gained.

    Is evaluate expression any better than compare two values? I never use the valuate, always use the compare.

  • My slight fear of a reply would be "measure" or "would this affect real world scenarios", but I feel the difference here is:

    I don't think this is an unusual or fake case. The issue came up while optimizing a project I've been working on for months. Since I can't share that project publicly, I made a smaller example. The numbers may not match a typical project, but they help isolate and show the problem, problem that I'm facing now.

    I think most people will understand the need for abstraction here.

  • If you are using Object has tags when you use only one tag all the time, you'd better reconsider using some instance variable instead.

    The CPU test shows massive changes in the CPU profile per groups. (tested with r449.2)

    Try it yourself, disable all the groups and enable them 1 by 1, then run the project and check the CPU profile: OneDrive

  • BTW fun fact, if you set opacity to a decimal number, I recall it eating CPU far more. Makes sense in some ways, C3 gotta convert it to int, but I recall it was a measurable different in cpu. Worth noting if you make your own opacity fading systems, better to wrap your opacity result with int() instead.

    Very interesting, thanks for sharing!

  • I think I've found the Never-Ending Fountain of Knowledge. The more combinations you try which achieve the same thing, the more random results you get.

    Also a real world example of attempting to use these optimizations ended up in a 5% CPU decrease for my tooltip system! (Not 5% overall, but from the CPU profile)

  • Checking for visibility before collision wastes CPU power. Collision detection already handles visibility efficiently, so extra checks slow performance. Make sure collision systems do their job by not over-optimizing.

    Collision detection has NOTHING to do with visibility. What are you talking about?

  • Sad to see your work went to waste 😢

  • It's ridiculous that we have to do such intense testing to figure out which way to write code in C3 when, visually, every version that dop2000 wrote made sense.

    I agree there can be some small changes in resource usage but from 10% to 60% is completely insane.

    Now you can argue that "well there are a lot of objects" but if a project has 90% less objects but more code (complex UI, combat, quests, NPCs, etc) writing it in a "bad" (???) way can lead to say 1% CPU increase for no other reason than checking if Sprite.X instance var = something, versus Comparing if Sprite.X = something.

    And then we have projects that look like this:

    So imagine how that small 1% can stack up with the amount of code we had to write.

    We enjoy the easy entry in C3 but mastering it is a whole new level. And while we can test for every small thing that we need to implement, it kinda starts to waste our time when ridiculous bugs like these (10%-60%) appear.

    I remember something similar when a Tween that was disabled was still causing havoc. fedca reported it and it got fixed. Can only hope for something similar here, but then it begs the question: what other hidden CPU hogs are there in C3?

    Would be interesting to hear Ashley 's opinion on this.

  • I figure that setting opacity is just setting a number so it’s light to set it twice. Else does a bit more than just run if the previous event was false. I tend to only use it in simple cases like you did since how it behaves with more complex picking isn’t entirely intuitive to me. However, it may be mostly a personal preference to do things in as few events as possible.

    I was almost going to write out some pseudocode for all that the events are functionally doing under the hood but I’m not sure that would be useful. I’ve only perused the source for the picking system in the C2 days, but mostly regard it as a black box so I don’t know all that’s going on other than basic behavior.

    Thanks for explaining :)

  • igortyhon thank you for the testing. My point was to first filter all the objects and then do the loop with the check, to make it run better.

    Obviously it didn't work, hence why I asked if people know the reason.

  • Thank you guys for your feedback!

    skymen also mentioned that putting a check before a collision removes the collision cell optimizations, with BeatsByZann that did some testing on this.

    I also thought, as dop2000 , that filtering before a loop is more efficient because it makes the SOL much smaller and that loops are very resource intensive.

    R0J0hound great ideas, thank you! But why skip the Else though? I always use it because I think an else is always cheaper than doing something on the same tick again, just to be replaced. In your example you set the opacity to 33 each time, and if it's overlapping it gets replaced from 33 to 100 (basically setting the opacity on the same sprite twice in that tick).

    It's less code and it looks cleaner that's for sure.