Skip to content

Add JSPI support for PHP WASM extensions#2560

Merged
wojtekn merged 27 commits into
trunkfrom
wojtekn/add/jspi-support
Feb 20, 2026
Merged

Add JSPI support for PHP WASM extensions#2560
wojtekn merged 27 commits into
trunkfrom
wojtekn/add/jspi-support

Conversation

@wojtekn
Copy link
Copy Markdown
Contributor

@wojtekn wojtekn commented Feb 10, 2026

Related issues

Fixes STU-1244

Proposed Changes

I propose to:

  • Enable JSPI support via --experimental-wasm-jspi flag
  • Remove unused asyncify WASM builds (~250MB) from production artifacts

Testing Instructions

Confirm:

  • Sites can be created, started, stopped, deleted
  • Studio CLI works

Pre-merge Checklist

  • Have you checked for TypeScript, React or other console errors?

@wojtekn wojtekn self-assigned this Feb 10, 2026
@wojtekn wojtekn force-pushed the wojtekn/add/jspi-support branch from 247fa61 to b1bade5 Compare February 10, 2026 13:18
@wpmobilebot
Copy link
Copy Markdown
Collaborator

wpmobilebot commented Feb 10, 2026

📊 Performance Test Results

Comparing cc4c26a vs trunk

site-editor

Metric trunk cc4c26a Diff Change
load 1434.00 ms 1435.00 ms +1.00 ms ⚪ 0.0%

site-startup

Metric trunk cc4c26a Diff Change
siteCreation 7118.00 ms 7085.00 ms -33.00 ms ⚪ 0.0%
siteStartup 3940.00 ms 3940.00 ms 0.00 ms ⚪ 0.0%

Results are median values from multiple test runs.

Legend: 🟢 Improvement (faster) | 🔴 Regression (slower) | ⚪ No change (<50ms diff)

@wojtekn wojtekn force-pushed the wojtekn/add/jspi-support branch from b1bade5 to 029766e Compare February 10, 2026 14:02
@wojtekn wojtekn requested a review from a team February 10, 2026 15:00
Comment thread cli/lib/wordpress-server-manager.ts Outdated

