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

Add async resolver and JS transformer functions using rayon #9147

Merged
merged 3 commits into from
Sep 11, 2023
Merged

Conversation

devongovett
Copy link
Member

Depends on #9146.

With all of our rust modules in a single binary, we can now share a single Rayon thread pool between modules. This adds async versions of the resolver and JS transformer so that IO and processing can happen outside of the JS thread(s). This results in a ~20% performance improvement on esbuild's benchmark.

For the resolver, this is only supported when using a NodeFS, and when PnP is not enabled, because our implementation of calling JS functions from Rust is not currently thread safe. It could potentially be made so but this was a bunch of extra work for an edge case, so I think for now falling back to the sync version and accepting a small perf hit is fine.

Copy link
Contributor

@marcins marcins left a comment

Choose a reason for hiding this comment

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

Seems to make sense to me - again, I'm not a Rust expert so not sure if there's any gotchas with the way rayon is being used (like does there need to be any "config" done for rayon, or do it's defaults just make maximal use of available system resources?

Are we likely to run into any issues in CI where the OS can report different values for max CPUs compared to what our Docker agents are allocated, for example?

Can we make the async stuff opt-in via an environment variable initially so we can validate in CI safely?

let (deferred, promise) = env.create_deferred()?;
let resolver = &self.resolver;

if matches!(resolver.cache.fs, EitherFs::A(..)) || resolver.module_dir_resolver.is_some() {
Copy link
Contributor

Choose a reason for hiding this comment

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

I tried to understand how the EitherFS thing works, but I don't think I fully have my head around it.

It's an enum with two generic params, and the value of the enum is either of the generics.. and the generic type of the Fs used for the resolver is EitherFS<JsFileSystem, OsFileSystem> - does that just mean that the fs has to be one of those two? I'm sure there's a good reason (that I'd love to know more about), but why can't the type just be JsFileSystem | OsFilesSystem?

It's a bit off topic, but I'd love to learn more.

Copy link
Member Author

Choose a reason for hiding this comment

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

In rust there are no union types like that. EitherFs is a generic enum that chooses between (any) two options and proxies all of the FileSystem methods to the underlying instance.

@devongovett devongovett marked this pull request as ready for review September 10, 2023 19:05
@parcel-benchmark
Copy link

parcel-benchmark commented Sep 10, 2023

Benchmark Results

Kitchen Sink ✅

Timings

Description Time Difference
Cold 1.46s -32.00ms
Cached 274.00ms +42.00ms ⚠️

Cold Bundles

Bundle Size Difference Time Difference
dist/legacy/index.b8ae99ba.css 94.00b +0.00b 276.00ms +16.00ms ⚠️
dist/modern/index.31cedca9.css 94.00b +0.00b 275.00ms +15.00ms ⚠️

Cached Bundles

Bundle Size Difference Time Difference
dist/legacy/index.8692583b.js 1.48kb +0.00b 377.00ms -19.00ms 🚀
dist/legacy/index.a2819fc3.js 1.06kb +0.00b 376.00ms -20.00ms 🚀
dist/modern/index.d90ef1a6.js 917.00b +0.00b 376.00ms -19.00ms 🚀
dist/legacy/index.b8ae99ba.css 94.00b +0.00b 262.00ms -14.00ms 🚀
dist/modern/index.31cedca9.css 94.00b +0.00b 261.00ms -15.00ms 🚀

React HackerNews ✅

Timings

Description Time Difference
Cold 4.00s -61.00ms
Cached 380.00ms -15.00ms

Cold Bundles

Bundle Size Difference Time Difference
dist/PermalinkedComment.3145598b.js 3.94kb +0.00b 415.00ms -50.00ms 🚀
dist/UserProfile.b37bbaff.js 1.38kb +0.00b 414.00ms -51.00ms 🚀
dist/NotFound.c08212ea.js 265.00b +0.00b 415.00ms -49.00ms 🚀
dist/logo.8dd07848.png 244.00b +0.00b 260.00ms +18.00ms ⚠️

Cached Bundles

Bundle Size Difference Time Difference
dist/index.js 460.91kb +0.00b 967.00ms -72.00ms 🚀
dist/PermalinkedComment.3145598b.js 3.94kb +0.00b 402.00ms +62.00ms ⚠️
dist/UserProfile.b37bbaff.js 1.38kb +0.00b 402.00ms +62.00ms ⚠️
dist/NotFound.c08212ea.js 265.00b +0.00b 402.00ms +62.00ms ⚠️

AtlasKit Editor ✅

Timings

Description Time Difference
Cold 35.18s -3.92s 🚀
Cached 2.14s -86.00ms

Cold Bundles

Bundle Size Difference Time Difference
dist/index.c9a8b20f.js 3.79mb +0.00b 17.01s -1.18s 🚀
dist/pdfRenderer.4cf5cffc.js 1.11mb +0.00b 12.87s -789.00ms 🚀
dist/editorView.3f952334.js 619.77kb +0.00b 14.02s -911.00ms 🚀
dist/refractor.61b4c5e1.js 601.68kb +0.00b 11.74s -883.00ms 🚀
dist/media-viewer.bc1a2415.js 537.32kb +0.00b 11.74s +2.59s ⚠️
dist/popup.32b3d0ef.js 324.46kb +0.00b 11.74s -882.00ms 🚀
dist/ConfigPanelFieldsLoader.8648eeee.js 306.81kb +0.00b 8.35s -807.00ms 🚀
dist/EmojiPickerComponent.c199902f.js 189.68kb +0.00b 11.72s -899.00ms 🚀
dist/card.3521c96b.js 140.18kb +0.00b 8.34s -807.00ms 🚀
dist/ConfigPanelFieldsLoader.1a016f33.js 82.96kb +0.00b 11.72s -900.00ms 🚀
dist/esm.5b5f7c9f.js 63.36kb +0.00b 11.74s -883.00ms 🚀
dist/ElementBrowser.c496dd44.js 62.20kb +0.00b 8.34s -808.00ms 🚀
dist/archive.fe044de4.js 60.16kb +0.00b 11.74s +2.59s ⚠️
dist/esm.ce3e12df.js 59.72kb +0.00b 8.34s -810.00ms 🚀
dist/esm.f9edadb2.js 39.42kb +0.00b 11.74s -883.00ms 🚀
dist/smartMediaEditor.226eb0f2.js 21.76kb +0.00b 11.74s -882.00ms 🚀
dist/esm.a421c1ca.js 20.52kb +0.00b 11.74s -882.00ms 🚀
dist/ConfigPanelFieldsLoader.8efb299e.js 15.82kb +0.00b 8.34s -808.00ms 🚀
dist/ui.8e1e1200.js 14.49kb +0.00b 8.34s -808.00ms 🚀
dist/ConfigPanelFieldsLoader.f78f3b60.js 13.65kb +0.00b 8.34s -808.00ms 🚀
dist/dropzone.452cdf0e.js 13.48kb +0.00b 11.74s -883.00ms 🚀
dist/pdfRenderer.6335b9a2.js 12.08kb +0.00b 8.34s -807.00ms 🚀
dist/dropzone.eff4ce1e.js 11.51kb +0.00b 11.74s -883.00ms 🚀
dist/Toolbar.759db587.js 9.40kb +0.00b 11.74s -883.00ms 🚀
dist/clipboard.121b0510.js 7.94kb +0.00b 11.74s -883.00ms 🚀
dist/mobile-upload.c687ddb2.js 7.86kb +0.00b 8.35s -807.00ms 🚀
dist/mobile-upload.e9eb996a.js 7.86kb +0.00b 8.35s -807.00ms 🚀
dist/mobile-upload.d7818b6e.js 7.86kb +0.00b 11.74s -882.00ms 🚀
dist/index.runtime.1064c960.js 7.29kb +0.00b 11.75s -878.00ms 🚀
dist/browser.857fa69b.js 7.20kb +0.00b 11.74s -882.00ms 🚀
dist/index.b16227d6.css 4.08kb +0.00b 11.78s -863.00ms 🚀
dist/media-viewer-analytics-error-boundary.54c54975.js 3.19kb +0.00b 11.74s +2.59s ⚠️
dist/media-picker-analytics-error-boundary.6a30027d.js 3.19kb +0.00b 11.74s -883.00ms 🚀
dist/media-card-analytics-error-boundary.b80de757.js 3.19kb +0.00b 11.74s -882.00ms 🚀
dist/ru.aaea8ba6.js 2.81kb +0.00b 8.34s +1.91s ⚠️
dist/uk.5d2e97bd.js 2.76kb +0.00b 8.34s -807.00ms 🚀
dist/codeViewerRenderer.7d374cd5.js 2.61kb +0.00b 11.74s +2.59s ⚠️
dist/th.df60823c.js 2.60kb +0.00b 8.34s -807.00ms 🚀
dist/vi.3e6d5bcb.js 2.09kb +0.00b 8.34s -807.00ms 🚀
dist/tr.4de346b9.js 2.03kb +0.00b 8.34s -807.00ms 🚀
dist/sv.b893ead3.js 1.97kb +0.00b 8.34s -807.00ms 🚀
dist/zh_TW.3d130b76.js 1.85kb +0.00b 8.34s -808.00ms 🚀
dist/zh.fb21f066.js 1.83kb +0.00b 8.34s -808.00ms 🚀
dist/workerHasher.322762e4.js 1.56kb +0.00b 8.35s -807.00ms 🚀
dist/workerHasher.8fdadeba.js 1.56kb +0.00b 8.35s -807.00ms 🚀
dist/workerHasher.65b703b7.js 1.56kb +0.00b 11.74s -883.00ms 🚀
dist/workerHasher.100ea0bf.js 1.56kb +0.00b 11.74s -883.00ms 🚀
dist/workerHasher.81697bdc.js 1.56kb +0.00b 11.74s -883.00ms 🚀
dist/heading3.82217cc7.js 1.35kb +0.00b 5.51s -760.00ms 🚀
dist/heading2.a43a84af.js 1.17kb +0.00b 5.51s -760.00ms 🚀
dist/heading4.bc1ea347.js 1.12kb +0.00b 5.51s -760.00ms 🚀
dist/heading1.a2a2d506.js 1.01kb +0.00b 5.51s -760.00ms 🚀
dist/sk.1a0c584e.js 652.00b +0.00b 8.34s -808.00ms 🚀
dist/simpleHasher.0488d56a.js 585.00b +0.00b 8.35s -807.00ms 🚀
dist/simpleHasher.180c1d91.js 585.00b +0.00b 8.35s -806.00ms 🚀
dist/simpleHasher.88f1b387.js 585.00b +0.00b 11.74s -883.00ms 🚀
dist/simpleHasher.2c36efc2.js 585.00b +0.00b 11.74s -883.00ms 🚀
dist/simpleHasher.37be2994.js 585.00b +0.00b 11.74s -883.00ms 🚀
dist/ro.ee42c980.js 478.00b +0.00b 8.34s +1.91s ⚠️
dist/index.html 248.00b +0.00b 11.79s +6.42s ⚠️

Cached Bundles

Bundle Size Difference Time Difference
dist/media-viewer.bc1a2415.js 537.32kb +0.00b 8.70s -3.44s 🚀
dist/ConfigPanelFieldsLoader.1a016f33.js 82.96kb +0.00b 8.71s -3.43s 🚀
dist/archive.fe044de4.js 60.16kb +0.00b 8.70s -3.44s 🚀
dist/component-lazy.aeb22f50.js 59.50kb +0.00b 6.22s +444.00ms ⚠️
dist/index.html 248.00b +0.00b 6.22s -5.99s 🚀

Three.js ✅

Timings

Description Time Difference
Cold 3.01s -43.00ms
Cached 323.00ms +25.00ms ⚠️

Cold Bundles

No bundle changes detected.

Cached Bundles

No bundle changes detected.

Click here to view a detailed benchmark overview.

Base automatically changed from rust-bindings to v2 September 11, 2023 01:34
@devongovett
Copy link
Member Author

does there need to be any "config" done for rayon, or do it's defaults just make maximal use of available system resources

yep, it defaults to the number of physical CPUs on the system.

@devongovett devongovett merged commit 7eaaec5 into v2 Sep 11, 2023
15 of 16 checks passed
@devongovett devongovett deleted the async-rust branch September 11, 2023 03:20
@marcins
Copy link
Contributor

marcins commented Sep 11, 2023

yep, it defaults to the number of physical CPUs on the system.

My only concern about this is in our Kube/Docker based CI environment, in Node for example, process.os.cpus().length returns the numbers of CPUs of the host machine not the number of "virtual CPUs" actually allowed to the instance (e.g. we might use an agent size with 10 CPUs for Parcel builds, but process.os.cpus().length could (for example) return 40.

Is there a standard way to override this for rayon (with say an environment), or can we make it use PARCEL_WORKERS (since we already set that to the right amount in our builds).

@devongovett
Copy link
Member Author

Yes, there is a RAYON_NUM_THREADS environment variable. https://github.com/rayon-rs/rayon/blob/master/FAQ.md

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.

3 participants