Skip to content

Commit

Permalink
Add async benchmark and perf iterations (#386)
Browse files Browse the repository at this point in the history
  • Loading branch information
JoviDeCroock authored Aug 8, 2024
1 parent 45b8e8b commit 220ad45
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 51 deletions.
5 changes: 5 additions & 0 deletions .changeset/angry-sheep-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'preact-render-to-string': patch
---

Add async benchmarks and iterate on perf improvements
42 changes: 42 additions & 0 deletions benchmarks/async.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { h } from 'preact';
import { lazy } from 'preact/compat';

function Leaf() {
return (
<div>
<span class="foo" data-testid="stack">
deep stack
</span>
</div>
);
}

const lazies = new Array(600)
.fill(600)
.map(() =>
lazy(() =>
Promise.resolve().then(() => ({
default: (props) => <div>{props.children}</div>
}))
)
);
function PassThrough(props) {
const Lazy = lazies(props.id);
return <Lazy {...props} />;
}

function recursive(n, m) {
if (n <= 0) {
return <Leaf />;
}
return <PassThrough id={n * m}>{recursive(n - 1)}</PassThrough>;
}

const content = [];
for (let i = 0; i < 5; i++) {
content.push(recursive(10, i));
}

export default function App() {
return <div>{content}</div>;
}
16 changes: 14 additions & 2 deletions benchmarks/index.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
import { h } from 'preact';
import Suite from 'benchmarkjs-pretty';
import renderToStringBaseline from 'baseline-rts';
import renderToStringBaseline, {
renderToStringAsync as renderToStringAsyncBaseline
} from 'baseline-rts';
// import renderToString from '../src/index';
import renderToString from '../dist/index.module.js';
import renderToString, { renderToStringAsync } from '../dist/index.module.js';
import TextApp from './text';
import StackApp from './stack';
import { App as IsomorphicSearchResults } from './isomorphic-ui/search-results/index';
import { App as ColorPicker } from './isomorphic-ui/color-picker';

function suite(name, Root) {
return new Suite(name)
.add('baseline', () => renderToStringAsyncBaseline(<Root />))
.add('current', () => renderToStringAsync(<Root />))
.run();
}

function asyncSuite(name, Root) {
return new Suite(name)
.add('baseline', () => renderToStringBaseline(<Root />))
.add('current', () => renderToString(<Root />))
Expand All @@ -20,4 +29,7 @@ function suite(name, Root) {
await suite('SearchResults', IsomorphicSearchResults);
await suite('ColorPicker', ColorPicker);
await suite('Stack Depth', StackApp);

const { App: Async } = await import('./async.js');
await asyncSuite('async', Async);
})();
82 changes: 34 additions & 48 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import {
UNSAFE_NAME,
NAMESPACE_REPLACE_REGEX,
HTML_LOWER_CASE,
SVG_CAMEL_CASE
SVG_CAMEL_CASE,
createComponent
} from './lib/util.js';
import { options, h, Fragment } from 'preact';
import {
Expand All @@ -22,6 +23,7 @@ import {
CATCH_ERROR
} from './lib/constants.js';

const EMPTY_OBJ = {};
const EMPTY_ARR = [];
const isArray = Array.isArray;
const assign = Object.assign;
Expand Down Expand Up @@ -147,13 +149,6 @@ export async function renderToStringAsync(vnode, context) {
}
}

// Installed as setState/forceUpdate for function components
function markAsDirty() {
this.__d = true;
}

const EMPTY_OBJ = {};

/**
* @param {VNode} vnode
* @param {Record<string, unknown>} context
Expand Down Expand Up @@ -238,9 +233,9 @@ function _renderToString(

let vnodeType = typeof vnode;
// Text VNodes: escape as HTML
if (vnodeType !== 'object') {
if (vnodeType === 'function') return EMPTY_STR;
return vnodeType === 'string' ? encodeEntities(vnode) : vnode + EMPTY_STR;
if (vnodeType != 'object') {
if (vnodeType == 'function') return EMPTY_STR;
return vnodeType == 'string' ? encodeEntities(vnode) : vnode + EMPTY_STR;
}

// Recurse into children / Arrays
Expand All @@ -250,7 +245,7 @@ function _renderToString(
parent[CHILDREN] = vnode;
for (let i = 0; i < vnode.length; i++) {
let child = vnode[i];
if (child == null || typeof child === 'boolean') continue;
if (child == null || typeof child == 'boolean') continue;

const childRender = _renderToString(
child,
Expand All @@ -262,10 +257,12 @@ function _renderToString(
renderer
);

if (typeof childRender === 'string') {
if (typeof childRender == 'string') {
rendered = rendered + childRender;
} else {
renderArray = renderArray || [];
if (!renderArray) {
renderArray = [];
}

if (rendered) renderArray.push(rendered);

Expand Down Expand Up @@ -294,14 +291,14 @@ function _renderToString(
if (beforeDiff) beforeDiff(vnode);

let type = vnode.type,
props = vnode.props,
cctx = context,
contextType,
rendered,
component;
props = vnode.props;

// Invoke rendering on Components
if (typeof type === 'function') {
if (typeof type == 'function') {
let cctx = context,
contextType,
rendered,
component;
if (type === Fragment) {
// Serialized precompiled JSX.
if ('tpl' in props) {
Expand All @@ -315,7 +312,7 @@ function _renderToString(

// Check if we're dealing with a vnode or an array of nodes
if (
typeof value === 'object' &&
typeof value == 'object' &&
(value.constructor === undefined || isArray(value))
) {
out =
Expand All @@ -340,9 +337,7 @@ function _renderToString(
} else if ('UNSTABLE_comment' in props) {
// Fragments are the least used components of core that's why
// branching here for comments has the least effect on perf.
return (
'<!--' + encodeEntities(props.UNSTABLE_comment || EMPTY_STR) + '-->'
);
return '<!--' + encodeEntities(props.UNSTABLE_comment) + '-->';
}

rendered = props.children;
Expand All @@ -354,22 +349,15 @@ function _renderToString(
}

let isClassComponent =
type.prototype && typeof type.prototype.render === 'function';
type.prototype && typeof type.prototype.render == 'function';
if (isClassComponent) {
rendered = /**#__NOINLINE__**/ renderClassComponent(vnode, cctx);
component = vnode[COMPONENT];
} else {
vnode[COMPONENT] = component = {
__v: vnode,
props,
context: cctx,
// silently drop state updates
setState: markAsDirty,
forceUpdate: markAsDirty,
__d: true,
// hooks
__h: []
};
vnode[COMPONENT] = component = /**#__NOINLINE__**/ createComponent(
vnode,
cctx
);

// If a hook invokes setState() to invalidate the component during rendering,
// re-render it up to 25 times to allow "settling" of memoized states.
Expand Down Expand Up @@ -402,7 +390,7 @@ function _renderToString(
rendered != null &&
rendered.type === Fragment &&
rendered.key == null &&
!('tpl' in rendered.props);
rendered.props.tpl == null;
rendered = isTopLevelFragment ? rendered.props.children : rendered;

try {
Expand All @@ -416,8 +404,6 @@ function _renderToString(
renderer
);
} catch (err) {
let str = EMPTY_STR;

if (type.getDerivedStateFromError) {
component[NEXT_STATE] = type.getDerivedStateFromError(err);
}
Expand All @@ -438,10 +424,10 @@ function _renderToString(
rendered != null &&
rendered.type === Fragment &&
rendered.key == null &&
!('tpl' in rendered.props);
rendered.props.tpl == null;
rendered = isTopLevelFragment ? rendered.props.children : rendered;

str = _renderToString(
return _renderToString(
rendered,
context,
isSvgMode,
Expand All @@ -452,7 +438,7 @@ function _renderToString(
);
}

return str;
return EMPTY_STR;
} finally {
if (afterDiff) afterDiff(vnode);
vnode[PARENT] = null;
Expand All @@ -468,7 +454,7 @@ function _renderToString(
rendered != null &&
rendered.type === Fragment &&
rendered.key == null &&
!('tpl' in rendered.props);
rendered.props.tpl == null;
rendered = isTopLevelFragment ? rendered.props.children : rendered;

try {
Expand Down Expand Up @@ -513,7 +499,7 @@ function _renderToString(

if (!asyncMode) throw error;

if (!error || typeof error.then !== 'function') throw error;
if (!error || typeof error.then != 'function') throw error;

const renderNestedChildren = () => {
try {
Expand All @@ -527,7 +513,7 @@ function _renderToString(
renderer
);
} catch (e) {
if (!e || typeof e.then !== 'function') throw e;
if (!e || typeof e.then != 'function') throw e;

return e.then(
() =>
Expand Down Expand Up @@ -557,7 +543,7 @@ function _renderToString(
for (let name in props) {
let v = props[name];

if (typeof v === 'function') continue;
if (typeof v == 'function') continue;

switch (name) {
case 'children':
Expand Down Expand Up @@ -663,7 +649,7 @@ function _renderToString(
' ' +
name +
'="' +
(typeof v === 'string' ? encodeEntities(v) : v + EMPTY_STR) +
(typeof v == 'string' ? encodeEntities(v) : v + EMPTY_STR) +
'"';
}
}
Expand Down Expand Up @@ -711,7 +697,7 @@ function _renderToString(
const startTag = s + '>';

if (isArray(html)) return [startTag, ...html, endTag];
else if (typeof html !== 'string') return [startTag, html, endTag];
else if (typeof html != 'string') return [startTag, html, endTag];
return startTag + html + endTag;
}

Expand Down
2 changes: 1 addition & 1 deletion src/lib/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ export function createComponent(vnode, context) {
forceUpdate: markAsDirty,
__d: true,
// hooks
__h: []
__h: new Array(0)
};
}

Expand Down

0 comments on commit 220ad45

Please sign in to comment.