$ node bench-01-pull-stream.js
pull3*100000: 1995.798ms
pull_compose*100000: 1968.901ms
pull_chain*100000: 1871.930ms
$ node bench-02-async-stream.js
async3*100000: 3295.666ms
async3*100000: 3145.636ms
async3*100000: 2943.988ms
$ node bench-03-async-stream-improved.js
async3*100000: 1720.648ms
async3*100000: 1742.325ms
async3*100000: 1617.521ms
Last active
January 15, 2018 11:16
-
-
Save juliangruber/987042aea462212785dd9e235cc507b5 to your computer and use it in GitHub Desktop.
benchmark: pull-stream vs async-stream
- async-streams are composed by default, hence the same benchmark function was repeated three times
- in
bench-03-async-stream-improved
thefastbench
library was made promise aware, yielding in a huge performance boost
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const bench = require('fastbench') | |
const pull = require('../') | |
const values = [ | |
JSON.stringify({ hello: 'world' }), | |
JSON.stringify({ foo: 'bar' }), | |
JSON.stringify({ bin: 'baz' }) | |
] | |
const run = bench([ | |
function pull3 (done) { | |
const source = pull.values(values) | |
const through = pull.asyncMap(function (val, done) { | |
const json = JSON.parse(val) | |
done(null, json) | |
}) | |
const sink = pull.collect(function (err, array) { | |
if (err) return console.error(err) | |
setImmediate(done) | |
}) | |
pull(source, through, sink) | |
}, | |
function pull_compose (done) { | |
const source = pull.values(values) | |
const through = pull.asyncMap(function (val, done) { | |
const json = JSON.parse(val) | |
done(null, json) | |
}) | |
const sink = pull.collect(function (err, array) { | |
if (err) return console.error(err) | |
setImmediate(done) | |
}) | |
pull(source, pull(through, sink)) | |
}, | |
function pull_chain (done) { | |
const source = pull.values(values) | |
const through = pull.asyncMap(function (val, done) { | |
const json = JSON.parse(val) | |
done(null, json) | |
}) | |
const sink = pull.collect(function (err, array) { | |
if (err) return console.error(err) | |
setImmediate(done) | |
}) | |
pull(pull(source, through), sink) | |
} | |
], 100000) | |
run() | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const bench = require('fastbench') | |
const values = [ | |
JSON.stringify({ hello: 'world' }), | |
JSON.stringify({ foo: 'bar' }), | |
JSON.stringify({ bin: 'baz' }) | |
] | |
const run = bench([ | |
function async3 (done) { | |
(async () => { | |
let i = 0 | |
const source = () => async () => values[i++] | |
const through = read => async () => { | |
const str = await read() | |
if (!str) return | |
return JSON.parse(str) | |
} | |
const sink = async read => { | |
let data | |
while (data = await read()) {} | |
} | |
const read = through(source()) | |
await sink(read) | |
setImmediate(done) | |
})() | |
}, | |
function async3 (done) { | |
(async () => { | |
let i = 0 | |
const source = () => async () => values[i++] | |
const through = read => async () => { | |
const str = await read() | |
if (!str) return | |
return JSON.parse(str) | |
} | |
const sink = async read => { | |
let data | |
while (data = await read()) {} | |
} | |
const read = through(source()) | |
await sink(read) | |
setImmediate(done) | |
})() | |
}, | |
function async3 (done) { | |
(async () => { | |
let i = 0 | |
const source = () => async () => values[i++] | |
const through = read => async () => { | |
const str = await read() | |
if (!str) return | |
return JSON.parse(str) | |
} | |
const sink = async read => { | |
let data | |
while (data = await read()) {} | |
} | |
const read = through(source()) | |
await sink(read) | |
setImmediate(done) | |
})() | |
} | |
], 100000) | |
run() | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// fastbench modification that supports promise wrappers | |
// | |
'use strict' | |
var fastseries = require('fastseries') | |
var chalk = require('chalk') | |
var colors = ['red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white', 'gray'] | |
var console = require('console') | |
function fastbench (functions, opts) { | |
var max | |
var series = fastseries() | |
var currentColor = 0 | |
if (typeof opts === 'object') { | |
max = opts.max || opts.iterations | |
} else { | |
max = opts | |
} | |
if (!max) { | |
throw new Error('missing number of iterations') | |
} | |
return run | |
function run (done) { | |
currentColor = 0 | |
series(null, bench, functions, done || noop) | |
} | |
function bench (func, done) { | |
var key = func.name + '*' + max | |
var count = -1 | |
// true by default | |
if (opts.color !== false) { | |
key = chalk[nextColor()](key) | |
} | |
console.time(key) | |
end() | |
function end () { | |
if (++count < max) { | |
func().then(end) | |
} else { | |
console.timeEnd(key) | |
if (done) { | |
done() | |
} | |
} | |
} | |
} | |
function nextColor () { | |
if (currentColor === colors.length) { | |
currentColor = 0 | |
} | |
return colors[currentColor++] | |
} | |
} | |
function noop () {} | |
// | |
// benchmark | |
// | |
const values = [ | |
JSON.stringify({ hello: 'world' }), | |
JSON.stringify({ foo: 'bar' }), | |
JSON.stringify({ bin: 'baz' }) | |
] | |
const run = fastbench([ | |
async function async3 () { | |
let i = 0 | |
const source = () => async () => values[i++] | |
const through = read => async () => { | |
const str = await read() | |
if (!str) return | |
return JSON.parse(str) | |
} | |
const sink = async read => { | |
let data | |
while (data = await read()) {} | |
} | |
const read = through(source()) | |
await sink(read) | |
}, | |
async function async3 () { | |
let i = 0 | |
const source = () => async () => values[i++] | |
const through = read => async () => { | |
const str = await read() | |
if (!str) return | |
return JSON.parse(str) | |
} | |
const sink = async read => { | |
let data | |
while (data = await read()) {} | |
} | |
const read = through(source()) | |
await sink(read) | |
}, | |
async function async3 () { | |
let i = 0 | |
const source = () => async () => values[i++] | |
const through = read => async () => { | |
const str = await read() | |
if (!str) return | |
return JSON.parse(str) | |
} | |
const sink = async read => { | |
let data | |
while (data = await read()) {} | |
} | |
const read = through(source()) | |
await sink(read) | |
} | |
], 100000) | |
run() | |
Hmm, if pull.drain
is that slow that is something we could improve.
It's as complicated as it is because it prevents stack overflows (in the case that a cb is actually sync) which is faster than having a stackoverflow... but this (micro) benchmark only has 3 items in the stream, so am not very worried about stack overflows, wondered what would be like with the minimum implementations... and turned out drain was the heaviest. hmm...
oh sorry, those previous results had asyncMap and values replaced too, but difference is ~100 ms
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
oh okay... I just realized something! pull.drain seems to be the heavy thing here! swap out
pull.collect
which depends onpull.drain
for the simplest possible:and I'm getting results like these:
💥