diff --git a/.eslintrc b/.eslintrc index 91c8258c..750903b7 100644 --- a/.eslintrc +++ b/.eslintrc @@ -8,16 +8,18 @@ "max-len": [2, 255, 4], "no-param-reassign": 0, "spaced-comment": [2, "always", { "markers": ["/", "!"] }], - "no-plusplus": 0 + "no-plusplus": 0, + "no-console": 0, + "import/no-extraneous-dependencies": [2, { "devDependencies": true }], }, "env": { "amd": true, "node": true, "commonjs": true, "es6": true, - "browser": true + "browser": true, }, "globals": { - "VerEx": true - } + "VerEx": true, + }, } diff --git a/README.md b/README.md index 5354eff6..90496364 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,34 @@ npm test npm run test:verbose ``` +## Running Benchmarks + +```sh +npm run bench +``` + +With around 15 benchmarks, running takes approximately 2 minutes. + +Example output: + +```sh +$ npm run bench + +> verbal-expressions@1.0.0 bench /Users/shreyasminocha/dev/forks/JSVerbalExpressions +> node benchmark/run.js + +╔═════════════════════╤═══════╤═════════════╗ +║ Description │ Time │ Uncertainty ║ +╟─────────────────────┼───────┼─────────────╢ +║ VerEx constructor │ 0ms │ ±0.00% ║ +║ RegExp constructor │ 132ns │ ±12.29% ║ +╟─────────────────────┼───────┼─────────────╢ +║ ... │ ... │ ... ║ +╚═════════════════════╧═══════╧═════════════╝ +``` + +The uncertainty column is a measure of how accurate the time in the `Time` column is. + ## Creating a minified version ```sh diff --git a/benchmark/run.js b/benchmark/run.js new file mode 100644 index 00000000..31ee96ae --- /dev/null +++ b/benchmark/run.js @@ -0,0 +1,218 @@ +const { Suite } = require('benchmark'); +const { table } = require('table'); +const prettyMs = require('pretty-ms'); +const { bold } = require('chalk'); + +const VerEx = require('../dist/verbalexpressions'); + +const benchmarks = {}; +const todo = {}; + +benchmarks.constructor = { + VerEx: () => { VerEx(); }, + RegExp: () => { RegExp(); }, +}; + +benchmarks.startOfLine = { + VerEx: () => { VerEx().startOfLine('a').test('alpha'); }, + RegExp: () => { /^a/gm.test('alpha'); }, +}; + +benchmarks.endOfLine = { + VerEx: () => { VerEx().endOfLine('a').test('alpha'); }, + RegExp: () => { /a$/gm.test('alpha'); }, +}; + +benchmarks['then|find'] = { + VerEx: () => { VerEx().find('alpha').test('alpha'); }, + RegExp: () => { /alpha/gm.test('alpha'); }, +}; + +benchmarks.maybe = { + VerEx: () => { + const expr = VerEx().maybe('a'); + expr.test('a'); expr.test(''); + }, + RegExp: () => { + const expr = /a?/gm; + expr.test('a'); expr.test(''); + }, +}; + +benchmarks.anything = { + VerEx: () => { VerEx().anything().test('alpha'); }, + RegExp: () => { /.*/gm.test('alpha'); }, +}; + +benchmarks.anythingBut = { + VerEx: () => { + const expr = VerEx().anythingBut('a'); + expr.test('a'); expr.test('e'); + }, + RegExp: () => { + const expr = /.*/gm; + expr.test('a'); expr.test('e'); + }, +}; + +benchmarks.something = { + VerEx: () => { + const expr = VerEx().something(); + expr.test('a'); expr.test(''); + }, + RegExp: () => { + const expr = /.+/gm; + expr.test('a'); expr.test(''); + }, +}; + +benchmarks.somethingBut = { + VerEx: () => { + const expr = VerEx().somethingBut('a'); + expr.test('a'); expr.test('e'); expr.test(''); + }, + RegExp: () => { + const expr = /.+/gm; + expr.test('a'); expr.test('e'); expr.test(''); + }, +}; + +benchmarks['anyOf|any'] = { + VerEx: () => { + const expr = VerEx().anyOf(['a', 'b', 'c']); + expr.test('b'); expr.test('f'); + }, + RegExp: () => { + const expr = /[abc]/gm; + expr.test('b'); expr.test('f'); + }, +}; + +benchmarks.not = { + VerEx: () => { + const expr = VerEx().not('foo'); + expr.test('foo'); expr.test('bar'); + }, + RegExp: () => { + const expr = /(!=foo)/gm; + expr.test('foo'); expr.test('bar'); + }, +}; + +benchmarks.range = { + VerEx: () => { + const expr = VerEx().range(0, 9, 'a', 'f'); + expr.test('d'); expr.test('g'); + }, + RegExp: () => { + const expr = /[0-9a-f]/gm; + expr.test('d'); expr.test('g'); + }, +}; + +benchmarks['lineBreak|br'] = { + VerEx: () => { + const expr = VerEx().lineBreak(); + expr.test('\n'); expr.test('lf'); + }, + RegExp: () => { + const expr = /\r\n|\r|\n/gm; + expr.test('\n'); expr.test('lf'); + }, +}; + +benchmarks.tab = { + VerEx: () => { + const expr = VerEx().tab(); + expr.test('\t'); expr.test('tab'); + }, + RegExp: () => { + const expr = /\t/gm; + expr.test('\t'); expr.test('tab'); + }, +}; + +benchmarks.word = { + VerEx: () => { + const expr = VerEx().word(); + expr.test('word_1'); expr.test('@#!$'); + }, + RegExp: () => { + const expr = /\w+/gm; + expr.test('word_1'); expr.test('@#!$'); + }, +}; + +benchmarks.digit = { + VerEx: () => { + const expr = VerEx().digit(); + expr.test('3'); expr.test('a'); + }, + RegExp: () => { + const expr = /\w+/gm; + expr.test('3'); expr.test('a'); + }, +}; + +benchmarks.whitespace = { + VerEx: () => { + const expr = VerEx().whitespace(); + expr.test(' '); expr.test('w'); + }, + RegExp: () => { + const expr = /\s/gm; + expr.test(' '); expr.test('w'); + }, +}; + +todo.addModifier = { VerEx: () => {}, RegExp: () => {} }; +todo.removeModifier = { VerEx: () => {}, RegExp: () => {} }; +todo.withAnyCase = { VerEx: () => {}, RegExp: () => {} }; +todo.stopAtFirst = { VerEx: () => {}, RegExp: () => {} }; +todo.searchOneLine = { VerEx: () => {}, RegExp: () => {} }; +todo.repeatPrevious = { VerEx: () => {}, RegExp: () => {} }; +todo.oneOrMore = { VerEx: () => {}, RegExp: () => {} }; +todo.multiple = { VerEx: () => {}, RegExp: () => {} }; +todo.capture = { VerEx: () => {}, RegExp: () => {} }; +todo.replace = { VerEx: () => {}, RegExp: () => {} }; + +benchmarks.toRegExp = { + VerEx: () => { const expr = VerEx.find('foo').toRegExp(); }, + RegExp: () => { const expr = /foo/gm; }, +}; + +const suite = new Suite(); + +for (const [name, bench] of Object.entries(benchmarks)) { + const VerExRun = bench.VerEx; + const RegExpRun = bench.RegExp; + + suite + .add(`VerEx ${name}`, VerExRun) + .add(`RegExp ${name}`, RegExpRun); +} + +const tableData = []; + +tableData.push([ + bold('Description'), bold('Time'), bold('Uncertainty'), +]); + +suite.on('cycle', (event) => { + const { name, stats } = event.target; + + tableData.push([ + name, + prettyMs(stats.mean * 1000, { formatSubMs: true }), + `±${stats.rme.toFixed(2)}%`, + ]); +}); + +suite.run(); + +const tableConfig = { + // Draw a line for the 0-th index and every odd index + drawHorizontalLine: index => index === 0 || index % 2 !== 0, +}; + +console.log(table(tableData, tableConfig)); diff --git a/package-lock.json b/package-lock.json index d59c49a8..8ff43597 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "verbal-expressions", - "version": "0.3.0", + "version": "1.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -503,6 +503,12 @@ "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=", "dev": true }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true + }, "async": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", @@ -610,6 +616,23 @@ "trim-off-newlines": "^1.0.1", "unique-temp-dir": "^1.0.0", "update-notifier": "^2.3.0" + }, + "dependencies": { + "parse-ms": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-1.0.1.tgz", + "integrity": "sha1-VjRtR0nXjyNDDKDHE4UK75GqNh0=", + "dev": true + }, + "pretty-ms": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-3.2.0.tgz", + "integrity": "sha512-ZypexbfVUGTFxb0v+m1bUyy92DHe5SyYlnyY0msyms5zd3RwyvNgyxZZsXXgoyzlxjx5MiqtXUdhUfvQbe0A2Q==", + "dev": true, + "requires": { + "parse-ms": "^1.0.0" + } + } } }, "ava-init": { @@ -1373,6 +1396,16 @@ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, + "benchmark": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/benchmark/-/benchmark-2.1.4.tgz", + "integrity": "sha1-CfPeMckWQl1JjMLuVloOvzwqVik=", + "dev": true, + "requires": { + "lodash": "^4.17.4", + "platform": "^1.3.3" + } + }, "binary-extensions": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz", @@ -1572,9 +1605,9 @@ } }, "chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "requires": { "ansi-styles": "^3.2.1", @@ -2187,6 +2220,20 @@ "resolved": "https://registry.npmjs.org/globals/-/globals-11.7.0.tgz", "integrity": "sha512-K8BNSPySfeShBQXsahYB/AbbWruVOTyVpgoIDnl8odPpeSfP2J5QO2oLFFdl2j7GfDCtZj2bMKar2T49itTPCg==", "dev": true + }, + "table": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz", + "integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==", + "dev": true, + "requires": { + "ajv": "^5.2.3", + "ajv-keywords": "^2.1.0", + "chalk": "^2.1.0", + "lodash": "^4.17.4", + "slice-ansi": "1.0.0", + "string-width": "^2.1.1" + } } } }, @@ -3539,6 +3586,17 @@ "trim-off-newlines": "^1.0.1", "unique-temp-dir": "^1.0.0", "update-notifier": "^2.3.0" + }, + "dependencies": { + "pretty-ms": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-3.2.0.tgz", + "integrity": "sha512-ZypexbfVUGTFxb0v+m1bUyy92DHe5SyYlnyY0msyms5zd3RwyvNgyxZZsXXgoyzlxjx5MiqtXUdhUfvQbe0A2Q==", + "dev": true, + "requires": { + "parse-ms": "^1.0.0" + } + } } }, "is-observable": { @@ -3550,6 +3608,12 @@ "symbol-observable": "^0.2.2" } }, + "parse-ms": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-1.0.1.tgz", + "integrity": "sha1-VjRtR0nXjyNDDKDHE4UK75GqNh0=", + "dev": true + }, "source-map-support": { "version": "0.4.18", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", @@ -6554,6 +6618,12 @@ "find-up": "^2.1.0" } }, + "platform": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.5.tgz", + "integrity": "sha512-TuvHS8AOIZNAlE77WUDiR4rySV/VMptyMfcfeoMgs4P8apaZM3JrnbzBiixKUv+XR6i+BXrQh8WAnjaSPFO65Q==", + "dev": true + }, "plur": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/plur/-/plur-2.1.2.tgz", @@ -6598,18 +6668,18 @@ } }, "pretty-ms": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-3.2.0.tgz", - "integrity": "sha512-ZypexbfVUGTFxb0v+m1bUyy92DHe5SyYlnyY0msyms5zd3RwyvNgyxZZsXXgoyzlxjx5MiqtXUdhUfvQbe0A2Q==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-4.0.0.tgz", + "integrity": "sha512-qG66ahoLCwpLXD09ZPHSCbUWYTqdosB7SMP4OffgTgL2PBKXMuUsrk5Bwg8q4qPkjTXsKBMr+YK3Ltd/6F9s/Q==", "dev": true, "requires": { - "parse-ms": "^1.0.0" + "parse-ms": "^2.0.0" }, "dependencies": { "parse-ms": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-1.0.1.tgz", - "integrity": "sha1-VjRtR0nXjyNDDKDHE4UK75GqNh0=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.0.0.tgz", + "integrity": "sha512-AddiXFSLLCqj+tCRJ9MrUtHZB4DWojO3tk0NVZ+g5MaMQHF2+p2ktqxuoXyPFLljz/aUK0Nfhd/uGWnhXVXEyA==", "dev": true } } @@ -7291,17 +7361,58 @@ "dev": true }, "table": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz", - "integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/table/-/table-5.2.1.tgz", + "integrity": "sha512-qmhNs2GEHNqY5fd2Mo+8N1r2sw/rvTAAvBZTaTx+Y7PHLypqyrxr1MdIu0pLw6Xvl/Gi4ONu/sdceP8vvUjkyA==", "dev": true, "requires": { - "ajv": "^5.2.3", - "ajv-keywords": "^2.1.0", - "chalk": "^2.1.0", - "lodash": "^4.17.4", - "slice-ansi": "1.0.0", + "ajv": "^6.6.1", + "lodash": "^4.17.11", + "slice-ansi": "2.0.0", "string-width": "^2.1.1" + }, + "dependencies": { + "ajv": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.7.0.tgz", + "integrity": "sha512-RZXPviBTtfmtka9n9sy1N5M5b82CbxWIR6HIis4s3WQTXDJamc/0gpCWNGz6EWdWp4DOfjzJfhz/AS9zVPjjWg==", + "dev": true, + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", + "dev": true + }, + "slice-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.0.0.tgz", + "integrity": "sha512-4j2WTWjp3GsZ+AOagyzVbzp4vWGtZ0hEZ/gDY/uTvm6MTxUfTUIsnMIFb1bn8o0RuXiqUw15H1bue8f22Vw2oQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + } + } } }, "term-size": { diff --git a/package.json b/package.json index 5c33c40e..162c036b 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,8 @@ "babel-core": "^6.26.3", "babel-plugin-transform-builtin-extend": "^1.1.2", "babel-preset-env": "^1.7.0", + "benchmark": "^2.1.4", + "chalk": "^2.4.2", "eslint": "^4.19.1", "eslint-config-airbnb": "^17.0.0", "eslint-plugin-import": "^2.13.0", @@ -25,7 +27,9 @@ "grunt-markdownlint": "^2.1.0", "grunt-sourcemap-localize": "^0.1.0", "grunt-umd": "^3.0.0", - "nyc": "^13.1.0" + "nyc": "^13.1.0", + "pretty-ms": "^4.0.0", + "table": "^5.2.1" }, "repository": { "type": "git", @@ -42,7 +46,8 @@ "compile": "grunt compile", "grunt": "grunt", "build": "grunt build", - "docs": "grunt docs" + "docs": "grunt docs", + "bench": "node benchmark/run.js" }, "types": "./typings/VerbalExpressions.d.ts", "engines": {