- Joined
- Dec 12, 2010
- Messages
- 2,070
JASS:
loop
set u=FirstOfGroup(g)
exitwhen u==null
call GroupRemoveUnit(g,u)
.....
endloop
loop
set u=FirstOfGroup(g)
exitwhen u==null
call GroupRemoveUnit(g,u)
.....
endloop
this one is interesting enough.FirstOfGroup is faster because it otherwise creates an entire thread per unit enumerated.
The problem is exasperated by local variables being able to be referenced within a FirstOfGroup loop.
The only time it's slower is when someone is using a filter instead of null. A null filter and a FirstOfGroup loop is the fastest way to grab units in a circle and do something.
function Tether_EnemiesEffectForGroup takes nothing returns nothing
local unit u=GetEnumUnit()
if not IsUnitInGroup(u,Tether_AffectedTempGroup) and IsUnitEnemy(u,Tether_WispPlayer) and AliveNonBuildOrMarker(u) and not IsUnitType(u,UNIT_TYPE_MAGIC_IMMUNE) and IsUnitVulnerable(u) then
call GroupAddUnit(Tether_AffectedTempGroup,u)
call UnitAddAbilityTimedExF(u,'A2TP',1,0.25+0.5*Tether_AffectedTempLvl,'B0GU')//A2TP -> Tether Slow, B0GU -> Tether
call MyAssistsDebuffWrap(Tether_Wisp,u)
call DestroyEffect(AddSpecialEffectTarget("Abilities\\Weapons\\DragonHawkMissile\\DragonHawkMissile.mdl",u,"chest"))
endif
set u=null
endfunction
function DefaultEnemyFilterWithAncientsNonImmune takes nothing returns boolean
set tt_filterunit=GetFilterUnit()
return IsUnitEnemy(tt_filterunit,tt_player1) and AliveNonBuildOrMarker(tt_filterunit) and not IsUnitType(tt_filterunit,UNIT_TYPE_MAGIC_IMMUNE)
endfunction
function Tether_EnemiesFilter takes nothing returns boolean
return DefaultEnemyFilterWithAncientsNonImmune() and not IsUnitInGroup(tt_filterunit,Tether_AffectedTempGroup) and IsUnitVulnerable(tt_filterunit)
endfunction
function Tether_ProvideEffectsOnEnemiesBetween takes unit wisp,unit target,group g, real dist returns nothing
local real wispX=GetUnitX(wisp)
local real wispY=GetUnitY(wisp)
local real targetX=GetUnitX(target)
local real targetY=GetUnitY(target)
local real angle=AngleBetweenXYRad(wispX,wispY,targetX,targetY)
local real groupX
local real groupY
local group gg=GetAvailableGroup()
local integer i=1
local real dx=dist/8.*Cos(angle)
local real dy=dist/8.*Sin(angle)
local unit u2
local player p=GetOwningPlayer(wisp)
set Tether_AffectedTempGroup=g
set Tether_Wisp=wisp
set Tether_WispPlayer=p
set Tether_AffectedTempLvl=GetUnitAbilityLevel(wisp,'A1TA')//A1TA -> Tether
loop
set groupX=wispX+dx*i
set groupY=wispY+dy*i
set tt_unit1=wisp
set tt_player1=GetOwningPlayer(tt_unit1)
call fStartTimer()
call GroupEnumUnitsInRange2(gg,groupX,groupY,100,null)//Condition(function Tether_EnemiesFilter)
// call ForGroup(gg,function Tether_EnemiesEffectForGroup)
//
loop
set u2=FirstOfGroup(gg)
exitwhen u2==null
call GroupRemoveUnit(gg,u2)
if IsUnitEnemy(u2,Tether_WispPlayer) and AliveNonBuildOrMarker(u2) and not IsUnitType(u2,UNIT_TYPE_MAGIC_IMMUNE) and not IsUnitInGroup(u2,Tether_AffectedTempGroup) and IsUnitVulnerable(u2) then
call GroupAddUnit(Tether_AffectedTempGroup,u2)
call UnitAddAbilityTimedExF(u2,'A2TP',1,0.25+0.5*Tether_AffectedTempLvl,'B0GU')//A2TP -> Tether Slow, B0GU -> Tether
call MyAssistsDebuffWrap(Tether_Wisp,u2)
call DestroyEffect(AddSpecialEffectTarget("Abilities\\Weapons\\DragonHawkMissile\\DragonHawkMissile.mdl",u2,"chest"))
endif
endloop
call echo("t="+I2S(fStopTimer()))
set i=i+1
exitwhen i>8
endloop
call KillGroup(gg)
endfunction
scope Stopwatch initializer onInit
globals
private constant integer ITERATIONS = 1800
private Hashtable ht = InitHashtable()
private integer temp
endglobals
private function Setup takes nothing returns nothing
local integer i = 0
loop
exitwhen i == ITERATIONS
call SaveInteger(ht, 0, i, i)
set i = i + 1
endloop
endfunction
private function Iterative_1 takes nothing returns nothing
local integer i = ITERATIONS
loop
exitwhen i == 0
call SaveInteger(ht, 0, i, LoadInteger(ht, 0, i - 1))
set i = i - 1
endloop
endfunction
private function Iterative takes nothing returns nothing
local intger first = 0
local intger last = ITERATIONS - 1
loop
exitwhen first >= last
set temp = LoadInteger(ht, 0, first)
call SaveInteger(ht, 0, first, LoadInteger(ht, 0, last))
call SaveInteger(ht, 0, last, temp)
set first = first + 1
set last = last - 1
endloop
endfunction
private function Recursive takes integer first, integer last returns nothing // arg.length == 2
if first < last then
set temp = LoadInteger(ht, 0, first)
call SaveInteger(ht, 0, first, LoadInteger(ht, 0, last))
call SaveInteger(ht, 0, last, temp)
endif
endfunction
private function Recursive_1 takes integer i returns nothing // arg.length == 1
if i > 0 then
call SaveInteger(ht, 0, i, LoadInteger(ht, 0, i - 1))
call Recursive_1(i - 1)
endif
endfunction
private function Actions takes nothing returns boolean
local integer sw
local integer i = 0
local real array ticks
local string output
set sw = StopWatchCreate()
call Iterative()
set ticks[0] = StopWatchTicks(sw)
set output = I2S(ITERATIONS) + " iterations of Test #1 took " + I2S(StopWatchMark(sw)) + " milliseconds to finish.\n"
call StopWatchDestroy(sw)
set sw = StopWatchCreate() // recursive
call Recursive()
set ticks[1] = StopWatchTicks(sw)
set output = output + I2S(ITERATIONS) + " iterations of Test #2 took " + I2S(StopWatchMark(sw)) + " milliseconds to finish.\n\n"
call StopWatchDestroy(sw)
if (ticks[0] > ticks[1]) then
set ticks[2] = 100 - (ticks[1]/ticks[0] * 100)
set output = output + "Test #2 was " + R2S(ticks[2]) + "% faster than Test #1\n\n"
else
set ticks[2] = 100 - (ticks[0]/ticks[1] * 100)
set output = output + "Test #1 is " + R2S(ticks[2]) + "% faster than Test #2\n\n"
endif
call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, output)
return false
endfunction
//===========================================================================
private function onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterPlayerEvent(t, Player(0), EVENT_PLAYER_END_CINEMATIC)
call TriggerAddCondition(t, function Actions)
call Setup()
endfunction
endscope
I'm just curious as the difference in OP limit is around 15% between the two (in favor of recursion), or was it more? Don't remeber right now. ^^Just do whatever one makes sense from the logic in your code. The more locals/parameters get declared the heavier the recursive function will be.
Your code contains calls of undeclared functions and some spelling mistakes.I wrote a test suite for it which I hope compiles.
whenever a unit is attacked, it searchs for triggers which react on PLAYER_UNIT_ATTACKED event, then for triggers on UNIT_ATTACKED. Doesn't matter which one you use, no difference at all, since both are called anyway
I think he's saying if you have a fixed number of units who should expect a unit event, and there is no need for a playerunitevent of that type in the entire map, that that unitevent will have a performance improvement due to not needing to evaluate to the user for every instance.
obviously the less objects (triggers) you have, the better, but JASS is very slow unlike inner object handling, which turns the long if-then-else into a bad coding style as you won't have any win there
They wouldn't (shouldn't) do it without having some kind of JASS -> Lua map converter which automatically converts a JASS map to a Lua map.I do not think that Blizzard will improve JASS since they add Lua. In future (a year or later) they can even remove its support at all.
ForGroup
iteration (when keeping the group) appears to be faster than the FirstOfGroup
loop and swap operation, especially when dealing with a large group of units. Moreover, iteration via BlzGroupUnitAt
is even faster than ForGroup
for a certain amount of units (not more than 1600). If the group has that many units, ForGroup
actually becomes the fastest among the three methods.Apologies for the necropost.
In the most recent patch version (1.31+ - Classic), while in the Lua environment,ForGroup
iteration (when keeping the group) appears to be faster than theFirstOfGroup
loop and swap operation, especially when dealing with a large group of units. Moreover, iteration viaBlzGroupUnitAt
is even faster thanForGroup
for a certain amount of units (not more than 1600). If the group has that many units,ForGroup
actually becomes the fastest among the three methods.
Testing procedure is done in this manner
I suppose it might have something to be on my end, but I thought it might be interesting to post such results.
- Store the initial time stamp via os.clock()
- Perform the iterations
- Obtain time elapsed by subtracting the current time stamp by the initial time stamp.
Apologies for the necropost.
In the most recent patch version (1.31+ - Classic), while in the Lua environment,ForGroup
iteration (when keeping the group) appears to be faster than theFirstOfGroup
loop and swap operation, especially when dealing with a large group of units. Moreover, iteration viaBlzGroupUnitAt
is even faster thanForGroup
for a certain amount of units (not more than 1600). If the group has that many units,ForGroup
actually becomes the fastest among the three methods.
Testing procedure is done in this manner
I suppose it might have something to be on my end, but I thought it might be interesting to post such results.
- Store the initial time stamp via os.clock()
- Perform the iterations
- Obtain time elapsed by subtracting the current time stamp by the initial time stamp.
while true do
u = FirstOfGroup(g)
if u == nil then break else
GroupRemoveUnit(g, u)
end
end
400 units (x10000)
GroupEnumUnitsInRect: 8.163
GroupEnumUnitsOfPlayer: 2.164
GroupEnumUnitsInRange: 8.245
ForGroup: 8.003
ForGroup with anonymous function: 7.992
BlzGroupUnitAt: 2.981
Table Loop: 0.127
FirstOfGroup (with GroupEnumUnitsOfPlayer and variable bind): 6.526 (-2.164 = 4.362)
BlzGroupUnitAt (with GroupEnumUnitsOfPlayer and variable bind): 5.346 (-2.164 = 3.182)
10 units (x1000000)
GroupEnumUnitsInRect: 6.149
GroupEnumUnitsOfPlayer: 2.435
GroupEnumUnitsInRange: 7.004
ForGroup: 20.282
ForGroup with anonymous function: 20.488
BlzGroupUnitAt: 5.186
Table Loop: 0.395
FirstOfGroup (with GroupEnumUnitsOfPlayer and variable bind): 13.505 (-2.435 = 11.070)
BlzGroupUnitAt (with GroupEnumUnitsOfPlayer and variable bind): 7.910 (-2.435 = 5.475)
function f()
GetEnumUnit()
end
function TestNatives()
g = CreateGroup()
r = GetWorldBounds()
p = Player(0)
l = {}
GroupEnumUnitsInRect(g, r, nil)
t0 = os.clock()
for i=1,0 do
GroupEnumUnitsInRect(g, r, nil)
end
t1 = os.clock()-t0
t0 = os.clock()
for i=1,0 do
GroupEnumUnitsOfPlayer(g, p, nil)
end
t2 = os.clock()-t0
t0 = os.clock()
for i=1,0 do
GroupEnumUnitsInRange(g, 0, 0, 99999, nil)
end
t3 = os.clock()-t0
t0 = os.clock()
for i=1,0 do
ForGroup(g, f)
end
t4 = os.clock()-t0
t0 = os.clock()
for i=1,0 do
ForGroup(g, function() GetEnumUnit() end)
end
t5 = os.clock()-t0
t0 = os.clock()
for i=1,0 do
for x=1,BlzGroupGetSize(g) do
BlzGroupUnitAt(g, x)
end
end
t6 = os.clock()-t0
for x=1,BlzGroupGetSize(g) do
l[#l+1] = BlzGroupUnitAt(g, x)
end
t0 = os.clock()
for i=1,0 do
for k,v in pairs(l) do
end
end
t7 = os.clock()-t0
t0 = os.clock()
for i=1,0 do
GroupEnumUnitsOfPlayer(g, p, nil)
while FirstOfGroup(g) ~= nil do
u = FirstOfGroup(g)
GroupRemoveUnit(g, u)
end
end
t8 = os.clock()-t0
t0 = os.clock()
for i=1,0 do
GroupEnumUnitsOfPlayer(g, p, nil)
while true do
u = FirstOfGroup(g)
if u == nil then break else
GroupRemoveUnit(g, u)
end
end
end
t9 = os.clock()-t0
t0 = os.clock()
for i=1,1000000 do
GroupEnumUnitsOfPlayer(g, p, nil)
for x=1,BlzGroupGetSize(g) do
u = BlzGroupUnitAt(g, x)
end
end
t10 = os.clock()-t0
print("GroupEnumRect:", t1)
print("GroupEnumPlayer:", t2)
print("GroupEnumRange:", t3)
print("ForGroup:", t4)
print("ForGroupAnon:", t5)
print("BlzGroupUnitAt:", t6)
print("TableLoop:",t7)
print("FirstOfGroup(w/EnumPlayer):", t8)
print("FirstOfGroupType2(w/EnumPlayer):", t9)
print("BlzGroupUnitAt(w/EnumPlayer):", t10)
end
I also saw that GroupEnumUnitsOfPlayer is 4x faster than GroupEnumUnitsInRect and GroupEnumUnitsInRange (though this might be well known already).
Seems nice, but i I am not sure it can replace GroupEnumUnitsInRange, as you then need to calculate the distance and compare it to your range.
also a correction - thru EVERY unit ingame and filters out those which are owned by other player. So the more crappy units you have, the worse the perfomance of this function.because there are no real iteration throught each unit to find out if he located somewhere inside the area, instead it moves through the list of all player's units