diff --git a/benchmark/benchmarks.js b/benchmark/benchmarks.js new file mode 100644 index 0000000..5450ddb --- /dev/null +++ b/benchmark/benchmarks.js @@ -0,0 +1,9 @@ +import './reference-before.js' + +import './bencode.js' +import './buffer-vs-string.js' +import './compare-decode.js' +import './compare-encode.js' +import './encoding-length.js' + +import './reference-after.js' diff --git a/benchmark/benchmarks.json b/benchmark/benchmarks.json new file mode 100644 index 0000000..7fbec11 --- /dev/null +++ b/benchmark/benchmarks.json @@ -0,0 +1,237 @@ +{ + "suites": [ + { + "name": "reference before benchmarks", + "scenarios": [ + { + "name": "fibonacci loop", + "executions": 1000000, + "time": 559.685652, + "error": 3.1487334028716933 + }, + { + "name": "fibonacci recursive", + "executions": 7934, + "time": 630177.2286362491, + "error": 1.0079477172579856 + }, + { + "name": "fibonacci recmemo", + "executions": 629322, + "time": 7945.0529029654135, + "error": 0.5723554930927267 + } + ] + }, + { + "name": "bencode", + "scenarios": [ + { + "name": "bencode.encode() [buffer]", + "executions": 35127, + "time": 142342.0572494093, + "error": 1.2212460556968496 + }, + { + "name": "bencode.encode() [utf8]", + "executions": 1349, + "time": 3704002.3409933285, + "error": 0.6776323429925276 + }, + { + "name": "bencode.encode() [ascii]", + "executions": 1387, + "time": 3604457.93222783, + "error": 0.6959704620115251 + }, + { + "name": "bencode.encode() [binary]", + "executions": 1384, + "time": 3611082.62283237, + "error": 0.886904952461389 + }, + { + "name": "bencode.decode() [buffer]", + "executions": 42809, + "time": 116795.72767408722, + "error": 0.7320742513453905 + }, + { + "name": "bencode.decode() [utf8]", + "executions": 2313, + "time": 2161073.1547773452, + "error": 0.6380497099955478 + }, + { + "name": "bencode.decode() [ascii]", + "executions": 2409, + "time": 2074895.296803653, + "error": 0.5471675241633337 + }, + { + "name": "bencode.decode() [binary]", + "executions": 2396, + "time": 2086413.753338898, + "error": 0.5403395352166391 + } + ] + }, + { + "name": "buffer vs string", + "scenarios": [ + { + "name": "decode buffer", + "executions": 45024, + "time": 111051.06445451315, + "error": 0.5052997397476986 + }, + { + "name": "decode string", + "executions": 27973, + "time": 178745.74042827013, + "error": 0.5037603579470834 + } + ] + }, + { + "name": "compare decode", + "scenarios": [ + { + "name": "bencode.decode()", + "executions": 44568, + "time": 112188.04153204092, + "error": 0.5311823992431295 + }, + { + "name": "bencoding.decode()", + "executions": 54228, + "time": 92203.5159696098, + "error": 0.5519025773079677 + }, + { + "name": "bncode.decode()", + "executions": 1570, + "time": 3184351.9987261146, + "error": 4.031632965687692 + }, + { + "name": "btparse()", + "executions": 132325, + "time": 37785.48271301719, + "error": 0.49077930951590953 + }, + { + "name": "dht.decode()", + "executions": 47400, + "time": 105483.08683544304, + "error": 0.4728875447697615 + }, + { + "name": "dhtBencode.decode()", + "executions": 28774, + "time": 173762.59105442412, + "error": 0.4921701716738526 + } + ] + }, + { + "name": "compare encode", + "scenarios": [ + { + "name": "bencode.encode()", + "executions": 37666, + "time": 132742.29127064196, + "error": 0.8299521400901717 + }, + { + "name": "bencoding.encode()", + "executions": 25, + "time": 195132381.92, + "error": 8.551313598927392 + }, + { + "name": "bncode.encode()", + "executions": 1, + "time": 3939521850, + "error": 0 + }, + { + "name": "dht.encode()", + "executions": 50, + "time": 99655056.16, + "error": 6.321304669124846 + }, + { + "name": "dhtBencode.encode()", + "executions": 14, + "time": 351528698.78571427, + "error": 9.904070184501599 + } + ] + }, + { + "name": "encoding length", + "scenarios": [ + { + "name": "bencode.encodingLength(torrent)", + "executions": 162351, + "time": 30797.4295569476, + "error": 2.6093067216240846 + }, + { + "name": "bencode.encodingLength(buffer)", + "executions": 1000000, + "time": 1151.522846, + "error": 0.17154410205677803 + }, + { + "name": "bencode.encodingLength(string)", + "executions": 1000000, + "time": 4384.510143, + "error": 0.33437787838758865 + }, + { + "name": "bencode.encodingLength(number)", + "executions": 1000000, + "time": 557.416746, + "error": 0.29860269449773136 + }, + { + "name": "bencode.encodingLength(array)", + "executions": 1000000, + "time": 2784.717913, + "error": 0.20104464676215553 + }, + { + "name": "bencode.encodingLength(small object)", + "executions": 311927, + "time": 16029.371202236422, + "error": 0.34310281585091806 + } + ] + }, + { + "name": "reference after benchmarks", + "scenarios": [ + { + "name": "fibonacci loop", + "executions": 1000000, + "time": 689.084318, + "error": 0.231909777005114 + }, + { + "name": "fibonacci recursive", + "executions": 7080, + "time": 706143.8394067796, + "error": 0.963718658922282 + }, + { + "name": "fibonacci recmemo", + "executions": 614393, + "time": 8138.115209645944, + "error": 0.2215156965189563 + } + ] + } + ] +} \ No newline at end of file diff --git a/benchmark/bencode.js b/benchmark/bencode.js index d56afd8..9cadcfc 100644 --- a/benchmark/bencode.js +++ b/benchmark/bencode.js @@ -1,109 +1,98 @@ import fs from 'fs' import path from 'path' -import bench from 'nanobench' +import { fileURLToPath } from 'url' import bencode from '../index.js' +const __filename = fileURLToPath(import.meta.url) +const __dirname = path.dirname(__filename) + const buffer = fs.readFileSync(path.join(__dirname, 'test.torrent')) const object = bencode.decode(buffer) const objectUtf8 = bencode.decode(buffer, 'utf8') const objectAscii = bencode.decode(buffer, 'ascii') const objectBinary = bencode.decode(buffer, 'binary') -const ITERATIONS = 10000 +const ITERATIONS = 1 -bench(`bencode.encode() [buffer] ⨉ ${ITERATIONS}`, function (run) { - let result = null +suite('bencode', () => { + scenario('bencode.encode() [buffer]', function () { + let result = null - run.start() - for (let i = 0; i < ITERATIONS; i++) { - result = bencode.encode(object) - } - run.end() + for (let i = 0; i < ITERATIONS; i++) { + result = bencode.encode(object) + } - return result -}) + return result + }) -bench(`bencode.encode() [utf8] ⨉ ${ITERATIONS}`, function (run) { - let result = null + scenario('bencode.encode() [utf8]', function () { + let result = null - run.start() - for (let i = 0; i < ITERATIONS; i++) { - result = bencode.encode(objectUtf8) - } - run.end() + for (let i = 0; i < ITERATIONS; i++) { + result = bencode.encode(objectUtf8) + } - return result -}) + return result + }) -bench(`bencode.encode() [ascii] ⨉ ${ITERATIONS}`, function (run) { - let result = null + scenario('bencode.encode() [ascii]', function () { + let result = null - run.start() - for (let i = 0; i < ITERATIONS; i++) { - result = bencode.encode(objectAscii) - } - run.end() + for (let i = 0; i < ITERATIONS; i++) { + result = bencode.encode(objectAscii) + } - return result -}) + return result + }) -bench(`bencode.encode() [binary] ⨉ ${ITERATIONS}`, function (run) { - let result = null + scenario('bencode.encode() [binary]', function () { + let result = null - run.start() - for (let i = 0; i < ITERATIONS; i++) { - result = bencode.encode(objectBinary) - } - run.end() + for (let i = 0; i < ITERATIONS; i++) { + result = bencode.encode(objectBinary) + } - return result -}) + return result + }) -bench(`bencode.decode() [buffer] ⨉ ${ITERATIONS}`, function (run) { - let result = null + scenario('bencode.decode() [buffer]', function () { + let result = null - run.start() - for (let i = 0; i < ITERATIONS; i++) { - result = bencode.decode(buffer) - } - run.end() + for (let i = 0; i < ITERATIONS; i++) { + result = bencode.decode(buffer) + } - return result -}) + return result + }) -bench(`bencode.decode() [utf8] ⨉ ${ITERATIONS}`, function (run) { - let result = null + scenario('bencode.decode() [utf8]', function () { + let result = null - run.start() - for (let i = 0; i < ITERATIONS; i++) { - result = bencode.decode(buffer, 'utf8') - } - run.end() + for (let i = 0; i < ITERATIONS; i++) { + result = bencode.decode(buffer, 'utf8') + } - return result -}) + return result + }) -bench(`bencode.decode() [ascii] ⨉ ${ITERATIONS}`, function (run) { - let result = null + scenario('bencode.decode() [ascii]', function () { + let result = null - run.start() - for (let i = 0; i < ITERATIONS; i++) { - result = bencode.decode(buffer, 'ascii') - } - run.end() + for (let i = 0; i < ITERATIONS; i++) { + result = bencode.decode(buffer, 'ascii') + } - return result -}) + return result + }) -bench(`bencode.decode() [binary] ⨉ ${ITERATIONS}`, function (run) { - let result = null + scenario('bencode.decode() [binary]', function () { + let result = null - run.start() - for (let i = 0; i < ITERATIONS; i++) { - result = bencode.decode(buffer, 'binary') - } - run.end() + for (let i = 0; i < ITERATIONS; i++) { + result = bencode.decode(buffer, 'binary') + } - return result + return result + }) }) diff --git a/benchmark/buffer-vs-string.js b/benchmark/buffer-vs-string.js index 0caa0a7..c46149d 100644 --- a/benchmark/buffer-vs-string.js +++ b/benchmark/buffer-vs-string.js @@ -1,33 +1,34 @@ import fs from 'fs' import path from 'path' +import { fileURLToPath } from 'url' import bencode from '../index.js' -import bench from 'nanobench' + +const __filename = fileURLToPath(import.meta.url) +const __dirname = path.dirname(__filename) const buffer = fs.readFileSync(path.join(__dirname, 'test.torrent')) const str = buffer.toString('ascii') -const ITERATIONS = 10000 +const ITERATIONS = 1 -bench(`decode buffer ⨉ ${ITERATIONS}`, function (run) { - let result = null +suite('buffer vs string', () => { + scenario('decode buffer', function () { + let result = null - run.start() - for (let i = 0; i < ITERATIONS; i++) { - result = bencode.decode(buffer) - } - run.end() + for (let i = 0; i < ITERATIONS; i++) { + result = bencode.decode(buffer) + } - return result -}) + return result + }) -bench(`decode string ⨉ ${ITERATIONS}`, function (run) { - let result = null + scenario('decode string', function () { + let result = null - run.start() - for (let i = 0; i < ITERATIONS; i++) { - result = bencode.decode(str) - } - run.end() + for (let i = 0; i < ITERATIONS; i++) { + result = bencode.decode(str) + } - return result + return result + }) }) diff --git a/benchmark/compare-decode.js b/benchmark/compare-decode.js index 920e970..ee4cf22 100644 --- a/benchmark/compare-decode.js +++ b/benchmark/compare-decode.js @@ -1,86 +1,80 @@ import fs from 'fs' import path from 'path' -import bench from 'nanobench' +import { fileURLToPath } from 'url' import bencode from '../index.js' + import bencoding from 'bencoding' import bncode from 'bncode' import btparse from 'btparse' -import dht from 'dht.js/lib/dht/bencode' +import dht from 'dht.js/lib/dht/bencode.js' import dhtBencode from 'dht-bencode' +const __filename = fileURLToPath(import.meta.url) +const __dirname = path.dirname(__filename) + const buffer = fs.readFileSync(path.join(__dirname, 'test.torrent')) -const ITERATIONS = 10000 +const ITERATIONS = 1 -bench(`bencode.decode() ⨉ ${ITERATIONS}`, function (run) { - let result = null +suite('compare decode', () => { + scenario('bencode.decode()', function () { + let result = null - run.start() - for (let i = 0; i < ITERATIONS; i++) { - result = bencode.decode(buffer) - } - run.end() + for (let i = 0; i < ITERATIONS; i++) { + result = bencode.decode(buffer) + } - return result -}) + return result + }) -bench(`bencoding.decode() ⨉ ${ITERATIONS}`, function (run) { - let result = null + scenario('bencoding.decode()', function () { + let result = null - run.start() - for (let i = 0; i < ITERATIONS; i++) { - result = bencoding.decode(buffer) - } - run.end() + for (let i = 0; i < ITERATIONS; i++) { + result = bencoding.decode(buffer) + } - return result -}) + return result + }) -bench(`bncode.decode() ⨉ ${ITERATIONS}`, function (run) { - let result = null + scenario('bncode.decode()', function () { + let result = null - run.start() - for (let i = 0; i < ITERATIONS; i++) { - result = bncode.decode(buffer) - } - run.end() + for (let i = 0; i < ITERATIONS; i++) { + result = bncode.decode(buffer) + } - return result -}) + return result + }) -bench(`btparse() ⨉ ${ITERATIONS}`, function (run) { - let result = null + scenario('btparse()', function () { + let result = null - run.start() - for (let i = 0; i < ITERATIONS; i++) { - result = btparse(buffer) - } - run.end() + for (let i = 0; i < ITERATIONS; i++) { + result = btparse(buffer) + } - return result -}) + return result + }) -bench(`dht.decode() ⨉ ${ITERATIONS}`, function (run) { - let result = null + scenario('dht.decode()', function () { + let result = null - run.start() - for (let i = 0; i < ITERATIONS; i++) { - result = dht.decode(buffer) - } - run.end() + for (let i = 0; i < ITERATIONS; i++) { + result = dht.decode(buffer) + } - return result -}) + return result + }) -bench(`dhtBencode.decode() ⨉ ${ITERATIONS}`, function (run) { - let result = null + scenario('dhtBencode.decode()', function () { + let result = null - run.start() - for (let i = 0; i < ITERATIONS; i++) { - result = dhtBencode.bdecode(buffer) - } - run.end() + for (let i = 0; i < ITERATIONS; i++) { + result = dhtBencode.bdecode(buffer) + } - return result + return result + }) }) diff --git a/benchmark/compare-encode.js b/benchmark/compare-encode.js index 179b409..88623e0 100644 --- a/benchmark/compare-encode.js +++ b/benchmark/compare-encode.js @@ -1,74 +1,70 @@ import fs from 'fs' import path from 'path' -import bench from 'nanobench' +import { fileURLToPath } from 'url' import bencode from '../index.js' + import bencoding from 'bencoding' import bncode from 'bncode' -import dht from 'dht.js/lib/dht/bencode' +import dht from 'dht.js/lib/dht/bencode.js' import dhtBencode from 'dht-bencode' +const __filename = fileURLToPath(import.meta.url) +const __dirname = path.dirname(__filename) + const buffer = fs.readFileSync(path.join(__dirname, 'test.torrent')) const object = bencode.decode(buffer) -const ITERATIONS = 10000 +const ITERATIONS = 1 -bench(`bencode.encode() ⨉ ${ITERATIONS}`, function (run) { - let result = null +suite('compare encode', () => { + scenario('bencode.encode()', function () { + let result = null - run.start() - for (let i = 0; i < ITERATIONS; i++) { - result = bencode.encode(object) - } - run.end() + for (let i = 0; i < ITERATIONS; i++) { + result = bencode.encode(object) + } - return result -}) + return result + }) -bench(`bencoding.encode() ⨉ ${ITERATIONS}`, function (run) { - let result = null + scenario('bencoding.encode()', function () { + let result = null - run.start() - for (let i = 0; i < ITERATIONS; i++) { - result = bencoding.encode(object) - } - run.end() + for (let i = 0; i < ITERATIONS; i++) { + result = bencoding.encode(object) + } - return result -}) + return result + }) -bench(`bncode.encode() ⨉ ${ITERATIONS}`, function (run) { - let result = null + scenario('bncode.encode()', function () { + let result = null - run.start() - for (let i = 0; i < ITERATIONS; i++) { - result = bncode.encode(object) - } - run.end() + for (let i = 0; i < ITERATIONS; i++) { + result = bncode.encode(object) + } - return result -}) + return result + }) -bench(`dht.encode() ⨉ ${ITERATIONS}`, function (run) { - let result = null + scenario('dht.encode()', function () { + let result = null - run.start() - for (let i = 0; i < ITERATIONS; i++) { - result = dht.encode(object) - } - run.end() + for (let i = 0; i < ITERATIONS; i++) { + result = dht.encode(object) + } - return result -}) + return result + }) -bench(`dhtBencode.encode() ⨉ ${ITERATIONS}`, function (run) { - let result = null + scenario('dhtBencode.encode()', function () { + let result = null - run.start() - for (let i = 0; i < ITERATIONS; i++) { - result = dhtBencode.bencode(object) - } - run.end() + for (let i = 0; i < ITERATIONS; i++) { + result = dhtBencode.bencode(object) + } - return result + return result + }) }) diff --git a/benchmark/encoding-length.js b/benchmark/encoding-length.js index 7f300bf..818ea34 100644 --- a/benchmark/encoding-length.js +++ b/benchmark/encoding-length.js @@ -1,81 +1,74 @@ import fs from 'fs' import path from 'path' +import { fileURLToPath } from 'url' import bencode from '../index.js' -import bench from 'nanobench' + +const __filename = fileURLToPath(import.meta.url) +const __dirname = path.dirname(__filename) const buffer = fs.readFileSync(path.join(__dirname, 'test.torrent')) const torrent = bencode.decode(buffer) -const ITERATIONS = 10000 +const ITERATIONS = 1 -bench('bencode.encodingLength(torrent)', function (run) { - const result = null +suite('encoding length', () => { + scenario('bencode.encodingLength(torrent)', function () { + const result = null - run.start() - for (let i = 0; i < ITERATIONS; i++) { - bencode.encodingLength(torrent) - } - run.end() + for (let i = 0; i < ITERATIONS; i++) { + bencode.encodingLength(torrent) + } - return result -}) + return result + }) -bench('bencode.encodingLength(buffer)', function (run) { - const result = null + scenario('bencode.encodingLength(buffer)', function () { + const result = null - run.start() - for (let i = 0; i < ITERATIONS; i++) { - bencode.encodingLength(buffer) - } - run.end() + for (let i = 0; i < ITERATIONS; i++) { + bencode.encodingLength(buffer) + } - return result -}) + return result + }) -bench('bencode.encodingLength(string)', function (run) { - const result = null + scenario('bencode.encodingLength(string)', function () { + const result = null - run.start() - for (let i = 0; i < ITERATIONS; i++) { - bencode.encodingLength('Test, test, this is a string') - } - run.end() + for (let i = 0; i < ITERATIONS; i++) { + bencode.encodingLength('Test, test, this is a string') + } - return result -}) + return result + }) -bench('bencode.encodingLength(number)', function (run) { - const result = null + scenario('bencode.encodingLength(number)', function () { + const result = null - run.start() - for (let i = 0; i < ITERATIONS; i++) { - bencode.encodingLength(87641234567) - } - run.end() + for (let i = 0; i < ITERATIONS; i++) { + bencode.encodingLength(87641234567) + } - return result -}) + return result + }) -bench('bencode.encodingLength(array)', function (run) { - const result = null + scenario('bencode.encodingLength(array)', function () { + const result = null - run.start() - for (let i = 0; i < ITERATIONS; i++) { - bencode.encodingLength([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) - } - run.end() + for (let i = 0; i < ITERATIONS; i++) { + bencode.encodingLength([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) + } - return result -}) + return result + }) -bench('bencode.encodingLength(small object)', function (run) { - const result = null + scenario('bencode.encodingLength(small object)', function () { + const result = null - run.start() - for (let i = 0; i < ITERATIONS; i++) { - bencode.encodingLength({ a: 1, b: 'c', d: 'abcdefg', e: [1, 2, 3] }) - } - run.end() + for (let i = 0; i < ITERATIONS; i++) { + bencode.encodingLength({ a: 1, b: 'c', d: 'abcdefg', e: [1, 2, 3] }) + } - return result + return result + }) }) diff --git a/benchmark/fibonacci.js b/benchmark/fibonacci.js new file mode 100644 index 0000000..ed57997 --- /dev/null +++ b/benchmark/fibonacci.js @@ -0,0 +1,41 @@ +// run a constant benchmark suite before and after our tests +// so we can normalize performance across different CPUs + +// bipbip/__benchmarks__/fibonacci.js + +export function fibonacciSuite (name) { + function loop (input) { + let num = input + let a = 1 + let b = 0 + let temp + while (num >= 0) { + temp = a + a += b + b = temp + num -= 1 + } + return b + } + + function recursive (num) { + if (num <= 1) return 1 + return recursive(num - 1) + recursive(num - 2) + } + + function recmemo (num, memo = {}) { + if (memo[num]) return memo[num] + if (num <= 1) return 1 + memo[num] = + recmemo(num - 1, memo) + + recmemo(num - 2, memo) + return memo[num] + } + + suite(name, () => { + const input = 20 + scenario('fibonacci loop', () => { loop(input) }) + scenario('fibonacci recursive', () => { recursive(input) }) + scenario('fibonacci recmemo', () => { recmemo(input) }) + }) +} diff --git a/benchmark/reference-after.js b/benchmark/reference-after.js new file mode 100644 index 0000000..26735b1 --- /dev/null +++ b/benchmark/reference-after.js @@ -0,0 +1,3 @@ +import { fibonacciSuite } from './fibonacci.js' + +fibonacciSuite('reference after benchmarks') diff --git a/benchmark/reference-before.js b/benchmark/reference-before.js new file mode 100644 index 0000000..45f6cdc --- /dev/null +++ b/benchmark/reference-before.js @@ -0,0 +1,3 @@ +import { fibonacciSuite } from './fibonacci.js' + +fibonacciSuite('reference before benchmarks') diff --git a/package.json b/package.json index be55cb3..373e1c0 100644 --- a/package.json +++ b/package.json @@ -21,12 +21,12 @@ "devDependencies": { "@webtorrent/semantic-release-config": "1.0.10", "bencoding": "latest", + "bipbip": "github:milahu/bipbip", "bncode": "latest", "browserify": "^17.0.0", "btparse": "latest", "dht-bencode": "latest", "dht.js": "latest", - "nanobench": "3.0.0", "semantic-release": "21.1.1", "standard": "17.1.0", "tap-spec": "5.0.0", @@ -51,7 +51,8 @@ "url": "git://github.com/webtorrent/node-bencode.git" }, "scripts": { - "benchmark": "nanobench benchmark/*.js", + "benchmark": "bipbip --compare benchmark/benchmarks.json --save benchmark/benchmarks.json benchmark/benchmarks.js", + "benchmark-nosave": "bipbip --compare benchmark/benchmarks.json benchmark/benchmarks.js", "bundle": "mkdir -p dist && npm run bundle:lib && npm run bundle:test", "bundle:lib": "browserify lib/index.js -s bencode -o dist/bencode.js", "bundle:test": "browserify test/*.test.js -o dist/tests.js",