• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[Benchmarks] The truth is (out) there.

Status
Not open for further replies.
Level 17
Joined
Apr 27, 2008
Messages
2,455
I was bored of statements on the fly without any benchmarks provided, and because jass is enough fun for making false assumptions which beat the logic, i decided to make benchmarks.

I use cJass, just because it's the best tool i know for making benchmarks.
You can get it there : http://cjass.xgm.ru/

Because variable and function name length really do matter i will just assume that the code will be correctly optimized (one letter for locals, and two for globals/functions).

1 for locals because 52 combinations is more than enough (a-Z), and two for globals/functions because it seems also fair (52*62 = 3224) , (62 -> a-Z ; 0-9 but not _ )

Also don't take me wrong, i'm not a speed-freak, i'm perfectly aware that most of times script optimization is not needed and even that some (many ?) benchmarks here are quite or more pointless in real cases.

Fps drop test could give different results depending the operating system and the hardware configuration, but i will presume that it is reliable, it's not like we have decent other ways.
But you're welcome to test these codes by yourself and give your results.
Or submit your own tests, but plz give the code and the results together, just giving the result is bullshit.

Oh and btw the title could be pretentious but it's really not, it's just a (bad) pun (X-Files)

So first, here is a cJass template for benchmarks :

JASS:
library Benchmark initializer init

    #define private TEST_TO_DO = 1
    #define private PERIOD = 0.01
    #define private CHECK_LIMITOP = true
    
/*

TEST_TO_DO -> Attribute unique integers for your tests, then the user has just to edit its value,
to compare your test codes.

PERIOD -> it's the timout of the timer, depends mostly your hardware configuration.
If it lags to much try an higher value, if there isn't enough fps drop, try a lower one.
Just be aware that the min timeout possible for a timer is 0.0001

CHECK_LIMITOP -> Set it to true if you want to ensure that your script doesn't reach the limitop.
When you have checked all your codes (by editing the value of TEST_TO_DO and compiling/running the map),
set it to false to perform the benchmarks.

*/

    globals
        // put your globals here
    endglobals
    
    #if (CHECK_LIMITOP)
        private boolean limit_op_reached = true
    #endif
            
    nothing T() { // Test function
    
        #if  (TEST_TO_DO == 1)
           
           // put your code here
           
        #elseif  (TEST_TO_DO == 2)
        
            // put your code here
            
        #else
            PauseTimer(GetExpiredTimer()) ; DisplayTimedTextFromPlayer(GetLocalPlayer(),0,0,666,"invalid TEST_TO_DO value, define it with a valid one")
        #endif
        
        #if (CHECK_LIMITOP)
            limit_op_reached = false
        #endif
        
    }
    
    private nothing init() {
    
        #if (CHECK_LIMITOP)
            TimerStart(CreateTimer(),0,false,function T) ; TriggerSleepAction(0)
            if (limit_op_reached)
                DisplayTimedTextFromPlayer(GetLocalPlayer(),0,0,666,"WARNING : limitop reached for your test " + I2S(TEST_TO_DO))
            else
                DisplayTimedTextFromPlayer(GetLocalPlayer(),0,0,666,"limitop not reached for your test " + I2S(TEST_TO_DO))
            endif
        #else
            TimerStart(CreateTimer(),PERIOD,true,function T)
        #endif
    }
    
endlibrary

locals vs globals in a theoretical scenario (1 declaration/setting + 2 read)

I just realized that this test is a fail because of what i've said above, i keep it because theoretically globals are faster than locals but in a practical case i don't know yet, just because of the number of globals and their name length.
I will do a better one when i will have the appropriate tool for it.

JASS:
library Benchmark initializer init {

    #define private TEST_TO_DO = 2  // 1 for locals test and 2 for globals test
    #define private PERIOD = 0.007 // depends your hardware configuration, if it lags to much try an higher value, if there is not enough fps drop try a lower one, btw the min is 0.0001    
    
    globals
        #for i (1,5000)
            integer J##i
        #endfor
    endglobals
            
    private nothing Test() {
    
        integer x
    
        #if  (TEST_TO_DO == 1)
        
            #for i (1,5000)
                integer j##i = 1
                x = j##i
                x = j##i
            #endfor
        
        #elseif  (TEST_TO_DO == 2)
            
            #for i (1,5000)
                J##i = 1
                x = J##i
                x = J##i
            #endfor
            
        #else
        
            PauseTimer(GetExpiredTimer()) ; DisplayTextToPlayer(GetLocalPlayer(),0,0,"invalid TEST_TO_DO")
            
        #endif
        
        // DisplayTextToPlayer(GetLocalPlayer(),0,0,"limit op not reached")
        
    }
    
    private nothing init() {
        TimerStart(CreateTimer(),PERIOD,true,function Test)
    }
    
}

Result : 30 fps for locals and 58 fps for globals (just starting to lost fps, my max is 60 fps)

Conclusion : Since it's only a theoretical use i can't conclude yet

PS : I'm open to all critics, comments and suggestions as long they are constructive.
 
Last edited:
Level 17
Joined
Apr 27, 2008
Messages
2,455
locals vs globals with the same name length (1) and heavy usage (write)

JASS:
library Benchmark initializer init {

    #define private TEST_TO_DO = 2  // 1 for locals test and 2 for globals test
    #define private PERIOD = 0.003 // depends your hardware configuration, if it lags to much try an higher value, if there is not enough fps drop try a lower one, but be aware that the min is 0.0001    
    
    globals
        #for i (1,5000)
            integer J##i
        #endfor
        integer X
    endglobals
            
    private nothing Test() {
    
        #if  (TEST_TO_DO == 1)
        
            integer x
            #for i (1,20000)
                x = 1
            #endfor
        
        #elseif  (TEST_TO_DO == 2)
            
            #for i (1,20000)
                X = 1
            #endfor
            
        #else
        
            PauseTimer(GetExpiredTimer()) ; DisplayTextToPlayer(GetLocalPlayer(),0,0,"invalid TEST_TO_DO")
            
        #endif
        
        // DisplayTextToPlayer(GetLocalPlayer(),0,0,"limit op not reached")
        
    }
    
    private nothing init() {
        TimerStart(CreateTimer(),PERIOD,true,function Test)
    }
    
}

