• 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.

Unit Group Loops; which function wins the fight?

Status
Not open for further replies.
W H O --- W O U L D --- W I N --- I N --- F I G H T ?

Contestants:
gt_UnitLoops_Func vs gt_UnitLoops2_Func

JASS:
// WHO WOULD WIN IN FIGHT?

bool gt_UnitLoops_Func (bool testConds, bool runActions) {
    unitgroup   lv_ug = UnitGroupEmpty();
    unit        lv_u;
    // We shall pretend we're using a valid unit group that
    // actually has units in it.

    UnitGroupLoopBegin(lv_ug);
    while (!UnitGroupLoopDone()) {
        lv_u        = UnitGroupLoopCurrent();

        // Things we do with our unit go here.

        UnitGroupLoopStep();
    }
    UnitGroupLoopEnd();

    return true;
}

bool gt_UnitLoops2_Func (bool testConds, bool runActions) {
    unitgroup   lv_ug = UnitGroupEmpty();
    int         lv_i  = 1;
    unit        lv_u;
    // We shall pretend we're using a valid unit group that
    // actually has units in it.
    
    while(lv_u != null) {
        UnitGroupUnit(lv_ug, lv_i);
        
        // Things we do with our unit go here.
        
        lv_i += 1;
    }

    return true;
}
(which one is more efficient / better?)
 
Level 11
Joined
Jul 20, 2004
Messages
2,760
I ran a very simple test for you to show up the actual results:

1) 76 random units placed on the map and all stored in a Unit Group at Map Initialization.
2) Global variable keeps track of iterations (multiple of 10 starting with a=10 for the first function, b=10 for the second).
3) Triggering the execution by simple keyboard events (A key for the first, B key for the second).
4) Displaying the total number of iterations at the beginning of the trigger.
5) Increasing iteration variables by 10 at the end of the trigger.

The body of the functions is described below (I have fixed the second one for you - you forgot to place the result of UnitGroupUnit in the local variable u).

JASS:
lv_i = 1;
while (lv_i <= gv_a)
{
   lv_j = 1;
   while (lv_j <= gv_a)
   {
    // We shall pretend we're using a valid unit group that
    // actually has units in it.

    UnitGroupLoopBegin(gv_g);
    while (!UnitGroupLoopDone()) {
        lv_u = UnitGroupLoopCurrent();

        // Things we do with our unit go here.

        UnitGroupLoopStep();
    }
    UnitGroupLoopEnd();
    lv_j = lv_j + 1;
   }
   lv_i = lv_i + 1;
}

JASS:
lv_i = 1;
while (lv_i <= gv_b)
{
   lv_j = 1;
   lv_k = 1;
   while (lv_j <= gv_b)
   {
     // We shall pretend we're using a valid unit group that
     // actually has units in it.
    
    lv_u = UnitGroupUnit(gv_g, lv_k);
    while(lv_u != null) {
        lv_u = UnitGroupUnit(gv_g, lv_k);
        
        // Things we do with our unit go here.
        
        lv_k += 1;
    }
    lv_j = lv_j + 1;

   }
   lv_i = lv_i + 1;
}

The result of the test is as follows:
Trigger 1 timed out at A=120 (that is 14400 iterations).
Trigger 2 timed out at B=590 (that is 348100 iterations).

Conclusion
Function 2 is more than 20x faster than Function 1.


Reason behind the experiment results
Even though function calls are highly improved in StarCraft II as compared to Warcraft III, they are still time consuming! While the first code makes 4 function calls inside the loop, the second performs only one. It is evident that the access of a unit in a group given its index is quite fast - probably units are stored in a vector, accessible directly by the index.

So why would one even use the native functions provided by Blizzard in the first place? I suspect commodity - why attach an integer variable to every group when you can intrinsically use the native behavior? I can't figure out another reason why someone would use that approach...

I attach the map so you can check the experiment yourself.
 

Attachments

  • Test.SC2Map
    172.8 KB · Views: 159
Level 11
Joined
Jul 20, 2004
Messages
2,760
No, text message execution, but the message is displayed ONCE at the beginning of the trigger's execution (just to keep track of the iteration count), and in both cases (for both functions), so it is far too small to be relevant as compared to the number of iterations and it does not favor one piece of code or the other. There is no message display inside the loops, and executing the code for different number of iterations is done by a separate thread (hence the 'keyboard triggering' to launch a separate thread, and be able to control the experiment).

Keep in mind that the numbers are relative - the 'threshold' number of iterations is somewhere between 110x110 and 120x120 for the first, and 580x580 and 590x590 for the second. The order of magnitude does give us a pretty good idea about the difference, though.

Edit: this experiment does not test real execution time. It simply tests about how many iterations the thread can execute before it crashes. The order of magnitude speaks for itself: over 300.000 iterations as compared to 14.400 - I think the results are clear without the need of 'finer measurements'.
 
Status
Not open for further replies.
Top