I've been using Deno a little bit. For some reason, I've decided to compare the performance between Deno and Node. The performance of Deno seems to be a popular topic these days, but people are almost always discussing the HTTP throughput. What I'm interested is to see how Deno compares to Node when it comes to basic JS functionalities such as for loops, Array.map, JSON.stringify, ...
I'm comparing Deno 1.5.2 (v8 8.7.220.3) with Node 15.2.0 (v8 8.6.395.17). Since JS is interpreted by a very similar version of v8 in both cases, I would expect the code to run pretty much as fast in both cases.
I prepared a .js file to run 10 functions 1 million times on both runtimes. I used Date.now() to measure the duration in milliseconds because it's common between both runtimes.
const list = [1, 2, 3, 4, 5, 6, 7, 8, 9];
const obj1 = {foo: 1};
const obj2 = {bar: 1};
const json = JSON.stringify(obj1);
// Declare the performance tool (different between Node and Deno)
let perf;
try {
// Use perf_hooks in Node
perf = require("perf_hooks").performance;
} catch {
// Use performance in Deno
perf = performance
}
// Declare the test functions
function mapFn() {return list.map(num => num*10);}
function reduceFn() {return list.reduce((a, b) => a+b);}
function forEachFn() {return list.forEach(num => num*10);}
function sliceFn() {return list.slice(0, 5);}
function forFn() {for (let i = 0; i < list.length; i++) {list[i]*10};}
function objectAssignFn() {return Object.assign({}, obj1, obj2)};
function jsonStringifyFn() {return JSON.stringify(obj1)};
function jsonParseFn() {return JSON.parse(json)};
function markFn() {perf.mark()};
function nowFn() {perf.now()};
// Loop through each test function and measure the time it takes
// to execute 1 million iterations.
[
mapFn,
reduceFn,
forEachFn,
sliceFn,
forFn,
objectAssignFn,
jsonStringifyFn,
jsonParseFn,
markFn,
nowFn,
]
.forEach(fn => {
let start = Date.now();
for (let i = 0; i < 1000000; i++) {
fn();
}
let end = Date.now();
console.log(`${fn.name} - Time elapsed: ${end-start} ms`);
});
Here are the results:
Results - Deno
// deno run app.js
mapFn - Time elapsed: 171 ms
reduceFn - Time elapsed: 26 ms
forEachFn - Time elapsed: 17 ms
sliceFn - Time elapsed: 54 ms
forFn - Time elapsed: 17 ms
objectAssignFn - Time elapsed: 95 ms
jsonStringify - Time elapsed: 287 ms
jsonParse - Time elapsed: 472 ms
markFn - Time elapsed: 11905 ms
nowFn - Time elapsed: 10988 ms
Results - Node
// node app.js
mapFn - Time elapsed: 64 ms
reduceFn - Time elapsed: 27 ms
forEachFn - Time elapsed: 17 ms
sliceFn - Time elapsed: 76 ms
forFn - Time elapsed: 17 ms
objectAssignFn - Time elapsed: 116 ms
jsonStringify - Time elapsed: 283 ms
jsonParse - Time elapsed: 400 ms
markFn - Time elapsed: 2459 ms
nowFn - Time elapsed: 95 ms
Both runtimes give very similar results, +/- 20%. However, 3 results are quite surprising:
- Array.map is almost 3x slower in Deno. I'd expect a very similar performance for
Array.mapsince it's built in V8. Why so much difference whenArray.forEachandArray.reduceperform in a similar way on both runtimes? - performance.now() in Deno is approx. 110x slower in Deno. I understand that the performance class is primarily here to benchmark our code, so how can it be so slow? Is it still considered as an unstable feature?
- performance.mark() in Deno is approx. 5x slower in Deno. Why is this the case?
Thanks!