Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf(server-render): avoid unnecessary checks in createBuffer #11364

Merged
merged 4 commits into from
Jul 15, 2024

Conversation

negezor
Copy link
Contributor

@negezor negezor commented Jul 15, 2024

Early return in buffer.push() for appendable yields almost a twofold increase in speed, as we no longer need to check for Promise and other buffers, nor do we need to reassign appendable.

Performance was tested on the following hardware:
CPU: AMD Ryzen 7950x3D
System: WSL 2 Arch Linux on Windows 11
Node.js: v22.4.0

Before:

✓ createBuffer (3) 2732ms
    name                                hz     min      max    mean     p75     p99    p995    p999      rme  samples
· string only               1,487,338.27  0.0005   7.0505  0.0007  0.0006  0.0009  0.0011  0.0020   ±6.03%   743670   fastest
· string with nested          521,437.76  0.0010   5.9832  0.0019  0.0012  0.0028  0.0034  0.0170   ±9.86%   260719
· string with nested async    194,771.89  0.0013  13.2062  0.0051  0.0015  0.0035  0.0070  0.0988  ±16.83%    97386   slowest

After:

✓ createBuffer (3) 3315ms
    name                                hz     min     max    mean     p75     p99    p995    p999      rme  samples
· string only               2,996,147.77  0.0002  6.6710  0.0003  0.0003  0.0004  0.0005  0.0008   ±9.32%  1498074   fastest
· string with nested          598,703.89  0.0008  4.5703  0.0017  0.0010  0.0023  0.0028  0.0077   ±9.39%   300976
· string with nested async    338,962.42  0.0011  5.4922  0.0030  0.0014  0.0031  0.0036  0.0145  ±12.75%   169677   slowest

I also conducted some research to understand where most of the time is spent when working with the buffer in several different applications. For testing, I had two medium-sized Nuxt 3 applications and one large project with its own SSR. The results were quite similar; I'll provide an example from the latter. I went through all the pages once with a variant that completely changes the content, and here are the results:

What is the first element in the buffer

│ (index)       │ min │ max  │ mean │ p50 │ p75 │ p95 │ p99  │
├───────────────┼─────┼──────┼──────┼─────┼─────┼─────┼──────┤
│ first string  │ 82  │ 4194 │ 312  │ 171 │ 289 │ 853 │ 4194 │
│ first buffer  │ 20  │ 218  │ 48   │ 42  │ 59  │ 104 │ 218  │
│ first promise │ 3   │ 14   │ 7    │ 7   │ 8   │ 10  │ 14   │

When all elements in the buffer are of the same type

│ (index)       │ min │ max  │ mean │ p50 │ p75 │ p95 │ p99  │
├───────────────┼─────┼──────┼──────┼─────┼─────┼─────┼──────┤
│ only string   │ 43  │ 4114 │ 221  │ 99  │ 172 │ 488 │ 4114 │
│ only buffer   │ 20  │ 218  │ 48   │ 42  │ 59  │ 104 │ 218  │
│ only promise  │ 3   │ 14   │ 7    │ 7   │ 8   │ 10  │ 14   │

Total number of different elements

│ (index)       │ min │ max   │ mean │ p50  │ p75  │ p95  │ p99   │
├───────────────┼─────┼───────┼──────┼──────┼──────┼──────┼───────┤
│ total string  │ 507 │ 57268 │ 2282 │ 1190 │ 1897 │ 5283 │ 14448 │
│ total buffer  │ 99  │ 4256  │ 357  │ 221  │ 334  │ 918  │ 4256  │
│ total promise │ 4   │ 15    │ 8    │ 8    │ 9    │ 11   │ 15    │
│ buffer length │ 1   │ 6032  │ 2    │ 1    │ 1    │ 5    │ 11    │

I also considered changing the initial buffer behavior based on this data by setting appendable: true and initializing the buffer with an empty string buffer = [''] by default. However, I couldn't get consistent measurements, as the results vary significantly. Do you have any thoughts on this?

Copy link

github-actions bot commented Jul 15, 2024

Size Report

Bundles

File Size Gzip Brotli
runtime-dom.global.prod.js 89.6 kB 34.2 kB 30.9 kB
vue.global.prod.js 146 kB 53.7 kB 47.9 kB

Usages

Name Size Gzip Brotli
createApp 49.6 kB 19.5 kB 17.8 kB
createSSRApp 53 kB 20.9 kB 19 kB
defineCustomElement 51.9 kB 20.2 kB 18.4 kB
overall 63.1 kB 24.5 kB 22.2 kB

@@ -73,6 +73,7 @@ export function createBuffer() {
const isStringItem = isString(item)
if (appendable && isStringItem) {
buffer[buffer.length - 1] += item as string
return
} else {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could remove this else altogether with the early return

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. I originally did, but it had a strange effect on synthetics, maybe some JIT optimizations can optimize branching work differently in this case. Fortunately this is more often good than bad.

@negezor negezor requested a review from yyx990803 July 15, 2024 14:20
@yyx990803 yyx990803 merged commit fc205bf into vuejs:main Jul 15, 2024
11 checks passed
@negezor negezor deleted the optimize-create-buffer branch July 15, 2024 14:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants