Skip to content

Instantly share code, notes, and snippets.

@isaacs
Created August 3, 2021 20:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save isaacs/3234a28dc76f0a4a43b227e49a0f8b43 to your computer and use it in GitHub Desktop.
Save isaacs/3234a28dc76f0a4a43b227e49a0f8b43 to your computer and use it in GitHub Desktop.
#!/usr/bin/env node
const re = /\/+$/
const batchStrings = [
new Array(Math.pow(2, 20) + 1).join('/'),
new Array(Math.pow(2, 18) + 1).join('/'),
new Array(Math.pow(2, 17) + 1).join('/'),
new Array(Math.pow(2, 16) + 1).join('/'),
new Array(Math.pow(2, 15) + 1).join('/'),
new Array(Math.pow(2, 14) + 1).join('/'),
new Array(Math.pow(2, 13) + 1).join('/'),
new Array(Math.pow(2, 12) + 1).join('/'),
new Array(Math.pow(2, 11) + 1).join('/'),
new Array(Math.pow(2, 10) + 1).join('/'),
new Array(Math.pow(2, 9) + 1).join('/'),
new Array(Math.pow(2, 8) + 1).join('/'),
new Array(Math.pow(2, 7) + 1).join('/'),
new Array(Math.pow(2, 6) + 1).join('/'),
new Array(Math.pow(2, 5) + 1).join('/'),
new Array(Math.pow(2, 4) + 1).join('/'),
new Array(Math.pow(2, 3) + 1).join('/'),
new Array(Math.pow(2, 2) + 1).join('/'),
new Array(Math.pow(2, 1) + 1).join('/'),
'/',
]
const short = '/a/b/c/'
const medium = short + (new Array(1e3)).join('/')
const long = short + (new Array(1e6)).join('/')
exports.compare = {
redos: function() {
return long.replace(re, '')
},
// endsWith_pow20: function() {
// let stripped = long
// for (const str of batchStrings) {
// while (stripped.endsWith(str))
// stripped = stripped.slice(0, -1 * str.length)
// }
// return stripped
// },
endsWithManual_pow20: function() {
let stripped = long
for (const str of batchStrings) {
while (stripped.slice(-1 * str.length) === str)
stripped = stripped.slice(0, -1 * str.length)
}
return stripped
},
endsWithManual_pow20_oneSlice: function() {
let i = long.length
for (const str of batchStrings) {
while (i > str.length && long.substr(i - str.length, str.length) === str)
i -= str.length
}
return long.substr(0, i)
},
//endsWith_pow10: function() {
// let stripped = long
// for (const str of batchStrings.slice(-11)) {
// while (stripped.endsWith(str))
// stripped = stripped.slice(0, -1 * str.length)
// }
// return stripped
//},
// endsWith_pow5: function() {
// let stripped = long
// for (const str of batchStrings.slice(-6)) {
// while (stripped.endsWith(str))
// stripped = stripped.slice(0, -1 * str.length)
// }
// return stripped
// },
// endsWith_pow2: function() {
// let stripped = long
// for (const str of batchStrings.slice(-3)) {
// while (stripped.endsWith(str))
// stripped = stripped.slice(0, -1 * str.length)
// }
// return stripped
// },
// endsWith_pow1: function() {
// let stripped = long
// for (const str of batchStrings.slice(-2)) {
// while (stripped.endsWith(str))
// stripped = stripped.slice(0, -1 * str.length)
// }
// return stripped
// },
//endsWith_single: function() {
// let stripped = long
// const str = '/'
// while (stripped.endsWith(str))
// stripped = stripped.slice(0, -1 * str.length)
// return stripped
//},
checkTail: function() {
let stripped = long
while (stripped.charAt(stripped.length - 1) === '/')
stripped = stripped.slice(0, -1)
return stripped
},
checkTail_oneSlice: function() {
for (let i = long.length - 1; i >= 0; i--) {
if (long.charAt(i) === '/')
continue
return long.slice(0, i + 1)
}
return ''
},
}
const assert = require('assert')
for (const [name, fn] of Object.entries(exports.compare)) {
assert.equal(fn(), '/a/b/c', name)
console.error('ok', name)
}
require('../').runMain()
/*
$ node examples/redos-slash-ending.js
ok redos
ok endsWithManual_pow20
ok endsWithManual_pow20_oneSlice
ok checkTail
ok checkTail_oneSlice
benchmarking /Users/isaacs/dev/js/bench/examples/redos-slash-ending.js
Please be patient.
{
node: '16.5.0',
v8: '9.1.269.38-node.20',
uv: '1.41.0',
zlib: '1.2.11',
brotli: '1.0.9',
ares: '1.17.1',
modules: '93',
nghttp2: '1.42.0',
napi: '8',
llhttp: '6.0.2',
openssl: '1.1.1k+quic',
cldr: '39.0',
icu: '69.1',
tz: '2021a',
unicode: '13.0',
ngtcp2: '0.1.0-DEV',
nghttp3: '0.1.0-DEV'
}
Scores: (bigger is better)
endsWithManual_pow20_oneSlice
Raw:
> 19.66568338249754
> 18.962075848303392
> 19.627085377821395
> 19.743336623889437
Average (mean) 19.499545308127942
endsWithManual_pow20
Raw:
> 19.607843137254903
> 18.645731108930324
> 19.607843137254903
> 19.64636542239686
Average (mean) 19.376945701459245
redos
Raw:
> 1.408450704225352
> 1.3280212483399734
> 1.4134275618374559
> 1.4194464158978
Average (mean) 1.3923364825751454
checkTail_oneSlice
Raw:
> 0.47058823529411764
> 0.4570383912248629
> 0.4522840343735866
> 0.44424700133274103
Average (mean) 0.45603941555632704
checkTail
Raw:
> 0.09706853038245002
> 0.09672115291614276
> 0.0971534052268532
> 0.09766578767457759
Average (mean) 0.0971522190500059
Winner: endsWithManual_pow20_oneSlice
Compared with next highest (endsWithManual_pow20), it's:
0.63% faster
1.01 times as fast
0 order(s) of magnitude faster
BASICALLY THE SAME
Compared with the slowest (checkTail), it's:
99.5% faster
200.71 times as fast
2.3 order(s) of magnitude faster
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment