Benchmark Benchmarks around dynamic arrays and hashtables

weaaddar

New Member
Reaction score
6
Hey all,
So its a little known fact but SaveAgentHandle fails on storing null in the hashtable. This totally sucks, but as a result I decided to collect some benchmarks.
There are 9 results here, #1 is the iteration penalty, quiet frankly its result may be ignored. The other 8 results are basically testing array set (the control in the group) vs hashtable set, vs safe hashtable set*, vs dynamic array.

Safe hashtable set is testing to see if the value trying to be stored is null as hashtables can't store null handles.

I'll compare the first 4 real results against array set, and the last 4 result against their array set.

I am using different parent keys in the hashtable in case there is something with hashtable nodes being used again that might give some speed gain and make the tests unfair for the next set.

The metric of testing is doing each action 10 times in a loop that is run 500 times, collecting that result and multiplying it by 100. Then after that, each set is tried 10 times and the result is averaged.
Comparison:
Iteration penalty
[LJASS]/* Do nothing */[/ljass]
vs
When u is not null
[LJASS]norm = u[/LJASS] vs [LJASS]SaveUnitHandle(ht,0,i,u)[/LJASS] vs [ljass]if(!SaveUnitHandle(ht,1,i,u)) RemoveSavedHandle(ht,1,i);[/ljass]vs [ljass]dynam = u;[/ljass]


when u is null
[LJASS]norm = u[/LJASS] vs [LJASS]RemoveSavedHandle(ht,2,i)[/LJASS] vs [ljass]if(!SaveUnitHandle(ht,3,i,u)) RemoveSavedHandle(ht,3,i);[/ljass]vs [ljass]dynam = u;[/ljass]

Approximate Results & Conclusions:

Tested on Warcraft III Version 1.24c.
Everything is measured as % of norm = u.
Iteration penalty (cost of testing):: .033


Set 1 (u != null)
  • [LJASS]norm = u;[/LJASS] 100% (.270)
    [*][LJASS]SaveUnitHandle(ht,0,i,u);[/ljass] 233% (.630)
    [*][LJASS]if(!SaveUnitHandle(ht,1,i,u)) RemoveSavedHandle(ht,1,i);[/ljass] 242% (.655)
    [*][LJASS]dynam = u [/ljass] 492% (1.329)




Set 2 (u == null)
  • [LJASS]norm = u[/LJASS] 100% (.274)
    [*][LJASS]RemoveSavedHandle(ht,2,i);[/ljass] 188% (.514)
    [*][LJASS]if(!SaveUnitHandle(ht,1,3,u)) RemoveSavedHandle(ht,3,i);[/ljass] 418% (1.146)
    [*][LJASS]dynam = u [/ljass] 486% (1.332)


Comments & Personal Criticism:
  • Use an array when you can. The hashtable is about 2x slower then using a regular array.
  • A hashtable is ALWAYS faster then a dynamic array. Even if you are using the safe testing way.
  • The remove native is faster then using the set native.
  • Unshown here, but testing if(u == null) SetUnitHandle else RemoveSavedHandle is obviously much faster, but this isn't good because you can't just treat it like a function call. (i.e. if( CreateUnit(...) == null) SetUnitHandle(...,CreateUnit(...)) is very bad).
  • Conditional evaluation seems to be very cheap and fast in jass.
  • The penalty isn't bad for the safe set when storing non-null values. the cost of calling both natives though is pretty high, but still better then using a dynamic array.
  • The difference between trying to save something stored in a variable that is null vs something that is not null is pretty small. And would go either way in my tests.
  • The iteration cost is near nothing for only 500 iterations. However if this was 5000 the cost would be quiet severe proving that Jesus4lyf was right about this testing business :p

Code:
JASS:
    ///////////////////////////////////////////////
    // Native declarations for stopwatch natives //
    //  - Requires no modified common.j import   //
    ///////////////////////////////////////////////
    native StopWatchCreate  takes nothing returns integer
    native StopWatchMark    takes integer stopwatch returns real
    native StopWatchDestroy takes integer stopwatch returns nothing
// Why this is in Zinc
//! textmacro TestPattern takes code
    TriggerSleepAction(0);
    testNum+=1;
    testNames[testNum] = "$code$";
    //BJDebugMsg("Test Name::$code$");
    sw = StopWatchCreate();
    for(i = 0; i < iterations; i+=1)
    {
        $code$
        $code$ 
        $code$
        $code$
        $code$
        $code$
        $code$ 
        $code$
        $code$
        $code$
    }
    results[testNum] += (StopWatchMark(sw) * 100);
    StopWatchDestroy(sw);