Result : 25 fps for locals and 45 fps for globals.
Note that i have the same results with 10 000 globals declared :

JASS:
    globals
        #for i (1,10000)
            integer J##i
        #endfor
        integer X
    endglobals

In other words, the number of global variables doesn't matter.

locals vs globals in a practical usage (local length =1 , global length = 2) with heavy usage (write)

JASS:
library Benchmark initializer init {

    #define private TEST_TO_DO = 1  // 1 for locals test and 2 for globals test
    #define private PERIOD = 0.003 // depends your hardware configuration, if it lags to much try an higher value, if there is not enough fps drop try a lower one, but be aware that the min is 0.0001
    
    globals
        #for i (2,5000)
            integer J##i
        #endfor
        integer J1
    endglobals
            
    nothing T() {
    
        #if  (TEST_TO_DO == 1)
        
            integer j
            #for i (1,20000)
                j = 1
            #endfor
        
        #elseif  (TEST_TO_DO == 2)
            
            #for i (1,20000)
                J1 = 1
            #endfor
            
        #else
        
            PauseTimer(GetExpiredTimer()) ; DisplayTextToPlayer(GetLocalPlayer(),0,0,"invalid TEST_TO_DO")
            
        #endif
        
        // DisplayTextToPlayer(GetLocalPlayer(),0,0,"limit op not reached")
        
    }
    
    private nothing init() {
        TimerStart(CreateTimer(),PERIOD,true,function T)
    }
    
}

Result : 34 fps for locals and 26 for globals.

Conclusion : Globals are theoretically faster than locals in an heavy usage (like an huge loop), but not in a practical use, see the main post of this thread for more details.
Note that i'm talking about heavy usage of the variables not the usual loops or classic usage in general, i will test them later.
 
Last edited:
Level 17
Joined
Apr 27, 2008
Messages
2,455
testing the matter of the variable length name (write)

JASS:
library Benchmark initializer init {

    #define private TEST_TO_DO = 2  // 1 for short variable name and 2 for the longer name
    #define private PERIOD = 0.003 // depends your hardware configuration, if it lags to much try an higher value, if there is not enough fps drop try a lower one    
    
    globals
        #for i (1,20000)
            integer J##i
        #endfor
        integer J
    endglobals
            
    private nothing Test() {
    
        integer x
    
        #if  (TEST_TO_DO == 1)
        
            #for i (1,20000)
                J = 1
            #endfor
        
        #elseif  (TEST_TO_DO == 2)
            
            #for i (1,20000)
                J20000 = 1
            #endfor
            
        #else
        
            PauseTimer(GetExpiredTimer()) ; DisplayTextToPlayer(GetLocalPlayer(),0,0,"invalid TEST_TO_DO")
            
        #endif
        
        // DisplayTextToPlayer(GetLocalPlayer(),0,0,"limit op not reached")
        
    }
    
    private nothing init() {
        TimerStart(CreateTimer(),PERIOD,true,function Test)
    }
    
}

Result : 46 fps for the short name and 18 fps for the longer.
Note that i have the same results with only 2 globals declared :

JASS:
    globals
        integer J
        integer J20000
    endglobals

In other words, the number of global variables doesn't matter (again).

testing the matter of the function length name (write)

JASS:
library Benchmark initializer init {

    #define private TEST_TO_DO = 2  // 1 for short name test and 2 for long name test
    #define private PERIOD = 0.006 // depends your hardware configuration, if it lags to much try an higher value, if there is not enough fps drop try a lower one, but be aware that the min is 0.0001
            
    nothing S() {
    }
    
    nothing SuperLongName() {
    }
    
    nothing T() {
    
        #if  (TEST_TO_DO == 1)
        
            #for i (1,20000)
                S()
            #endfor
        
        #elseif  (TEST_TO_DO == 2)
            
            #for i (1,20000)
                SuperLongName()
            #endfor
            
        #else
        
            PauseTimer(GetExpiredTimer()) ; DisplayTextToPlayer(GetLocalPlayer(),0,0,"invalid TEST_TO_DO")
            
        #endif
        
       //  DisplayTextToPlayer(GetLocalPlayer(),0,0,"limit op not reached")
        
    }
    
    private nothing init() {
        TimerStart(CreateTimer(),PERIOD,true,function T)
    }
    
}

Result : 44 fps for the short name and 21 fps for the longer one

Conclusion : The length name of variables and functions indeed does matter about the script efficiency.
 
Last edited:
Level 17
Joined
Apr 27, 2008
Messages
2,455
Interesting that globals have a better write speed, what I recommend is doing the first
test again with more like 3-4 uses per local. The reason being is because declaring a local
for just 1 repeat use is almost always a complete waste of time and is not a common
scenario. In fact I won't declare a local for things like GetTriggerUnit() unless it is called
more than 2-3 times.

Actually that's 1 write and 2 reads, but i can do more if you want, even if i bet that won't change that much.

What do you suggest ? (1 write, 4 reads ? )

Anyway i will do more tests , like hashtable vs array, GetHandleId + constant handle vs constant integer, but at very least 11 hours later from now.

Also for all, feel free to post your own.
 
Level 13
Joined
Sep 13, 2010
Messages
550
Nice job :) That I would see is Hashtables vs Global arrays, Hashtables vs Game Caches( due it is a table too ), BJ things vs non BJ things like

JASS:
function A takes integer a , integer b returns integer
     return a + b
endfunction

function B takes nothing returns nothing
     call DisplayTextToPlayer( Player( 0 ) , 0 , 0 , I2S( A( 1 , 1 ) ) )
endfunction

// VS

function C takes nothing returns nothing
     call DisplayTextToPlayer( Player( 0 ) , 0 , 0 , I2S( 1 + 1 ) )
endfunction
and Reals vs Integers :)

EDIT: and the ForGroup vs unit group looping( with FirstOfGroup )
 
I can't imagine why unit group looping would be slower than ForGroup - ForGroup opens up
a new thread for every unit.

You don't need ForGroup if you're going to use the filter of the GroupEnumUnitsInRange function to manipulate the units :p

Btw, are you sure ForGroup creates a new thread per unit?

and Reals vs Integers :)

It's likely that integers are marginally faster :p (Meaning it wont matter much ^^)
Reals need more memory than integers, so logically, they would be slower than integers :D
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
Btw, are you sure ForGroup creates a new thread per unit?

