From 442b3aadf84b567b2227233ddc55fb3b780474c3 Mon Sep 17 00:00:00 2001 From: Negezor Date: Fri, 12 Jul 2024 18:04:38 +1100 Subject: [PATCH 1/3] test(server-renderer): add bench for unrollBuffer --- .../__tests__/unrollBuffer.bench.ts | 74 +++++++++++++++++++ .../server-renderer/src/renderToString.ts | 2 +- 2 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 packages/server-renderer/__tests__/unrollBuffer.bench.ts diff --git a/packages/server-renderer/__tests__/unrollBuffer.bench.ts b/packages/server-renderer/__tests__/unrollBuffer.bench.ts new file mode 100644 index 00000000000..b5e03cea602 --- /dev/null +++ b/packages/server-renderer/__tests__/unrollBuffer.bench.ts @@ -0,0 +1,74 @@ +import { bench, describe } from 'vitest' + +import { type SSRBuffer, createBuffer } from '../src/render' +import { unrollBuffer } from '../src/renderToString' + +function createSyncBuffer(levels: number, itemsPerLevel: number): SSRBuffer { + const buffer = createBuffer() + + function addItems(buf: ReturnType, level: number) { + for (let i = 1; i <= levels * itemsPerLevel; i++) { + buf.push(`sync${level}.${i}`) + } + if (level < levels) { + const subBuffer = createBuffer() + addItems(subBuffer, level + 1) + buf.push(subBuffer.getBuffer()) + } + } + + addItems(buffer, 1) + return buffer.getBuffer() +} + +function createMixedBuffer(levels: number, itemsPerLevel: number): SSRBuffer { + const buffer = createBuffer() + + function addItems(buf: ReturnType, level: number) { + for (let i = 1; i <= levels * itemsPerLevel; i++) { + if (i % 3 === 0) { + // @ts-expect-error testing... + buf.push(Promise.resolve(`async${level}.${i}`)) + } else { + buf.push(`sync${level}.${i}`) + } + } + if (level < levels) { + const subBuffer = createBuffer() + addItems(subBuffer, level + 1) + buf.push(subBuffer.getBuffer()) + } + } + + addItems(buffer, 1) + return buffer.getBuffer() +} + +describe('unrollBuffer', () => { + let syncBuffer = createBuffer().getBuffer() + let mixedBuffer = createBuffer().getBuffer() + + bench( + 'sync', + () => { + return unrollBuffer(syncBuffer) as any + }, + { + setup() { + syncBuffer = createSyncBuffer(5, 3) + }, + }, + ) + + bench( + 'mixed', + () => { + return unrollBuffer(mixedBuffer) as any + }, + { + setup() { + mixedBuffer = createMixedBuffer(5, 3) + }, + }, + ) +}) diff --git a/packages/server-renderer/src/renderToString.ts b/packages/server-renderer/src/renderToString.ts index 0e9299ee834..32298cd4fa5 100644 --- a/packages/server-renderer/src/renderToString.ts +++ b/packages/server-renderer/src/renderToString.ts @@ -11,7 +11,7 @@ import { type SSRBuffer, type SSRContext, renderComponentVNode } from './render' const { isVNode } = ssrUtils -async function unrollBuffer(buffer: SSRBuffer): Promise { +export async function unrollBuffer(buffer: SSRBuffer): Promise { if (buffer.hasAsync) { let ret = '' for (let i = 0; i < buffer.length; i++) { From 64e7e73c8d983074a693472a89cc8c9abc0fe1ce Mon Sep 17 00:00:00 2001 From: Negezor Date: Fri, 12 Jul 2024 18:06:04 +1100 Subject: [PATCH 2/3] perf(server-renderer): optimize unrollBuffer by avoiding Promise --- .../server-renderer/src/renderToString.ts | 62 +++++++++++++------ 1 file changed, 44 insertions(+), 18 deletions(-) diff --git a/packages/server-renderer/src/renderToString.ts b/packages/server-renderer/src/renderToString.ts index 32298cd4fa5..358fd87a6b7 100644 --- a/packages/server-renderer/src/renderToString.ts +++ b/packages/server-renderer/src/renderToString.ts @@ -11,26 +11,52 @@ import { type SSRBuffer, type SSRContext, renderComponentVNode } from './render' const { isVNode } = ssrUtils -export async function unrollBuffer(buffer: SSRBuffer): Promise { - if (buffer.hasAsync) { - let ret = '' - for (let i = 0; i < buffer.length; i++) { - let item = buffer[i] - if (isPromise(item)) { - item = await item - } - if (isString(item)) { - ret += item - } else { - ret += await unrollBuffer(item) - } +function nestedUnrollBuffer( + buffer: SSRBuffer, + parentRet: string, + startIndex: number, +): Promise | string { + if (!buffer.hasAsync) { + return parentRet + unrollBufferSync(buffer) + } + + let ret = parentRet + + for (let i = startIndex; i < buffer.length; i += 1) { + const item = buffer[i] + + if (isString(item)) { + ret += item + + continue + } + + if (isPromise(item)) { + return item.then(nestedItem => { + buffer[i] = nestedItem + + return nestedUnrollBuffer(buffer, ret, i) + }) } - return ret - } else { - // sync buffer can be more efficiently unrolled without unnecessary await - // ticks - return unrollBufferSync(buffer) + + const result = nestedUnrollBuffer(item, ret, 0) + + if (isPromise(result)) { + return result.then(nestedItem => { + buffer[i] = nestedItem + + return nestedUnrollBuffer(buffer, '', i) + }) + } + + ret = result } + + return ret +} + +export function unrollBuffer(buffer: SSRBuffer): Promise | string { + return nestedUnrollBuffer(buffer, '', 0) } function unrollBufferSync(buffer: SSRBuffer): string { From 1dc8fbe22d661f3be4903c465578f05c09c32c56 Mon Sep 17 00:00:00 2001 From: Evan You Date: Fri, 12 Jul 2024 22:37:21 +0800 Subject: [PATCH 3/3] chore: blank lines --- packages/server-renderer/src/renderToString.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/server-renderer/src/renderToString.ts b/packages/server-renderer/src/renderToString.ts index 358fd87a6b7..b931a4d55b8 100644 --- a/packages/server-renderer/src/renderToString.ts +++ b/packages/server-renderer/src/renderToString.ts @@ -21,30 +21,24 @@ function nestedUnrollBuffer( } let ret = parentRet - for (let i = startIndex; i < buffer.length; i += 1) { const item = buffer[i] - if (isString(item)) { ret += item - continue } if (isPromise(item)) { return item.then(nestedItem => { buffer[i] = nestedItem - return nestedUnrollBuffer(buffer, ret, i) }) } const result = nestedUnrollBuffer(item, ret, 0) - if (isPromise(result)) { return result.then(nestedItem => { buffer[i] = nestedItem - return nestedUnrollBuffer(buffer, '', i) }) }