1

I have a small project I'm working on that handles a VERY large array (1024 items), outputting the data from the array to 1024 separate elements on the doc (yes, i tried canvases, but they're too blurry for what i'm doing).

What I need is some way to optimize this loop as much as possible.

for(var i = 0; i < 1024; i++){
    elems[i].style.height = data[i] + 'px';
    elems[i].style.backgroundColor = 'rgb(0,' + data[i] + ',' + (255 - data[i]) + ')';
}

For every item in the array data, which is always 1024 items long, the loop sets the height of one of the 1024 div's on the page stored in elems, along with setting its color to be more green for larger values, and more blue for lower values. The values inside data always range from 0 to 255. The loop is run every animationFrame and I cannot make it go in sections over time. The data must be updated live.

My main issue is that running the loop outputs a VERY low FPS count, usually around 15fps. My question is:

In what ways can I optimize the loop above to run a fast as it possibly can? The data is updated live every render frame. I'm going for high FPS as my main target. Is this possible?

If it helps, I'm making a music visualizer with the new Google Chrome audio analyzer.

I can also see this helping for whenever I need to crunch or display very large datasets in the future as well. Every method, even if largely unreadable (that's what /*comments*/ are for), helps!

MineAndCraft12
  • 671
  • 5
  • 15
  • 4
    1024 is not large at all... – omarjmh Jun 16 '16 at 21:58
  • 1
    1024 items is not very large. In what way is the canvas "blurry" ? – Mulan Jun 16 '16 at 21:58
  • Why is `Math.round` needed? Won't it always be an integer? Also, could you create some kind of example where we could test it ourselves? – 4castle Jun 16 '16 at 22:04
  • Canvases are always blurry because they use the different pixel ratios - based on a very sucessfull web search, I found that I needed values from the browser to change the ratio in some way - values my browser didnt report accurately, and therefore didnt work. I put Math.round() in there when I made the project - I was paranoid about JS's weird float-point issues before I realised it was integers being subtracted. I forgot to remove it. 1024 items seems large to me as what I'm doing with it is intense. DOM is slow - two DOM interactions per value is 2048 DOM interactions every animationFrame. – MineAndCraft12 Jun 16 '16 at 22:13
  • 2
    check out this SO on delaying dom rendering. http://stackoverflow.com/questions/1392068/delay-rendering-of-dom-element-when-changing-properties Your issues could stem to how Chrome is reflowing the DOM with each iteration of the loop. If you hide the element or make the changes first on a clone of the parent, then update it all at once you may see some performance improvement. – scrappedcola Jun 16 '16 at 22:17
  • Another doc on minimizing reflow: https://developers.google.com/speed/articles/reflow – scrappedcola Jun 16 '16 at 22:18
  • Canvas is only blurry if you don't set the proper size of it using its width/height properties/attributes, and not by using CSS. –  Jun 16 '16 at 22:20
  • @scrappedcola The first SO on DOM rendering *seems* to have helped, about 3 or so FPS faster; though for such a small number it just be CPU happening to be under less load than it was last time. – MineAndCraft12 Jun 16 '16 at 22:26
  • @K3N I did set element.height AND element.style.height, same with the width. el.height is the height of the canvas content, and el.style.height is the height of the element on the page. No matter what I do, the thing always is blurry, as if it's not fitting right. I try even a el.width/height that is twice as large as needed, and it still seems as if you're stretching a PNG larger than the original. The main source of my info: accepted answer in http://stackoverflow.com/questions/15661339/how-do-i-fix-blurry-text-in-my-html5-canvas – MineAndCraft12 Jun 16 '16 at 22:26
  • @MineAndCraft12 - are you certain that the lag comes from this loop? Have you profiled it and/or emptied the loop to ensure that this is the bottleneck? – nrabinowitz Jun 16 '16 at 22:39
  • @nrabinowitz In Chrome JS profiler, 71.74% of time taken by JS was something called `(program)`. I dont have access to it, and it does not seem to be linked to my code. 27.67% was the renderFrame function (it grabs the data from audio analyser, then runs the for loop). Within renderFrame, which had 1603.6ms of the profile, 889.6ms was `getFrequencyByteData`, which is Chrome's built-in function that I cant change. That leaves the other 714ms for the loop. Everything else in the doc uses less than 1% of the time. In order, `(program)`, `getFrequencyByteData`, and my loop (12.35% of render time). – MineAndCraft12 Jun 16 '16 at 22:53
  • this for loop would be 12.35% of the render time - (program) and getFrequencyByteData would be almost all other JS time, and are out of my control. – MineAndCraft12 Jun 16 '16 at 22:54

1 Answers1

0

I know this isn't much of an answer, per se, but I wanted to show the animation here is quite quick, and can't do that in a comment.

There may be something else going on in your code other than that code that you're showing. I whipped up this little demo to try and get a handle on what could be going on, and I routinely get ~50fps (in Chrome 51 on Win 8.1). It does depend on your DOM structure and what else you have going on the page at the same time...

function getRandomInt(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

var start;

function updatePositions(timestamp) {
  var container = document.getElementById('container');
  var elems = container.children;
  for (var i = 0; i < 1024; i++) {
    var itemValue = getRandomInt(0, 255);
    elems[i].style.height = itemValue + 'px';
    elems[i].style.backgroundColor = 'rgb(0,' + itemValue + ',' + (255 - itemValue) + ')';
  }
  start = start || timestamp;
  var duration = timestamp - start;
  if (duration < 5000) {
    window.requestAnimationFrame(updatePositions);
  }
}

function initialize() {
  var items = [];
  for (var i = 0, z = 1024; i < z; i++) {
    items.push('<div id="item' + i + '">' + i + '</div>');
  }
  container.innerHTML = items.join('');
  window.requestAnimationFrame(updatePositions);
}

window.onload = initialize;
<div id="container">
</div>
Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122
  • My code is actually drawing its info from a source, however. It has the audioContext playing audio to the audioAnalyzer. You seem to have used random numbers. For everything else on the page, I only have a line of controls next to the visualizer, using only text, and a few input fields and buttons. For the examples, nrabinowitz's got a 40fps average, and Mike's got about a 35fps average on my machine. (hp laptop, chrome on win10). I did just start the web server, if I'm allowed to share the link to the site itself. (only allowed to run for a few hours at a time without paying subscription) – MineAndCraft12 Jun 16 '16 at 23:00
  • Other devices I've run this on are Chromebases and Chromebooks at school, with similar results – MineAndCraft12 Jun 16 '16 at 23:01
  • Yeah @MineAndCraft12, I used random, since I've not played with audio and I'm not sure that you can access audio from a sandboxed iframe (which is what the snippets run in). Just wanted to show that from the code you have, it shouldn't be slow. It may be something in audioAnalyzer that's slowing things down, rather than the animation itself. – Heretic Monkey Jun 16 '16 at 23:05
  • Wasn't bashing it, just noting its effect on performance. I saw in my profiler that 71% of the time JS took was something called (program), and 15% was the visualizer grabbing data from the analyser. The other 14% was the for loop, and the remaining <1% was everything else. Noticing that about 40fps compared to about 20fps on mine, the analyzer itself seems to be about half of the FPS cut. However, playing the audio without the visualizer active, I still get a full 60fps. It seems that the loop itself took about 20fps, and the analyzer took about another 20fps. – MineAndCraft12 Jun 16 '16 at 23:08