Jump to content
A 2021 backup has been restored. Forums are closed and work in progress. Join our Discord server for more updates! ×
SoaH City Message Board

MMF2/CF2.5 Optimization Superthread


Recommended Posts

Something else worth mentioning is that in general, you should name things as minimally as possible, ESPECIALLY fastloops. The longer the string, the slower your game will be. I have found that converting loop names from 12 character to 3 character strings that run on average 150 times per frame increases performance by 20%...

.............

Link to comment
Share on other sites

The longer the string, the slower your game will be. I have found that converting loop names from 12 character to 3 character strings that run on average 150 times per frame increases performance by 20%...

Uhh......... Who names them with sentences? I use CamelCase abbreviations if they're longer than 6 characters anyway.

Link to comment
Share on other sites

@TailsSena: I personally have a tendency to name things in meaningful ways that make it easier for others to see my code and know what's going on. For example:

iterate.[objectname]

"iterate." is 8 characters already, and object names consisting of 4 characters or more is in no way uncommon. If Clickteam was smart and converted the names into unique identifiers during build time, this wouldn't be a problem whatsoever. But alas...

Edit:

I figured it might be helpful if I uploaded my version of DW's original optimization example. This has a few more methods including the ForEach group iteration technique as well as an optimized method of handling arrays for even more performance from the generic pick method (only reason it's slightly faster than the ForEach technique).

http://larkss.thisisourcorner.net/files/optimization.zip

Edit2:

I've updated my family system for those who may have downloaded it for the family technique rather than to see how the optimized process can be used. It builds family trees considerably faster now and also fixes an issue with porting. (MMF2 sucks with qualifiers/event order, and I had to split an event in two to make sure it works properly in different applications.) I also updated the notes to point out something important that must be done or else your family tree will not start construction.

http://larkss.thisisourcorner.net/files/FamiliesV2.zip

Link to comment
Share on other sites

I just thought I'd verify that yes, fastloop management in MMF2 is horrible and you can see huge performance improvements if you stuff your loops into groups and only activate the group for the specific loop you want to execute. Ridiculous. Something else worth mentioning is that in general, you should name things as minimally as possible, ESPECIALLY fastloops. The longer the string, the slower your game will be. I have found that converting loop names from 12 character to 3 character strings that run on average 150 times per frame increases performance by 20%...

That is hilarious. I guess Clickteam has never heard of a hash table.

Although it's worth noting that string hashes could still have to be built every time the event is run, slowing performance with larger strings.

Link to comment
Share on other sites

  • 2 months later...

Sorry for reviving this somewhat old topic, but can slowdowns happen because of rendering instead of non-optimized code?

I mean, I've had quite some problems with slowdowns with Sonic Medley. Although I can't really reproduce them by myself, I've had at least two persons complaining about it. What's more strange is that some people seemingly have problems running a Direct 3D 9 display build, which somehow causes a massive slowdown, but can run a Standard display one perfectly fine, while for others it's the opposite.

I tried optimizing some of the code (such as the original rings events of Worlds Delta and my gimmicks events that used loops) with techniques such as putting the events of the loop into groups, then opening the group, running the loop and closing the group when needed, and also using the ForEach extension to iterate through objects instances when possible instead of Spread Values... but it didn't help.

Link to comment
Share on other sites

Direct 3D 8 gives me around 40 FPS... D3D9 gives me 60 most of the time and Standard is more or less the same, but with occasional slowdowns. Everything changes from one computer to another though.

Okay, now I'M SURE this has something to do with rendering, not with code. Maybe it's because I'm building it with an old HWA build (249 Beta, to be exact)?

Link to comment
Share on other sites

Please don't discuss piracy on the forums, even if you are not discussing you methods of acquiring a pirated copy. If you use MMF2 a lot you should look into obtaining a legit copy; you do not need the expensive Developer version to fangame.

In regards to the rendering problem, I have seen my share of odd issues. For me having vsync on is usually the major cause, but for others they just need to update their DirectX runtime. Also, updating your HWA build is very important due to Yves doing lots of HWA optimizations and compatibility fixes.

Link to comment
Share on other sites

Er... sorry about that. I didn't mean to cause problems or break the forum rules...

I think I'll try activating/deactivating vsync by now, and tell people to update their DirectX runtime.

