Ask coding questions

← Back to all posts
SortaCanvas slows down after running for a while
tussiez (1669)

XXD Seems weird to ask questions about my own library.. but anyway:

If you try this demo Demo ATM, not a game yet, and leave the tab open for a while, you'll notice that the FPS goes down very quickly.

What I'm doing:
Simple loop over 20 or so objects and moving them
SortaCanvas.render() --> loops through all objects, runs ctx.draw for the corresponding object (e.g images, rectangles, text). What may the problem is that even thought the object isn't being rotated, ctx.save() and ctx.restore() could be running, which is slowing down the game.

What do you think?
Script

Comments
hotnewtop
mwilki7 (1133)

I've had a similar problem
The problem with me was a memory leak due to duplicate references from multiple global arrays. So I had to check every list to make sure when an entity was removed, it was removed from all lists (luckily I just had 2 lists to check)

Snippet that I used in case it helps

    // entlist - global array
    // layers  - global array
    // killSelf() member function of class Entity
    killSelf()
    {
        // remove from global entlist
        for (var i = 0; i < entlist.length; i++)
        {
            if (entlist[i] == this)
            {
                entlist.splice(i, 1);
            }
        }

        // remove from layer list
        for (var i = 0; i < layers.length; i++)
        {
            var layerobject = layers[i];
            for (var j = 0; j < layerobject.entlist.length; j++)
            {
                var elem = layerobject.entlist[j];
                if (elem == this)
                {
                    layerobject.entlist.splice(j, 1);
                }
            }
        }
    }
tussiez (1669)

@mwilki7 Hmm... I'm not removing any objects though

mwilki7 (1133)

@tussiez
Are there any arrays or lists that keeps track of all the objects that you can print to see if it gets any bigger?

mwilki7 (1133)

@tussiez
I did a comparison on my project, it looks like the memory thing is normal, but somehow mine doesn't do as much background (in the System category) tasks

mwilki7 (1133)

@tussiez
Sorry for the bzillion messages but I think I've found the problem:

script.js line 182

function render() {
  requestAnimationFrame(render);//render @ ~60fps

  SortaCanvas.render();//Draw the objects

}

requestAnimationFrame() is being called in this render loop (which is normal)

but it's also being called a second time in
lib.js line 252

  render: function () {
    if (this.loop == true) {
      requestAnimationFrame(this.render);
    }

I haven't tested whether this actually causes the slowdown but it looks like it might attempt to draw it twice as much.

tussiez (1669)

@mwilki7
1. Yes, SortaCanvas.objects, this is being looped
2. Hmmm
3. this.loop should be false, lemme check

mwilki7 (1133)

@tussiez
maybe it has something to do with tween.js
it might completely break the program, but does it still slow down if you comment it out?

tussiez (1669)

@mwilki7 Sorry for the late reply, but TWEEN isn't doing anything (not running any animations)

Baconman321 (1097)

This is what us programmers call a "memory leak". If you notice, playing some people's games for a while starts to slow things down by a significant amount. I remember making stargaze and finding that the meteors slowed down the game significantly. Another time when I had an error it kept replicating an object or whatever causing the memory usage to spike to gigabytes!

One of the great things in JavaScript is that it has Garbage collection. It's as simple as assigning a variable to null (there are other ways but this is the main way). If the object/value is not in use anymore the Garbage collector (more so, if it has no references) it will be deleted.

let MyObj = {
   "Name":"John"
}

This has a reference because it is able to be accessed in the program.

user = null;

Now that the user is assigned to null, the previous value ("Name":"John") is freed from any references. The garbage collector detects this and will junk the data and delete it to free up memory.

Try seeing if any variables are being declared in a loop or something of the sort. If so, they might just pile up but still have references making them still considered "in-use". You can always open devtools and check the memory section. It will show the overall memory change from the last two minutes (either more memory used or less memory used).

The easiest way is to set the variable to null and make sure there are no more references to that variable.

Best of luck coding!

LOL I can't believe I'm helping someone I could just talk to when we collab on something. HAI TUSSIEZ!! :D
Sources:

tussiez (1669)

@Baconman321 LOL ::D
I checked DevTools, seems to be hovering around 2-3MB when it lags + before it does..

I don't think I'm deleting any objects/creating any here - the crewmates are teleporting back to the canvas once they fly out

Either way, I'll check the SortaCanvas code and see if I have something creating new objects that's lagging it out ::)

