V8 Engine Voodoo: Why is it Faster / Slower?

I am currently working on an image editor and have stumbled upon this strange behavior regarding pixel manipulation and / or function calls in V8.

http://jsperf.com/canvas-pixelwise-manipulation-performance

There are two test cases. Both test cases should manipulate image data in a canvas in memory to increase brightness. Thus, they have to iterate over each pixel and manipulate the 4 color values ​​of each pixel.

Case 1

Case 1 performs a β€œ1 function call as a whole”, which means it passes the context and imageData to the function, which then iterates through the pixels and manipulates the data. All in one function

Case 2

Case 2 performs a β€œ1 function call per pixel ”, which means that it iterates over the pixels and calls a method for each pixel, which then manipulates the image data for that pixel. This results in (in this case) 250,000 additional function calls.

My expectation

I would expect case 1 to be much faster than case 2, since case 2 makes 250,000 extra function calls.

Result

In Chrome, it's quite the opposite. If I make 250,000 extra function calls, this is faster than a single function call processing all the images.

My question is: WHY?

+7
source share
2 answers

No code manipulates any canvas and does not define a function inside a series of benchmarks. What you want are static functions that are not recreated, so that as soon as the JIT optimizes them, they remain optimized. You do not want to measure the creation of utility utility functions, since the real application will define the function only once.

As soon as you fix the control code, they should work at the same speed, because the manipulatePixel function will receive an embedded one.

http://jsperf.com/canvas-pixelwise-manipulation-performance/4

enter image description here

I also created another jsperf, where I purposefully manipulate the V8 * heuristic so as not to manipulatePixel Function:

http://jsperf.com/canvas-pixelwise-manipulation-performance/5

enter image description here

As you can see, it is now 50% slower. The only difference between the two jsperfs is the huge comment in the manipulatePixel function.


* V8 considers the raw text size of a function (including comments) as a heuristic when defining an attachment.

+3
source

I'm not very familiar with V8's mastery of optimization, but I would say that case 2 leaves more room for the V8 engine to rewrite the code.
Although, at first glance, case 1 should work better, but it does not leave much space for the V8.
Although there is only one function, a call object is created, inside this area of ​​the function object, a couple of variables are declared and a huge object is processed.
The second case, however, can simply be converted to a loop or even byte shifts, which eliminates the need for functional objects and areas.
In addition to the fact that the scope / function is not specified, your variables (arguments) do not need to be copied, so there are no unnecessary references to objects to cause any overhead.

In addition to the copied variables and references, it is also necessary to consider scanning the scope: Math.abs called inside the function is (less) slower than in the global scope. I don't know if this is true or not, but I have this hidden suspicion that masking variables declared in a higher scope can also affect performance.
You also use width and height in a single-functional approach that looks as if they mean global variables. This causes a region scan at each iteration of the loops, which is likely to cause more resistance than these arguments, and calls to Math.* ...

+1
source

All Articles