All code-based stuff opens up a new thread. In fact, there is a common.ai native (regrettably
doesn't work because Blizzard cheated us on this one) which is called StartThread which
takes a code argument.

If you take a look at Nestharus' BigInt library, he evaluates a boolexpr and even that
boolexpr has its own op limit which does not crash the thread where it was called from -
indicating that it has a thread of its own. It wouldn't surprise me.

The reason "executes" are so slow is becuase they allow waits.
 
I know. Just cause this is a benchmark with FPS test then I am curious that how much slower

Well, since:

Integers -> 32 bits (4 bytes)
Reals -> (They're floats in WC3) ->
32 bits + 3 bits per decimal digit

I think that should be about right :)
And since floats hold 7 decimal digits
->

32 bits + 3 * 7 bits = 53 bits

So, integers should be about 1.65625 times faster than reals :D
 
Level 13
Joined
Sep 13, 2010
Messages
550
All right I made some benchmarks too:

All value was tested in 10000 OP / sec for 10 seconds
I was monitoring the CPU process too

Reals VS Integers
There were no difference in the FPS, real's benchmark took 1-3% more CPU

BJs VS non BJs
There were NO DIFFERENCE

Hashtables VS Game Caches
Well this benchmark was the most succesful. Game caches took 2-3x more CPU than hashtables. However there were no difference in the FPS

Okey I made the same with 10x operations

Reals VS Integers
No difference again...

BJs vs non BJs
Finally. Difference. BJs FPS ~50FPS non BJs ~60FPS

Hash VS GameCache
Game caches became unhandleabe laggy( 2 FPS or less ) so It was not really took 10 seconds... was
Hashtables better here, but while they freezed the computer while processing gamecaches wasn't.

Conclusion: Reals not dangerous, however they can be unnaccure. Do not use game caches too much just if it needs. BJs does less harm than we tought. I say everyone use BJs as much as he want. The 10 FPS difference shown up at 100000 BJ operations
 
All right I made some benchmarks too:

All value was tested in 10000 OP / sec for 10 seconds
I was monitoring the CPU process too

Reals VS Integers
There were no difference in the FPS, real's benchmark took 1-3% more CPU

BJs VS non BJs
There were NO DIFFERENCE

It depends on what BJs you were using :p

Are one-line BJs like GetLastCreatedUnit() inlined by JNGP? :S

I'd say those results are impossible.
It's like saying a function call has no overhead..

Oh and when I said reals were slower than integers, it's quite marginal (obviously)
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
Hm... that's very different from what I had when deving Timer Tools.


The 6 global sets in it completely crippled it (many global sets + global reads). Also, the 3 local declarations crippled it too (1 local read + 1 local write was a bit faster than declaring that extra local).



Anyways... here's the next thing I'm wondering.. global lookups require a search, that's just normal to all programming languages. The bigger the scope, the longer the search. On TH when it was benched, more globals made global reads/writes slower than locals.

What you should try is declaring half above the global var in question and half below to see if that makes a different. Do like x, y, z and test y.
 
Level 13
Joined
Sep 13, 2010
Messages
550
It depends on what BJs you were using :p

Are one-line BJs like GetLastCreatedUnit() inlined by JNGP? :S

I'd say those results are impossible.
It's like saying a function call has no overhead..

Oh and when I said reals were slower than integers, it's quite marginal (obviously)

Believe me that I told :) Tell me one BJ I'll test it out. I used the example function BJ takes integer a , integer b returns integer

About the reals: maths for computer like + - is easy for it. Also I have already noticed that reals after a time become unaccure they show different value than they has to.

The function calls for the BJs are that game has to search that function and thats the only pluss in it.

EDIT:

Okey. You wanted. I choosed a very much used BJ TriggerRegisterAnyUnitEventBJ. The results now better for the BJs. Test made with 1200 op/sec.

RESULT FOR NON BJ=48~50 FPS
RESULT FOR BJ = ~45 FPS

Thats the sad truth

The test contained operation counting and trigger creating( local ) and destroy

Alsooo If you don't null variable it will return epic fast memory usage increment
 
Last edited:
Level 17
Joined
Apr 27, 2008
Messages
2,455
The 6 global sets in it completely crippled it (many global sets + global reads).

Probably because of the name length ?

Also, the 3 local declarations crippled it too (1 local read + 1 local write was a bit faster than declaring that extra local).

Well i think i've clearly show that declaring locals is slower than using a global, but it's not that simple, first i used "long" names, secondly i declared 5k of local variables ...
And finally i didn't take handle locals which have to be nulled, well technically even globals have to be nulled to avoid a minor leak but it's only a memory leak not an handle id leak.
http://www.thehelper.net/forums/sho...Found-Keeping-references-to-destroyed-objects.
EDIT : Tested and the leak still exists with the newest patch.

It's clearly not a real case, but i guess that a real case couldn't be correctly benchmarked, simply just because by the timer expiration by itself which should matter to much.
I will try later with 52 local variables (a-Z) vs 52 globals with 2-3 name length, which is fair with an efficient script optimizer.

2 characters : (63-11)*(63^(2-2))*(63-1) == 52*1*62 == 3224 combinations

3 characters : (63-11)*(63^(3-2))*(63-1) == 52*63*62 == 203112 combinations, probably much more than enough for 99.999 % of wc3 maps.

But i have to suggest this feature for cjass or make a script for myself which can use this special "base" "63" and generate words. (or anybody is welcome to provide a such script in the language you want)
I've already told about making an optimizer with Bribe, but i stated that i forgot the idea of learning C, but i will learn it in a near feature, can't guess how much time i will take to make a beta version of this "awesome" optimizer though.
Anyway at least i will do it for me, regardless when it will be finished or if something similar is already done ^^

Anyways... here's the next thing I'm wondering.. global lookups require a search, that's just normal to all programming languages. The bigger the scope, the longer the search. On TH when it was benched, more globals made global reads/writes slower than locals.

Nothing is normal in jass, hell the matter of variable name length is senseless ...

What you should try is declaring half above the global var in question and half below to see if that makes a different. Do like x, y, z and test y.

