Skip to content

Instantly share code, notes, and snippets.

@sapphi-red
Last active April 21, 2024 12:01
Show Gist options
  • Save sapphi-red/f7b20873e454d3bd2003696e65bc3bc6 to your computer and use it in GitHub Desktop.
Save sapphi-red/f7b20873e454d3bd2003696e65bc3bc6 to your computer and use it in GitHub Desktop.
devalue stringify_string performance
import { Bench } from 'tinybench'
import { original_stringify_string, new_stringify_string } from './func.js'
const inputs = ['longg', 'long\n', '""\n""', '<foo>', '<<<<<']
const repeats = [2, 20, 200, 2000]
const bench = new Bench()
for (const [i, input] of inputs.entries()) {
for (const r of repeats) {
const repeatedInput = input.repeat(r)
bench.add(`original,${i},${r * 5}`, () =>
original_stringify_string(repeatedInput)
)
bench.add(`new,${i},${r * 5}`, () => new_stringify_string(repeatedInput))
}
}
await bench.warmup()
await bench.run()
const result = {}
for (const task of bench.tasks) {
const [type, input, chars] = task.name.split(',')
result[`[${input}] ${chars} characters`] ||= {}
result[`[${input}] ${chars} characters`][type] = task.result.hz
}
console.table(
Object.entries(result).map(([k, v]) => ({
'Task Name': k,
Original: v.original.toFixed(2),
New: v.new.toFixed(2),
'New / Original': (v.new / v.original).toFixed(2)
}))
)
/** @param {string} char */
function get_escaped_char(char) {
switch (char) {
case '"':
return '\\"'
case '<':
return '\\u003C'
case '\\':
return '\\\\'
case '\n':
return '\\n'
case '\r':
return '\\r'
case '\t':
return '\\t'
case '\b':
return '\\b'
case '\f':
return '\\f'
case '\u2028':
return '\\u2028'
case '\u2029':
return '\\u2029'
default:
return char < ' '
? `\\u${char.charCodeAt(0).toString(16).padStart(4, '0')}`
: ''
}
}
/** @param {string} str */
export function original_stringify_string(str) {
let result = ''
let last_pos = 0
const len = str.length
for (let i = 0; i < len; i += 1) {
const char = str[i]
const replacement = get_escaped_char(char)
if (replacement) {
result += str.slice(last_pos, i) + replacement
last_pos = i + 1
}
}
return `"${last_pos === 0 ? str : result + str.slice(last_pos)}"`
}
const escaped = {
'<': '\\u003C',
'\\': '\\\\',
'\b': '\\b',
'\f': '\\f',
'\n': '\\n',
'\r': '\\r',
'\t': '\\t',
'\u2028': '\\u2028',
'\u2029': '\\u2029'
}
const escape_chars = /["<\\\n\r\t\b\f\u2028\u2029\x00-\x1f]/
/** @param {string} str */
export function new_stringify_string(str) {
if (!escape_chars.test(str)) {
return `"${str}"`
}
return JSON.stringify(str).replace(
/[<\u2028\u2029]/g,
(char) => escaped[char]
)
}
{
"name": "compare",
"type": "module",
"dependencies": {
"tinybench": "^2.8.0"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment