Benchmark Collection shoot out (Array vs Table vs HashSet vs LinkedList)

weaaddar

New Member
Reaction score
6
Hi all,
Here is a benchmark between 4 different types of collections. I am trying 4 different standard things to try against a collection.

Each of these collections was populated with the first 1000 values of the function Sum(n), [Sum(n) = (n * n+1)/2].

The benchmarks rules were slacked because of concerns of the op limit and the constraint of uniqueness. Each result will be compared to the winner of the set. The array was mostly there as the token winner, and the real comparison is against table.
Comparison:
Set 1:: Build collection
Set 2:: Find a cache_hit (value 280875) and set it 280876
Set 3:: Contains cache_miss (value 8675309)
Set 4:: iteration
[LJASS]norm[][/LJASS] vs [LJASS]table[/LJASS]vs[ljass]hset[/ljass] vs [ljass]linkedlist[/ljass]​

Approximate Results & Conclusions:
Tested on Warcraft III Version 1.24c.
Set 1:: Build collection (here we're just testing the setter or the Add function, as all are suffering the cost of reading the values array and iteration)
  • [LJASS]norm[][/LJASS] 100% (1.553)
  • [Ljass]table[/ljass] 192% (2.984)
  • [LJASS]hset[/LJASS] 1335% (20.734)
  • [LJASS]list[/LJASS] 719% (11.166)
Set 2:: Find a cache_hit (value 280875) and set it 280876.
Array and table are just doing the following::

[ljass]for(int i = 0; i < 1000; i+=1) if(colname == 280875){ colname = 280876 [/ljass]

Hashset is removing the old one, and adding the new one.
List is using its fetch method to grab a node and modify its value.
  • [LJASS]norm[][/LJASS] 1111% (.733)
  • [Ljass]table[/ljass] 2141% (1.413)
  • [LJASS]hset[/LJASS] 100% (.066)
  • [LJASS]list[/LJASS] 1553% (1.147)


Set 3::Contains cache_miss (value 8675309)
Same as before, except nothing was put in the conditional evaluations as apparently, we don't have Jenny's number, and it was less typing :p
  • [LJASS]norm[][/LJASS] 7885% (1.025)
  • [Ljass]table[/ljass] 15085% (1.961)
  • [LJASS]hset[/LJASS] 100% (.013)
  • [LJASS]list[/LJASS] 11600% (1.508)

Set 4::Iteration through the collection
Here we are just looping through and assigning it to a local variable. Standard iteration patterns are being used. Norm & Table have the same code, and Hset and list have the same code.
  • [LJASS]norm[][/LJASS] 100% (1.098)
  • [Ljass]table[/ljass] 172% (1.894)
  • [LJASS]hset[/LJASS] 147% (1.616)
  • [LJASS]list[/LJASS] 154% (1.695)
Comments & Personal Criticism:
  • First, as before unless you need don't use a hashtable, array is faster, using a hashtable like an array is much slower then array.
  • Iterating through linked list is slower then iterating through arrays, but still iterating through a table. If you have an unknown number of elements the linked list or set provide nice functionality as array or table actually don't know when they end. You'll have to store that in another var or as an index.
  • The hashtable natives are really fast. Much faster then o(n) collection searches.

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

//! textmacro TestPattern takes Name,code
    TriggerSleepAction(0);
    BJDebugMsg(&quot;Test Name::$Name$&quot;);
    sw = StopWatchCreate();
    $code$
    result = StopWatchMark(sw);
    StopWatchDestroy(sw);
    BJDebugMsg(R2S(result*1000));
//! endtextmacro
//! zinc
library CollectionTest requires LinkedList,Dictionary,Table
{
    integer values[];
    integer norm[];
    Table   table;
    Set_integer hset;
    List list;
    public function Trig_CollectionTest_Actions()
    {
        integer i;
        integer sw;
        Node node;
        RO_SetNode_integer ro;
        real result;
        integer num;
        BJDebugMsg(&quot;Set 1::Build collection&quot;);
        //! runtextmacro TestPattern(&quot;norm[]&quot;,&quot;for(i = 0;i&lt;1000;i+=1) norm<i> = values<i>;&quot;)
        //! runtextmacro TestPattern(&quot;table&quot;,&quot;for(i = 0;i&lt;1000;i+=1) table<i> = values<i>;&quot;)
        //! runtextmacro TestPattern(&quot;hset&quot;,&quot;for(i = 0;i&lt;1000;i+=1) hset.Add(values<i>);&quot;)
        //! runtextmacro TestPattern(&quot;list&quot;,&quot;for(i = 0;i&lt;1000;i+=1) list.Add(values<i>);&quot;)
        TriggerSleepAction(0);
        BJDebugMsg(&quot;Set 2:: Find a cache_hit (value 280875) and set it 280876&quot;);
        //! runtextmacro TestPattern(&quot;norm[]&quot;,&quot;for(i = 0; i &lt; 1000; i+=1) if(norm<i> == 280875){norm<i>=280876; break;}&quot;)
        //! runtextmacro TestPattern(&quot;table&quot;,&quot;for(i = 0; i &lt; 1000; i+=1) if(table<i> == 280875){table<i>=280876; break;}&quot;)
        //! runtextmacro TestPattern(&quot;hset&quot;,&quot;if(hset.Remove(280875)) hset.Add(280876);&quot;)
        //! runtextmacro TestPattern(&quot;list&quot;,&quot;node =list.Fetch(280875);if(node != 0) node.Object=280876;&quot;)
        TriggerSleepAction(0);
        BJDebugMsg(&quot;Set 3::Contains cache_miss (value 8675309)&quot;);
        //! runtextmacro TestPattern(&quot;norm[]&quot;,&quot;for(i = 0; i &lt; 1000; i+=1) if(norm<i> == 8675309) break;&quot;)
        //! runtextmacro TestPattern(&quot;table&quot;,&quot;for(i = 0; i &lt; 1000; i+=1) if(table<i> == 8675309) break;&quot;)
        //! runtextmacro TestPattern(&quot;hset&quot;,&quot;if(hset.Contains(8675309)){}&quot;)
        //! runtextmacro TestPattern(&quot;list&quot;,&quot;if(list.Contains(8675309)){}&quot;)
        BJDebugMsg(&quot;Set 4:: iteration.&quot;);
        //! runtextmacro TestPattern(&quot;norm[]&quot;,&quot;for(i = 0;i&lt;1000;i+=1) num = norm<i>;&quot;)
        //! runtextmacro TestPattern(&quot;table&quot;,&quot;for(i = 0;i&lt;1000;i+=1) num = table<i>;&quot;)
        //! runtextmacro TestPattern(&quot;hset&quot;,&quot;for(ro = hset.GetReadOnlyNode();ro != hset;ro = ro.Next) num = r<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="smilie smilie--sprite smilie--sprite12" alt="o_O" title="Er... what?    o_O" loading="lazy" data-shortname="o_O" />bject;&quot;)
        //! runtextmacro TestPattern(&quot;list&quot;,&quot;for(node = list.First;node != list;node = node.Next){ num = node.Object;}&quot;)
        

    }

    function onInit()
    {
        integer i;
        table = table.create();
        hset = hset.create();
        list = list.create();
        for(i = 1; i &lt; 1000; i+=1)
        {
            values<i> = i+ values[i-1];
        }
    }
}

//! endzinc
//===========================================================================
function InitTrig_CollectionTest takes nothing returns nothing
    set gg_trg_CollectionTest = CreateTrigger(  )
    call TriggerRegisterPlayerEventEndCinematic( gg_trg_CollectionTest, Player(0) )
    call TriggerAddAction( gg_trg_CollectionTest, function Trig_CollectionTest_Actions )
endfunction
</i></i></i></i></i></i></i></i></i></i></i></i></i></i></i>


As for the linked list its a custom grown solution, pretty close to what the set used.
JASS:
//! zinc
library LinkedList
{
    public struct Node
    {
        thistype Next;
        thistype Prev;
        integer Object;
        static method create(integer object)-&gt;thistype
        {
            thistype n = thistype.allocate();
            n.Object = object;
            return n;
        }
    }
    public struct List extends Node
    {
        method operator Count()-&gt;integer
        {
            return Object;
        }
        
        method operator Count=(integer value)
        {
            Object = value;
        }
        
        method operator First()-&gt; Node
        {
            return Next;
        }
        method Add(integer object) // not checking because it&#039;s faster.
        {
            Node node = Node.create(object);
            Prev.Next = node;
            node.Next = this;
            Prev = node;
            Count+=1;
        }
        
        method Fetch(integer object) -&gt; Node
        {
            Node node;
            for(node = Next; node != this; node=node.Next)
            {
                if(node.Object == object)
                {
                    return node;
                }
            }
            return 0;
        }
        
        method Contains(integer object)-&gt; boolean
        {
            return Fetch(object) &gt; 0;
        }
        
        method RemoveNode(Node node) -&gt; integer
        {
            integer retVal = node.Object;
            if(node != 0)
            {
                node.Prev.Next = node.Next;
                node.Next.Prev = node.Prev;
                node.destroy();
                Count -=1;
            }
            return retVal;
        }
        method Remove(integer object)-&gt;boolean
        {
            Node node = Fetch(object);
            if(node != 0)
            {
                node.Prev.Next = node.Next;
                node.Next.Prev = node.Prev;
                node.destroy();
                Count-=1;
                return true;
            }
            return false;
        }
        static method create()-&gt;thistype
        {
            thistype n = thistype.allocate(0);
            n.Next = n;
            n.Prev = n;
            return n;
        }
    }
}
//! endzinc
Set is the resource submitted on this site.

Anyother requests? I'll try bsearch vs hashtable natives on table and norm[] but its hard to write it as a one liner :p
 

Jesus4Lyf

Good Idea™
Reaction score
397
It looks really messy. I think you've butchered the template horribly.

Are you testing O(n) complexity searches against O(1) complexity hashing? How do you even hope to come up with a useful number? The efficiency is completely different in scale...

>Iterating through linked list is slower then iterating through arrays
That's not truth.

Stop using Zinc. I can't read your tests, they're a real mess.

>The hashtable natives are really fast. Much faster then o(n) collection searches.
That is a conclusion, not criticism.

Edit:
The guidelines state:
  • This code must be pastable into a blank WC3 map and work without any other modifications (if possible).
  • The code being benchmarked must be executed at least 10 times consecutively at a time (without loop checks).
  • Code must be readable (reasonable layout).
 

weaaddar

New Member
Reaction score
6
Well Zinc is still easier to work with :/. As for the iteration pattern just take a look, Linked List/Set which are both linked list doing the following::
JASS:
for(node = list.First; node != list;node = node.Next)
{
    num = node.Object;
}


was much slower then
JASS:
for(i = 0; i &lt; 1000; i+=1)
{
    num = normalarray<i>;
}
</i>



As for the unfair comparison there was some theory that hashtables were slow (or atleast vex use to think that).
 

Azlier

Old World Ghost
Reaction score
461
You should really put each separate benchmark into different functions called via TriggerExecute.
 

vuongkkk

New Member
Reaction score
1
Someone can tell me why linked list can be faster than array while "List" is a strust and strust base on array :-??
 

tooltiperror

Super Moderator
Reaction score
231
Not really sure what you're asking, because you didn't specify. Linked lists are faster for some things while arrays are faster for some things.
 

kingkingyyk3

Visitor (Welcome to the Jungle, Baby!)
Reaction score
216
Because when iteration is in progress, there is no maths involved, just array reading.
 

Nestharus

o-o
Reaction score
84
Actually, linked list iteration speed depends on the exitwhen statement.

There are 3 types of exiting linked list. One type compared to the head node, the slowest of the three. The second type compares the current node to 0 and the third type stores a boolean into the head for comparing against that.

Comparing against 0 requires overhead be added to the add/remove operations on a list.

Iterating through a list comparing to the head node might actually be slower than iterating through an array (slower in this thread). Iterating through a linked list comparing to a boolean or comparing to 0 is actually faster depending on how many globals are in the map. Iterating through a 100% local linked list would be blazing fast when comparing to 0 or a boolean.
 
General chit-chat
Help Users
  • No one is chatting at the moment.
  • The Helper The Helper:
    I am great and it is fantastic to see you my friend!
    +1
  • The Helper The Helper:
    If you are new to the site please check out the Recipe and Food Forum https://www.thehelper.net/forums/recipes-and-food.220/
  • Monovertex Monovertex:
    How come you're so into recipes lately? Never saw this much interest in this topic in the old days of TH.net
  • Monovertex Monovertex:
    Hmm, how do I change my signature?
  • tom_mai78101 tom_mai78101:
    Signatures can be edit in your account profile. As for the old stuffs, I'm thinking it's because Blizzard is now under Microsoft, and because of Microsoft Xbox going the way it is, it's dreadful.
  • The Helper The Helper:
    I am not big on the recipes I am just promoting them - I use the site as a practice place promoting stuff
    +2
  • Monovertex Monovertex:
    @tom_mai78101 I must be blind. If I go on my profile I don't see any area to edit the signature; If I go to account details (settings) I don't see any signature area either.
  • The Helper The Helper:
    You can get there if you click the bell icon (alerts) and choose preferences from the bottom, signature will be in the menu on the left there https://www.thehelper.net/account/preferences
  • The Helper The Helper:
    I think I need to split the Sci/Tech news forum into 2 one for Science and one for Tech but I am hating all the moving of posts I would have to do
  • The Helper The Helper:
    What is up Old Mountain Shadow?
  • The Helper The Helper:
    Happy Thursday!
    +1
  • Varine Varine:
    Crazy how much 3d printing has come in the last few years. Sad that it's not as easily modifiable though
  • Varine Varine:
    I bought an Ender 3 during the pandemic and tinkered with it all the time. Just bought a Sovol, not as easy. I'm trying to make it use a different nozzle because I have a fuck ton of Volcanos, and they use what is basically a modified volcano that is just a smidge longer, and almost every part on this thing needs to be redone to make it work
  • Varine Varine:
    Luckily I have a 3d printer for that, I guess. But it's ridiculous. The regular volcanos are 21mm, these Sovol versions are about 23.5mm
  • Varine Varine:
    So, 2.5mm longer. But the thing that measures the bed is about 1.5mm above the nozzle, so if I swap it with a volcano then I'm 1mm behind it. So cool, new bracket to swap that, but THEN the fan shroud to direct air at the part is ALSO going to be .5mm to low, and so I need to redo that, but by doing that it is a little bit off where it should be blowing and it's throwing it at the heating block instead of the part, and fuck man
  • Varine Varine:
    I didn't realize they designed this entire thing to NOT be modded. I would have just got a fucking Bambu if I knew that, the whole point was I could fuck with this. And no one else makes shit for Sovol so I have to go through them, and they have... interesting pricing models. So I have a new extruder altogether that I'm taking apart and going to just design a whole new one to use my nozzles. Dumb design.
  • Varine Varine:
    Can't just buy a new heatblock, you need to get a whole hotend - so block, heater cartridge, thermistor, heatbreak, and nozzle. And they put this fucking paste in there so I can't take the thermistor or cartridge out with any ease, that's 30 dollars. Or you can get the whole extrudor with the direct driver AND that heatblock for like 50, but you still can't get any of it to come apart
  • Varine Varine:
    Partsbuilt has individual parts I found but they're expensive. I think I can get bits swapped around and make this work with generic shit though
  • Ghan Ghan:
    Heard Houston got hit pretty bad by storms last night. Hope all is well with TH.
  • The Helper The Helper:
    Power back on finally - all is good here no damage
    +2
  • V-SNES V-SNES:
    Happy Friday!
    +1
  • The Helper The Helper:
    New recipe is another summer dessert Berry and Peach Cheesecake - https://www.thehelper.net/threads/recipe-berry-and-peach-cheesecake.194169/

      The Helper Discord

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top