• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

Efficiency question: 1 trigger for all custom abilities?

Status
Not open for further replies.
Level 14
Joined
Aug 30, 2004
Messages
909
I'm updating my Battle for Tatooine map. I'd love to improve the efficiency of the coding. I heard that sometimes it is better to have 1 trigger for each event. I'm curious if this is true. I'll be very specific; which of the following two options is more efficient (in terms of avoiding lag, not in terms of the programer's time):

Option #1:
  • Slip
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • Or - Any (Conditions) are true
        • Conditions
          • (Ability being cast) Equal to Slip Left
          • (Ability being cast) Equal to Slip Right
    • Actions
      • Set tempUnit = (Triggering unit)
      • Set tempInteger = (Custom value of tempUnit)
      • // blah blah... lots of actions specific to this trigger
  • Barrel Roll Cast
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Barrel Roll
    • Actions
      • Set tempUnit = (Triggering unit)
      • Set tempInteger = (Custom value of tempUnit)
      • //blah blah...lots of actions specific to this trigger
OR - removing the events from those triggers and using one global ability trigger:

Option #2:
  • Ultimate Ability Trigger
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
    • Actions
      • Set tempUnit = (Triggering unit)
      • Set tempInteger = (Custom value of tempUnit)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Or - Any (Conditions) are true
            • Conditions
              • (Ability being cast) Equal to Slip Left
              • (Ability being cast) Equal to Slip Right
        • Then - Actions
          • Trigger - Run Slip <gen> (checking conditions)
        • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Ability being cast) Equal to Barrel Roll
            • Then - Actions
              • Trigger - Run Barrel Roll Cast <gen> (checking conditions)
            • Else - Actions

And so on...

By the way, if you have suggestions or requests for my map, please let me know here:

http://www.hiveworkshop.com/forums/map-development-202/battle-tatooine-update-255342/#post2563841
 
In general, it is better to use option 1. If you think about it, option 1 is direct whereas option 2 follows a queue.

Let's say you have spells A, B, C, D, E and triggers for them. And E is cast.

Option 1:
A fires -> spell A cast? -> no
B fires -> spell B cast? -> no
C fires -> spell C cast? -> no
D fires -> spell D cast? -> no
E fires -> spell E cast? -> yes -> fire actions

Option 2:
Trigger fires -> spell A cast? -> no -> spell B cast? -> no -> spell C cast? -> no -> spell D cast? -> no -> spell E cast? -> yes -> fire trigger E

You'll end up with a large tree of if/then/elses for option 2. These two diagrams above may seem similar, but option 1's order is determined internally by wc3. Option 2's order is determined through your own GUI code, and custom code is almost always going to be slower than the internal wc3 code.

Technically, your option saves some handles from being created due to the event, but a few handles isn't going to make any difference memory wise. In JASS, we have a script that does something similar to option 2 so that every spell runs off of 1 event, but it uses a hashtable to determine which trigger should be ran (which is O(1) [direct], similar to wc3). But it is a little more work to implement in GUI, so it doesn't pay off as well.
 
In general, it is better to use option 1. If you think about it, option 1 is direct whereas option 2 follows a queue.

Let's say you have spells A, B, C, D, E and triggers for them. And E is cast.

Option 1:
A fires -> spell A cast? -> no
B fires -> spell B cast? -> no
C fires -> spell C cast? -> no
D fires -> spell D cast? -> no
E fires -> spell E cast? -> yes -> fire actions

Option 2:
Trigger fires -> spell A cast? -> no -> spell B cast? -> no -> spell C cast? -> no -> spell D cast? -> no -> spell E cast? -> yes -> fire trigger E

You'll end up with a large tree of if/then/elses for option 2. These two diagrams above may seem similar, but option 1's order is determined internally by wc3. Option 2's order is determined through your own GUI code, and custom code is almost always going to be slower than the internal wc3 code.

Technically, your option saves some handles from being created due to the event, but a few handles isn't going to make any difference memory wise. In JASS, we have a script that does something similar to option 2 so that every spell runs off of 1 event, but it uses a hashtable to determine which trigger should be ran (which is O(1) [direct], similar to wc3). But it is a little more work to implement in GUI, so it doesn't pay off as well.
I wonder why you suggest that. Option 3 is clearly the least taxing.

Option 1 spams events like crazy.
Option 2 has useless triggerconditions that could be part of the triggeraction.
Option 3 has an ineffective case selection structure, but as everything runs in the same thread (compared to a seperate triggercondition), option 3 is still by far the fastest, unless you have more than 20-30 spells. It also reduces overhead by not having multiple spell events.

However; my suggestion is: if you use GUI anyway, don't worry about such minor optimization and go for the best readability (Option 1). Or even better: use the time to learn JASS if you want to optimize your map script!

Do you think it's worth it for just 12 triggers?
Yes, but the speed increase will not be measurable by any means. Unless you don't have like 30-40 scripted spells, just use if-casing.
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,259
The solution is to abstract ability triggers from the actual event and instead attach them to a cast system which handles the actual event and forwards calls as appropriate to a handler trigger. In case data like TriggeringUnit and such are lost, you can just store them in globals and read those values in the ability triggers from those globals.

To keep performance high a hashtable has to be used to match the triggering ability ID to the appropriate handler function (in any) in a single high efficiency call. Yes that means that you can register several thousand trigger abilities to the system and it will find the appropriate one in O(1) time (as good as, some hashtable collisions may have occurred but this are mitigated natively so very efficiently).

This means that instead of doing this...
Ability Z cast -> match A failed
Ability Z cast -> match B failed
Ability Z cast -> match C failed
Ability Z cast -> match Z passed -> do stuff

It will do this...
Ability Z cast -> located ability Z handler -> do stuff

The result is something far more scalable as each additional triggered ability adds as good as no overhead.

An improvement to this is an "reachable" detection system which only initializes triggered abilities that can be cast. This means that trigger abilities will not be loaded for heroes no one choose to use. It also means that trigger abilities can be unloaded when a hero becomes no-longer reachable such as when a player drops. The definition of reachable is obviously map dependant but the result is you can avoid burdening the game with trigger handles and such for abilities that will not be used that session.
 
Level 14
Joined
Aug 30, 2004
Messages
909
To answer some questions:

I'm probably going to stick with just 12-15 triggered abilities. A few more will be "a player summons a unit" events. This is an action game where you fly a ship around, so the ship commands are the only real abilities. I have some custom heroes with new spells, but I don't turn those triggers on unless someone picks the hero.

Dr. Super Good, I'm afraid you've gone over my head again! Regardless, I'm just touching up a map I've already completed so I think I'll stick with option #1 for now.

Thanks everyone, + rep all around.
 
Status
Not open for further replies.
Top