Benchmark GetUnitUserData vs LoadInteger

Jesus4Lyf

Good Idea™
Reaction score
397
Comparison:
[LJASS]GetUnitUserData(Unit)[/LJASS] vs [LJASS]LoadInteger(Hash,GetHandleId(Unit),0)[/LJASS]​

Approximate Results & Conclusions:
  • Tested on Warcraft III Version 1.24b.
  • [LJASS]LoadInteger(Hash,GetHandleId(Unit),0)[/LJASS] takes 1.83 times as long to execute as [LJASS]GetUnitUserData(Unit)[/LJASS].
  • [LJASS]GetUnitUserData(Unit)[/LJASS] is therefore 45% faster than [LJASS]LoadInteger(Hash,GetHandleId(Unit),0)[/LJASS].

Comments & Personal Criticism:
  • There was a generally low fluctuation demonstrated, showing a stable test.
  • Due to a rumour that UnitUserData is O(n) complexity, I tested 250 units (realistic) and also 1 unit, and found no significant difference.
  • Due to experience with the order of the code execution changing results, I tried switching the test order. No visible difference.

Code:
JASS:
library Benchmark initializer OnInit
    ///////////////////////////////////////////////
    // 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
    
    /////////////////////////
    // Benchmarking script //
    /////////////////////////
    
    // init
    globals
        unit Unit
        integer DataSave
        hashtable Hash=InitHashtable()
    endglobals
    
    private function Init takes nothing returns nothing
        local integer i=250
        loop
            set i=i-1
            set Unit=CreateUnit(Player(15),'hpea',0,0,0)
            call SetUnitUserData(Unit,57)
            call SaveInteger(Hash,GetHandleId(Unit),0,57)
            exitwhen i==0
        endloop
    endfunction
    
    // test
    private function TestA1000 takes nothing returns nothing
        local integer i=1000
        loop
            exitwhen i==0
            set i=i-1
            // Repeat x10
            set DataSave=LoadInteger(Hash,GetHandleId(Unit),0) // 1
            set DataSave=LoadInteger(Hash,GetHandleId(Unit),0) // 2
            set DataSave=LoadInteger(Hash,GetHandleId(Unit),0) // 3
            set DataSave=LoadInteger(Hash,GetHandleId(Unit),0) // 4
            set DataSave=LoadInteger(Hash,GetHandleId(Unit),0) // 5
            set DataSave=LoadInteger(Hash,GetHandleId(Unit),0) // 6
            set DataSave=LoadInteger(Hash,GetHandleId(Unit),0) // 7
            set DataSave=LoadInteger(Hash,GetHandleId(Unit),0) // 8
            set DataSave=LoadInteger(Hash,GetHandleId(Unit),0) // 9
            set DataSave=LoadInteger(Hash,GetHandleId(Unit),0) // 10
        endloop
    endfunction
    private function TestB1000 takes nothing returns nothing
        local integer i=1000
        loop
            exitwhen i==0
            set i=i-1
            // Repeat x10
            set DataSave=GetUnitUserData(Unit) // 1
            set DataSave=GetUnitUserData(Unit) // 2
            set DataSave=GetUnitUserData(Unit) // 3
            set DataSave=GetUnitUserData(Unit) // 4
            set DataSave=GetUnitUserData(Unit) // 5
            set DataSave=GetUnitUserData(Unit) // 6
            set DataSave=GetUnitUserData(Unit) // 7
            set DataSave=GetUnitUserData(Unit) // 8
            set DataSave=GetUnitUserData(Unit) // 9
            set DataSave=GetUnitUserData(Unit) // 10
        endloop
    endfunction
    
    private function OnEsc takes nothing returns nothing
        local integer sw
        local integer i
        
        set i=0
        set sw=StopWatchCreate()
        loop
            set i=i+1
            call TestA1000.evaluate() // x10 - 100,000 executions altogether.
            exitwhen i==10
        endloop
        call BJDebugMsg("Hashtable: "+R2S(StopWatchMark(sw)*100))
        call StopWatchDestroy(sw)
        
        set i=0
        set sw=StopWatchCreate()
        loop
            set i=i+1
            call TestB1000.evaluate() // x10 - 100,000 executions altogether.
            exitwhen i==10
        endloop
        call BJDebugMsg("UnitData: "+R2S(StopWatchMark(sw)*100))
        call StopWatchDestroy(sw)
    endfunction
    
    ///////////////////////////////
    // Registers the OnEsc event //
    ///////////////////////////////
    private function OnInit takes nothing returns nothing
        local trigger t=CreateTrigger()
        call TriggerRegisterPlayerEvent(t,Player(0),EVENT_PLAYER_END_CINEMATIC)
        call TriggerAddAction(t,function OnEsc)
        call Init()
    endfunction
