Skip to content

Commit

Permalink
perf(server-renderer): optimize unrollBuffer by avoiding promises (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
negezor authored Jul 12, 2024
1 parent d76dd9c commit 05779a7
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 18 deletions.
74 changes: 74 additions & 0 deletions packages/server-renderer/__tests__/unrollBuffer.bench.ts
Original file line number Diff line number Diff line change
@@ -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<typeof createBuffer>, 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<typeof createBuffer>, 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)
},
},
)
})
56 changes: 38 additions & 18 deletions packages/server-renderer/src/renderToString.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,46 @@ import { type SSRBuffer, type SSRContext, renderComponentVNode } from './render'

const { isVNode } = ssrUtils

async function unrollBuffer(buffer: SSRBuffer): Promise<string> {
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> | 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
}
return ret
} else {
// sync buffer can be more efficiently unrolled without unnecessary await
// ticks
return unrollBufferSync(buffer)

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)
})
}

ret = result
}

return ret
}

export function unrollBuffer(buffer: SSRBuffer): Promise<string> | string {
return nestedUnrollBuffer(buffer, '', 0)
}

function unrollBufferSync(buffer: SSRBuffer): string {
Expand Down

0 comments on commit 05779a7

Please sign in to comment.