System Faster Table

Troll-Brain

You can change this now in User CP.
Reaction score
85
of course, extended array are basically a bunch of function calling.
For module, it has been released today.
Honestly i didn't read your code so far, but it is for inlining and it is sexy to use, so maybe it would be usefull for your code.
 

Jesus4Lyf

Good Idea™
Reaction score
397
>but it is for inlining and it is sexy to use, so maybe it would be usefull for your code

Pretty sure modules are glorified textmacros.

Anyway, did a hack-job optimization. MEH. Here's yer 7%. If people use it, I'll optimise the rest... But for 7% it seems kind of redundant. Would you use it for 7%? Let me know. I'd like to see other people bench test it in case it is actually faster on other people's comps (compared to normal Table). But. MEH.

And I don't think it gets much faster than this, except for removing hashing and storing in one massive array, which means it would lose speed after certain amounts of time playing as the strings go up and stuff. And it would be generally unstable. Etc.

So let the records show that gamecache is actually not that slow. And that I beat it by writing a JASS equivalent. But only by ~7%.

-_-

I'm gonna try two more things later.
 

Azlier

Old World Ghost
Reaction score
461
Ow, only a 7% increase over how limited it is (erases after save/load, etc.)? Perhaps I'll just use normal Table :p.
 

Azlier

Old World Ghost
Reaction score
461
Just a thought. The point of converting a handle to an integer to a string was to instantly get it array sized. If you're going to use a hash, what's the point of string conversion?
 

Jesus4Lyf

Good Idea™
Reaction score
397
Why hash AND get stringid?

Reason 1. To attach to strings.

I know what your saying, but table is actually 2d. So reason 2 is to attach to 2 integers by separating them by an x. This allows converting 2 integers into one unique integer.

Reason 3 is because the stringid can go over 8000. The next thing I'm going to try (I said I had 2 things left to try) is simply have a massive extended array, and bench test that. :D

But I don't know if anyone actually looked at my code, but I was really quite pround of my 2d doubly linked list stuff. Which IS faster than gamecache. I actually wrote a native in JASS and made it faster than the precompiled code. :( lol

Edit: MEH. Put this in your pipe and smoke it. I made Table 10% faster. Turns out Vexorian's code was pretty slack anyway. -.- No disadvantages, this is exactly Table, made 10% faster. o.o
JASS:
library Table
//***************************************************************
//* Table object
//* ------------
//*
//*   set t=Table.create() - instanceates a new table object
//*   call t.destroy()     - destroys it
//*   t[1234567]           - Get value for key 1234567
//*                          (zero if not assigned previously)
//*   set t[12341]=32      - Assigning it.
//*   call t.flush(12341)  - Flushes the stored value, so it
//*                          doesn't use any more memory
//*   t.exists(32)         - Was key 32 assigned? Notice
//*                          that flush() unassigns values.
//*   call t.reset()       - Flushes the whole contents of the
//*                          Table.
//*
//*   call t.destroy()     - Does reset() and also recycles the id.
//*
//*   If you use HandleTable instead of Table, it is the same
//* but it uses handles as keys, the same with StringTable.
//*
//***************************************************************

//=============================================================
    globals
        private constant integer MAX_INSTANCES=8100 //400000
 
    //=========================================================
        private gamecache gc
    endglobals
 
    private struct GTable[MAX_INSTANCES]
        // A "protected" keyword would've been really nice here.
        // Don't play with "base" in your map.
        // Looking for ideas on how to protect it, still.
        string base
        
        static method create takes nothing returns GTable
            local GTable d=GTable.allocate()
            set d.base=I2S(d)
            return d
        endmethod
        
        method reset takes nothing returns nothing
            call FlushStoredMission(gc,this.base)
        endmethod
 
        private method onDestroy takes nothing returns nothing
            call FlushStoredMission(gc,this.base)
        endmethod
    endstruct
 
    //Hey: Don't instanciate other people's textmacros that you are not supposed to, thanks.
    //! textmacro Table__make takes name, type, key
    struct $name$ extends GTable
 
        method operator [] takes $type$ key returns integer
            return GetStoredInteger(gc,this.base,$key$)
        endmethod
 
        method operator []= takes $type$ key, integer value returns nothing
            call StoreInteger(gc,this.base,$key$, value)
        endmethod
 
        method flush takes $type$ key returns nothing
            call FlushStoredInteger(gc,this.base,$key$)
        endmethod
 
        method exists takes $type$ key returns boolean
            return HaveStoredInteger(gc,this.base,$key$)
        endmethod
 
    endstruct
    //! endtextmacro
 
 
 
    private function H2I takes handle h returns integer
        return h
        return 0
    endfunction
    
    //! runtextmacro Table__make("Table","integer","I2S(key)" )
    //! runtextmacro Table__make("StringTable","string","key" )
    //! runtextmacro Table__make("HandleTable","handle","I2S(H2I(key))" )
 
    //=============================================================
    // initialize it all.
    //
    private function init takes nothing returns nothing
        call FlushGameCache(InitGameCache("libtable.gc"))
        set gc=InitGameCache("libtable.gc")
    endfunction
