1. Head to the 33rd Modeling Contest Poll and drink to your heart's desire.
    Dismiss Notice
  2. Choose your means of doom in the 17th Mini Mapping Contest Poll.
    Dismiss Notice
  3. A slave to two rhythms, the 22nd Terraining Contest is here.
    Dismiss Notice
  4. The heavens smile on the old faithful. The 16th Techtree Contest has begun.
    Dismiss Notice
  5. The die is cast - the 6th Melee Mapping Contest results have been announced. Onward to the Hive Cup!
    Dismiss Notice
  6. The glory of the 20th Icon Contest is yours for the taking!
    Dismiss Notice
  7. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

JASS Benchmarking Results

Discussion in 'The Lab' started by TriggerHappy, Dec 13, 2013.

  1. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,789
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    JASS Benchmarking & Efficiency Results

    Feel free to post your own tests or dispute one of mine. I'll be updating this thread with as much info as I can. Remember results may vary depending on your computer.

    Much of this has already been known or could be assumed, but it's nice to have the actual numbers and a reference like this.

    All tests are done with the StopWatch Natives.

    Native comparisons
    • ExecuteFunc
      • It seems length of the function name significantly changes the execution time. With a lengths of 1 and 70, the function with a name of length 1 was ~13% faster
      • ~27% slower than
        TriggerEvaluate
        - [Results]


    • TriggerEvaluate
      • ~87% slower than a function call - [Results]
      • ~25% faster than
        TriggerExecute
        which leads us to believe that
        triggercondition
        is more efficient than
        triggeraction
        in terms of execution time.


    • UnitAlive
      • ~15-20% faster than
        GetWidgetLife(u) > 0.405
      • ~47-50% faster than
        IsUnitType(u, UNIT_TYPE_DEAD) or GetUnitTypeId(u) == 0


    • SetUnitPosition
      • ~90% slower than
        SetUnitX
        &
        SetUnitY


    • SetWidgetLife
      • ~15-20% faster than
        SetUnitState(u, UNIT_STATE_LIFE, 5)


    • GetWidgetLife
      • ~20% faster than
        GetUnitState(u, UNIT_STATE_LIFE)
        - [Results]


    • GetWidgetX
      • ~7-9% slower than
        GetUnitX
      • ~2-4% slower than
        GetItemX

    Other comparisons

    • Group Enumeration (Test Code)
      ForGroup
      is always the slowest way to enumerate through a unit group. The
      FirstOfGroup
      method is the fastest except in cases where the unit group has less than a few units. In that case placing your actions inside the enum filter will prove to be considerably faster. In cases where you want to keep the unit group you should swap groups. See vJass Optimization: Using a First of Group Loop for Enumeration
    • Recursive Calls
      Recursive function calls (like many BJ's) have a relatively significant impact on performance. For example
      GetHandleId
      was ~24-28% faster than
      GetHandleIdBJ
      and it increases within each recursion.​
    • Hashtable vs. Game Cache
      Hashtables are faster, nothing too surprising here.

      All variables used in my tests were just 1 character long and they were globals as opposed to local. There are too many small tests to include here but they are simple and I'm sure there were no errors.​
      • SaveInteger
        (hashtable) was ~35-40% faster than
        StoreInteger
        (game cache). The same goes for
        SaveStr
        vs.
        StoreString
        while booleans may be a tiny bit slower.
      • SaveUnitHandle
        was only ~8-12% faster than
        StoreUnit
        which is a bit odd.
      The same speeds mostly still apply for loading, respectively. However when comparing
      LoadUnitHandle
      vs.
      RestoreUnit
      the latter is ~40-45% slower.​

    Benchmark Library

    Code (vJASS):
    library Stopwatch initializer onInit
       
        //===========================================================================
       
        //! textmacro STOPWATCH_BENCHMARK_LOCAL_VARIABLES
            local real c
        //! endtextmacro
       
        //! textmacro STOPWATCH_BENCHMARK_TEST_1
            set c = Pow(5, 2)
        //! endtextmacro
       
        //! textmacro STOPWATCH_BENCHMARK_TEST_2
            set c = (5 * 5)
        //! endtextmacro
       
        //! textmacro STOPWATCH_BENCHMARK_INIT
        //! endtextmacro
       
        globals
            private constant integer ITERATIONS = 1000 // how many times to run each test
            private constant string  TEST_1     = "Pow(5, 2)"
            private constant string  TEST_2     = "(5 * 5)"
            private constant boolean USE_SLEEP  = false // add TriggerSleepAction's between tests
            private constant boolean STACK      = true // stack iterations
           
            // personal variables here
        endglobals
       
        //===========================================================================
       
        globals
            private integer C=1 // stack count
            private real array R // results
        endglobals
       
        // Declare the StopWatch natives
        native StopWatchCreate  takes nothing returns integer
        native StopWatchTicks   takes integer id returns integer
        native StopWatchMark    takes integer id returns integer
        native StopWatchDestroy takes integer id returns nothing

        private function Actions takes nothing returns nothing
            local integer sw
            local integer i
            local string output = ""
           
            //! runtextmacro STOPWATCH_BENCHMARK_LOCAL_VARIABLES()
           
            // Disable the trigger so it can't be interrupted
            call DisableTrigger(GetTriggeringTrigger())
           
            static if (USE_SLEEP) then
                call TriggerSleepAction(0)
            endif
           
            set i  = 0
            set sw = StopWatchCreate() // Create the StopWatch
           
            loop
                exitwhen i == ITERATIONS // Run test #1 X amount of times
                //! runtextmacro STOPWATCH_BENCHMARK_TEST_1()
                set i = i + 1
            endloop
           
            // If stacking is enabled, the results will add
            static if (STACK) then
                set R[0] = R[0] + StopWatchTicks(sw)
                set R[3] = R[3] + StopWatchMark(sw)
            else
                set R[0] = StopWatchTicks(sw)
                set R[3] = StopWatchMark(sw) // the length in milliseconds it took to run
            endif
           
            call StopWatchDestroy(sw)
           
            static if (USE_SLEEP) then
                call TriggerSleepAction(0)
            endif
           
            set output = "|cff008200===========================================================================|r\n"
            set output =  output + "|cffffcc00" + I2S(ITERATIONS * C) + "|r iterations of " + TEST_1 + " took |cffffcc00" + I2S(R2I(R[3])) + "|r milliseconds to finish.\n"
           
            static if (USE_SLEEP) then
                call TriggerSleepAction(0)
            endif
           
            // Set the iteration count to 0
            set i  = 0
            set sw = StopWatchCreate()
           
            loop
                exitwhen i == ITERATIONS
                // This is where test #2 is being executed
                //! runtextmacro STOPWATCH_BENCHMARK_TEST_2()
                set i = i + 1
            endloop
           
            static if (STACK) then
                set R[1] = R[1] + StopWatchTicks(sw)
                set R[4] = R[4] + StopWatchMark(sw)
            else
                set R[1] = StopWatchTicks(sw)
                set R[4] = StopWatchMark(sw)
            endif
           
            call StopWatchDestroy(sw)
           
            // Enable the trigger
            call EnableTrigger(GetTriggeringTrigger())
           
            set output = output + "|cffffcc00" + I2S(ITERATIONS * C) + "|r iterations of " + TEST_2 + " took |cffffcc00" + I2S(R2I(R[4])) + "|r milliseconds to finish.\n\n"
       
            // Display the results
            if (R[0] > R[1]) then
                set R[2] = 100 - (R[1]/R[0] * 100)
                set output = output + TEST_2 + " was " + R2S(R[2]) + "% |cff80ff80faster|r than " + TEST_1 + "\n\n"
            else
                set R[2] = 100 - (R[0]/R[1] * 100)
                set output = output + TEST_1 + " is " + R2S(R[2]) + "% |cff80ff80faster|r than " + TEST_2 + "\n\n"
            endif
           
            static if (STACK) then
                set C=C+1
            endif
           
            set output = output + "|cff008200===========================================================================|r\n\n\n\n\n"
           
            call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, output)
        endfunction

        private function onInit takes nothing returns nothing
            local trigger t = CreateTrigger()
            local integer i = 0
            loop
                exitwhen i == 15
                call TriggerRegisterPlayerEvent(t, Player(i), EVENT_PLAYER_END_CINEMATIC)
                set i = i + 1
            endloop
            call TriggerAddAction(t, function Actions)
            //! runtextmacro STOPWATCH_BENCHMARK_INIT()
        endfunction
       
        //===========================================================================
       
    endlibrary



    Example Script

    Code (vJASS):

    scope Stopwatch initializer onInit
       
        globals
            private constant integer ITERATIONS = 4000
        endglobals
       
        private function Actions takes nothing returns boolean
            local integer sw
            local integer i
            local real array ticks
            local string output

            set i  = 0
            set sw = StopWatchCreate()
               
            loop
                exitwhen i == ITERATIONS
                // TEST 1 HERE
                set i = i + 1
            endloop
               
            set ticks[0] = StopWatchTicks(sw)
            set output = I2S(ITERATIONS) + " iterations of Test #1 took " + I2S(StopWatchMark(sw)) + " milliseconds to finish.\n"
            call StopWatchDestroy(sw)
               
            set i  = 0
            set sw = StopWatchCreate()
               
            loop
                exitwhen i == ITERATIONS
                // TEST 2 HERE
                set i = i + 1
            endloop
               
            set ticks[1] = StopWatchTicks(sw)
            set output = output + I2S(ITERATIONS) + " iterations of Test #2 took " + I2S(StopWatchMark(sw)) + " milliseconds to finish.\n\n"
            call StopWatchDestroy(sw)
               
            if (ticks[0] > ticks[1]) then
                set ticks[2] = 100 - (ticks[1]/ticks[0] * 100)
                set output = output + "Test #2 was " + R2S(ticks[2]) + "% faster than Test #1\n\n"
            else
                set ticks[2] = 100 - (ticks[0]/ticks[1] * 100)
                set output = output + "Test #1 is " + R2S(ticks[2]) + "% faster than Test #2\n\n"
            endif
           
            call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, output)
           
            return false
        endfunction

        //===========================================================================
        private function onInit takes nothing returns nothing
            local trigger t = CreateTrigger()
            call TriggerRegisterPlayerEvent(t, Player(0), EVENT_PLAYER_END_CINEMATIC)
            call TriggerAddCondition(t, function Actions)
        endfunction

    endscope
     

     
    Last edited: Jan 29, 2014
  2. bowser499

    bowser499

    Joined:
    Jul 20, 2009
    Messages:
    782
    Resources:
    7
    Tools:
    1
    Maps:
    3
    Spells:
    3
    Resources:
    7
    It's all very interesting, but not that surprising :p
    What is UnitAlive btw?
     
  3. GhostWolf

    GhostWolf

    Joined:
    Jul 29, 2007
    Messages:
    4,952
    Resources:
    2
    Tools:
    1
    Tutorials:
    1
    Resources:
    2
    native UnitAlive takes unit id returns boolean


    Located in common.ai
     
  4. Malhorne

    Malhorne

    Joined:
    Sep 14, 2012
    Messages:
    2,327
    Resources:
    6
    Spells:
    4
    Tutorials:
    1
    JASS:
    1
    Resources:
    6
    Could you do the GetUnitX/Y vs GetWidgetX/Y vs GetItemX/Y
    It should be GetUnitX/Y > GetWidgetX/Y > GetItemX/Y
     
  5. edo494

    edo494

    Joined:
    Apr 16, 2012
    Messages:
    3,846
    Resources:
    5
    Spells:
    1
    JASS:
    4
    Resources:
    5
    why should item be slower than widget while unit be faster?
     
  6. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,429
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    Blizzard is weirdTM.

    Those results were posted in a different thread with benchmarks.
     
  7. edo494

    edo494

    Joined:
    Apr 16, 2012
    Messages:
    3,846
    Resources:
    5
    Spells:
    1
    JASS:
    4
    Resources:
    5
    I know I saw that thread :D but its weird
    and nice trademark :D

    and TriggerHappy is away currently, he wont be here for a bit of time(6 hrs maybe more)
     
  8. moyackx

    moyackx

    Joined:
    Feb 15, 2006
    Messages:
    801
    Resources:
    7
    Maps:
    4
    Spells:
    2
    Tutorials:
    1
    Resources:
    7
    Subbbbscribing.....
     
  9. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,146
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    Could you bench a balanced trigger vs an unbalanced trigger vs one with only trigger conditions?

    Think back to my BooleanExpression resource ^)^.
     
  10. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    8,364
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    ExecuteFunc is twice as slow as TriggerExecute in FPS tests.

    Also, the gamecache comparisons should vary based on hardware
     
  11. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,789
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    Done, updated main post.
     
    Last edited: Dec 13, 2013
  12. Magtheridon96

    Magtheridon96

    Joined:
    Dec 12, 2008
    Messages:
    6,003
    Resources:
    26
    Maps:
    1
    Spells:
    8
    Tutorials:
    7
    JASS:
    10
    Resources:
    26
    When it comes to death checks, it's not about speed, but correctness.

    GetWidgetLife(u) > 0.405 can be true for a dead unit:

    Code (vJASS):
    local unit u = CreateUnit(Player(0), 'hpea', 0, 0, 0)
    call SetWidgetLife(u, 0)
    call SetWidgetLife(u, 100)
    call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 6000, R2S(GetWidgetLife(u)))


    That'll print 100 while you have a dead peasant laying there, rotting like fuck.
     
  13. Malhorne

    Malhorne

    Joined:
    Sep 14, 2012
    Messages:
    2,327
    Resources:
    6
    Spells:
    4
    Tutorials:
    1
    JASS:
    1
    Resources:
    6
    Kinda strange that those values aren't the same as Maker's
     
  14. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,789
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    EDIT http://www.hiveworkshop.com/forums/2456309-post18.html

    It appears that in patch 1.26a SetUnitPosition is no longer slow.

    In 1.24 it was ~95% slower than SetUnitX/Y, but in 1.26 it's ~15-20% faster


    1.26 script

    Code (vJASS):

    scope Stopwatch initializer onInit
       
        globals
            private constant integer ITERATIONS = 5000
            real x
            item j = CreateItem('ratf', 0, 0)
            unit u = CreateUnit(Player(0), 'hfoo', 0, 0, 0)
        endglobals
       
        private function Actions takes nothing returns boolean
            local integer sw
            local integer i
            local real array ticks
            local string output = ""
           
            set i  = 0
            set sw = StopWatchCreate()
               
            loop
                exitwhen i == ITERATIONS
                call SetUnitPosition(u, 1000, 1000)
                set i = i + 1
            endloop
               
            set ticks[0] = StopWatchTicks(sw)
            call StopWatchDestroy(sw)
           
            //set output = I2S(ITERATIONS) + " iterations of Test #1 took " + I2S(StopWatchMark(sw)) + " milliseconds to finish.\n"
               
            set i  = 0
            set sw = StopWatchCreate()
               
            loop
                exitwhen i == ITERATIONS
                call SetUnitX(u, 1000)
                call SetUnitY(u, 1000)
                set i = i + 1
            endloop
               
            set ticks[1] = StopWatchTicks(sw)
            call StopWatchDestroy(sw)
           
            //set output = output + I2S(ITERATIONS) + " iterations of Test #2 took " + I2S(StopWatchMark(sw)) + " milliseconds to finish.\n\n"
               
            if (ticks[0] > ticks[1]) then
                set ticks[2] = 100 - (ticks[1]/ticks[0] * 100)
                set output = output + "Test #2 was " + R2S(ticks[2]) + "% faster than Test #1\n\n"
            else
                set ticks[2] = 100 - (ticks[0]/ticks[1] * 100)
                set output = output + "Test #1 is " + R2S(ticks[2]) + "% faster than Test #2\n\n"
            endif
           
            call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, output)
           
            return false
        endfunction

        //===========================================================================
        private function onInit takes nothing returns nothing
            local trigger t = CreateTrigger()
            call TriggerRegisterPlayerEvent(t, Player(0), EVENT_PLAYER_END_CINEMATIC)
            call TriggerAddCondition(t, function Actions)
            set j = CreateItem('ratf', 0, 0)
        endfunction

    endscope
     


    1.24 script

    Code (vJASS):

    scope BenchmarkSetUnitPosition initializer Init

        globals
            private constant integer ITERATIONS = 8000
            private trigger t = CreateTrigger()
            unit u = gg_unit_Hblm_0001
        endglobals
       
        private function Actions takes nothing returns nothing
            local integer curLoop
            local integer sw      
            local real array result
           
            call DisableTrigger(t)
            call BJDebugMsg("\n\n")
           
            // TEST 1
            set curLoop = 0
            set sw = StopWatchCreate()
            loop
                exitwhen curLoop >= ITERATIONS
                call SetUnitPosition(u, 0, 0)
                set curLoop = curLoop + 1
            endloop
            set result[0] = StopWatchMark(sw)
            call StopWatchDestroy(sw)
           
            call BJDebugMsg("SetUnitPosition: " + I2S(curLoop) +" iterations took " + R2S(result[0]))
            call PolledWait(1)
           
            // TEST 2
            set curLoop = 0
            set sw = StopWatchCreate()
            loop
                exitwhen curLoop >= ITERATIONS
                call SetUnitX(u, 0)
                call SetUnitY(u, 0)
                set curLoop = curLoop + 1
            endloop
            set result[1] = StopWatchMark(sw)
            call StopWatchDestroy(sw)
           
            call BJDebugMsg("SetUnitX/Y: " + I2S(curLoop) +" iterations took " + R2S(result[1]))
           
            if (result[0] < result[1]) then
                set result[2] = 100 - (result[0]/result[1] * 100)
                call BJDebugMsg("Test #1 was " + I2S(R2I(result[2])) + "% faster than Test #2")
            else
                set result[2] = 100 - (result[1]/result[0] * 100)
                call BJDebugMsg("Test #1 was " + I2S(R2I(result[2])) + "% slower than Test #2")
            endif
           
            call EnableTrigger(t)
        endfunction

        //===========================================================================
        private function Init takes nothing returns nothing
            call TriggerRegisterPlayerEvent(t, Player(0), EVENT_PLAYER_END_CINEMATIC)
            call TriggerAddAction(t, function Actions)
            set u = gg_unit_Hblm_0001
        endfunction

    endscope
     


    I thought this was a mistake, but when spamming SetUnitPosition in 1.24, I got lag. However there was no lag in 1.26a

    Good thing UnitAlive is fastest then;)
     
    Last edited: Dec 13, 2013
  15. Malhorne

    Malhorne

    Joined:
    Sep 14, 2012
    Messages:
    2,327
    Resources:
    6
    Spells:
    4
    Tutorials:
    1
    JASS:
    1
    Resources:
    6
    So what test is the best ?

    Anyway cool for UnitAlive :)

    SetUnitPosition is faster now oO ?

    I'm gonna eat see you after
     
  16. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,789
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    Yes.
    No.
     
    Last edited: Dec 13, 2013
  17. Magtheridon96

    Magtheridon96

    Joined:
    Dec 12, 2008
    Messages:
    6,003
    Resources:
    26
    Maps:
    1
    Spells:
    8
    Tutorials:
    7
    JASS:
    10
    Resources:
    26
    Can we be happy and pretend SetUnitPosition is slower?
    Please?

    HIDE THE TRUTH.
     
  18. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,789
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    False alarm everyone;)

    Code (vJASS):

    globals
        unit u = CreateUnit(Player(0), 'hfoo', 0, 0, 0)
    endglobals
     

    Doesn't actually create a unit so "u" was null in my tests. I had some other hidden trigger create a footman so I didn't realize the unit wasn't being created.

    This also makes my GetWidgetX test invalid, so let me re-do that.

    EDIT: Updated main thread.
     
  19. Magtheridon96

    Magtheridon96

    Joined:
    Dec 12, 2008
    Messages:
    6,003
    Resources:
    26
    Maps:
    1
    Spells:
    8
    Tutorials:
    7
    JASS:
    10
    Resources:
    26
    Good.

    We can all live in peace now.
     
  20. Malhorne

    Malhorne

    Joined:
    Sep 14, 2012
    Messages:
    2,327
    Resources:
    6
    Spells:
    4
    Tutorials:
    1
    JASS:
    1
    Resources:
    6
    Holy ^^'
    Now it is better ^^