//! endtextmacro
//! zinc
library Tester
{
    type Hugearray extends unit[9000,81000];
    Hugearray dynam;
    hashtable ht;
    unit norm[];
    unit someunit; 
    
    function SafeSet(hashtable ht,integer pk,integer ck,unit ux)
    {
        if(ux == null) RemoveSavedHandle(ht,pk,ck); else SaveUnitHandle(ht,pk,ck,ux);
    }
    
    public function Trig_Text_Actions()
    {
        real results[];
        string testNames[];
        integer testNum;
        integer sw;
        integer i;
        integer j;
        unit u;
        integer iterations =   S2I(GetEventPlayerChatString());
        BJDebugMsg(I2S(GetHandleId(someunit)));
        for( j = 0; j < 10; j+=1)
        {
            testNum = -1;
            //! runtextmacro TestPattern("/* do nothing */")
            u = someunit;
            //! runtextmacro TestPattern(&quot;norm<i> = u;&quot;)
            //! runtextmacro TestPattern(&quot;SaveUnitHandle(ht,0,i,u);&quot;)
            //! runtextmacro TestPattern(&quot;if(!SaveUnitHandle(ht,1,i,u)) RemoveSavedHandle(ht,1,i);&quot;)
            //! runtextmacro TestPattern(&quot;dynam<i> = u;&quot;)
            u = null;
            //! runtextmacro TestPattern(&quot;norm<i> = u;&quot;)
            //! runtextmacro TestPattern(&quot;RemoveSavedHandle(ht,2,i);&quot;)
            //! runtextmacro TestPattern(&quot;if(!SaveUnitHandle(ht,3,i,u)) RemoveSavedHandle(ht,3,i);&quot;)
            //! runtextmacro TestPattern(&quot;dynam<i> = u;&quot;)
        }
        TriggerSleepAction(0);
        BJDebugMsg(&quot;u is not null&quot;);
        for(i = 0; i &lt; 9 ; i+=1)
        {
            if(i == 5) BJDebugMsg(&quot;u is null&quot;);
            BJDebugMsg(testNames<i>);
            BJDebugMsg(&quot;AVG time::&quot; +R2S(results<i>/10));
        }
    }
    private function onInit()
    {
        dynam = Hugearray.create();
        ht = InitHashtable();
        someunit = CreateUnit(Player(0),&#039;hfoo&#039;,0,0,0);
    }
}
//! endzinc

//===========================================================================
function InitTrig_Text takes nothing returns nothing
    set gg_trg_Text = CreateTrigger(  )
    call TriggerRegisterPlayerChatEvent( gg_trg_Text, Player(0), &quot;&quot;, false )
    call TriggerAddAction( gg_trg_Text, function Trig_Text_Actions )
endfunction

</i></i></i></i></i></i>



I am slightly out of standard approach, as I like the ability to enter the number of iterations. 500 is what I tested with though.

If you really want I can change the trigger so that it uses Escape.
 

weaaddar

New Member
Reaction score
6
It is difficult to use Vjass here, because Zinc provides the ability to use ;, and if then as a one liner to do delimiting of lines. As for the test it is basically was to prove if it reasonable to use Hashtables as opposed to the dynamic array types. If you don't mind modifying the guidelines to allow Zinc I can switch the test around to do 10 rounds and print out the headers. (the results are stable mind you).
 

weaaddar

New Member
Reaction score
6
It makes my macro easier :p This allows me to use one macro to write::
//! runtextmacro TestPattern("if(something) doThis(); else doThat();")

Compared to your other benchmarked resource you have to enter all sorts of macros for each scenerio. Since I'm testing multiple scenarios... it simply is easier to use this approach.

Anyway, I'll clean up the OP and post the whole set in like twenty minutes.

edit to clarify:
Vjass I can't type
[ljass]//! runtextmacro Testpattern("if(something)then[/ljass]
case1
else
case2
endif") It just doesn't work right TextMacro invocations must all be on one line. having the ";" and if then else inliner means I can have multiple calls tested in a benchmarking run.
 

weaaddar

New Member
Reaction score
6
Okay, OP is updated. Again a little out of standard, but the standard is meant for testing 2 things where as I'm testing 8 things. Everything is measured as a percentage of array set.
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top