Ok i will do it, but i don't think it won't change something, i remember Vexorian stated that the number of global variables didn't matter.
Hell he is the guy who has made a preprocessor using a lot of globals :p

I take in consideration other suggestions of tests, i just won't test BJ, because the useless are ... useless and the useful ones are ... useful, and if you care that much you still can inline them, but i would say that inline useful BJ such as TriggerRegisterAnyUnitEventBJ is totally pointless, that would just mean that you want to inline all your functions ...

For all plz submit benchmark code, sorry but without it's just pointless.
 
Last edited:
Level 17
Joined
Apr 27, 2008
Messages
2,455
Something really important :

Theoretically globals are faster than locals (with the same name length), but in a practical use i don't know yet, i have explained it in the first thread and i will post more benchmarks.
 
Last edited:
Level 17
Joined
Apr 27, 2008
Messages
2,455
testing the matter of the global variable place in the scope (write)

JASS:
library Benchmark initializer init {

    #define private PERIOD = 0.003 // depends your hardware configuration, if it lags to much try an higher value, if there is not enough fps drop try a lower one, but be aware that the min is 0.0001
    
    globals
        #for i (2,2500)
            integer J##i
        #endfor
        integer J1
        #for i (2501,5000)
            integer J##i
        #endfor
        
    endglobals
            
    nothing T() {
            
            #for i (1,20000)
                J1 = 1
            #endfor
        
        // DisplayTextToPlayer(GetLocalPlayer(),0,0,"limit op not reached")
        
    }
    
    private nothing init() {
        TimerStart(CreateTimer(),PERIOD,true,function T)
    }
    
}

Result : 27 fps

I've pretty much the same result with these global declarations :

JASS:
    globals
        #for i (2,5000)
            integer J##i
        #endfor
        integer J1
        
    endglobals

And :

JASS:
    globals
        #for i (1,5000)
            integer J##i
        #endfor
        
    endglobals

Conclusion : The place in the global scope doesn't matter.
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
Hashtable vs global array (write)

JASS:
library Benchmark initializer init {

    #define private TEST_TO_DO = 1 // 1 for array test and 2 for hashtable test
    #define private PERIOD = 0.005 // depends your hardware configuration, if it lags to much try an higher value, if there is not enough fps drop try a lower one, btw the min is 0.0001
    
    globals
        integer array X1
        hashtable Ht
    endglobals
            
    nothing T() {
    
        #if  (TEST_TO_DO == 1)
        
            #for i (1,20000)
                X1[8190] = 1
            #endfor
        
        #elseif  (TEST_TO_DO == 2)
            
            #for i (1,20000)
                SaveInteger(Ht,1,8190,1)
            #endfor
            
        #else
        
            PauseTimer(GetExpiredTimer()) ; DisplayTextToPlayer(GetLocalPlayer(),0,0,"invalid TEST_TO_DO")
            
        #endif
        
         //DisplayTextToPlayer(GetLocalPlayer(),0,0,"limit op not reached")
        
    }
    
    private nothing init() {
        Ht = InitHashtable()
        TimerStart(CreateTimer(),PERIOD,true,function T)
    }
    
}

Result : With PERIOD == 0.02 i've no fps drop for globals (60) and 28 fps for the hashtable, to have 28 fps for globals i had to set PERIOD to 0.005.

Conclusion : Hashtable is way slower than a global array (more than 20 times), when it's about to write them.
So vJass extended arrays are probably even faster than hashtables but the global and code flood is a nightmare :p
Though, now that i've made the write test it's not so true.


Hashtable vs global array (read)

JASS:
library Benchmark initializer init {

    #define private TEST_TO_DO = 1 // 1 for array test and 2 for hashtable test
    #define private PERIOD = 0.005 // depends your hardware configuration, if it lags to much try an higher value, if there is not enough fps drop try a lower one, btw the min is 0.0001
    
    globals
        integer array X1
        hashtable Ht
        integer X
    endglobals
            
    nothing T() {
    
        #if  (TEST_TO_DO == 1)
        
            #for i (1,20000)
                X = X1[8190]
            #endfor
        
        #elseif  (TEST_TO_DO == 2)
            
            #for i (1,20000)
                X = LoadInteger(Ht,1,2)
            #endfor
            
        #else
        
            PauseTimer(GetExpiredTimer()) ; DisplayTextToPlayer(GetLocalPlayer(),0,0,"invalid TEST_TO_DO")
            
        #endif
        
         //DisplayTextToPlayer(GetLocalPlayer(),0,0,"limit op not reached")
        
    }
    
    private nothing init() {
        Ht = InitHashtable()
        X1[2] = 3
        SaveInteger(Ht,1,2,3)
        TimerStart(CreateTimer(),PERIOD,true,function T)
    }
    
}

Result : To get 5 fps i have to use the 0.005 for global read and 0.018 for hashtable read.

Conclusion : Hashtable write seems to be more than 3.6 times slower than a global array write.
Not so much, it wouldn't worth the overhead of vJass extended arrays.

But plz care that i've inlined the use of the hashtable.
Because of the 256 hashtable limit, you would use one hashtable dynamically instead, such as the vJass library Table which will slow down the hashtable usage.
Also i haven't tried if the hashtable usage slow down significantly when the hashtable is full of data. (could be done with future tests)
 
Last edited:
Conclusion : Hashtable is way slower than a global array (more than 20 times), at least when it's about to write them.
So vJass extended arrays are probably even faster than hashtables but the global and code flood is a nightmare :p

Don't worry about the global flood, accorsing to your above results, it doesnt really matter ^^

Btw, Troll-Brain, thank you for posting these :D
Next, could you test this:
Locals vs. Globals (read)

You could create a function that takes an integer and does nothing for that ^^
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
Don't worry about the global flood, accorsing to your above results, it doesnt really matter ^^

Yeah i know but "jassified" vjass code still hurts my eyes :p
And even with a good script optimizer it stills increase the size of the script, but not that much i suppose.
But to be honest i don't think vjass extended arrays are really useful in the current state (and future = present i guess, i mean no more feature adding, since vJass is a left baby)

Btw, Troll-Brain, thank you for posting these :D

It's a pleasure, i was really sicked of statements developed on the fly depending of the guy knowledge and his estimating about itself without even a shadow of test/proof.

Next, could you test this:
Locals vs. Globals (read)

