Skip to content

Encode output modules in parallel - #58

Draft
svieujot wants to merge 1 commit into
WorldSEnder:mainfrom
svieujot:parallel-split
Draft

Encode output modules in parallel#58
svieujot wants to merge 1 commit into
WorldSEnder:mainfrom
svieujot:parallel-split

Conversation

@svieujot

Copy link
Copy Markdown

Problem

emit_modules encodes every output module serially. On large inputs this phase dominates the whole transform: on a 2.39 GB module with 6,720 output modules (a large Leptos application, split via cargo-leptos), the phase timings are

phase time
parse 1.3 s
dependency graph + split modules 19.3 s
emit state 1.9 s
encode output modules 46.0 s
write output 2.1 s

Change

The output modules are independent — each ModuleEmitState borrows the shared EmitState immutably and encodes only its own module — so encode them with a rayon parallel iterator. The indexed collect keeps the output_modules order. RelocInfo's warn-once invalid_reloc_warn cell becomes a std::sync::OnceLock so the borrowed state is Sync; the warning still fires once.

On the module above the encoding phase drops from 46.0 s to 2.8 s and the whole transform from 72 s to 29 s (384-core host; smaller hosts gain proportionally to core count).

Verification

  • cargo test --workspace in integration/ (host leg) passes; I could not run the browser-mode wasm leg locally, so CI is the authority there.
  • Functional equivalence on the 2.39 GB module: every emitted module passes wasm-tools validate, and wasm-bindgen 0.2.127 accepts the emitted main module.
  • A byte-for-byte comparison is not meaningful for this: two identical serial runs already emit differing bytes run-to-run (hash-map iteration order) — 6,024 of 6,722 outputs differ between two unpatched runs. If deterministic output is a goal I am happy to look at that separately.

The output modules are independent: each ModuleEmitState borrows the
shared EmitState immutably and encodes its own module. Encoding them
with a rayon parallel iterator keeps the output order (indexed
collect). Measured on a 2.39 GB input with 6,720 output modules
(384-core host): the encoding phase drops from 46.0 s to 2.8 s and
the whole transform from 72 s to 29 s.

RelocInfo's warn-once invalid_reloc_warn cell becomes a
std::sync::OnceLock so the shared state is Sync; behaviour is
unchanged (the warning still fires once).

Output equivalence was verified functionally on that module: every
emitted module passes wasm-tools validate and wasm-bindgen 0.2.127
accepts the emitted main module. A byte comparison is not meaningful
here: identical serial runs already differ run-to-run (hash-map
iteration order), before this change.
@WorldSEnder

Copy link
Copy Markdown
Owner

two identical serial runs already emit differing bytes run-to-run

I fixed a bunch of this behaviour in #47 which was not in a released version yet. If you are comparing against the git version and still find unstable output, that is a separate bug. Nice wins overall.

I was looking into a more involved parallelisation approach with a jobserver protocol a few months ago instead of just spreading over as many cores as rayon guesses there are, but realised that the build tools don't expose this anyway (trunk, wasm-pack and neither cargo-leptos). If you know if that has changed, let me know. I would accept as is.

Are you looking into multi-threading for the dependency graph analysis? I expect this to be a bit more involved, since the algo choice is not so clear from a preliminary literature dive. If you want, we can have that as a separate PR.

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