Edit: Yeah, it looks like it was really Vsync causing all those issues after all this time. After I deactivated it, the problem seems to be gone, D3D8 and Standard are running smoothly and I can achieve up to 180 FPS in this computer if I remove the 60 FPS limit. Looks like MMF2 doesn't like Vsync for some strange reason...

Link to comment
Share on other sites

Tell me about it. Vsync naturally caps to the screen refresh rate (its function after-all is to sync with the vertical refresh of the display) but in MMF2 it caps the framerate to 30 or lower on some machines which is out-right terrible. HOWEVER on other machines it fixes a frame-drop issue providing a gorgeously smooth 60 frames per second. I wish they would have fixed their vsync code by now, but alas...

Link to comment
Share on other sites

It's more or less like you said. In this computer, it tends to cap the speed at 60 FPS, but it is not perfect and causes some occasional slowdowns, especially in D3D8 mode. Deactivating Vsync makes everything run smoothly, without those occasional slowdowns.

However, on some other machines (normally the slower ones), it caps FPS around 30-40 with it activated, but runs smoothly with it deactivated. On faster machines, nothing or almost nothing changes. It's almost like it's using the speed of the machine to cap the FPS instead of the screen refresh rate.

Link to comment
Share on other sites

I just wanted to chime in here really quick with a random tidbit. I cannot emphasize enough how expensive collision checks are compared to just about anything else, especially if you're using the "Handle background collisions even out of window" option. I'm embarrassed that I didn't see/realize the magnitude of this sooner than I did.

To use an example from Emerald Ties. I just found that a major cause to our performance woes was performing collision checks for item box movement as the first action in the event condition list.

Our conditions originally looked something like this:

+ Item Box is overlapping backdrop

+ Internal flag 0 is on

+ Internal flag 1 is off

+ (NEGATE) X("Item Box"), Y Top("Item Box") is an obstacle

We had a bunch of events like this helping to control hitting item boxes from underneath to make them fall to the ground (controlling gravity of the box, stopping on the ground, etc). By rearranging the condition list to the following I was able to increase the performance of the game considerably:

+ Internal flag 0 is on

+ Internal flag 1 is off

+ Item Box is overlapping backdrop

+ (NEGATE) X("Item Box"), Y Top("Item Box") is an obstacle

Obviously the magnitude of a change like this is directly dependent on how many of the objects being checked are in the frame. With something that's plentiful, like item boxes, this becomes an issue rather quickly. To measure the impact I changed the frame rate in MMF to 250. Before adding this change of ordering, my computer averaged 155FPS (sounds high, but my computer is a beast). After making the above change, I saw an increase to an average of 206FPS. That is huge.

TLDR: Whenever possible, perform checks for collisions/against the collision mask as late in your event list as possible especially if you're using "Handle background collisions even out of window". Use other means to narrow down the object selection process first.

I realize that maybe most here probably already knew this, however maybe this will prove useful to someone coming to this topic for the first time.

Link to comment
Share on other sites

Good information Gamerdude, that I rarely see people worrying about when coding things in MMF2. Just like with standard programming languages that support lazy condition checking (as in only test what is needed), MMF2 also stops checking conditions if one turns out to be false. As you stated, collision checks are very expensive, ESPECIALLY if you use the collision mask pixel test. You generally want to keep those conditions last to avoid them if possible, but there is a significance to the rest of the condition order too. Any condition that you know will me false more often than another should be on top as that will falsify the event more often for quicker processing. Take this for example:

If Ground = 1;

If SpeedX <> 0;

::Do stuff::

That condition order is best because there is roughly a 50-50 chance in a standard platformer that you will be on the ground, but it is EXTREMELY likely that you will actually be moving. I also find that it is good practice to keep input conditions on the very top.

@Gamerdude (specifically): If you really want to optimize your performance I'd look into getting rid of the need for out-of-view collision checks. If you put all objects that use BG collision into a common qualifier, you can toggle a flag based on whether they are within camera view or not. Then, you can disable movements if an object is out-of-view. For moving badniks you can disable slope handling if they are out-of-view and do a quick snap-on routine as soon as they are near again so you don't see them glitching in any way. It may not be a very convenient change but you should see large performance gains on older machines.

