• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

[vJASS] Timer32

Status
Not open for further replies.
Level 20
Joined
Jul 6, 2009
Messages
1,885
So i thought of using Timer32 for a spell, but something bothers me. There's a global integer - Tick, that can be used as a counter. What bothers me is that Tick is increasing all the time, it's never decreased. Wouldn't it be better, when there are no instance running, to set Tick to 0? Couldn't a permanently increasing integer hit some limit? Even if it couldn't, i don't think it's efficient to operate with large integer since Tick would become large after a bit of time.
 
Well, he never pauses the timer anywhere as multiple things are using it, so it'd be kind of impossible to reset it.


He really should pause it though if nothing is running >.>.


After the trigger evaluate, if there are 0 instances up, pause the timer =).


On register, start the timer if only one instance is up.


From here, he should be adding/removing conditions. If a struct has 0 instances, there's no need for there to be a condition in the trigger for it >.>.


Really, T32 can be improved, but I don't think it ever will and if someone wrote an improved version of T32 and submitted it to TH, it'd be graveyarded. If someone suggested improvements, they'd never make it in. So really, that resource is kind of stuck = ).
 
Check this out
JASS:
    globals
        private timer t = CreateTimer()         //regular timer
        private trigger tr = CreateTrigger()    //expiration trigger
        private triggercondition array rp       //condition to be destroyed
        private integer rc = 0                  //recycler count
        private integer ic = 0                  //instance count
    endglobals
    
    private function Run takes nothing returns nothing
        call TriggerEvaluate(tr)
    endfunction
    
    //recycler function
    private function Rec takes nothing returns nothing
        loop
            set rc = rc - 1
            call TriggerRemoveCondition(tr, rp[rc])
            set ic = ic - 1
            set rp[rc] = null
            exitwhen rc == 0
        endloop
        
        if (ic > 0) then
            //run expiration
            call TriggerEvaluate(tr)
            
            //start the regular timer back up
            call TimerStart(t, .031250000, true, function Run)
        endif
    endfunction


So what happens is that as you start a struct, if that struct's condition wasn't already added to the trigger, it's added. If the instance count is 0, the timer is started.

When an instance is removed from the struct, the timer is started again using the Rec function instead, which removes all of the conditions (removed on next period).


Now, here's a thought... will adding a condition to a trigger that is being run fire that condition?

fire condition1
fire condition2
--->add condition4
condition3

will condition4 fire? : P


If condition4 would fire, then condition4 should be added on the next period (a third function that adds conditions).


It does add a slight amount of overhead for adding/removing instances, but it is a smarter design as only the stuff that should be running will be running =).



T32 Op Limit Safe (a lib I wrote) did this design but for multiple conditions for each struct, so it's a bit more complex ^)^.
 
So...should i use T32 Op Limit Safe?
I just want a system to loop through struct instances each 0.03 seconds :eek:

Don't use T32 Op Limit Safe as that's designed specifically for cases where you will hit the op limit with T32. It's still unknown as to whether T32 Op Limit Safe is even useful considering that at that point a map may become unplayable.


I suggest that you take that design I showed you and make your own version of T32 =).


Be sure to always compare against 0 in the loops to make the thing run faster, use short variable names, and put as many variables as possible into globals =). Also, use as little global variables as possible ; P.


Keep the library name short.


Given how often this would run, you'd want to make sure variable names are smaller to keep it running faster. Keep in mind that not all maps can use vexorian's optimizer for shortening names.


Or... you can have me code it eventually (whenever that will be), lol.
 
Level 20
Joined
Jul 6, 2009
Messages
1,885
Well, i certainly could give it a try, but i'd rather have you do it. You're a master when it comes to efficiency :D
It would be nice to have an improved version of T32, i find it very useful.

