HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Optimization -> Handle2ArrayIndex

09-24-2007, 02:34 PM#1
Themerion
Summay

If I have an array filled with different handles; which would be the fastest way to retrieve a handle's index in the array?

Theory + Theorems
Hidden information:

Given an integer value an array will quickly return a corresponding object/primitive that I've put there. That is the function of an array.

As you may or may not know; any function f is inversible as long as
Collapse JASS:
//Logic and math
x!=y <=> f(x)!=f(y)

I will assume that the (function of the) array is inversible. No handle exists twice in the array.


-------------------
Small arrays

Some logic has led me to the conclusion that for small arrays, simply iterating through the array and comparing would be the fastest:
Collapse JASS:
function reverse takes handle h returns integer
  local integer i=0
  loop
    exitwhen myArray[i]==h
    set i=i+1
// Right here there should be a check if i has gotten insanely large; in case the input for the function (handle h) was not included into the array.
  endloop
  return i
endfunction

The approach above would work just fine if myArray was limited to a few objects. However, the function would become quite slow if myArray reached larger sizes (like 7777).

Large arrays

For large arrays, my hyphothesis is that a GameCache approach would be faster.
Collapse JASS:
// Definition:
// (handle) h = myArray[GetAttachedInt(h,"z")]

function reverse takes handle h returns integer
// GetAttachedInt is a function from Vexorian's CSCache
  return GetAttachedInt(h,"z")
endfunction

My assumption is thus that somewhere; there is a performance border between the "large" and "small" arrays. When the middle item in the array takes just as long time to reach with both methods; the border is found.

My question is, simply; how large does my array have to be before I should switch to the game cache-approach?
09-24-2007, 03:24 PM#2
vesuvan doppleganger
Whats pretty cool is that if you make a loop that creates handles, the integer ID of each handle will be +1 the last created handle. So you can just store the ID of handlearray[0] and subtract that from each handle to get its index. This only works for something you would plan on creating ahead of time, and just recycling however.

the integer attaching will usually be faster, but using gamecache isn't really necessary. There are plenty of ways to attach a single integer to a handle that are as simple as a single global array. Just use something like CSData and you'll be fine.
09-24-2007, 04:04 PM#3
Vexorian
well you may try harder and eventually reinvent the wheel hash table .

If you need iterations through the array you may even use a balanced binary search tree and that stuff...

Although with a hash table it is easy to iterate through the elements.

If you use gamecache you are pretty much using array+hash table, doesn't matter too much.
09-24-2007, 04:27 PM#4
cohadar
Answer seriously depends on what type of handles are we talking about...
09-24-2007, 05:34 PM#5
Themerion
Quote:
Just use something like "CSData" and you'll be fine.

Please read through the entire post before replying. What I was asking for was a comparison in speed between CSCache and looping through a global array, in order to retrieve the array index for a given element (handle / object).

Quote:
Originally Posted by Vexorian
well you may try harder and eventually reinvent the wheel hash table .

I like humor. :)

Quote:
If you need iterations through the array you may even use a balanced binary search tree and that stuff...

Although with a hash table it is easy to iterate through the elements.

I just want to get the integer index corresponding to the handle (as I pretty much mentioned above). Dunno if that classifies as "need iterations".

Quote:
If you use gamecache you are pretty much using array+hash table, doesn't matter too much.

But the post below says...

Quote:
Answer seriously depends on what type of handles are we talking about...

Really? That's interesting. Can you please explain why?
09-24-2007, 06:03 PM#6
cohadar
Quote:
Originally Posted by Themerion
Really? That's interesting. Can you please explain why?

Well if you are using timers my preferred way is ABC + Collections
If you are using plain units I would go for PUI
If you are using only hero handles you are better with player array

If you are using arrays for special effects and something like that you best see a doctor...
09-24-2007, 07:27 PM#7
Themerion
Quote:
If you are using arrays for special effects and something like that you best see a doctor...

Hey! :P Are you claiming that the effect-list structs that I've constructed for my maps are a waste!? :P

Now, I plan to attach the integers to triggers. Once again, I wonder; how many times can the loop run before a game-cache call would be faster?
Collapse JASS:
loop
exitwhen myTrigger=myTriggerArray[i]
set i=i+1
endloop
09-24-2007, 07:45 PM#8
Captain Griffen
Probably ~5.
09-24-2007, 07:58 PM#9
cohadar
You are doing something wrong,
I can not think of a single example where attaching integers to triggers would be useful ...
09-24-2007, 08:21 PM#10
vesuvan doppleganger
Quote:
please read through the entire post before replying.
CSData does not use gamecache, its just a single global array, and has nothing to do with handlevars.

Quote:
Really? That's interesting. Can you please explain why?
We need to know this for the same reason tuna suppliers need to know if you are using the product for fish patties or raw sushi.

