7

This is, in a way, a follow-up to my previous question.

I created a jsPerf which compares a number of ways to take a 1-dimensional array of RGB pixel values

var rgb = [R, G, B, R, G, B...]

And convert those into RGBA values for an HTML5 canvas (where the alpha channel is always 255, fully opaque).

var rgba = [R, G, B, 255, R, G, B, 255...]

In my tests, I found that one of the loops I tested, titled "For Loop", is astronomically slower than the other loops. Where other loops were completing the operation hundreds of millions of times per second, it weighed in at a whopping 86 times per second. The loop can be found in the jsPerf link above, but here's a bit of code with "For Loop" and "4*unrolled, skip alpha", one of the faster loops in the test.

//Setup for each test
function newFilledArray(length, val) {
    var array = Array(length);
    for (var i = 0; i < length; i++) {
        array[i] = val;
    }
    return array;
}

var w = 160;  //width
var h = 144;  //height

var n = 4 * w * h; //number of length of RGBA arrays
var s = 0, d = 0;  //s is the source array index, d is the destination array index

var rgba_filled = newFilledArray(w*h*4, 255);  //an RGBA array to be written to a canvas, prefilled with 255's (so writing to the alpha channel can be skipped
var rgb = newFilledArray(w*h*3, 128);  //our source RGB array (from an emulator's internal framebuffer)

//4*unrolled, skip alpha - loop completes (exits) 185,693,068 times per second
while (d < n) {
    rgba_filled[d++] = rgb[s++];
    rgba_filled[d++] = rgb[s++];
    rgba_filled[d++] = rgb[s++];
    d++;
}

//For Loop - loop completes (exits) 85.87 times per second
for (var d = 0; d < n; ++d) {
    rgba_filled[d++] = rgb[s++];
    rgba_filled[d++] = rgb[s++];
    rgba_filled[d++] = rgb[s++];
}

How can it be so incredibly similar in syntax, yet is so far removed in terms of performance?

Community
  • 1
  • 1
Trey Keown
  • 1,345
  • 3
  • 16
  • 23
  • have you tried switching the order of the loops – aaronman Jul 09 '13 at 03:29
  • Did you try using [`dis()`](https://developer.mozilla.org/en-US/docs/SpiderMonkey/Introduction_to_the_JavaScript_shell#dis.28.5Bfunction.5D.29)? – Ignacio Vazquez-Abrams Jul 09 '13 at 03:30
  • 1
    @aaronman What do you mean? jsperf runs each test in a fresh JS environment. – Barmar Jul 09 '13 at 03:30
  • didn't even read the question just looked at the code, my bad @Barmar – aaronman Jul 09 '13 at 03:32
  • 1
    The first indicator that something's not quite right with those benchmarks is that if the entire while loop were really running that many times per second, that would mean it was carrying out about 290 **trillion** operations per second! That would have to be one heck of a browser/CPU combo. So it's not that the for loop is exceptionally slow (it's not; it's carrying out about 134 million operations per second), but rather that the while loop was showing as absurdly fast. – JLRishe Jul 09 '13 at 05:10

1 Answers1

10

The reason why only the for loop is so slow is because it's the only correct test case; all the other test cases never reset, amongst others, the value of d, so the first iteration is normal and the rest is obviously super fast :)

This jsperf gives a better outcome, whereby the for-loop is only slightly slower than the fastest result.

Update

As bfavaretto suggested, you should also reset s and the target array that you're building for a more consistent result. His results can be found here.

Community
  • 1
  • 1
Ja͢ck
  • 170,779
  • 38
  • 263
  • 309
  • 2
    I think he was assuming that the prep code was re-run before each iteration of the test. – Barmar Jul 09 '13 at 03:38
  • @Barmar That would be my guess as well :) – Ja͢ck Jul 09 '13 at 03:41
  • Good catch! What confused me is that setup states that it "runs before each clocked test loop, outside of the timed code region". That wording still maked it sound like setup should operate as I thought it would! – Trey Keown Jul 09 '13 at 03:43
  • @TreyKeown The words aren't very explicit; your code is the body of that test loop basically :) – Ja͢ck Jul 09 '13 at 03:45
  • 1
    @TreyKeown If you also reset `s` and the target array, you get a lot more ops/sec (and a more consistent end state) on all tests. http://jsperf.com/rgb-to-rgba/9 – bfavaretto Jul 09 '13 at 03:50
  • @bfavaretto Thanks, I've added that to my answer, because I think that's another important take-away :) – Ja͢ck Jul 09 '13 at 03:54
  • @Jack I'm still unsure if all the tests are doing the same thing. Too tired to figure out all those increments right now :) – bfavaretto Jul 09 '13 at 04:00
  • @bfavaretto Yeah, personally I think there are too many tests in there to begin with ;-) – Ja͢ck Jul 09 '13 at 04:03
  • 1
    @bfavaretto thanks! I took your revision and updated it so that everything is, truly, getting reset. What is hopefully the final revision of this test can be found here: http://jsperf.com/rgb-to-rgba/11 – Trey Keown Jul 09 '13 at 04:04