ID:178301
 
I am working on a semi-advanced AI code for animals for my game. The problem is, for each animal the AI proc is called every second. My computer can handle this, but the garbage collection is killing it. The average time it takes to run the proc goes from .010 seconds or so, to over 4 seconsds! How can I forcibly clean up this garbage, and where does it come from?
Hmmm...
I had the same problem, but if you didn't rat on my game maybe I would tell you!
In response to Jap
I'd love to know how I ratted on your game. I said nothing about your game.
In response to Garthor
Doesn't anyone know? I'm sorry for bumping this so soon, but I can't continue progress on my game until this is fixed. Do you want me to provide the code?
The garbage collector gets rid of objects that are no longer referenced in your code (lost objects, as it were.. since there's no way to get them back). Yes, it does add some overhead, but it shouldn't affect those particular procs, unless you're creating and abandoning a lot of objects.

-AbyssDragon
In response to AbyssDragon
Then why does the time it takes to process all my procs increase constantly until the game is unplayable? And how do I correct it?
In response to Garthor
Garthor wrote:
Then why does the time it takes to process all my procs increase constantly until the game is unplayable? And how do I correct it?

It sounds to me like extra objs are being created, but not properly deleted. Either that or your AI is dependent on lists that are growing out of proportion.

One important thing is that you don't have to rely on garbage collection in DM: You can manually delete things with del(). My best guess as to what's happening is that you're not using del() on objects when you should, but because you're also not removing all the references to them, they're not being destroyed by the collector.

Lummox JR
In response to Lummox JR
I don't know what the problem is. I have a bunch of for(var/mob/Sheep in oview(8))'s and all that, but I reduced the range and it still continuously increases. I tried dumbing down the AI (it reads the situations, then acts on them multiple times) and it STILL goes up, but not as fast. I'd post my code but it'd take you twice as long to make any sense of it, considering how sloppy it is and the fact that I never use comments in my code (I know, I should, but I never forget what my code does...). Unless if the for() lists accumulate and aren't deleted, I don't see where else the garbage is coming from. Could it be that?
In response to Garthor
Ok then, I guess I'll post the code so that people can look at it. I am not liable for any damage caused to your brain from reading my code closely. Also, there are no comments because I just don't use them (I should use them more, I know, but I don't =P )
    Sheep
icon = 'Sheep.dmi'
New()
spawn(rand(0,6)) Starve()
spawn(rand(0,6)) Walking()
new /obj/Meat(src)
new /obj/Meat(src)
var/Hunger = 0
var/Nearplant
var/Leadcheck = 0
var/Running = 0
var/FoundFood
var/Spooked = 0
var/Angry = 0
var/Pregnant = 0
proc
Starve()
if(src.Hunger > 50)
del(src)
else
src.Hunger += 1
spawn(600) Starve()
Walking()
if(src)
for(var/mob/Sheep/S in view(6))
if(S.Leader == 1) src.Leadcheck = 1
if(src.Leadcheck == 0)
src.Leader = 1
src.Leadercheck()
src.Leadcheck = 0
for(var/mob/M in oview(15))
if(M.Attacking == 1)
if(M.Constitution >= src.Strength * 3)
src.Spooked = 1
else
src.Angry = 1
if(src.Hunger == 0 && src.Spooked == 0 && src.Angry == 0 && src.Pregnant == 0 && istype(src,/mob/Sheep/Sheep))
for(var/mob/Sheep/Ram/S in oview(8))
if(S.Hunger == 0 && S.Spooked == 0 && S.Angry == 0)
step_to(src,S)
sleep(8)
step_to(src,S)
sleep(8)
Walking()
if(src.Hunger == 0 && src.Spooked == 0 && istype(src,/mob/Sheep/Ram))
for(var/mob/Sheep/Sheep/S in oview(8))
if(S.Hunger == 0 && S.Pregnant == 0 && S.Spooked == 0 && S.Angry == 0 && src.Angry == 0 && src.Spooked == 0)
step_to(src,S,1)
if(S in oview(1))
S.Pregnant = 6
spawn() S.Birth()
src.Hunger = 10
sleep(10)
Walking()
if(src.Leader)
src.Leadercheck()
src.Nearplant = 0
if(src.Spooked == 0 && src.Angry == 0)
for(var/obj/plant/P in oview(round(src.Hunger / 5))) src.Nearplant = 1
if(!src.Nearplant)
if(!src.Leader)
for(var/mob/Sheep/Ram/R in oview(10))
if(R.Leader == 1)
step_to(src,R,3)
sleep(10)
step_to(src,R,3)
sleep(10)
step_to(src,R,3)
sleep(10)
Walking()
else
step_rand(src)
sleep(10)
step_rand(src)
sleep(10)
step_rand(src)
sleep(10)
Walking()
else
for(var/obj/plant/P in oview(5))
if(get_dist(src.loc,P.loc) > 0 && FoundFood == 0)
FoundFood = 1
step_to(src,P,0)
sleep(10)
Walking()
else
src.Eat(P)
sleep(10)
Walking()
else
if(Spooked)
for(var/mob/M in oview(10))
if(src.Spooked == 1 && !istype(M,/mob/Sheep))
step_away(src,M,100)
sleep(2)
step_away(src,M,100)
sleep(2)
step_away(src,M,100)
sleep(2)
if(rand(1,20) == 20)
Spooked = 0
sleep(2)
Walking()
else
for(var/mob/M in oview(10))
if(src.Angry == 1 && !istype(M,/mob/Sheep) && get_dist(src,M) > 1)
step_to(src,M,1)
sleep(2)
step_to(src,M,1)
sleep(2)
Walking()
else if(src.Angry == 1 && !istype(M,/mob/Sheep) && get_dist(src,M) == 1)
Battle(M)
sleep(10)
Walking()
if(rand(1,40) == 40)
Angry = 0
Leadercheck()
if(src)
if(src.Leader)
for(var/mob/Sheep/R in oview(15))
if(R.Strength > src.Strength)
src.Leader = 0
R.Leader = 1
else
R.Leader = 0
src.Leader = 1
Eat(var/obj/plant/P)
if(src.Hunger > 0)
src.Hunger -= 1
del(P)
Battle(mob/M)
ohearers() << "<font color=red><b>[usr] attacks [M]!</font color=red></b>"
M.HP -= round(src.Strength / 3 + rand(1,3))
M.Deathcheck()
var/Leader = 1
var/Any
Baby_Sheep
icon_state = "Baby"
New()
..()
Strength = 3 + rand(-2,2)
Constitution = 10 + rand(5,-5)
HP = Constitution
Leader = 0
spawn(12000)
var/newmob = pick("Sheep","Ram")
if(newmob == "Ram")
var/mob/Sheep/Ram/O = new (src.loc)
O.Hunger = src.Hunger
del(src)
else
var/mob/Sheep/Sheep/O = new (src.loc)
O.Hunger = src.Hunger
del(src)
Sheep
icon_state = "Sheep"
New()
..()
Strength = 7 + rand(-3,3)
Constitution = 20 + rand(10,-10)
HP = Constitution
var/Age = 0
proc
/* Breed()
if(src.Hunger == 0 && src.Spooked == 0 && src.Angry == 0 && src.Pregnant == 0)
for(var/mob/Sheep/Ram/S in oview(10))
if(S.Hunger == 0 && S.Spooked == 0 && S.Angry == 0)
walk_to(src,S,1,8)
sleep(100)
Breed()*/

Birth()
if(src)
if(src.Pregnant == 1)
new /mob/Sheep/Baby_Sheep(src.loc)
else
src.Pregnant -= 1
sleep(1200)
Birth()
Ram
icon_state = "Ram"
New()
..()
Strength = 15 + rand(-3,3)
Constitution = 50 + rand(20,-20)
HP = Constitution
/* proc
Breed()
if(src.Hunger == 0 && src.Spooked == 0)
for(var/mob/Sheep/Sheep/S in oview(10))
if(S.Hunger == 0 && S.Pregnant == 0 && S.Spooked == 0 && S.Angry == 0 && src.Angry == 0 && src.Spooked == 0)
walk_to(src,S,1,4)
if(S in oview(1))
S.Pregnant = 6
S.Birth()
src.Hunger = 10*/
Whew, that was a lot. Notice the commented out Breed() procs. Long story made short, I combined those into the movement proc for reasons which I forget because I was half-asleep. I had the sense to keep the procs intact by commenting them out, though. Also, a few vars that are referred to belong to every mob. Sorry to post so much, but I just have to show it all in order for it to make sense.
In response to Garthor
In case you were wondering, I was doing a little stress-test with the sheep. I put up to 528 sheep on the map at once in a clustered area, and the game was JUST BARELY playable (it was like playing with 100-200 ping.) The CPU time / Call started at around 0.010 (or less) and constantly got higher. Apparently, with so many sheep near each other, it skyrocketed. It was at 0.350 in a minute. I'm convinced it's a problem with the garbage collected from the for procs, but I'm not exactly extremely experienced, so I'm not positive. I would love to have some help on how to clean up this garbage.
In response to Garthor
You definitely have serious problems in it that code that are contributing to the problem. I'll start with what I found first, the Birth() proc:
Birth()
if(src)
if(src.Pregnant == 1)
new /mob/Sheep/Baby_Sheep(src.loc)
else
src.Pregnant -= 1
sleep(1200)
Birth()

That proc is severely messed up.
First off, if src is null the proc won't even execute, so the whole if(src) check is redundant. Next, you've got a problem with your pregnancy countdown: When Pregnant==1, the pregnancy counter is never reset to 0. But the big problem is right in the end: The proc should be spawning itself, not calling itself recursively.

Your stack at the end of a birth will look something like this:

mob/Sheep/proc/Birth
called by mob/Sheep/proc/Birth
called by mob/Sheep/proc/Birth
called by mob/Sheep/proc/Birth
called by mob/Sheep/proc/Birth
called by mob/Sheep/proc/Birth

That's nested 5 times; this is not what you want.
A better Birth() proc would look like this:
Birth()
if(Pregnant<=0) return
--Pregnant // do this first, so it always decrements
if(Pregnant>0)
spawn(1200) Birth() // spawn the proc again to loop
else
sleep(1200)
new /mob/Sheep/Baby_Sheep(loc)

The meat of the problem you're having, though, is caused by a similar design flaw in Walking(). Walking() doesn't spawn a new call to the proc, but instead calls itself recursively. This is an even bigger issue than Birth(), because the proc is called more often. You need to use spawn() on that as well, everywhere it appears.

Lummox JR
In response to Lummox JR
Well, I made those changes. Tell me if this is correct. Also, now that I've made the changes, it seems like I can't get as many sheep in the same area. Here's the code...
    Sheep
icon = 'Sheep.dmi'
New()
spawn(rand(0,20)) Starve()
spawn(rand(0,20)) Walking()
new /obj/Meat(src)
new /obj/Meat(src)
var/Hunger = 0
var/Nearplant
var/Leadcheck = 0
var/Running = 0
var/FoundFood
var/Spooked = 0
var/Angry = 0
var/Pregnant = 0
proc
Starve()
if(src.Hunger > 50)
del(src)
else
src.Hunger += 1
spawn(600) Starve()
Walking()
if(src)
for(var/mob/Sheep/S in view(6))
if(S.Leader == 1)
src.Leadcheck = 1
break
if(src.Leadcheck == 0)
src.Leader = 1
src.Leadercheck()
src.Leadcheck = 0
for(var/mob/M in oview(10))
if(M.Attacking == 1 && !istype(M,/mob/Sheep))
if(M.Constitution >= src.Strength * 3)
src.Spooked = 1
src.Angry = 0
else
src.Spooked = 0
src.Angry = 1
break
if(src.Hunger == 0 && src.Spooked == 0 && src.Angry == 0 && src.Pregnant == 0 && istype(src,/mob/Sheep/Sheep))
for(var/mob/Sheep/Ram/S in oview(8))
if(S.Hunger == 0 && S.Spooked == 0 && S.Angry == 0)
step_to(src,S)
spawn(8) Walking()
if(src.Hunger == 0 && src.Spooked == 0 && istype(src,/mob/Sheep/Ram))
for(var/mob/Sheep/Sheep/S in oview(8))
if(S.Hunger == 0 && S.Pregnant == 0 && S.Spooked == 0 && S.Angry == 0 && src.Angry == 0 && src.Spooked == 0)
step_to(src,S,1)
if(S in oview(1))
S.Pregnant = 6
spawn() S.Birth()
src.Hunger = 10
spawn(10) Walking()
if(src.Leader)
src.Leadercheck()
src.Nearplant = 0
if(src.Spooked == 0 && src.Angry == 0)
for(var/obj/plant/P in oview(round(src.Hunger / 5))) src.Nearplant = 1
if(!src.Nearplant)
if(!src.Leader)
for(var/mob/Sheep/Ram/R in oview(10))
if(R.Leader == 1)
step_to(src,R,3)
sleep(10)
step_to(src,R,3)
sleep(10)
step_to(src,R,3)
spawn(10) Walking()
break
else
step_rand(src)
sleep(10)
step_rand(src)
sleep(10)
step_rand(src)
spawn(10) Walking()
else
for(var/obj/plant/P in oview(5))
if(get_dist(src.loc,P.loc) > 0 && FoundFood == 0)
FoundFood = 1
step_to(src,P,0)
sleep(10)
step_to(src,P,0)
spawn(10) Walking()
else
src.Eat(P)
spawn(10) Walking()
else
if(Spooked)
for(var/mob/M in oview(10))
if(src.Spooked == 1 && !istype(M,/mob/Sheep))
step_away(src,M,100)
sleep(2)
step_away(src,M,100)
sleep(2)
step_away(src,M,100)
spawn(2) Walking()
if(rand(1,20) == 20)
Spooked = 0
else
for(var/mob/M in oview(10))
if(src.Angry == 1 && !istype(M,/mob/Sheep) && get_dist(src,M) > 1)
step_to(src,M,1)
spawn(2) Walking()
else if(src.Angry == 1 && !istype(M,/mob/Sheep) && get_dist(src,M) == 1)
Battle(M)
spawn(10) Walking()
if(rand(1,40) == 40)
Angry = 0
Leadercheck()
if(src)
if(src.Leader)
for(var/mob/Sheep/R in oview(15))
if(R.Strength > src.Strength)
src.Leader = 0
R.Leader = 1
break
else
R.Leader = 0
src.Leader = 1
Eat(var/obj/plant/P)
if(src.Hunger > 0)
src.Hunger -= 1
del(P)
Battle(mob/M)
ohearers() << "<font color=red><b>[usr] attacks [M]!</font color=red></b>"
M.HP -= round(src.Strength / 3 + rand(1,3))
M.Deathcheck()
var/Leader = 1
var/Any
Baby_Sheep
icon_state = "Baby"
New()
..()
Strength = 3 + rand(-2,2)
Constitution = 10 + rand(5,-5)
HP = Constitution
Leader = 0
spawn(12000)
var/newmob = pick("Sheep","Ram")
if(newmob == "Ram")
var/mob/Sheep/Ram/O = new (src.loc)
O.Hunger = src.Hunger
del(src)
else
var/mob/Sheep/Sheep/O = new (src.loc)
O.Hunger = src.Hunger
del(src)
Sheep
icon_state = "Sheep"
New()
..()
Strength = 7 + rand(-3,3)
Constitution = 20 + rand(10,-10)
HP = Constitution
var/Age = 0
proc
/* Breed()
if(src.Hunger == 0 && src.Spooked == 0 && src.Angry == 0 && src.Pregnant == 0)
for(var/mob/Sheep/Ram/S in oview(10))
if(S.Hunger == 0 && S.Spooked == 0 && S.Angry == 0)
walk_to(src,S,1,8)
sleep(100)
Breed()*/

Birth()
if(Pregnant<=0) return
--Pregnant
if(Pregnant>0)
spawn(1200) Birth()
else
sleep(1200)
new /mob/Sheep/Baby_Sheep(loc)
Ram
icon_state = "Ram"
New()
..()
Strength = 15 + rand(-3,3)
Constitution = 50 + rand(20,-20)
HP = Constitution
/* proc
Breed()
if(src.Hunger == 0 && src.Spooked == 0)
for(var/mob/Sheep/Sheep/S in oview(10))
if(S.Hunger == 0 && S.Pregnant == 0 && S.Spooked == 0 && S.Angry == 0 && src.Angry == 0 && src.Spooked == 0)
walk_to(src,S,1,4)
if(S in oview(1))
S.Pregnant = 6
S.Birth()
src.Hunger = 10*/
In response to Garthor
Garthor wrote:
considering how sloppy it is and the fact that I never use comments in my code

Take it from someone who's been here before: these are your actual problems. Everything else is just symptoms!