xxpertHacker (930)

@Baconman321 That is manual memory management, I'll check this out and provide a idiomatic JS solution that actually uses the GC. :)

(Should be back soon)

Baconman321 (1097)

@xxpertHacker Huh. How do you use the GC then (this comes straight from the tutorials so)? Weakmaps (that uses the GC if it isn't referenced or something like that right)?

Also, I believe it should be self.terminate() (for your bio) from the looks of it. Depends on what you're writing it in tho...

xxpertHacker (930)

@Baconman321 Worker#terminate is manual memory management.

WeakMap, WeakSet, WeakRef, and FinalizationRegistry are all what I'm used to in my regular code.

FinalizationRegistry is more of a mix, but whatever.

Baconman321 (1097)

@xxpertHacker ?
self.kill isn't defined tho... is it?

You technically can in some browsers call the garbage collector, but it's not supported that much as far as I know. I believe you can schedule the garbage collector at certain times, but I don't know too much about that (I couldn't find the SO answer. Like you said too, SO is old and the answer might be outdated).

xxpertHacker (930)

@Baconman321 self.kill reference to PyGame, unrelated to JS.

Sure, V8 provides a native function for it:

%CollectGarbage(true);

Otherwise, you cannot call the GC in a browser, I could make a function that would indirectly force it to sweep the heap, but that's the best that you could get, then again, maybe it'll crash ¯\_(ツ)_/¯.

Forcefully calling the GC feels like manual memory management anyway.

Baconman321 (1097)

@xxpertHacker Yeah. How could you force it to sweep? Make a reference then delete it so the garbage collector from it's perch up high sees it and swoops down to collect it and the other stuff?

V8 itself probably allows that yeah... why doesn't chrome allow that then?

Oh wait, it's V8 based... I bet they took that out (or never added it in) :/

Yeah that makes sense (with self.kill).

xxpertHacker (930)

@Baconman321 Cause a large set of small allocations; i.e. spam first generation garbage.

Baconman321 (1097)

@xxpertHacker Ah.

Basically create objects then delete them (or their references)?

Or something like that?

xxpertHacker (930)

@Baconman321 Delete them? You can't delete anything in JS.

Baconman321 (1097)

@xxpertHacker I mean set them to null or dereference them in some other way.

xxpertHacker (930)

@Baconman321 Setting them to null doesn't delete them, and JS can't dereference anything.

Thinking about it, Wasm doesn't have these problems...

Baconman321 (1097)

@xxpertHacker No but setting them to null will detach any references. Oh sorry, I mixed up words. But basically, detaching any references leaves the GC free to collect them. That said, the browser mostly controls when the garbage collector runs. In devtools you can manually collect the garbage though.

Baconman321 (1097)

@xxpertHacker I'm pretty sure setting variables to null detaches any references. Of course, this depends on how you are referring the variable. javascript.info literally says set the variables to null to detach any references.

Why can't you set them (what is "them" anyways) to null?

xxpertHacker (930)

@Baconman321 I was talking about this specific Repler's situation, but I was wrong anyway.

Generally, leaving space in an object or variable is gross anyway.

const objects = [ obj0, obj1, obj2 ];

// oh no, leak, let's clear
objects[0] = null; // gross

vs

{
    const objects = [ obj0, obj1, obj2 ];
} // GCed!

or

const objects = new WeakSet;

{
    const obj0 = ...;
    obejcts.add(obj0);
} // GCed!

null, with the exception of null protoype object, isn't something that I ever use.

Then again, think of it in another way: you can't fight the GC if you don't dynamically allocate memory in the first place.

Baconman321 (1097)

@xxpertHacker On the second one, why would that get collected? Is it because it is only in that scope and will get collected once it finishes running? And in the third one, what would ... be doing in this case?
Oh, I get it. It's adding whatever, adding that to an object, then that placeholder has no use anymore so it's collected.

Smart :D

Yeah, setting it to null just isn't very appealing.

You can use delete on objects, but I don't know too much about it in-depth.

xxpertHacker (930)

@Baconman321 Delete is terrible, don't.

If you're using delete, you're probably already mistreating an object like a dictionary, use a Map, and it's Map#delete instead.

Objects should be treated like a C struct.

As for the second one getting GCed, it's due to scoping, one it's out of scope then it is no-longer accessible from the script, which is what the memory that GC is literally meant to target+free.

... could be any object, idc, {}.

Baconman321 (1097)

@xxpertHacker LOL I thought the ... was a rest/spread operator XD

xxpertHacker (930)

You're filling up SortaCanvas.objects with strongly-referenced objects that can't be freed; you have a memory leak.

First of all, don't use an array, use a Set.

Also, getObjectByName is broken, and will never throw an exception.

But otherwise, the way that you've set up your ray casting, it can't really let the memory be collected on it's own, so you'll need to restructure your control flow.

tussiez (1669)

@xxpertHacker
1. Hmm
2. Hmmmm
3. I expected that LOL

tussiez (1669)

@xxpertHacker I have homework RN, I can add Sets later today

xxpertHacker (930)

@tussiez Also, log makes no sense at all, it probably doesn't work.

And randomColor returns floats, was that intended? Are you forgetting to floor/round/trunc the results from Math.round and multiplication?

If you're trying to get 3 bytes with random values, you might be better off using this:

const [ r, g, b ] = crypto.getRandomValues(new Uint8Array(3));
xxpertHacker (930)

@tussiez Opps, sorry for the false positive, your script link is irrelevant here.

The problem is in your actual script:
https://sortaamongus.sortagames.repl.co/script.js

menuItems is never being cleared at all!

Once again, use a Set, and once the flying object is not visible within the client's viewport, remove it.

I presume that this would be something like:

const { x, y } = menuItem;
if (
    x > self.innerWidth
    && y > self.innerHeight
    && .. and so on
) {
    SortaCanvas.remove(menuItem);
    menuItems.remove(menuItem);
}
tussiez (1669)

@xxpertHacker
This is a lot of comments!

  1. randomColor was test code, I should round the number (however floats does work)
    Ooh, I'll try that code snippet sometime

  2. The flying objects are teleported back into the canvas, they are never removed.

xxpertHacker (930)

@tussiez Oh, you're right, and createFlyingCrewmates is only ever called once, so... meybe it's not a memory leak after all?

CyberHacker101 (128)

The imposter is......CYAN THE SUS!

CyberHacker101 (128)

soooooo are you gonna now call all ur projects sorta again? @tussiez

RhinoRunner (866)

I don't use JS, but I have had this problem before with a game using pygame. For me, the problem was that instead of deleting the object I wanted gone, I was making it turn invisible, meaning it still moved and stuff. When you make the crewmates disappear, are they being deleted or are they just going invisible?

tussiez (1669)

@RhinoRunner They're being teleported to a new position, they're not deleted (reusing the same objects)

RhinoRunner (866)

@tussiez um have you been to Chernobyl lately because it seems like you have grown some more eyes...

. .
 o
tussiez (1669)

@RhinoRunner LEL you got me laughing hard there
@AngryDoge invented "Spider Emoticons" :D

RhinoRunner (866)

@tussiez hey, has the radiation gone upstairs, because:

tussiez (1669)

@RhinoRunner CHERNOBYL MIDDLE SCHOOL
LOL

tussiez (1669)

@RhinoRunner With so many Spongebobs you may summon himself

RhinoRunner (866)

@tussiez You code in CHER,NO,BYL and I code in SPON,GE,BOB

Baconman321 (1097)

@tussiez @RhinoRunner Going to chernobyl does not cause you to grow extra eyes. However, babies are affected. Look up children deformed by chernobyl, and you will see what I mean.

tussiez (1669)

@Baconman321 Yee, it's a joke, but yes, the problem was serious

Baconman321 (1097)

@tussiez LOL I know soooo much about nuclear disasters.

Ever heard of mayak?

What about tokaimura?

Sl-1?
https://en.wikipedia.org/wiki/SL-1
That one was really creepy.

An eerie silence around the reactor and control room. When you went inside, alarms were blaring.

tussiez (1669)

@Baconman321 Woah.. I will have nightmares XD

RhinoRunner (866)

@Baconman321 I looked at your name and now I want bacon, but all ive got is this granola bar...

tussiez (1669)

@RhinoRunner If you live near a BJ's, go to the section with the microwave breakfast and the bacon is next to it.

RhinoRunner (866)

@tussiez ah, well now im full offa this granola bar

Baconman321 (1097)

@tussiez Wanna work on sortacraft?