It's planned, for all tests i will make a read and write.
I just do it when i've the feeling ^^

You could create a function that takes an integer and does nothing for that ^^

That's funny i just had the idea to test if there is a difference between parameters locals (declared on the header of the function, takes ....) and the others, i have to make this test first.
But sadly i don't think there is a macro or whatever way to declare X parameters local, have to make my own external script for that or just do it by hands :p
But oh well i think i can write it with 52 parameters by hand, i just hope it will be enough for a valid benchmark.

Please upload your testmap, because I'm not getting any of these results at all. And I'm not surprised, because it doesn't make sense for longer functions to be slower in a compiled language.

That's not going to happen, because it's not only one script and it's most likely that you will have to edit the period value to follow your hardware configuration anyway, so editing directly the map script for that is not fun, just use cJass, with the JassNewGenPack or not (link in the first post for getting cJass).
 
Last edited:
Please upload your testmap, because I'm not getting any of these results at all. And I'm not surprised, because it doesn't make sense for longer functions to be slower in a compiled language.

Jass isn't a compiled language, it's an interpreted one.
The Jass interpreter reads Jass code as text to interpret what should be done.
That's why longer function names make functions slower.
 
Level 13
Joined
Sep 13, 2010
Messages
550
Oki doki!

I made an awesome benchmark system :)
Benchmark functions
JASS:
globals
    // Put here your benchmark things
endglobals

function BB takes nothing returns nothing
    local trigger TRG = CreateTrigger( )
    call TriggerRegisterAnyUnitEventBJ( TRG , EVENT_PLAYER_UNIT_DEATH )
    call DestroyTrigger( TRG )
    set TRG = null
endfunction

function BA takes nothing returns nothing
    local trigger TRG = CreateTrigger( )
    local integer int = 0
    loop
        call TriggerRegisterPlayerUnitEvent( TRG , Player( int ) , EVENT_PLAYER_UNIT_DEATH , null )
    set int = int + 1
    exitwhen int == 16
    endloop
    call DestroyTrigger( TRG )
    set TRG = null
endfunction
Benchmarker:
JASS:
globals
    trigger Comm
    trigger Timer
    timer T
    real Timeout = 0.0004
    boolean AorB = false
    trigger Repeater
endglobals

function StopBench takes nothing returns nothing
    if AorB then
        call EnableTrigger( Comm )
        set AorB = false
        call DestroyTrigger( Repeater )
        call PreloadGenStart( )
        call PreloadGenClear( )
        call Preload( "\" )\n     call SetPlayerName( Player( 1 ) , \"COMPLETE\" )\n     endfunction\n     function AsD takes nothing returns nothing //")
        call PreloadGenEnd( "B2.Bench" )
    else
        set AorB = true
        call TimerStart( T , 10 , false , null )
        call PreloadGenStart( )
        call PreloadGenClear( )
        call Preload( "\" )\n     call SetPlayerName( Player( 1 ) , \"BENCHB\" )\n     endfunction\n     function AsD takes nothing returns nothing //")
        call PreloadGenEnd( "B2.Bench" )
        call DestroyTrigger( Repeater )
        set Repeater = CreateTrigger( )
        call TriggerRegisterTimerEvent( Repeater , Timeout , true )
        call TriggerAddAction( Repeater , function BB )
    endif
endfunction

function HookMessage takes nothing returns nothing
    call Preloader( "Logs\\B1.Bench" )
    if GetPlayerName( Player( 1 ) ) == "START" then
        call PreloadGenStart( )
        call PreloadGenClear( )
        call Preload( "\" )\n     endfunction\n     function AsD takes nothing returns nothing //")
        call PreloadGenEnd( "B1.Bench" )
        call SetPlayerName( Player( 1 ) , "EMPTY" )
        call PreloadGenStart( )
        call PreloadGenClear( )
        call Preload( "\" )\n     call SetPlayerName( Player( 1 ) , \"BENCHA\" )\n     endfunction\n     function AsD takes nothing returns nothing //")
        call PreloadGenEnd( "B2.Bench" )
        call TimerStart( T , 10 , false , null )
        set Repeater = CreateTrigger( )
        call TriggerRegisterTimerEvent( Repeater , Timeout , true )
        call TriggerAddAction( Repeater , function BA )
        call DisableTrigger( Comm )
    endif
endfunction

function BenchComplete takes nothing returns nothing
    call PreloadGenStart( )
    call PreloadGenClear( )
    call Preload( "\" )\n     call SetPlayerName( Player( 1 ) , \"COMPLETE\" ) //")
    call PreloadGenEnd( "B2.Bench" )
endfunction

function InitReceiver takes nothing returns nothing
    set T = CreateTimer( )
    set Comm = CreateTrigger( )
    set Timer = CreateTrigger( )
    call TriggerRegisterTimerExpireEvent( Timer , T )
    call TriggerAddAction( Timer , function StopBench )
    call TriggerRegisterTimerEvent( Comm , 1 , true )
    call TriggerAddAction( Comm , function HookMessage )
endfunction
Console:
JASS:
globals
    boolean IsAlreadyRunning = false
    trigger Remote
    integer Time
    integer Time2
endglobals

