mathiasbynens/jsperf.com

Prepared data corruption, race condition?

Closed this issue · 6 comments

Take a look at this: http://jsperf.com/mutate-then-slice-vs-clone-then-mutate

6th test case ("To larger, mutable") hangs the browser.

This is the source:

function toLargerMutable(arr) {
    var arrLength = arr.length,
        el;
    for (var i = arrLength - 1; i >= 0; i--) {
        el = arr[i];
        arr.splice(i, 1, el - 1, el + 1);
    }
    return arr;
}

It's called like this:

toLargerMutable(toLargerData);

toLargerData is just an array I generate in the prepare step.

Going into the debugger, it seems this array is at some point replaced or enlarged into a huge array that hags the central loop with the splice call. I couldn't get any closer to the problem, because if I put debugger traps or even just add a console.log, the code works. That is, everything goes as expected.

The behavior is observed both in Chrome and Firefox. I tried creating a tight loop calling this function inside a browser console and on repl.it, and I couldn't reproduce the problem. However, if I just specify literal data instead of using prepared variable, everything is ok.

toLargerMutable([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]);  // <-- this works

All this leads me to suspect there's a problem with your VM and the way you handle preparation code.

Nothing wrong with our setup code. It works like:

<setup>
while (count--) { 
<test>
}
<teardown>

So you have to be mindful of that when creating your tests. A hot loop that pushes values to an array outside the loop will soon get into issues (massive arrays/memory).

Hmm, OK then, I guess I don't know enough about how javascript acts under pressure. I fiddled around some more and got it to work with literal arrays in JSPerf, and using JSLitmus library.

Starting with a fresh array in the test loop will work as it's not accumulating millions and millions of elements. If you initialize your array in the setup then in the loop, which gets hit potentially hundreds of millions of times, you're adding all those values to a single array and it will bust things.

Yes, this fixed it.

I still don't understand why initiating the arrays inside 'setup' step didn't work.

From the help for the setup box:

Define setup for all tests
(variables, functions, arrays or other objects that will be used in the tests)
(runs before each clocked test loop, outside of the timed code region)
(e.g. define local test variables, reset global variables, clear canvas, etc.)
(see FAQ)

It seems like the setup code should be executed before each run and the timed code should have a fresh array value to work on.

It seems like the setup code should be executed before each run and the timed code should have a fresh array value to work on.

That would make the setup clocked and we want to avoid that.

Actually, looking at your earlier pseudo-code, it's clear now.

<setup>
while (count--) { 
<test>
}
<teardown>

So, setup is executed once before ALL the timed loops. I thought the organization was like this:

while (count--) { 
<setup>
<start measuring>
<test>
<stop measuring>
<teardown>
}

That was the root of confusion. Although, in my defense, I still maintain that the help text could be clearer.