endlibrary
 

Troll-Brain

You can change this now in User CP.
Reaction score
85
Yes, you're right.
And i dunno how many µs you will win.

Why you ban H2I to your maps ?
 

Jesus4Lyf

Good Idea™
Reaction score
397
That's my thoughts exactly. However, you do spam more arrays then. So I don't know that it's worth it. Nonetheless, why the hell not.
JASS:
library Table initializer init
//***************************************************************
//* Table object
//* ------------
//*
//*   set t=Table.create() - instanceates a new table object
//*   call t.destroy()     - destroys it
//*   t[1234567]           - Get value for key 1234567
//*                          (zero if not assigned previously)
//*   set t[12341]=32      - Assigning it.
//*   call t.flush(12341)  - Flushes the stored value, so it
//*                          doesn't use any more memory
//*   t.exists(32)         - Was key 32 assigned? Notice
//*                          that flush() unassigns values.
//*   call t.reset()       - Flushes the whole contents of the
//*                          Table.
//*
//*   call t.destroy()     - Does reset() and also recycles the id.
//*
//*   If you use HandleTable instead of Table, it is the same
//* but it uses handles as keys, the same with StringTable.
//*
//***************************************************************
 
//=============================================================
    globals
        private constant integer MAX_INSTANCES=8190 //400000
 
    //=========================================================
        private gamecache gc
    endglobals
 
    //Hey: Don't instanciate other people's textmacros that you are not supposed to, thanks.
    //! textmacro Table__make takes name, type, key
    struct $name$[MAX_INSTANCES]
        private string base
        
        static method create takes nothing returns $name$
            local $name$ d=$name$.allocate()
            set d.base=I2S(d)
            return d
        endmethod
        
        method reset takes nothing returns nothing
            call FlushStoredMission(gc,this.base)
        endmethod
 
        private method onDestroy takes nothing returns nothing
            call FlushStoredMission(gc,this.base)
        endmethod
 
        method operator [] takes $type$ key returns integer
            return GetStoredInteger(gc,this.base,$key$)
        endmethod
 
        method operator []= takes $type$ key, integer value returns nothing
            call StoreInteger(gc,this.base,$key$, value)
        endmethod
 
        method flush takes $type$ key returns nothing
            call FlushStoredInteger(gc,this.base,$key$)
        endmethod
 
        method exists takes $type$ key returns boolean
            return HaveStoredInteger(gc,this.base,$key$)
        endmethod
 
    endstruct
    //! endtextmacro
 
    private function H2I takes handle h returns integer
        return h
        return 0
    endfunction
    
    //! runtextmacro Table__make("Table","integer","I2S(key)" )
    //! runtextmacro Table__make("StringTable","string","key" )
    //! runtextmacro Table__make("HandleTable","handle","I2S(H2I(key))" )
 
    //=============================================================
    // initialize it all.
    //
    private function init takes nothing returns nothing
        call FlushGameCache(InitGameCache("libtable.gc"))
        set gc=InitGameCache("libtable.gc")
    endfunction
endlibrary
>Why you ban H2I to your maps ?
What would I use it for?

For attaching to triggers, I use TriggerExecCount (and might be the ONLY mapper to use this method). For attaching to units I use UnitUserData and for attaching to timers I use Key Timers 2. Aside from this, I haven't needed to attach anything.

H2I is slow once you associate it with protection mechanisms like hashing, which are necessary. All my attaching methods are lightning fast (and are probably as fast as they can possibly get). So therefore, H2I is a waste of time, and I ban it from my maps.
 