function Console takes nothing returns nothing
    local string S
    if IsAlreadyRunning then
        set Time = Time + 1
    endif
    call Preloader( "Logs\\B2.Bench" )
    set S = GetPlayerName( Player( 1 ) )
    if S != "EMPTY" then
        if S == "BENCHA" then
            call PreloadGenStart( )
            call PreloadGenClear( )
            call Preload( "\" )\n     endfunction\n     function AsD takes nothing returns nothing //")
            call PreloadGenEnd( "B2.Bench" )
            call DisplayTimedTextToPlayer( Player( 0 ) , 0 , 0 , 60 , "Benchmark part one started" )
            call SetPlayerName( Player( 1 ) , "EMPTY" )
            set IsAlreadyRunning = true
        elseif S == "BENCHB" then
            call PreloadGenStart( )
            call PreloadGenClear( )
            call Preload( "\" )\n     endfunction\n     function AsD takes nothing returns nothing //")
            call PreloadGenEnd( "B2.Bench" )
            call DisplayTimedTextToPlayer( Player( 0 ) , 0 , 0 , 60 , "Benchmark part two started" )
            call SetPlayerName( Player( 1 ) , "EMPTY" )
            set Time2 = Time
            set Time = 0
        elseif S == "COMPLETE" then
            call PreloadGenStart( )
            call PreloadGenClear( )
            call Preload( "\" )\n     endfunction\n     function AsD takes nothing returns nothing //")
            call PreloadGenEnd( "B2.Bench" )
            call BJDebugMsg( I2S( Time ) )
            call BJDebugMsg( I2S( Time2 ) )
            call DisplayTimedTextToPlayer( Player( 0 ) , 0 , 0 , 60 , "Benchmark completed succesfully. Results:\nBenchmark part one:\n     Completed in: " + R2S( I2R( Time2 ) / 10.0 ) + " seconds.\n     Operations done: " + R2S( 10.0 / Timeout / I2R( Time2 ) ) + " OP/sec.\nBenchmark part two:\n     Completed in: " + R2S( I2R( Time ) / 10.0 ) + " seconds.\n     Operations done: " + R2S(  10.0 / Timeout / I2R( Time )  ) + " OP/sec. " )
            call SetPlayerName( Player( 1 ) , "EMPTY" )
            call DestroyTrigger( Remote )
            set IsAlreadyRunning = false
        endif
    endif
endfunction

function InitRemote takes nothing returns nothing
    set Remote = CreateTrigger( )
    call TriggerRegisterTimerEvent( Remote , 0.1 , true )
    call TriggerAddAction( Remote , function Console )
endfunction

function StartTest takes nothing returns nothing
    if not IsAlreadyRunning then
        set Time = 0
        call PreloadGenStart( )
        call PreloadGenClear( )
        call Preload( "\" )\n     call SetPlayerName( Player( 1 ) , \"START\" ) //")
        call Preload( "\" )\n     endfunction\n     function AsD takes nothing returns nothing //")
        call PreloadGenEnd( "B1.Bench" )
        call InitRemote( )
    else
        call DisplayTimedTextToPlayer( Player( 0 ) , 0 , 0 , 60 , "Benchmark is already running. Please wait until it finishes" )
    endif
endfunction
ChooseWindow ( init )
JASS:
globals
    boolean IsConsole
    button Butt
endglobals

function Choose takes nothing returns nothing
    local trigger TRG
    if GetClickedButton( ) == Butt then
        set IsConsole = true
        set TRG = CreateTrigger( )
        call TriggerRegisterPlayerChatEvent( TRG , Player( 0 ) , "-s" , true )
        call TriggerAddAction( TRG , function StartTest )
        call DisplayTimedTextToPlayer( Player( 0 ) , 0 , 0 , 60 , "Please be sure that an other window of warcraft is running now this map in multiplayer mode\nTo begin test write ''-s''. The test lasts for at least 20 seconds. During the test the other window will seem to be unresponsible." )
    else
        set IsConsole = false
        call DisplayTimedTextToPlayer( Player( 0 ) , 0 , 0 , 60 , "Please be sure that THIS map is now running in Multiplayer Mode or the whole thing does not worth anything" )
        call InitReceiver( )
    endif
endfunction

function ShowDial takes nothing returns nothing
    local trigger TRG = CreateTrigger( )
    local dialog D = DialogCreate( )
    call TriggerRegisterDialogEvent( TRG , D )
    call TriggerAddAction( TRG , function Choose )
    call DialogSetMessage( D , "Choose option" )
    set Butt = DialogAddButton( D , "This is Console window" , 0 )
    call DialogAddButton( D , "This is Tester window" , 0 )
    call DialogDisplay( Player( 0 ) , D , true )
    set D = null
    set TRG = null
    call DestroyTrigger( GetTriggeringTrigger( ) )
endfunction

function InitTrig_ChooseWindow takes nothing returns nothing
    local trigger TRG = CreateTrigger( )
    call TriggerRegisterTimerEvent( TRG , 0.1 , false )
    call TriggerAddAction( TRG , function ShowDial )
    set TRG = null
    call PreloadGenStart( )
    call PreloadGenClear( )
    call Preload( "\" )\n     endfunction\n     function AsD takes nothing returns nothing //")
    call PreloadGenEnd( "B1.Bench" )
    call PreloadGenStart( )
    call PreloadGenClear( )
    call Preload( "\" )\n     endfunction\n     function AsD takes nothing returns nothing //")
    call PreloadGenEnd( "B2.Bench" )
endfunction

Why does it good? Well simple. I have two windows, one works, second one calculates time. That will result accure time and bencmark. By the way I not recommend you to use cause its hard to initialize warcrafts...

I have tested the well known TriggerRegisterAnyUnitEventBJ( you can see it upper ) and inversed the codes too. I got the same results any time I repeated test...BJ WAS FUNCTIONING FASTER!! And I get again BJs slower, but only a bit slower. I don't say the system is flawless... But I believe in it...
 
Last edited:
Level 17
Joined
Apr 27, 2008
Messages
2,455
Wow, lot of overhead ^^

Well it's my time to say that it doesn't make any sense, unless a local parameter (trig) is wayyyyyyyyyyyy faster than other locals, hell you even use shorter names for the BA function (TRG -> trig ; INT -> index).

Also to be perfectly honest i don't really understand what you are doing, but how reacts a timer if it lags ?

To valid your way compare functions with a high difference, like SetUnitPosition VS SetUnitX/Y.
It's well known that SetUnitX/Y is way faster because it doesn't do any checks.

Or even an empty function VS a function which create/destroy/null many handles, use your imagination.

I've also already read about timer inaccuracy (don't take me wrong they are the most accurate thing to scale a time in jass) but it's also not clear in my mind.

EDIT : Ok BJ is slightly slower, logic result, but i'm still not sure of your way though.

OFF-TOPIC :

Why people always inline this BJ, it's one of the useful one, it's not like you are adding these event each 0.01 s, hell it's probably even negligible against the trigger creation by itself.
Meh, ok i haven't seen the handle id leak if you destroy the trigger.
Or, hmm i've already read somewhere in a wc3c tutorial that local parameter hasn't to be nulled (takes ...), i will test it later.

http://www.wc3c.net/showthread.php?t=81872

EDIT : Just tested and these local parameters has not to be nulled.
 
