Performance of JavaScript object reduce
Performance of JavaScript object reduce
Just a short post to look at assumptions. A few weeks ago was having one of those classic debates with a co-worker about the performance of object construction. Since with ES6 it is very easy to write:
1const obj = data.reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {});
I had internally always made the assumption that JavaScript's runtime was making some tail call style optimizations on object reduce. But, after the discussion I realized that you should measure and not assume.
So measure we did, only to find that while it's short in syntax it's slow in performance:
Function | 10 items | 100 items | 1000 items | 10000 items |
---|---|---|---|---|
reduceHead | 0.094276 | 1.056871 | 113.687105 | 11013.714067 |
reduceTail | 0.117997 | 1.777858 | 110.682865 | 11794.382281 |
reduceAssign | 0.090573 | 0.161337 | 0.420563 | 2.184986 |
This is on the 8.9.0 node runtime. All times are in 'ms'
What you can see is this is pretty close to a exponentail growth based on the number of items. Sure it's fast enough if it's a small object (~10 items) but, very quickly grows if it's anything of size.
The test program (run at each of the different input counts)
1const {
2 performance,
3 PerformanceObserver
4} = require('perf_hooks');
5
6/*
7** Construct some test data
8*/
9const data = [];
10
11for (let i = 0; i < 1000; i ++) {
12 data.push([String(i), i]);
13}
14
15/*
16** Test variations
17*/
18function reduceHead() {
19 return data.reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {});
20}
21
22function reduceTail() {
23 return data.reduce((acc, [k, v]) => ({ [k]: v, ...acc }), {});
24}
25
26function reduceAssign() {
27 return data.reduce((acc, [k, v]) => {
28 acc[k] = v;
29 return acc;
30 }, {});
31}
32
33/*
34** Performance hooks
35*/
36const obs = new PerformanceObserver((list) => {
37 const entries = list.getEntries();
38 console.log(entries[0].name, entries[0].duration, 'ms');
39});
40
41const wrapHead = performance.timerify(reduceHead);
42const wrapTail = performance.timerify(reduceTail);
43const wrapAssign = performance.timerify(reduceAssign);
44
45obs.observe({ entryTypes: ['function'] });
46
47wrapHead();
48wrapTail();
49wrapAssign();