Troll-Brain

You can change this now in User CP.
Reaction score
85
Attaching what sort of thing to a trigger ?
I'm rather a speed freak man, but i prefer use H2I + Table to attach stuff, i keep UnitUserData free for something important, and in fact i never use it :p
I guess you're not the guy which use other's system but many of them use UserData for units, and then can't be used together if you don't edit them ...

PS : I know it's Vexorian's fault but you should put the constant to 8190, not 8100
 

Viikuna

No Marlo no game.
Reaction score
265
I use H2I(timer)-MIN_HANDLE_COUNT , which is pretty fucking fast btw, for non periodic timers.

Example: for 0.0 sec timers and for waiting Units Damage Point -time to get the exact moment when attack hits the unit ( Cassiels dynamic evasion module for UnitProperties ) and for all other cases where you need accurate timing.

I dont think these are possible to do with KT2 ( correct me if Im wrong )

Its foolines to bann H2I,since it has no downsides if you use it correctly.
But its also stupid to use it when you dont really need it. ( when there are better options )
 

Jesus4Lyf

Good Idea™
Reaction score
397
I use H2I(timer)-MIN_HANDLE_COUNT
...
Its foolines to bann H2I,since it has no downsides if you use it correctly.
Lol, you mean unlike your example, which utterly fails on maps with 9,000 handles without warning if you delete an early one? (And TU red fails the same way.)
Example: for 0.0 sec timers
...
But its also stupid to use it when you dont really need it. ( when there are better options )
Like with 0.0 second timers because you don't actually need to attach at all?
I dont think these are possible to do with KT2 ( correct me if Im wrong )
You stand corrected about 0.0 second timers. I even did many of my benchmarks for it using 0.0 seconds as the period. But KT2 is not fast for non-periodic timers (just easy to use). I also don't think KT2 could do the Units Damage Point wait thing. But I've never needed that myself.

>Attaching what sort of thing to a trigger ?
A struct.

>I guess you're not the guy which use other's system
Bingo.
Which is why I don't need H2I.
But if a system H2I for unit attaching, it should just rely on some generic unit attaching system that indexes UnitUserData instead.
And if it uses H2I for timer attaching, this should only be for when extreme precision is necessary on short periods.

In all my maps? I index UnitUserData, and attach to that. Sometimes I even have different sets of indexes for units if they are involved in mutually exclusive things.

PS. I find a certain satisfaction and challenge in banning H2I, as well.
 

Viikuna

No Marlo no game.
Reaction score
265
Lol, you mean unlike your example, which utterly fails on maps with 9,000 handles without warning if you delete an early one? (And TU red fails the same way.)

No, because you use static timers, you can have as many handles in your map as you need.
edit. Ohh, now I get what you mean. Then some new handle just gets old ones id. It doesnt affect timers ids any way.

edit2. The only problem in TU red -method is that you need to know how many timers you need.

Like with 0.0 second timers because you don't actually need to attach at all?

You need to attach to 0.0 timers. If use globals and start 2 of them at the same time, only the last one will work.

I also don't think KT2 could do the Units Damage Point wait thing.

It depends. How accurate is KT2 ? If I need 1.657 timeout can KT2 handle it?
If it cant handle it, dynamic evasion fails.

In all my maps? I index UnitUserData, and attach to that. Sometimes I even have different sets of indexes for units if they are involved in mutually exclusive things.

UnitUserData is perfect for units. I dont use H2I with units either. I just index them with UnitUserData and recycle them. ( Recycling saves the envirionment btw )
 

Jesus4Lyf

Good Idea™
Reaction score
397
>Ohh, now I get what you mean. Then some new handle just gets old ones id. It doesnt affect timers ids any way.

Timer ids? You mean handles? I think you're terribly mistaken. You'd have "H2I(timer)-MIN_HANDLE_COUNT" return ~1, and then ~8,930. Good luck putting that in your array. And hence...
>The only problem in TU red -method is that you need to know how many timers you need.
Wrong. You can delete a handle in initialisation just fine. It's just unlikely.

>You need to attach to 0.0 timers. If use globals and start 2 of them at the same time, only the last one will work.

Write a 0.0 stack. :thup: And if you can't be stuffed, go use KT2 or just do your attaching some other way. Who cares.