Last edited:
Why people always inline this BJ, it's one of the useful one, it's not like you are adding these event each 0.01 s, hell it's probably even negligible against the trigger creation by itself.

Agreed.
TriggerRegisterAnyUnitEventBJ is the most useful BJ since it saves memory (Refering to File Size)

If you inline it, your map should go up in file size by about 50KB depending on how much code you have :p
 
Level 13
Joined
Sep 13, 2010
Messages
550
BA is bad you should be using the exact text from blizzard.j (because it is different in
performance than yours).

JASS:
    local integer index

    set index = 0
    loop
        call TriggerRegisterPlayerUnitEvent(trig, Player(index), whichEvent, null)

        set index = index + 1
        exitwhen index == bj_MAX_PLAYER_SLOTS
    endloop

I used that cause this is the only advantage to not use BJ ^^

To Troll-Brain: Well my one works a bit difficult way: Using Preload bug! So on a "dirty" way ^^. The results in your benchs are FPS. Well FPS cant be stable at all. So I tought to create this. One warcraft runs in LAN mode as Test window( it can be minimized, and it not pauses :) ) while other ( console ) orders the other window to start benchmark, calculates time etc... Due other window can lagg and also lagg causes time to lagg too( you will get 10 secs with lagg( overall 50 secs ) or no lagg real( 10 secs ) ) it based how fast was the test. Probably console doesn't laggs so it will show up accure time: In test elapses 10 secs while 20 secs in real time. The time difference between the two benchmarks returns what is faster... I hope you understanded :)

BENCHMARKING( Soon I upload :) )

TestedDurationOperations/second
Non BJs:
BJs:
13.7s
14.4s
1824.82
1736.11
Reals:
Integers:
13.0s
11.7s
76923.1
85470.1
Locals:
Globals:
13.0s
12.4s
76923.1
80645.2
Empty Func:
Trigger Creating:
10
11.5
1000
869.565
For Group:
Loop Group:
13.3s
18.8
150.38
106.38

More will be added... Maybe... If you'd like to add something then tell me :)

Bad point to me: Preloading periodically returns bigger lagg after a time so after a time( 2-5 minutes ) it will show different values.
 
Last edited:
Level 17
Joined
Apr 27, 2008
Messages
2,455
Well, values without any code provided is not a good idea, you still can make errors and no one can see.
Today it's enough but i will remake all my tests, because i just realized that in most cases the fps drop again after several seconds (probably 2 min) but seems more stable and reliable than values after 1 min, so i will use these values instead.

I will also find the periods which give the same fps for the two tests, to have the ratio of speed between them, because as expected fps drop doesn't seem linear.

To be continued ...

Btw, i've just tested with the latest patch, local parameters (takes ...) don't have to be nulled, and the globals still leak if you don't null them :
http://www.thehelper.net/forums/sho...Found-Keeping-references-to-destroyed-objects
But it's only few bytes (or maybe just even 1 byte) and no handle id leak.
 
Last edited:
Level 5
Joined
Oct 14, 2010
Messages
100
Jass isn't a compiled language, it's an interpreted one.
The Jass interpreter reads Jass code as text to interpret what should be done.
That's why longer function names make functions slower.

I guess that explains the bytecode exploit and the fact there's an operation limit. Oh wait, no it doesn't.
So unless there's a good reason why OP_JASSCALL would do a string lookup instead of following a function pointer, I don't see how name length could influence execution speed. Compilation speed (i.e. map load time) perhaps, but execution speed?

Measuring the impact of function name length is irrelevant anyway. Is there anyone who doesn't use vexorian's optimizer these days?

About real vs int: Firstly, what's the point of testing this? If you need reals, you need reals. If you need integers, you need integers. They're not really alternatives the way ForGroup and group loop are.
Secondly, what exactly did you test? Read? Write? Global vs local? I'd be surprised if local reals are slower than local integers. Global real writing would make sense though, since there's a "real value changes" event.

You can manipulate the units in the filter of the EnumUnitsInRange function you're going to have to do in most cases anyways..
I don't think it's always a good idea to do this. I wouldn't be surprised if in a real use-case (with plenty of units on the map, code that actually does something) ForGroup is faster. Running a small EnumUnits a 1000 times and then running a big ForGroup on only 10 units is probably faster than running a big EnumUnits a 1000 times. So my advice: don't do it unless you're doing something real small.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
>> Is there anyone who doesn't use vexorian's optimizer these days?

First, most people are not using the optimizer

Second, and this is most important, most people (that I've talked to) don't
care about the speed gain from shortening names because they don't even
care enough to make their own code more efficient.

Third, when you select "compress names" the optimizer destroys any native
declaration (confirmed by me), destroys all TriggerRegisterVariableEvent
(confirmed by me) and I think ExecuteFunc has some issues but I have not
tested it. I think ExecuteFunc only breaks if you use concatenation but
TriggerRegisterVariableEvent does not work at all even though it is the only
way to do custom events in GUI.
 
I think a new optimizer should be written :)
Unfortunately, I'm not so good at C++ :(

edit
I don't think it's always a good idea to do this. I wouldn't be surprised if in a real use-case (with plenty of units on the map, code that actually does something) ForGroup is faster. Running a small EnumUnits a 1000 times and then running a big ForGroup on only 10 units is probably faster than running a big EnumUnits a 1000 times. So my advice: don't do it unless you're doing something real small.

It depends..
Usually, this is how my filter would look like:

JASS:
function filter takes nothing returns boolean
    if (boolean expression) then
        //do stuff
    endif
    return false
endfunction

In this case, I'm sure using the filter is faster.
WC3 is going to generate thousands of threads for thousands of units anyways.

The only time I ever needed ForGroup was when I wrote a system that deals to damage to units in a certain over time and heals other units over time too.

edit

I really think we should benchmark Comparisons:

JASS:
globals
    private integer s = 9
    private constant boolean TEST_ONE = true
endglobals

private function t takes nothing returns nothing
    local integer i = 0
    static if TEST_ONE then
        loop
            exitwhen i == 1000
            if 0 == s then
            endif
            set i=i+1
        endloop
    else
        loop
            exitwhen i == 1000
            if s == 0 then
            endif
            set i=i+1
        endloop
    endif
endfunction