I've got another question though, if it isn't too much =[
When using function interfaces, imagine this situation:
JASS:
library lib
    function SomeFunc takes real x returns nothing
    endfunction
endlibrary

library lib2

    function interface Interface takes real x returns nothing

    private function SomeFunc takes real x returns nothing
    endfunction

endlibrary
Now, if i'd use Interface.SomeFunc, to which function would it point? :/
Is there a safe way of getting a pointer to a specific function?
 
Level 20
Joined
Jul 6, 2009
Messages
1,885
I see, thanks for all the help.

EDIT: I feel bad for asking this much <____<
I'm not sure how would i use modules for this, but using conditionfunc would require a trigger for each function which is pretty much what function interfaces do from what i saw in outputwar3map.j when making an interface.

The way i thought of doing it, tell me if it's wrong: I'd create a trigger for each function of several, add those functions as conditions for those triggers and save triggers in an array. Then i'd save integer as a member of each struct instance and when i need to execute the function, i'd evaluate the trigger from array using the integer member as array index for trigger array (This is pretty much what interfaces do).
 

LeP

LeP

Level 13
Joined
Feb 13, 2008
Messages
543
[...]
And yea, working with larger integers gets slower as there are more bit comparisons.

I think this is bogus.
If we look at the internals of warcraft [1] we see that the value is stored as a long. So if warcraft wants to compare two int-variables it compares the values of the variables.
To see, if this actually gets slower the higher the number is wrote a little c-script:
Code:
#include <stdlib.h>
//#include <stdio.h>

int main(int argc, char **argv){
		/*
		if(argc != 2){
				fprintf(stderr, "Expected exact one argument.");
				return 1;
		}
		*/

		long range = atol(argv[1]);
		/*
		if(!range){
				fprintf(stderr, "Expected an integer.");
				return 2;
		}
		*/

		long a, b;
		for(int i= 0x1000000; i!=0; i--){
				a = (rand() % range);
				b = (rand() % range);
		}
}
For now this lacks the actual comparison.
So compiled to asm
Code:
% gcc -std=c99 -S cmp.c
And added the following line
Code:
  	movl	%edx, 16(%esp)
+ 	cmpl	%edx, 20(%esp)
  	subl	$1, 28(%esp)
  .L2:
  	cmpl	$0, 28(%esp)
Compiled into executable
Code:
% gcc cmp.s -o cmp.out
and run with different ranges
Code:
% for num in 10 100 1000 10000 100000 1000000; time ./cmp.out $num
./cmp.out $num  4,32s user 0,00s system 99% cpu 4,335 total
./cmp.out $num  4,35s user 0,00s system 99% cpu 4,362 total
./cmp.out $num  4,31s user 0,00s system 99% cpu 4,322 total
./cmp.out $num  4,30s user 0,00s system 99% cpu 4,311 total
./cmp.out $num  4,31s user 0,00s system 99% cpu 4,321 total
./cmp.out $num  4,38s user 0,00s system 99% cpu 4,408 total

I'm not really good at asm or c and it may not be this simple, but i really think, that the size of the integer does not matter in terms of comparison-speed in warcraft (as we see it does not matter in real programs).
And yeah, if i made a mistake, pls correct me.

e: I added the comparison in asm because if i added it in c without any action it gets optimized there is no cmp anymore. And i actually just wanted to have one cmp not a cmp + jmp and a possible action. You know, 'benchmark' as least as possible. But again, i'm not really into c or asm.

^1: http://www.wc3c.net/showthread.php?t=96208
 
Last edited:
My point is proven
JASS:
globals
    constant boolean F=false
    integer x
endglobals
function r takes nothing returns nothing
    //33 frames per second
    static if F then
        set x=20000
        loop
            set x=x-1
            exitwhen 0==x
        endloop
    //20 frames per second
    else
        set x=0
        loop
            set x=x+1
            exitwhen 20000==x
        endloop
    endif
endfunction
struct tester extends array
    private static method onInit takes nothing returns nothing
        call TimerStart(CreateTimer(),.015625000,true,function r)
    endmethod
endstruct


On another interesting note, this had the same speed as the other one-
exitwhen x==0

And the other one didn't matter if they were switched or not.


So apparently, putting the number before the variable is faster... lol
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,257
JASS is interpreted in a retarded way. It seems to generate some form of byte code which points at strings representing the names. These names then get looked up in a hashtable like structure. After multiple checks it finally carries out the opperation.

Nestharus, your script makes the stupid assumption that set x=x-1 is as fast as set x=x+1. Knowing how stupid JASS is, it is possible that they are not carried out at the same rate so you need to test them separatly first.

The problem is that JASS is interpreted and not compiled. As such it might do stupid things like itterating on a bit by bit baisis instead of executing a single instruction to do something.
 

LeP

LeP

Level 13
Joined
Feb 13, 2008
Messages
543
My point is proven
JASS:
globals
    constant boolean F=false
    integer x
endglobals
function r takes nothing returns nothing
    //33 frames per second
    static if F then
        set x=20000
        loop
            set x=x-1
            exitwhen 0==x
        endloop
    //20 frames per second
    else
        set x=0
        loop
            set x=x+1
            exitwhen 20000==x
        endloop
    endif
endfunction
struct tester extends array
    private static method onInit takes nothing returns nothing
        call TimerStart(CreateTimer(),.015625000,true,function r)
    endmethod
endstruct


On another interesting note, this had the same speed as the other one-
exitwhen x==0

And the other one didn't matter if they were switched or not.


So apparently, putting the number before the variable is faster... lol

If this is true, the wc3 engine sure is quirky. Like i stated in my post, it probably is not as easy as i tested it.
Too bad i currently have no chance to verify the result in wc3.

But you do realize that i tested it way different than you? My code is for every test the same. Maybe it's not the comparison but the +- or --operator? Or a cmp against zero is faster than any other cmp...

________
But maybe, maybe, i should stop doing my posts about dumb 'optimizations'.
If you really think counting a loop backwards or using cryptic names or destroying all type-safety or forcing a different behavior of code in debug-mode will heavily speed up your map(-making), go ahead and do it.
I actually prefer my code nice instead of 0.001s faster. Also with a nice code base i actually am faster developing my projects.
I can always later investigate into the parts of the map which are worth being optimized.
Not that i want you to blind follow some "mantra", but i actually like the following quote: "We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil."

edit:

JASS is interpreted in a retarded way. It seems to generate some form of byte code which points at strings representing the names. These names then get looked up in a hashtable like structure. After multiple checks it finally carries out the opperation.

Nestharus, your script makes the stupid assumption that set x=x-1 is as fast as set x=x+1. Knowing how stupid JASS is, it is possible that they are not carried out at the same rate so you need to test them separatly first.


The problem is that JASS is interpreted and not compiled. As such it might do stupid things like itterating on a bit by bit baisis instead of executing a single instruction to do something.
Yeah, but i hoped that since the value is stored inside a long, maybe for variables of type integer, they actually just do var1->value == var2->value.

edit again:
But hey, at least i got fun doing my posts ^^
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,257
Oh, something just occured to me...
exitwhen 0==x
exitwhen 20000==x

Both these are run the same numeber of times. However the 20000 constant might have a higher reference cost than the 0 constant. Especially if JASS does something stupid like storing them as strings instead of a value this would easilly explain the slower results.

LeP, comparing JASS to python or LUA is a more appropiate comparison as both are interpreted languages and so are a good benchmark of what we could expect JASS to perform like. I am sure though that those would probably show simlar results for 32 bit integer types to your C script (pythons long is a dynamic structure with near infinite size so will have a O(n) related complexity).

On a lesser note, there is probably a decriment instruction in assembly instead of "subl $1, 28(%esp)". These usually are more efficient as they save having to load extra memory for the constant.
 

LeP

LeP

Level 13
Joined
Feb 13, 2008
Messages
543
[...]

On a lesser note, there is probably a decriment instruction in assembly instead of "subl $1, 28(%esp)". These usually are more efficient as they save having to load extra memory for the constant.

Yeah, there is dec.
But i'm not into asm and i just used gcc to get the asm code and then insert my little cmp.

[...]
LeP, comparing JASS to python or LUA is a more appropiate comparison as both are interpreted languages and so are a good benchmark of what we could expect JASS to perform like.
Yeah, ofc. but if you only look at on single aspect, they may not differ very much.
Code:
/*pseudo code*/
if(var1->type == JInteger && var2->type == JInteger) return var1->value == var2->value;
And then other branches for other comparisons. maybe...

And i didn't compared the speed of jass to c, but i looked at the behavior of the c-code and then concluded that it (hopefully) may be the same in jass.
But again it shows, that you can't compare jass to any other language :/
 
Status
Not open for further replies.
Top