endlibrary
 

Rushhour

New Member
Reaction score
46
Could you upload your testmap? Or explain what you mean by "no modified common.j import" ? I got the file from wc3.net and imported it, but I can't test the map.. Also tried to change the path to what SFilip suggested.
And yeah I have NewGen and stuff. ^^
 

Jesus4Lyf

Good Idea™
Reaction score
397
Instructions [URL=http://www.wc3c.net/showthread.php?p=1098907][IMG]http://www.thehelper.net/forums/images/buttons/lastpost.gif[/IMG][/URL] said:
Copy war3.lua and stopwatch.dll to your NewGen directory (where NewGen WE.exe is located), replacing the original war3.lua in the process (you'll probably want to make a backup first). Then you can use NewGen Warcraft.exe to run WC3 with the natives injected.
Did you do this? All you need to do is take the code, c/p it into a blank map without triggers, run it and press "esc" in game...
I don't know if it works on 1.24c yet.
 

Rushhour

New Member
Reaction score
46
Yeah I did these things. Hm could be the version, I'm using 1.24c and all I see is the warcraft start window.. :( I do save the map before testing, even tried the regular gamestart ingame :D )
Edit: Same effect when I tried the map that came with the stopwatch.zip . Strange stuff
 

Jesus4Lyf

Good Idea™
Reaction score
397
>I do save the map before testing
Do not test. You must run using the NewGen Warcraft.exe to run WC3 with the natives injected. Quoting instructions, there...

I had the same issue, until I did that. I haven't tried 1.24c yet. But this is off topic, make a thread asking for help if your problem is a general issue regarding the natives, please. :)
 

weaaddar

New Member
Reaction score
6
Yes, there are indeed 1.24c natives. I really wish there was a way to do testmap from worldedit that'll call with injection.

I thought this was pretty well known, GetUserData is about as fast an array read.
 

Hatebreeder

So many apples
Reaction score
381
Ok, as far as I can see, Loadinteger doesn't come close to GetUnitUserData.

But, what about this:

[ljass] SomeInteger = GetHeroStr(SomeUnitArray[GetUnitUserData(Hero)]) + GetHeroStr(SomeUnitArray[GetUnitUserData(SomeOtherHero)] [/ljass]

and do the same with LoadInteger(...).

My question is: Is LoadInteger faster than GetUnitUserData if the Data is taken directly from the same Hashtable in the same instance? If yes, then LoadInteger would have some sort of O(n) complexity?

I have never benchmarked before (well, not for things like this) and my Guess does not have any solid background. So, you could also say that this is non-sence :D
 
Reaction score
341
@Hatebreeder

It would be better to just get it straight from an array. So it doesn't test the speed of GetHeroStr.
 

GetTriggerUnit-

DogEntrepreneur
Reaction score
129
I'm a little confused. :confused:

You're not alone.

I personally think that an indexing system made of Hashtables, LoadInteger, would be good instead of using GetUnitUserData. There you be two ways of indexing units, allowing to save more complex data.
 

Azlier

Old World Ghost
Reaction score
461
What more complex data? Now you're confuzzling me.
 

GetTriggerUnit-

DogEntrepreneur
Reaction score
129
Ex:

You want to have a global variable doubled-indexed.

but wait, these are called hashtables. Anyways...

You want a global which could store many effects for the same unit e.g.
JASS:

private unit array array WOZ
set WOZ[GetUnitUserData][0] = AddEffect..


Anyway! I think a such system would be nice :).
 

Lyerae

I keep popping up on this site from time to time.
Reaction score
105
2D arrays exist AFAIK. If not they are easily simulated.

Isn't a hashtable essentially an infinitely-sized (I think) 2D-Array?
If so, then why would you try and simulate something that we essentially have already, and is probably more efficient than the majority of systems we can make.

Actually, Vexorian could probably implement some kind of system to automatically turn something like this; [ljass]set 2dArray[0, 0][/ljass] into a hashtable write, and the reads into a hashtable read.
 
Reaction score
341
Yes, but speed freaks would like using arrays.

And 2D arrays are traditionally like this [ljass]arrayVar[index1][index2][/ljass].
 

Lyerae

I keep popping up on this site from time to time.
Reaction score
105
> And 2D arrays are traditionally like this arrayVar[index1][index2].

Mah bad.

> Yes, but speed freaks would like using arrays.

Still. It can't be that bad of an idea...
 
Reaction score
341
Maybe vex will do it but I doubt it, and there may not be hashtable functions for all types.

But it could be nice having 2d arrays without limits, but it would be weird with single arrays having limits.

And the array set was about 2x faster than the hashtable set (Benchmark).
 
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