private module I
    private static method onInit takes nothing returns nothing
        call TimerStart(CreateTimer(),0.01,true,function t)
    endmethod
endmodule
private struct S extends array
    implement I
endstruct

I can't test it, so can someone do me a small favor and benchmark comparisons for me? :>
 
Level 22
Joined
Nov 14, 2008
Messages
3,256
Blah I'm too tired to give my creative feedback on this but about the TriggerRegister BJ, isn't that the one that Nest, Bribe, J4L and others made these event libraries to replace that BJ? (I really need some sleep as I don't think my point was clear enough but I'll read this thread again tomorrow.)
 
Level 12
Joined
Feb 22, 2010
Messages
1,115
Can you test triggering unit vs other event responses such as dying unit, learning unit, hero manipulating item, casting unit vs.

Edit:Also I wonder why last one doesn't keep it's value after waits :D
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
Blah I'm too tired to give my creative feedback on this but about the TriggerRegister BJ, isn't that the one that Nest, Bribe, J4L and others made these event libraries to replace that BJ? (I really need some sleep as I don't think my point was clear enough but I'll read this thread again tomorrow.)

I suppose you are talking about something like this : http://www.thehelper.net/forums/showthread.php/123288-GTrigger-Event-System

The point is not to replace TriggerRegisterAnyUnitEventBJ, if you read it in details.
Also his functions Init$NAME$ could use this instead of his loop, i suppose he inline it because he thought there was a leak if the trigger is destroyed later, but it won't since local parameters (takes ...) are not concerned by the other locals bug (need to set them to null in order to avoid an handle id leak when the handle is destroyed)

Seriously the only "good" reason to inline this BJ is when you don't use all players in your map.

Can you test triggering unit vs other event responses such as dying unit, learning unit, hero manipulating item, casting unit.Also I wonder why last one doesn't keep it's value after waits :D

I did it a long time ago (with the custom StopWatch... natives) and GetTriggerUnit is slower than the specific event responses, and it makes sense, GetTriggerUnit has probably to figure which event fire and handle it.

But i will make one later (choice your event response).

And about the casting unit if i remember correctly it's reset when the ability endcast event fire, blame Blizzard's monkeys or trainees.

Today i'm not on the mood to make more benchmarks, but since tomorrow i will try to make one each day (and improve one already existent).

Also don't take me wrong i'm not a speed-freak, i'm totally aware that some benchmarks results are pointless in real cases, i just hate false statements which are created on the fly without any proof, or not tested by someone or an other.
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
Those you have listed are simple BJs that refer to triggering unit, it's simply one more call difference.

No they are not, all these natives exist in common.j

TriggeringUnit acts like a local and doesn't removes its value until a thread is over which can include waits of all sorts.

Most of other event responses act like that too.
Only few are not "MUI".

Here is a list (you will have to take my words as the truth or test all event responses by yourself, i did it myself the 5 december 2007.

Ability being cast

sold item ( just because the item is removed, but you can use his "object type" instead )

Issued Order

Target point of issued order
Target point of ability being cast

Casting Unit
Target unit of issued order
Cancelled structure ( just because the unit is removed but you can use his "unit type" instead )
Target unit of ability being cast (is reset when the endcast event fire )

How cannot TriggeringUnit be faster when DyingUnit calls GetTriggerUnit()? (Yeah I saw your sentence but it does not make any sense to me)

Dying unit (GetDyingUnit) is a native function and i say the opposite (read again), triggeringUnit (GetTriggerUnit) is slower than the native specific equivalent.

Only "attacked unit" (GetAttackedUnitBJ) is a wrapper of triggering unit.

JASS:
function GetAttackedUnitBJ takes nothing returns unit
    return GetTriggerUnit()
endfunction

Well, of course the "last created" things are also a wrapper of bj_ variables, but technically they are not event responses.
 
Last edited:
Level 12
Joined
Feb 22, 2010
Messages
1,115
YEAH VICTORY I KNEW IT :ogre_rage:.People who keep telling "Triggering unit faster and better" are wrong!I trust your old tests hehehe.(Even they are slower than triggering unit I prefer them :O)
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
Oh my, where have I always seen them as red highlighten? My bad.

I had to check it with jasscraft before write, i wasn't sure, i suppose you thought it because there are many of this useless BJ which just use the natives ones.
But event responses are not concerned (well, Blizzard has never created a GetAttackedUnit native), and also because like me you are not using GUI anymore since years.

I've heard that EndCast event cannot retrieve TargetUnit of the spell?

Hmm, i don't remember if it is when the event fire or just "after".

EDIT :

Anitarf says that it can't be retrieved when this event fire in his resource documentation :
http://www.wc3c.net/showthread.php?t=105374

YEAH VICTORY I KNEW IT :ogre_rage:.People who keep telling "Triggering unit faster and better" are wrong!I trust your old tests hehehe.(Even they are slower than triggering unit I prefer them :O)

People tends to copy/paste random statements without any proof, and unfortunately sometimes they are wrong (probability rule :p)

Worse, they tend also to make a particular case, a rule :

For example the old null parameter leak in GroupEnum... functions -> all null boolexpr parameter leak (wrong)
And i suppose here GetAttackedUnitBJ (copy of GetTriggerUnit) -> some GUI event response are also a copy of GetTriggerUnit (wrong)


Anyway, if you use GUI, efficiency should be your last priority :p

EDIT : Meh, sorry you are using jass ^^
 
Last edited:
Level 12
Joined
Feb 22, 2010
Messages
1,115
I use GUI when I am lazy to type(like pick unit group thingies, create triggers with a few actions, or some situations I REALLY don't want to type).Actually I am not using We unless I am bored.
 
Level 12
Joined
Feb 22, 2010
Messages
1,115
You can add all results to your first post if you really bored :grin: So people don't have to search all posts(I did)
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
You can add all results to your first post if you really bored :grin: So people don't have to search all posts(I did)

I will sort them in categories, and definitely use several posts and not big ones, hell i would reach the character limit anyway if i did it.
I have pmed Bribe to sort it, all i need is to keep several posts stuck together in the first ones, then i can edit them, if it's not possible i suppose i will create a new thread and ask it to put this one in the recycle bin (but not purely destroy it since there are comments).
 
Last edited by a moderator:
Status
Not open for further replies.
Top