Javascript overhead

Javascript overhead
0

#1

Does anyone know how much overhead to expect from the Javascript interpreter?

I have been working on the Game of Life (without React as SASS since I have not yet broken down to try and learn them from other sites). I figure that this way I wwill have a working framework and just adapt it to use React and SASS once I learn them. But that’s not my current issue…

In an effort to get it as fast as possible (higher framerate) I put in some times to see where the bottlenecks might be. This function starts and pauses the game:

function startStop() {
    var start = Date.now();

    if ($("#btnStartStop").html() === "Start") {
        timer = window.setInterval(function(){
            getNextGen();
            displayBoard();
            genNr++;
            var end = Date.now();
            times.push((end - start));
            start = end;
        }, 10);
        $("#btnStartStop").html("Pause");
    } else {
        window.clearInterval(timer);
        $("#btnStartStop").html("Start");
        console.log("Average: " + average(times));
        console.log("Std Dev:  " + standardDeviation(times));
        console.log("Count:    " + times.length);
    }
}

Originally, I had start being set at the beginning of the timer loop so that I could determine just how long each pass through the loop was taking. I was getting an average of about 60ms with a Std. Deviation of about 8.8ms. That should result in a framerate of about 16 or 17 fps, but I was getting closer to 5.5 fps. That is when I changed the code to the above, to get the actual time between frames. The results are:

Average: 178.4709388971684
Std Dev: 34.98488460723234
Count: 671

which matches the framerate that I was getting. Does javascript really have that much overhead? The actual framerate is about 1/3 what I would be getting based on the time spent in each iteration of the loop? That can’t be right. Before I added the last few items I was getting a framerate of almost 10 fps. With the overhead that something is taking now, I would have to get through my loop in negative time to get 10 fps and I have seen versions made by some of the other campers (using React and SASS) with significantly higher rate than that. Is there something that I can do to speed things up a bit?


#2

From where in your code are you calling this function? A performance crucial part of the loop?

If so, you can easily be running into the overhead of running jQuery to select and modify elements. There would be a lot of redundant selecting and reading of HTML contents, etc. there.

As for general updating, if your requested reads from and changes to the DOM are out of sync with the refresh rate of the screen, then this can dramatically reduce performance. The good news is there’s an API to help with that!

Have you looked into the requestAnimationFrame API? This allows you to defer updating the display until the browser itself is next ready to display a frame - rather than telling the browser to read from/update the DOM at points where it would be inefficient.

MDN - Request Animation Frame

From the docs:

The window.requestAnimationFrame() method
tells the browser that you wish to perform an animation and requests
that the browser call a specified function to update an animation before
the next repaint. The method takes as an argument a callback to be
invoked before the repaint.

and

The number of callbacks is usually 60 times per second, but will
generally match the display refresh rate in most web browsers as per W3C
recommendation.

and finally …

Your callback routine must itself call requestAnimationFrame() if you want to animate another frame at the next repaint.

So it seems that this:


getNextGen();
displayBoard();
genNr++;

Could be run inside of a requestAnimationFrame callback and that - as long as that processing itself doesn’t take more than 16ms per frame, you should get your 60fps.

That said … I’ve only read the docs, not used the requestAnimationFrame API myself. Been meaning to, but I’ve been able to get by with CSS transitions, all in all.

Hope that helps!
~Micheal


#3

Well, that code is being run when the user clicks the “Start” button. Nothing else should be happening at that time. But here is the interesting thing…

I knew that the method of displaying and updating the physical screen was far from the most efficient. It was probably the LEAST efficient method, but it was one that I knew how to do without looking anything up. I keep the entire board in an array of boolean. A cell is active, or it is not. getNextGen() goes through the entire array, checking its neighbors and building a new array for the next generation. The current generation array then gets set equal to the next generation array and then I am ready to update the display. That all happens fast. No issues there. Updating the display is where it all bogged down. The displayBoard() function went through the current generation array and built the html up cell by cell and then replaced the html in the div once that was done. Extremely inefficient, but it works.

After I posted this last night I decided to try something that I hoped would be more efficient. I had no idea HOW MUCH MORE EFFICIENT it would be. I replaced the div with a canvas. This made clearing the board a single call and I do it at the beginning of each display update. Then I draw the cells, but only active cells. Since the board is cleared I don’t need to do anything with inactive cells. That one change upped my framerate to over 90 fps. Then I changed the delay on the setInterval() from 10ms to 1ms and I am now getting upwards of 210 fps, as shown here:

Average: 4.620126367699183
Std Dev: 3.4908155366419167
Count: 12978

The massive overhead seems to have vanished, but I still have no idea what was causing it. I do know that I have just become a fan of the javascript canvas!

Marc


#4

Writing to the DOM is very slow. That is seriously the main issue React attempts to solve- it uses a fake DOM object that exists in javascript to determine when to update the real DOM - and then it updates everything that needs it in one pass, as opposed to each cell individually like I would assume your code did.

Drawing to canvas, however, is very fast.

Also note that console.log() is a fairly slow operation itself.

EDIT: Note that you should still use requestAnimationFrame with canvas if you are not


#5

Good that you found a solution!

Rebuilding the board in HTML from scratch every time would definitely be a very expensive operation.

210 frames a second is very nice - but might be a bit more than is needed or than people will be able to perceive! :slight_smile:


#6

Agreed. I added a slider for controlling the speed to the page and have set its limits from 10 to 100 fps. It also has an option to single step. I personally find 10 to 20 fps to be a good viewing speed. I might change the minimum to 1 fps just so it can be basically single stepped without having to click a button each time.