Something else I'd recommend (that you may already do) is to disable fine collisions for any BG or object that doesn't need per-pixel collision testing. MMF2 does not determine if a BG/object is 100% solid and wastefully does a per-pixel collision check if you kept fine collisions on. Disabling this when possible is very beneficial if you want to keep collisions out-of-view active.

Link to comment
Share on other sites

  • 2 weeks later...

Bump for another round of MMF2 optimization tips.

First up is multi-collision handling. For this we'll point our focus on ring collection. Sometimes you have a situation where the player perfectly collects 2 (or more) rings at exactly the same time. In many engines this causes both rings to be collected but only 1 ring is added to the ring counter. Why does this happen?

This is because of how MMF2 processes events. When you check if the player overlaps a ring, MMF2 processes this event once. It selects all overlapping rings, sends the destroy command to them, and adds 1 to your ring counter. Just once, so you only get 1 ring. So how do we fix this?

Until now my logic was to do a quick iteration when you touch a ring to check all rings and one-at-a-time collect any that are overlapping. This works, but I never liked doing it given that there are usually hundreds of rings in one level, so that's not efficient in the slightest. I've found out however that Overlapping vs Collision has a very important difference!

Overlapping will select all colliding objects, and continue selecting them every frame. Collision will select all colliding objects, but only do it once per collision. This seemed odd, and I found out that it's because it's meant to be used in a specific way; an interrupt event. If you place the Collision condition on TOP of the event, it becomes red signifying that logic will be interrupted when a collision occurs. MMF2 will process the collision for all colliding object pairs one at a time! So use "Collision between Player and Ring", have it as the first condition (it should turn red), and enjoy your now properly working ring collection.

------------

Second topic relates to objects which trigger with several objects at once. For example, if you hit two ring monitors simultaneously, the icons will float up and expire (an internal value hits a certain peak), in which you should get rings from both. However, many of you will know that in most fangame engines both icons disappear but you only obtain 10 rings. How do we fix this without the logic of the Collision event?

Well the fact that MMF2 destroys all icons is a good thing; this means it is properly selecting everything at once, just processing the event once. This would be fine if we could see how many objects MMF2 has selected. Thanks to the ForEach extension we can! The ForEach's grouping feature allows you to count how many objects exist in the group, and since it adds the selected list of objects into the group for you, you can utilize it like this:

ProperIconHandling.png

And that's it! No iteration, no extra events. You throw the selection into a group, use the object count, trash the group. You're done. Now go get yourself a congratulatory soda.

------------

Final topic relates to multi-object object creation. If you have made a badnik before, you may already know where I'm getting to. I'm going to point my finger at Crabmeat for this. He wobbles around, sees you, then fires two bullets like the little jerk he is. But uh-oh, why is it that when two Crabmeats try to fire simultaneously, only one actually fires? Answer: I have no idea. In my experience, shit hits the floor once you try to spawn an object on top of another, given that the event used conditions to select specific objects. Iteration is a quick fix, but come on, that's never a nice thing to do in MMF2. Is there a better way to solve this?

ForEach's grouping feature comes to the rescue again. Remember, the point of the grouping feature is to iterate through the group. Also remember that the grouping feature will add in the entire object selection automatically. Combine the two and the method is simple; use the group to select your objects and iterate them directly! If you use your trigger event to add the Crabmeats to a private group, iterate the group, and then trash the group, you have successfully made them shoot properly while not iterating through any Crabmeats that didn't need to actually fire at the time. Because of the direct selection, you don't even need extra conditions for the iteration events!

And that's it for today! The following optimization techniques among other approaches will be featured in Delta 1.3, which is due any day now (given I stop finding new bugs to fix). If anything I've said above doesn't make sense, wait until the release as my examples here are the same situations I applied them to in Delta. :P

Link to comment
Share on other sites

Sure. I've done some personal tests (the difference is notable) but I can whip something up for you guys to see upfront. The second technique I described doesn't need representation though as it's obvious why it's superior. I won't put up any tests however until Delta 1.3 is done, which I'm currently improving non-stop. *sees it's 9AM; decides it's time to sleep*

Edit:

Families V3 is now available.

-Fixed a major iteration bug that would ignore children in specific scenarios.

-Adjusted for compatibility with Sonic Worlds Delta.

-New parameters: RotAllow and RotQuality. These can be set per-component.

-Small optimizations, more comments, and code cleanup.

