The meetup

I have attended Meet.js KRK November where one of the talks was about Node.js performance. Krzysztof Słonka from Allegro presented his story about performance analysis

The story

The app that was examined basically has one job to do. Convert JSON files to HTML. He described them as a worker-renders. The node profiler together with a flamegraph was used to determined what to optimize. Below you can see an example output from flamegraph. Flamegraph You can manipulate with this graph to better determine ‘hot’ places of your application.

During their examination, they found that their app has a lot of calls to Array.prototype.map() where it is used only to apply some parameter on every element. Because .map() can take an additional parameter that was not used in their case, they replaced it with a simple for loop and gained about 11% performance boost. That’s amazing!

My investigation

I wanted to check it myself. Let’s first look at the .map() function syntax

var new_array = arr.map(function callback(currentValue[, index[, array]]) {
 // Return element for new_array
}[, thisArg])

The obligatory parameters are callback and currentValue.

Ok so let’s start with a simple task of generating a new array with numbers squared.

// setup
const forLoopMap = (array, fun) => {
 const newArray = [];
 for (let i = 0; i < array.length; i++) {
 newArray.push(fun(array[i]));
 }
 return newArray;
};

const square = (x) => {
 return x*x;
};

const numbers = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];

// execution
numbers.map(square);
forLoopMap(numbers, square);

Using jsperf.com the results shown that for this simple setup, the execution time of Array.map() is ~55% slower!

squares results


Then I was curious about what will happen when some recursive function like Fibonacci will be used.

const fibonacci = (num) => {
 if (num <= 1) return 1;

 return fibonacci(num - 1) + fibonacci(num - 2);
}

The results from both iterations over array are almost the same. When stack is overloaded with recursion calls there are no measurable benefits. fib results

Conclusion

It was very interesting to see in action that those small changes might actually affect your users. Krzysztof Słonka stared his presentation with famous saying that: “One second of Amazon render time could cost $1.6 Billion”.


resources


Karol Świeca

My thougths on JavaScript, AWS Cloud and other aspects of being Software Engineer