jsPerf.app is an online JavaScript performance benchmark test runner & jsperf.com mirror. It is a complete rewrite in homage to the once excellent jsperf.com now with hopefully a more modern & maintainable codebase.
jsperf.com URLs are mirrored at the same path, e.g:
https://jsperf.com/negative-modulo/2
Can be accessed at:
https://jsperf.app/negative-modulo/2
<h3>log</h3>
<pre id="log" style="border: 1px solid black"></pre>
<h1>Classic JSX Output using h() function</h1>
<textarea id="classicJsxOutput" cols="80" rows="20"></textarea>
<h1>Optimized JSX Output using arrays</h1>
<textarea id="optimizedJsxOutput" cols="80" rows="20"></textarea>
const tags =
'a abbr acronym address applet area article aside audio b base basefont bdi bdo bgsound big blink blockquote body br button canvas caption center cite code col colgroup content data datalist dd decorator del details dfn dir div dl dt element em embed fieldset figcaption figure font footer form frame frameset h1 h2 h3 h4 h5 h6 head header hgroup hr html i iframe img input ins isindex kbd keygen label legend li link listing main map mark marquee menu menuitem meta meter nav nobr noframes noscript object ol optgroup option output p param plaintext pre progress q rp rt ruby s samp script section select shadow small source spacer span strike strong style sub summary sup table tbody td template textarea tfoot th thead time title tr track tt u ul var video wbr xmp'.split(
' '
);
log('Initializing....');
const DEPTH = 5;
const REPS = 20;
const setAttribute = HTMLElement.prototype.setAttribute;
const appendChild = Node.prototype.appendChild;
const attrs = new Array(500)
.fill('')
.map((_, i) => 'a' + Number(i).toString(36));
const attrValues = attrs.map((v) => v.toUpperCase());
function roundRobinFactor(array) {
let idx = 0;
return Object.assign(
function () {
const value = array[idx++];
if (idx === array.length) idx = 0;
return value;
},
{ reset: () => (idx = 0) }
);
}
const getTagName = roundRobinFactor(tags);
const getAttrName = roundRobinFactor(attrs);
const getAttrValueName = roundRobinFactor(attrValues);
function generateJsxTestCase(output, depth) {
return output(
getTagName(), // tag
[
getAttrName(),
getAttrValueName(), // 1
getAttrName(),
getAttrValueName(), // 2
getAttrName(),
getAttrValueName(), // 3
getAttrName(),
getAttrValueName(), // 4
],
depth > 0
? [
generateJsxTestCase(output, depth - 1), // 1
generateJsxTestCase(output, depth - 1), // 2
generateJsxTestCase(output, depth - 1), // 3
generateJsxTestCase(output, depth - 1), // 4
generateJsxTestCase(output, depth - 1), // 5
generateJsxTestCase(output, depth - 1), // 6
// generateJsxTestCase(output, depth - 1), // 7
// generateJsxTestCase(output, depth - 1), // 8
// generateJsxTestCase(output, depth - 1), // 9
// generateJsxTestCase(output, depth - 1), // 10
]
: []
);
}
function outputJSXClassic(tagName, attrs, children) {
const output = ['h("', tagName, '", {'];
for (let i = 0; i < attrs.length; ) {
output.push(attrs[i++]);
output.push(':');
output.push('"', attrs[i++], '"');
if (i < attrs.length) output.push(', ');
}
output.push('}');
if (children.length) {
output.push(',\n');
for (let i = 0; i < children.length; i++) {
const child = children[i];
output.push(
child
.split('\n')
.map((line) => ' ' + line)
.join('\n'),
i < children.length - 1 ? ',\n' : '\n'
);
}
}
output.push(')');
return output.join('');
}
function outputJSXOptimized(tagName, attrs, children) {
const output = ['["', tagName, '"'];
for (let i = 0; i < attrs.length; ) {
output.push(',');
output.push(JSON.stringify(attrs[i++]));
output.push(',');
output.push(JSON.stringify(attrs[i++]));
}
if (children && children.length) {
output.push(',\n');
for (let i = 0; i < children.length; i++) {
const child = children[i];
output.push(
child
.split('\n')
.map((line) => ' ' + line)
.join('\n'),
i < children.length - 1 ? ',\n' : '\n'
);
}
}
output.push(']');
return output.join('');
}
log('Generating h() function JSX....');
const jsxCodeClassic = new Function(
'h',
'return () => ' + generateJsxTestCase(outputJSXClassic, DEPTH)
)(h);
log('Generating array based JSX....');
getTagName.reset();
getAttrName.reset();
getAttrValueName.reset();
const jsxCodeOptimized = new Function(
'h',
'return () => ' + generateJsxTestCase(outputJSXOptimized, DEPTH)
)(h);
// document.getElementById('classicJsxOutput').value = jsxCodeClassic.toString();
// document.getElementById('optimizedJsxOutput').value = jsxCodeOptimized.toString();
function renderDOMClassic(parent, jsx) {
const element = document.createElement(jsx.tag);
parent.appendChild(element);
const props = jsx.normalizedProps;
let children = null;
props &&
Object.keys(props).forEach((key) => {
if (key === 'children') children = props[key];
else element.setAttribute(key, props[key]);
});
children && children.forEach((child) => renderDOMClassic(element, child));
return parent;
}
function renderDOMOptimized(parent, jsx) {
const element = document.createElement(jsx[0]);
appendChild.call(parent, element);
const length = jsx.length;
for (let i = 1; i < length; ) {
const keyOrChildren = jsx[i++];
if (typeof keyOrChildren === 'string') {
const value = jsx[i++];
setAttribute.call(element, keyOrChildren, value);
} else {
renderDOMOptimized(element, keyOrChildren);
}
}
return parent;
}
function log(text) {
//logAppend(text + '\n');
}
function logAppend(text) {
//document.getElementById('log').textContent += text;
}
function h(tag, props, ...children) {
return { tag, normalizedProps: { ...props, children } };
}
Ready to run.
Test | Ops/sec | |
---|---|---|
Classical JSX |
| ready |
Optimized JSX |
| ready |
You can edit these tests or add more tests to this page by appending /edit to the URL.