I will be including an example of its usage with a badnik in Delta 1.3.

Edit2:

Families V3.1 is now available.

-Added floating point precision for position handling.

-Logically jumps to the next family on completing a tree (significant optimization).

-Notes file cleanup. (Make sure you read this!)

Link to comment
Share on other sites

  • 7 months later...

I've been told on the Clickteam forums that when building a game specifically for one of the exporters (Flash, Android, etc), that I should minimize the use of fastloops as much as possible. I'm pretty sure I've come across fastloops in the worlds engine that are used as function calls (i.e. they're run a single time), and in this case, I wonder if I can substitute them for something more efficient.

I don't want to use any third-party extensions for calling functions if I can avoid it, although the ForEach object is compatible with a decent number of runtimes, so I may use it if necessary.

Link to comment
Share on other sites

I've been told on the Clickteam forums that when building a game specifically for one of the exporters (Flash, Android, etc), that I should minimize the use of fastloops as much as possible. I'm pretty sure I've come across fastloops in the worlds engine that are used as function calls (i.e. they're run a single time), and in this case, I wonder if I can substitute them for something more efficient.

I don't want to use any third-party extensions for calling functions if I can avoid it, although the ForEach object is compatible with a decent number of runtimes, so I may use it if necessary.

Can't you just use a kind of... Miscellaneous Values Object to store values and flags, so that you can set those off and react to them, instead of using Fastloops? Fastloops are more reliable in faster games, but Flags and Fixed Values should do the trick, right?

Link to comment
Share on other sites

You have to use something to actually call your subroutine if you want to be able to reuse code. If you don't reuse code, you end up with a horrible burden to maintain. I imagine you can probably ignore the fast loop advice for single time uses. They are probably more referring to bad performance of the flash exporter module in general which makes loops that execute multiple times a burden. On the other hand, I've been wrong about fast loops before, and I could be wrong this time as well.

Link to comment
Share on other sites

  • 8 months later...

Greetings MMF fans.  I recently picked up CF2.5 because I was excited about a new feature for doing much of the stuff I've been exploring in this thread.  Today I've just finished some rudimentary performance tests.

 

The test is pretty simplistic in nature.  Every frame, a loop is ran on every object in the simulation twenty times. The event itself just sets the currently selected object's x position to a random value... hopefully that particular action choice doesn't impact performance too signifcantly.

 

The two methods I tested were the following:

 

Iterating over Arrays of fixed values (works in MMF2 and CF2.5)

For Each Object loops (CF2.5 only)

 

Performance comparison is simple enough, we just see how the frame rates fall under certain numbers of objects.

 

Iterating over array of fixed values:

MMF2 performance - falls to 30 fps (50%) at 1450 objects.  Falls to 15 fps (25%) at 2100 objects.

CF2.5 performance - falls to 30 fps (50%) at 1450 objects.  Falls to 15 fps (25%) at 2100 objects.

 

OK, so this test suggests a couple things.  First, Performance since performance is the same, it doesn't look like CF2.5's generally faster loops are providing a whole lot of gains in here.  The performance appears to be falling off slightly faster than linearly... I would guess O(n * Log(n)) because finding the object by fixed value might involve a binary search against the full object array.

 

Iterating using CF2.5 for each object loops:

CF2.5 performance - falls to 30 fps (50%) at 18000 objects. Can't check 25% performance since the object ceiling is 20000

for reference sake, I'm still able to hit 60 fps (100%) with 9000-9200 objects before frame rates take even a single fps dive, so this suggests that the relationship is closer to linear.

 

Also, I guess it's worth noting my processor is an i74770k running at stock clockrate since this is a processor bound operation... still, the actual ratios should be about the same on any processor.

 

So yeah, that's a shitload of difference and it works better with larger object counts too. I'm currently considering writing some more interesting tech demos, possibly something that demonstrates nested for each object loops, but right now my focus is elsewhere.  But I'm going to go ahead and recommend CF2.5 to anyone still working in MMF2 on this basis alone.

 

Attached are the mfa files for each test as well as executables in a zip for running the tests in case you don't have cf2.5... though without debug information, it's hard to tell how many objects you have.  Press '+' to add objects to the simulation.

cf25_iterator_test.mfa

mmf2_iterator_test.mfa

mmf2cf25_iterator_tests.zip

Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...