>If I need 1.657 timeout can KT2 handle it?
Yes. Change the MARGINOFERROR constant to 0.001. This constant is the accuracy of periods >= 0.3 seconds. But it may be out by up to 0.001. I'm kind of curious as to what may need this kind of accuracy though.
 

Viikuna

No Marlo no game.
Reaction score
265
It's just unlikely.

IMO its stupidy.

I should probably post this to KT2 thread..

The real problem with KT2 is, like Cassiel said in wc3c, that it cant be fast and flexible at the same time.

If you really want fast periodic timers, you just use struct arrays and static timers, which is faster than KT2.

If you need flexibility, like 0.0 timers or some real accurary like in Evasion module:
JASS:
set r = (GetHeroAgi(attacker, true) * AgiMod) + UnitGetAttackSpeed(attacker)
if r > 400 then
       set r = 400
endif
set r = UnitGetDamagePoint(attacker) * (100. / (100. + r))
call TimerStart(t, r, false, function Add)

then you just use TU red or some similiar TimerStack.

KT2 just can do stuff just OK, but it cant be speciliaced to everything, so it cant be the best option.

edit. And I still dont get it, how you can make H2I(timer)-MIN_HANDE_COUNT returning different values for same timer? Timers handle id does not change, and MIN_HANDE_COUNT is constant, so it should be same number every time, right?
 

Troll-Brain

You can change this now in User CP.
Reaction score
85
For attaching to triggers, I use TriggerExecCount
I fail to see how, it can be done with an empty trigger in a silly way, but i don't understand how you will do with a trigger which have conditions/actions, plz give an example.

EDIT : Oh wait you use conditions only.
I think it's silly anyway, if you got a struct with an index 100, you will execute 100 times the trigger o_O (in assuming the worst case, GetTriggerExecCount == 0).
And i know you can reset to 0 this number with ResetTrigger
 

Jesus4Lyf

Good Idea™
Reaction score
397
>If you really want fast periodic timers, you just use struct arrays and static timers, which is faster than KT2.

I beg to differ. KT2 is faster for spells which have 1-2 instances running max, and otherwise it's within some ridiculously small margin (I know the numbers, but I don't feel like reeling them off right now, but the highest it gets on my computer is 8 nanoseconds overhead per instance per tick (12 vs 4 nanosec). The wonderful thing is KT2 is faster to code with, and stable. (Oh, and I don't know Cassiel's comment off the top of my head, but at a guess I've released plenty of versions since then.)

>If you need flexibility... then you just use TU red or some similiar TimerStack.

Lol what? Timer stacks can't handle that at all. You use TU red or ABCT or w/e. And you mean absolute perfect accuracy, not flexibility. KT2 is plenty flexible.

>KT2 just can do stuff just OK, but it cant be speciliaced to everything

It's highly specialised for writing spells. And is absolutely the best at that when applied appropriately (have to say that because there's so many factors).

>And I still dont get it, how you can make H2I(timer)-MIN_HANDE_COUNT returning different values for same timer?

Don't need to. And of course, you can't. See below.
Ok, I'll explain this EXPLICITLY. Let's say you have a map with 9,000 preplaced handles or units creation on initialisation or whatever else. Let's say during initialisation, you delete handle #67. Your init for TU red then fires. The minimum handle you specify as #66 (when I say 66 I mean +0x100000). The initialisation starts, the first timer being at 67 (array slot 1) and then suddenly the next is at 9,001 (array slot 8,969). Fail. Have fun with a map that can only have one timer. I have a real life scenario of a map like this, but of course I used KT2 because I ban H2I for reasons like this.

>I think it's silly anyway, if you got a struct with an index 100, you will execute 100 times the trigger o_O
How many times do you need 100 dynamic triggers in existance at once? Besides, 100 do nothing calls is pretty fast, believe it or not. I've intialised structs 1-50 on map start before (so that's execute do nothing 1+2+3+...49+50 times) without any issues. You can, in fact, preload ;). Furthermore, the retreival speed is quite nice. Glad you figured out it's conditions only - another reason I ban trigger actions. :p
 

Azlier

Old World Ghost
Reaction score
461
Well, for TimerUtils red, you make sure it runs before the rest of your code. That way you can't delete anything before it finishes making its timers.
 
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