Timers are easily recyclable, and so are dummy units. You could use the trick I described earlier to get the array index of the handles. Then just make a parallel integer array for data storage

For other units you could use userdata, CSData, or a hashing algorithm.
09-24-2007, 09:11 PM#11
Themerion
Quote:
Probably ~5.

Woah! Somebody who is willing to answer, even if it is approximative!
Thanks a lot.

Quote:
Whats pretty cool is that if you make a loop that creates handles, the integer ID of each handle will be +1 the last created handle. So you can just store the ID of handlearray[0] and subtract that from each handle to get its index. This only works for something you would plan on creating ahead of time, and just recycling however.

Never understood that before. Do you mean the internal ID that the H2I retrieves? Like this?
Collapse JASS:
//This is not correct jass. It does demonstrate the idea though.
handle h1=CreateHandle()
handle h2=CreateHandle()
integer i = H2I(h2)-H2I(h1)
call BJDebugMsg(I2S(i)) //Would output "1"

Quote:
You are doing something wrong,
I can not think of a single example where attaching integers to triggers would be useful ...

You must have quite some confidence to state that I am doing something wrong, only because you can't think of a usage.

A splendid useage of it is if you somehow have a handle array where the order of the handles is important. If you have one handle, and want to retrieve the next, the previos, the -2:nd, etc; you have to be able to identify its index in the array.

This is what I intended it for:

Collapse JASS:
struct pathgroup
    integer destination
    group unitgroup
endstruct

I've made an automated system for making units move between a set of regions (DotA, AoS, AotZ, that kind of pathing). Also, I keep the units moving in a group (GroupIssueOrder). A unit's custom value is an integer value that is an array index for that unit's pathgroup.

All regions are manually put in a rect array. The pathgroup's destination integer are pointing to the rect to which the units are heading.

When a unit enters a region, it will check if the entered region was the destination for that group. Event Response - Entered Region... hm, doesn't extist, does it? Luckily, I can attach an integer value to the TriggeringTrigger (which does indeed exist), that value can then point to the rect array.

If the entered rect was equal to the myRectRegion[pathgroup[GetUnitUserData(GetEnteringUnit())].destination]; I will update the destination to destination +/-1; and Issue the group move order to that region.
09-24-2007, 10:52 PM#12
cohadar
How about this:

You put a bunch of regions/locations/(x,y) or whatever in an array.
say from 0 to 5


When unit enters map you order it to move to point[0]
when unit enters point 0, you increase UnitUserData to 1
when unit enters point 1, you increase UnitUserData to 2
...
That way you can give all units orders to always
Attack(PointArray[UnitUserData(unit)])

Oh and make sure that you set new UserData only if it is higher than previous one.
This will prevent a unit that returned from point 4 to point 2 (because it was chasing a hero for example) to get stupid and start going to point 3 again instead to point 4.
(Important only if unit path is crossing itself like in some tower defens maps - Gem TD for example )
09-25-2007, 01:36 PM#13
Themerion
cohadar, that is exactly what I wrote in my previous post. The only difference is that instead of letting the UnitUserData refer directly to the point-array; I let it refer to the unit's pathingstruct. In that struct, there is the destination integer, which acts precisely as you suggested.

pathinggroup[GetUnitUserData()].destination is equal to your GetUnitUserData()
As mentioned above; I need the group variable in the pathgroup struct in order to be able to issue group orders.
09-25-2007, 04:27 PM#14
cohadar
Why don't you just simple use UnitUserData?

Oh I see you need it for some other system don't ya?
That is why you are asking this hashing nonsense question.

Well it is time you learn of the benefits of PUI.
You can find it in my signature.
09-25-2007, 09:16 PM#15
Themerion
Quote:
Why don't you just simple use UnitUserData?

Oh I see you need it for some other system don't ya?
That is why you are asking this hashing nonsense question.

I do not simply use UnitUserData because I use it for other stuff too. True.
That is not why I'm asking the hashing questions though.

The problem is that I want to create the pathing automatically. I do NOT want to manually create a trigger for each of the onehundredfiftyeleven regions on my map. In other words; I have a function which automatically creates a trigger for each rect that I give to it.

The drawback with this is, of course, the lack of a GetEnteredRegion()-function. Even if I'm using GetUnitUserData() to know where the unit is heading; I have nothing to compare it with.

That's why I wonder if the fastest way is to attach an integer to the trigger; or if I should just create a trigger array parallell with the rect array.

The drawback of the GC-method is that every time a valid unit enters a pathing-region; a GC-call will be used.

Hidden information:
I just came up with something though! If I let the units know to which trigger they are heading to; then I can compare the triggers directly, avoiding unneccesary GC-calls! :D