async function isMultiWorkerEnabled() {
// Enable multi-worker support for E2E tests (requires JSPI-enabled Node binary)
if ( process.env.E2E === 'true' ) {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I couldn't make it work without enabling multi-worker for E2E yet.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I recommend we revert this and add more debugging to the CI E2E runs to understand why the failure occurs. Playwright has built-in support for capturing screenshots, which should be a good start.

If you write the screenshot as a PNG to {cwd}/test-results, it will show up in the Artifacts tab in Buildkite after the test finishes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, I wouldn't like to merge the PR with this condition.

Tests were failing as the Studio site was timing out, and tests couldn't find the element, so I think saving a screenshot wouldn't help in this case. We may need to add more debugging on the Studio CLI process side (or pm2, as I suspected the problem could be there).

@wojtekn wojtekn marked this pull request as draft February 11, 2026 20:06
@fredrikekelund fredrikekelund marked this pull request as ready for review February 16, 2026 15:17
@fredrikekelund
Copy link
Copy Markdown
Contributor

Marking as ready for review to trigger E2E tests.

@fredrikekelund fredrikekelund changed the title Add JSPI support for PHP WASM extensions [WIP] Add JSPI support for PHP WASM extensions Feb 16, 2026
@fredrikekelund
Copy link
Copy Markdown
Contributor

30% slower Site Editor load time isn't encouraging 🤔

@fredrikekelund
Copy link
Copy Markdown
Contributor

The E2E tests previously failed due to a 502 Bad Gateway error in the headless browser that accessed wp-admin. Updating to the latest version of Playground packages fixed that.

If the compare-perf job continues to report severe Site Editor load-time degradation, we should investigate.

@fredrikekelund
Copy link
Copy Markdown
Contributor

Looking at https://codevitals.run/public/Automattic/studio/metrics?metric=site-editor-load, there seems to be some inherent noise in this metric… Given that multi-worker support is around the corner and that the nominal load time isn't too bad, I suggest landing this PR and potentially returning to this if we find it to be an issue later.

@wojtekn, would you mind reviewing this PR and checking my changes? Here's what I've done:

  • Updated @php-wasm/universal and all @wp-playground/* packages.
  • Updated the @wp-playground/wordpress patch.
  • Moved the asyncify cleanup logic to vite.cli.config.ts. This is where we copy the cli/node_modules dir, so it makes sense to clean up the asyncify files here, too. It also helps us prevent intermittent states on disk (where dist/cli/node_modules exists, but still contains the asyncify binaries) and ensure that Studio CLI behaves the same in development as in production.

@fredrikekelund fredrikekelund changed the title [WIP] Add JSPI support for PHP WASM extensions Add JSPI support for PHP WASM extensions Feb 17, 2026
…port

# Conflicts:
#	cli/package-lock.json
#	cli/package.json
#	package-lock.json
#	package.json
@wojtekn
Copy link
Copy Markdown
Contributor Author

wojtekn commented Feb 17, 2026

Thanks for the updates @fredrikekelund. Asyncify cleanup looks safer now. FYI, I merged the trunk into the branch as I merged the package updates branch there.

I see the site editor load is consistently slower by 30%. @adamziel do you consider JSPI stable enough to use in production? Any thoughts on why it would result in a 30% performance drop?

@adamziel
Copy link
Copy Markdown
Contributor

adamziel commented Feb 18, 2026

I see the site editor load is consistently slower by 30%. @adamziel do you consider JSPI stable enough to use in production? Any thoughts on why it would result in a 30% performance drop?

It's much more stable than Asyncify. If it wasn't for Safari and older Node.js versions, Playground wouldn't even support Asyncify anymore. The majority of Playgrounds run on JSPI. It should be much faster, too. Asyncify adds a lot of extra instrumentation code that isn't necessary with JSPI. Let's dig into this.

I believe we made the test environment behave differently to appease Jest. If that’s not required now that we have Vitest, then it’s really better to follow the approach Wojtek took here.

I'm temporarily undoing this to verify that it doesn't affect the performance tests.
@epeicher
Copy link
Copy Markdown
Contributor

I have reviewed this and I have not found any issues, but regarding performance, I have asked Claude to run the metrics/tests/site-editor.test.ts adding traces on trunk and on this branch and compare the results.

This is the summary:

Summary

The wojtekn/add/jspi-support branch shows a consistent ~800ms regression in the site editor load metric compared to trunk.

Branch Load Time
trunk 2252 ms
wojtekn/add/jspi-support 3125 ms
Diff +873 ms

Load Phase Breakdown (measured run)

Phase trunk branch Diff
Navigation (PHP response) 533 ms 533 ms 0 ms
Iframe visible 254 ms 300 ms +46 ms
Frame DOM loaded 550 ms 675 ms +125 ms
First [data-block] 6 ms 5 ms -1 ms
Spinners cleared 909 ms 1612 ms +703 ms
TOTAL 2252 ms 3125 ms +873 ms

The regression is concentrated in the "Spinners cleared" phase, which corresponds to REST API requests made by the site editor after initial render.

Individual REST API Request Durations (measured run)

Endpoint trunk branch Diff Ratio
settings 303 ms 725 ms +422 ms 2.4x
taxonomies 582 ms 796 ms +214 ms 1.4x
posts (per_page=3) 873 ms 1871 ms +998 ms 2.1x
template-parts/header 1179 ms 1532 ms +353 ms 1.3x
template-parts/footer 1477 ms 2314 ms +837 ms 1.6x

Key Findings

  1. The serialization pattern is the same on both branches — PHP WASM is single-threaded, so requests are queued on both. That's not the issue.

  2. Each individual PHP request is significantly slower with JSPI — every endpoint takes 1.3-2.4x longer to process. The simplest endpoint (settings) shows the clearest overhead: 303ms vs 725ms.

  3. The slowdowns compound because requests are sequential. Each slower request pushes back the start of the next one, turning a ~400ms per-request overhead into an ~800ms total regression.

Conclusion

The regression is in PHP WASM execution speed itself, not in concurrency or browser-side behavior. JSPI appears to add overhead to every PHP request cycle. The simpler/faster endpoints (like settings) show the overhead most clearly as a ratio, while the heavier endpoints (like posts, footer) show it as larger absolute increases.

@adamziel
Copy link
Copy Markdown
Contributor

adamziel commented Feb 18, 2026

Let's measure once again after @brandonpayton merges the worker support PR later today, it changes how some message passing works internally

@brandonpayton
Copy link
Copy Markdown
Member

Multi-worker is now shipped for Playground CLI, and npm packages have been published. The --experimental-multi-worker flag is now deprecated and has no effect.

@wojtekn
Copy link
Copy Markdown
Contributor Author

wojtekn commented Feb 19, 2026

Multi-worker is now shipped for Playground CLI, and npm packages have been published. The --experimental-multi-worker flag is now deprecated and has no effect.

@adamziel @brandonpayton I'm updating PHP-WASM in #2610.

However, would it make sense to still investigate why requests got slower after switching from asyncify to JSPI? Even if enabling multi-worker masks the slowdown, we might be carrying that regression into the multi-worker setup as well — fixing it could make things even faster overall.

@wojtekn
Copy link
Copy Markdown
Contributor Author

wojtekn commented Feb 19, 2026

@fredrikekelund thanks for fixing lock file. I think I generated it using another version of NPM. I've just pulled changes, ran nvm use and npm install and it no longer changes lock file.

Copy link
Copy Markdown
Contributor

@fredrikekelund fredrikekelund left a comment

Choose a reason for hiding this comment

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

The performance tests are passing again, and it seems like the switch to JSPI no longer affects performance negatively after the Playground 3.1.1 upgrade. Let's land this 👍

@epeicher
Copy link
Copy Markdown
Contributor

I had my tests one prompt away, so I have executed them again with the latest changes. This is the result, very promising:

  • Huge improvement! Here's the comparison with the previous results:

Total Load Time

Metric trunk branch (before) branch (now)
TOTAL 2252 ms 3125 ms 1164 ms
Spinners cleared 909 ms 1612 ms 341 ms

Individual REST API Request Durations

Endpoint trunk branch (before) branch (now)
settings 303 ms 725 ms pending
taxonomies 582 ms 796 ms 317 ms
posts (per_page=3) 873 ms 1871 ms 337 ms
template-parts/header 1179 ms 1532 ms 349 ms
template-parts/footer 1477 ms 2314 ms 329 ms

Key Observations

  • Requests are now processed concurrently rather than sequentially
  • All requests start at ~790-802ms and complete by ~1150ms
  • The branch is now ~2x faster than trunk on this metric
  • The settings endpoint shows as pending because the editor no longer blocks on it to finish rendering

@fredrikekelund
Copy link
Copy Markdown
Contributor

@epeicher, that's not measuring against the latest trunk, right?

@epeicher
Copy link
Copy Markdown
Contributor

epeicher commented Feb 20, 2026

@epeicher, that's not measuring against the latest trunk, right?

@fredrikekelund , that's right, it's comparing against the previous metrics I took. Let me try again comparing with latest trunk.

Site Editor Load Performance: trunk vs jspi branch (latest)

trunk branch (jspi) Diff
TOTAL 1176 ms 1185 ms +9 ms
Navigation (PHP) 560 ms 563 ms +3 ms
Iframe visible 247 ms 244 ms -3 ms
Frame DOM loaded 0 ms 0 ms 0 ms
First [data-block] 7 ms 6 ms -1 ms
Spinners cleared 362 ms 372 ms +10 ms

REST API Request Durations (measured run)

Endpoint trunk branch (jspi)
settings (OPTIONS) 367 ms 321 ms
taxonomies 340 ms 334 ms
posts (per_page=3) — (pending) 361 ms
posts (per_page=10) 363 ms 350 ms
template-parts/header 339 ms 357 ms
template-parts/footer 351 ms 356 ms

Conclusion

Results are essentially identical — the 9ms difference is within normal variance. The JSPI changes are performance-neutral. Both branches show the same concurrent request pattern with individual REST API requests completing in ~320-370ms.

@fredrikekelund
Copy link
Copy Markdown
Contributor

Thanks for checking, @epeicher! It's good to have another confirmation that JSPI no longer seems to cause any performance regression.

@wojtekn
Copy link
Copy Markdown
Contributor Author

wojtekn commented Feb 20, 2026

@fredrikekelund @epeicher I merged trunk it, and I see there are no performance drops vs trunk.

@fredrikekelund
Copy link
Copy Markdown
Contributor

:shipit:

@wojtekn wojtekn merged commit cbce975 into trunk Feb 20, 2026
10 checks passed
@wojtekn wojtekn deleted the wojtekn/add/jspi-support branch February 20, 2026 13:30
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.

6 participants