diff --git a/benchmarks/.gitignore b/benchmarks/.gitignore new file mode 100644 index 0000000..1cf8963 --- /dev/null +++ b/benchmarks/.gitignore @@ -0,0 +1,2 @@ +# clinic.js +**clinic** diff --git a/benchmarks/README.md b/benchmarks/README.md new file mode 100644 index 0000000..b02525c --- /dev/null +++ b/benchmarks/README.md @@ -0,0 +1,33 @@ +Mplex Benchmarks +========== + +Benchmarks for both pull-mplex and libp2p-mplex + +## Setup + +1. `npm install` + +## Tests + +### Echo with New Streams +This test will send a ping message from the dialer to the listener, which is echo'd back to the dialer. +A new stream will be created for each echo. + +**pull-mplex**: `node bench.js --lib=pull-mplex` +**libp2p-mplex**: `node bench.js --lib=libp2p-mplex` + +You should see results like: +```sh +$ node bench.js --lib=pull-mplex +benchPingPong*100: 8027.089ms +benchPingPong*100: 7544.150ms +benchPingPong*100: 7553.991ms +benchPingPong*100: 8382.416ms +benchPingPong*100: 8384.485ms +$ node bench.js --lib=libp2p-mplex +benchPingPong*100: 9571.866ms +benchPingPong*100: 10309.212ms +benchPingPong*100: 10629.806ms +benchPingPong*100: 10558.181ms +benchPingPong*100: 10839.580ms +``` diff --git a/benchmarks/bench.js b/benchmarks/bench.js new file mode 100644 index 0000000..a4baec0 --- /dev/null +++ b/benchmarks/bench.js @@ -0,0 +1,95 @@ +'use strict' + +const minimist = require('minimist') +const bench = require('fastbench') +const net = require('net') +const childProcess = require('child_process') +const path = require('path') +const parallel = require('fastparallel')({ + results: false +}) +const pull = require('pull-stream') +const toPull = require('stream-to-pull-stream') + +const argv = minimist(process.argv.slice(2), { + boolean: 'child', + default: { + child: true, + port: 3000, + host: 'localhost', + lib: 'pull-mplex' + } +}) + +function buildPingPong (cb) { + let child + let dialer + const mplex = require(argv.lib || 'pull-mplex') + + if (argv.child) { + child = childProcess.fork(path.join(__dirname, 'mplex-echo.js'), { + stdio: 'inherit' + }) + + child.on('message', start) + + child.on('error', cb) + + child.on('exit', console.log) + } else { + start(argv) + } + + function start (addr) { + const client = net.connect(addr.port, addr.host) + client.on('connect', function () { + const connection = toPull.duplex(client) + dialer = mplex.dialer(connection) + cb(null, benchPingPong) + }) + } + + const max = 1000 + let functions = new Array(max) + + for (let i = 0; i < max; i++) { + functions[i] = sendEcho + } + + function benchPingPong (cb) { + parallel(null, functions, null, cb) + } + + function sendEcho (cb) { + const stream = dialer.newStream((err) => { + if (err) console.log(err) + }) + + pull( + pull.values(['ping']), + stream, + pull.onEnd(cb) + ) + } +} + +function times (num, run, cb) { + if (--num < 0) return cb() + run(function (err) { + if (err) throw err + + times(num, run, cb) + }) +} + +buildPingPong(function (err, benchPingPong) { + if (err) throw err + + var run = bench([benchPingPong], 100) + + // Do it 5 times + times(5, run, function () { + // close the sockets the bad way + process.exit(0) + }) +}) diff --git a/benchmarks/mplex-echo.js b/benchmarks/mplex-echo.js new file mode 100644 index 0000000..7ccd21c --- /dev/null +++ b/benchmarks/mplex-echo.js @@ -0,0 +1,72 @@ +'use strict' + +const minimist = require('minimist') +const net = require('net') +const toPull = require('stream-to-pull-stream') +const pull = require('pull-stream') +let count = 0 +let port + +const server = net.createServer(handle) + +const argv = minimist(process.argv.slice(2), { + boolean: 'child', + default: { + child: true, + port: 3000, + host: 'localhost', + lib: 'pull-mplex' + } +}) + +const mplex = require(argv.lib || 'pull-mplex') + +if (argv.child) { + port = 0 +} else { + port = 3000 +} + +function handle (socket) { + // Turn the socket into a duplex pull-stream + const connection = toPull.duplex(socket) + const listener = mplex.listener(connection) + + listener.on('stream', (stream) => { + pull( + stream, + pull.map((v) => { + count++ + return v + }), + stream + ) + }) +} + +server.listen(port, function (err) { + if (err) throw err + + if (argv.child) { + process.send(server.address()) + } else { + console.error('listening on', server.address().port) + } +}) + +process.on('disconnect', function () { + process.exit(0) +}) + +var signal = 'SIGINT' + +// Cleanly shut down process on SIGTERM to ensure that perf-.map gets flushed +process.on(signal, onSignal) + +function onSignal () { + console.error('count', count) + // IMPORTANT to log on stderr, to not clutter stdout which is purely for data, i.e. dtrace stacks + console.error('Caught', signal, ', shutting down.') + server.close() + process.exit(0) +} diff --git a/benchmarks/package.json b/benchmarks/package.json new file mode 100644 index 0000000..6317c85 --- /dev/null +++ b/benchmarks/package.json @@ -0,0 +1,22 @@ +{ + "name": "benchmarks", + "version": "1.0.0", + "description": "", + "main": "bench.js", + "scripts": { + "pull-mplex": "node bench.js --lib=pull-mplex", + "libp2p-mplex": "node bench.js --lib=libp2p-mplex" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "fastbench": "^1.0.1", + "fastparallel": "^2.3.0", + "libp2p-mplex": "~0.8.4", + "minimist": "^1.2.0", + "pull-mplex": "../", + "pull-stream": "^3.6.9", + "stream-to-pull-stream": "^1.7.2" + } +}