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

WIP: Rust-based JS transformer using SWC #5537

Closed
wants to merge 80 commits into from
Closed

Conversation

devongovett
Copy link
Member

@devongovett devongovett commented Dec 28, 2020

This is a WIP/POC of using a Rust-based transformer using SWC to replace the current JS transformer, for performance reasons. It works by passing the asset content to the native module, which returns the transformed code plus a list of dependencies. Overall it seems about 2x faster than the current JS transformer, but it would be much more if it also replaced Babel.

Most of the features are ported over, and it's passing most but not all of the JS tests - there are still a couple differences in e.g. ESM -> commonjs transformation to account for. Currently it's only for development mode as porting scope hoisting would be a much larger job. So in fact both the SWC and JS transformers are configured to run, but SWC will run without scope hoisting, and the JS transformer will run with scope hoisting enabled.

Although currently it only handles the job of the previous JS transformer, it could also take over the job of the Babel transformer in cases where there is no babel config. This includes transpiling syntax down to lower targets based on browserslist (SWC has an equivalent of Babel's preset-env).

It remains a question whether we want to go this route or stick with the JS version. Pros of sticking with JS include easier contributions for people who don't know Rust, ability to reuse ASTs in the case where custom Babel plugins are also being used, etc. Additionally, porting over scope hoisting will be a very large job, but at least we've already worked out most of the edge cases and have a good test suite so perhaps not completely unreasonable That said, the performance gains may be worth it. Open to opinions here!

dishuostec and others added 30 commits July 27, 2020 16:18
Co-authored-by: Amin Yahyaabadi <aminyahyaabadi74@gmail.com>
Co-authored-by: Amin Yahyaabadi <aminyahyaabadi74@gmail.com>
Co-authored-by: Amin Yahyaabadi <aminyahyaabadi74@gmail.com>
Co-authored-by: Amin Yahyaabadi <aminyahyaabadi74@gmail.com>
@height
Copy link

height bot commented Dec 28, 2020

Link Height tasks by mentioning a task ID in the pull request title or description, commit messages, or comments.

💡Tip: You can also use "Close T-X" to automatically close a task when the pull request is merged.

@parcel-benchmark
Copy link

parcel-benchmark commented Dec 28, 2020

Benchmark Results

Kitchen Sink ✅

Timings

Description Time Difference
Cold 3.63s -157.00ms
Cached 479.00ms +13.00ms

Cold Bundles

Bundle Size Difference Time Difference
dist/legacy/parcel.d5807e82.webp 102.94kb +0.00b 60.00ms -27.00ms 🚀
dist/modern/parcel.d5807e82.webp 102.94kb +0.00b 96.00ms -46.00ms 🚀
dist/legacy/index.76ce4b0c.js 1.12kb +0.00b 740.00ms -388.00ms 🚀
dist/modern/index.b43fcc94.js 1.12kb +0.00b 664.00ms -275.00ms 🚀
dist/legacy/index.html 701.00b +0.00b 730.00ms +196.00ms ⚠️
dist/modern/index.html 701.00b +0.00b 651.00ms -288.00ms 🚀
dist/modern/index.0bc656bc.css 77.00b +0.00b 1.16s -159.00ms 🚀

Cached Bundles

Bundle Size Difference Time Difference
dist/legacy/parcel.d5807e82.webp 102.94kb +0.00b 62.00ms -9.00ms 🚀
dist/modern/parcel.d5807e82.webp 102.94kb +0.00b 60.00ms -10.00ms 🚀
dist/legacy/index.76ce4b0c.js 1.12kb +0.00b 81.00ms -10.00ms 🚀
dist/modern/index.b43fcc94.js 1.12kb +0.00b 80.00ms -10.00ms 🚀
dist/legacy/index.html 701.00b +0.00b 65.00ms -25.00ms 🚀
dist/modern/index.html 701.00b +0.00b 81.00ms -10.00ms 🚀
dist/legacy/index.44edca46.css 77.00b +0.00b 62.00ms -29.00ms 🚀
dist/modern/index.0bc656bc.css 77.00b +0.00b 80.00ms -11.00ms 🚀

React HackerNews ✅

Timings

Description Time Difference
Cold 21.43s -231.00ms
Cached 1.05s -0.00ms

Cold Bundles

Bundle Size Difference Time Difference
dist/PermalinkedComment.0a19c7e4.js 4.24kb +0.00b 1.09s -142.00ms 🚀
dist/UserProfile.b2960402.js 1.78kb +0.00b 1.09s -142.00ms 🚀
dist/NotFound.f3269ae7.js 542.00b +0.00b 361.00ms +59.00ms ⚠️
dist/logo.24c8bf9e.png 274.00b +0.00b 56.00ms -35.00ms 🚀

Cached Bundles

Bundle Size Difference Time Difference
dist/index.js 483.21kb +0.00b 140.00ms +10.00ms ⚠️
dist/PermalinkedComment.0a19c7e4.js 4.24kb +0.00b 140.00ms +10.00ms ⚠️
dist/UserProfile.b2960402.js 1.78kb +0.00b 140.00ms +10.00ms ⚠️
dist/NotFound.f3269ae7.js 542.00b +0.00b 94.00ms +8.00ms ⚠️
dist/logo.24c8bf9e.png 274.00b +0.00b 93.00ms +7.00ms ⚠️

AtlasKit Editor ✅

Timings

Description Time Difference
Cold 2.18m -11.12s 🚀
Cached 3.22s +23.00ms

Cold Bundles

Bundle Size Difference Time Difference
dist/index.68c5e99f.js 2.46mb +4.00b ⚠️ 1.12m -13.93s 🚀
dist/pdfRenderer.f5a3f4a7.js 1.11mb +3.00b ⚠️ 44.58s -36.28s 🚀
dist/editorView.07b73572.js 588.08kb +0.00b 59.14s +26.59s ⚠️
dist/media-viewer.ebcb5e1e.js 73.43kb +0.00b 13.07s +817.00ms ⚠️
dist/card.df70c9b7.js 54.67kb +0.00b 12.13s +1.66s ⚠️
dist/Modal.3920d345.js 39.59kb +0.00b 4.82s +323.00ms ⚠️
dist/component.ad27194c.js 31.05kb +0.00b 2.12s +187.00ms ⚠️
dist/esm.e3419f0a.js 27.83kb +0.00b 8.18s -2.24s 🚀
dist/component.f25dcadb.js 22.80kb +0.00b 4.82s +330.00ms ⚠️
dist/DatePicker.faca45d0.js 21.38kb +0.00b 9.36s -841.00ms 🚀
dist/smartMediaEditor.21f91ed8.js 16.96kb +0.00b 15.55s +1.60s ⚠️
dist/js.8167e2d5.js 16.55kb +0.00b 4.82s +330.00ms ⚠️
dist/dropzone.3ae9a030.js 16.15kb +0.00b 14.19s -3.54s 🚀
dist/ui.15dd7acd.js 14.30kb +0.00b 11.82s +1.58s ⚠️
dist/media-viewer.f15e56f4.js 4.01kb +0.00b 12.77s +892.00ms ⚠️
dist/png-chunks-extract.608f438d.js 3.56kb +0.00b 4.82s +330.00ms ⚠️
dist/dropzone.d1e8004d.js 3.46kb +0.00b 13.16s -4.50s 🚀
dist/index.ece3a13e.css 3.46kb +0.00b 44.55s +26.69s ⚠️
dist/clipboard.6a6e5fbd.js 3.00kb +0.00b 17.64s +4.18s ⚠️
dist/16.b6ca7fd0.js 2.50kb +0.00b 2.12s +187.00ms ⚠️
dist/ResourcedEmojiComponent.5046c0ff.js 2.19kb +0.00b 11.69s +1.47s ⚠️
dist/card.2888338b.js 2.15kb +0.00b 13.14s +854.00ms ⚠️
dist/images.ece03ae5.js 1.91kb +0.00b 5.25s +324.00ms ⚠️
dist/feedback.facaac2e.js 1.87kb +0.00b 9.35s -842.00ms 🚀
dist/16.a9ea9b41.js 1.86kb +0.00b 5.00s +336.00ms ⚠️
dist/browser.8883c452.js 1.85kb +0.00b 17.68s +4.22s ⚠️
dist/workerHasher.d607e0b8.js 1.77kb +0.00b 14.53s +732.00ms ⚠️
dist/list-number.d4568b49.js 1.69kb +0.00b 5.25s -1.19s 🚀
dist/status.6595d133.js 1.69kb +0.00b 5.53s +277.00ms ⚠️
dist/code.efde0f48.js 1.61kb +0.00b 5.15s +341.00ms ⚠️
dist/link.5be155be.js 1.54kb +0.00b 5.25s -1.19s 🚀
dist/emoji.b5624cf5.js 1.46kb +0.00b 5.90s +991.00ms ⚠️
dist/16.b7da7bde.js 1.45kb +0.00b 2.12s +187.00ms ⚠️
dist/16.00100306.js 1.44kb +0.00b 5.70s +892.00ms ⚠️
dist/16.bdd61d2d.js 1.41kb +0.00b 5.70s +892.00ms ⚠️
dist/expand.4a483eb0.js 1.39kb +0.00b 9.35s -849.00ms 🚀
dist/16.fa5f411c.js 1.36kb +0.00b 5.00s +336.00ms ⚠️
dist/16.2a21d3f4.js 1.34kb +0.00b 2.12s +187.00ms ⚠️
dist/16.d10db5a3.js 1.32kb +0.00b 5.00s +336.00ms ⚠️
dist/16.c9db7e6b.js 1.32kb +0.00b 5.70s +892.00ms ⚠️
dist/mention.c44b48ed.js 1.30kb +0.00b 5.39s +297.00ms ⚠️
dist/Modal.44e980e0.js 1.29kb +0.00b 4.82s +330.00ms ⚠️
dist/layout.81f2196c.js 1.28kb +0.00b 5.25s -1.19s 🚀
dist/16.9e97e787.js 1.28kb +0.00b 2.12s +187.00ms ⚠️
dist/16.acff901d.js 1.27kb +0.00b 5.00s +335.00ms ⚠️
dist/quote.b6d8bffb.js 1.25kb +0.00b 5.53s +277.00ms ⚠️
dist/action.01ec8983.js 1.24kb +0.00b 5.15s +346.00ms ⚠️
dist/panel-warning.b5f3360d.js 1.22kb +0.00b 5.40s -1.15s 🚀
dist/16.90358f40.js 1.18kb +0.00b 5.00s +335.00ms ⚠️
dist/panel-error.7454ebc0.js 1.12kb +0.00b 5.39s +297.00ms ⚠️
dist/panel.fc70749c.js 1.11kb +0.00b 5.53s +277.00ms ⚠️
dist/table.77df779b.js 1.10kb +0.00b 5.54s +278.00ms ⚠️
dist/panel-success.d5752da0.js 1.06kb +0.00b 5.39s +288.00ms ⚠️
dist/panel-note.6a71a1f2.js 1.05kb +0.00b 5.39s +297.00ms ⚠️
dist/media-picker-analytics-error-boundary.49b32f4d.js 1023.00b +0.00b 14.19s +1.83s ⚠️
dist/media-card-analytics-error-boundary.9a500f44.js 1019.00b +0.00b 12.05s +1.71s ⚠️
dist/media-card-analytics-error-boundary.b884d580.js 1019.00b +0.00b 17.76s +3.96s ⚠️
dist/media-viewer-analytics-error-boundary.83da5db2.js 1015.00b +0.00b 12.85s +750.00ms ⚠️
dist/simpleHasher.79137cb4.js 767.00b +0.00b 9.38s +1.28s ⚠️
dist/simpleHasher.2c5206fd.js 767.00b +0.00b 15.55s +1.72s ⚠️

Cached Bundles

Bundle Size Difference Time Difference
dist/pdfRenderer.f5a3f4a7.js 1.11mb +0.00b 609.00ms -65.00ms 🚀
dist/editorView.07b73572.js 588.08kb +0.00b 640.00ms -53.00ms 🚀
dist/popup.cba766ad.js 171.12kb +0.00b 630.00ms -59.00ms 🚀
dist/EmojiPickerComponent.6d8f513a.js 139.84kb +0.00b 590.00ms -76.00ms 🚀
dist/Toolbar.56c4be53.js 100.68kb +0.00b 640.00ms -49.00ms 🚀
dist/media-viewer.ebcb5e1e.js 73.43kb +0.00b 609.00ms -65.00ms 🚀
dist/card.df70c9b7.js 54.67kb +0.00b 609.00ms -54.00ms 🚀
dist/card.1d931b4c.js 52.43kb +0.00b 631.00ms -58.00ms 🚀
dist/Modal.3920d345.js 39.59kb +0.00b 468.00ms -148.00ms 🚀
dist/component.ad27194c.js 31.05kb +0.00b 461.00ms -155.00ms 🚀
dist/esm.e3419f0a.js 27.83kb +0.00b 590.00ms -76.00ms 🚀
dist/component.f25dcadb.js 22.80kb +0.00b 469.00ms -102.00ms 🚀
dist/DatePicker.faca45d0.js 21.38kb +0.00b 551.00ms -115.00ms 🚀
dist/smartMediaEditor.21f91ed8.js 16.96kb +0.00b 640.00ms -53.00ms 🚀
dist/js.8167e2d5.js 16.55kb +0.00b 468.00ms -103.00ms 🚀
dist/dropzone.06ccbc64.js 16.15kb +0.00b 631.00ms -53.00ms 🚀
dist/ui.15dd7acd.js 14.30kb +0.00b 609.00ms -54.00ms 🚀
dist/workerHasher.bc6346e1.js 11.90kb +0.00b 591.00ms -92.00ms 🚀
dist/component.677fad45.js 6.23kb +0.00b 477.00ms -97.00ms 🚀
dist/card.3a4028bd.js 5.74kb +0.00b 609.00ms -65.00ms 🚀
dist/media-viewer.f15e56f4.js 4.01kb +0.00b 609.00ms -77.00ms 🚀
dist/EmojiPickerComponent.4991ddd4.js 3.68kb +0.00b 609.00ms -54.00ms 🚀
dist/png-chunks-extract.608f438d.js 3.56kb +0.00b 469.00ms -102.00ms 🚀
dist/dropzone.d1e8004d.js 3.46kb +0.00b 630.00ms -53.00ms 🚀
dist/index.ece3a13e.css 3.46kb +0.00b 640.00ms -53.00ms 🚀
dist/Modal.08282ab6.js 3.23kb +0.00b 602.00ms +39.00ms ⚠️
dist/clipboard.6a6e5fbd.js 3.00kb +0.00b 631.00ms -53.00ms 🚀
dist/16.b6ca7fd0.js 2.50kb +0.00b 461.00ms -102.00ms 🚀
dist/ResourcedEmojiComponent.5046c0ff.js 2.19kb +0.00b 609.00ms -54.00ms 🚀
dist/card.2888338b.js 2.15kb +0.00b 630.00ms -56.00ms 🚀
dist/date.a69000aa.js 1.97kb +0.00b 506.00ms -112.00ms 🚀
dist/images.ece03ae5.js 1.91kb +0.00b 515.00ms -105.00ms 🚀
dist/feedback.facaac2e.js 1.87kb +0.00b 551.00ms -98.00ms 🚀
dist/16.a9ea9b41.js 1.86kb +0.00b 497.00ms -109.00ms 🚀
dist/browser.8883c452.js 1.85kb +0.00b 631.00ms -53.00ms 🚀
dist/16.89d45d15.js 1.80kb +0.00b 477.00ms -105.00ms 🚀
dist/workerHasher.31326e89.js 1.77kb +0.00b 590.00ms -77.00ms 🚀
dist/workerHasher.d607e0b8.js 1.77kb +0.00b 640.00ms -53.00ms 🚀
dist/list-number.d4568b49.js 1.69kb +0.00b 523.00ms -110.00ms 🚀
dist/status.6595d133.js 1.69kb +0.00b 531.00ms -107.00ms 🚀
dist/code.efde0f48.js 1.61kb +0.00b 506.00ms -112.00ms 🚀
dist/link.5be155be.js 1.54kb +0.00b 515.00ms -105.00ms 🚀
dist/heading6.5a6082c8.js 1.53kb +0.00b 551.00ms -98.00ms 🚀
dist/heading3.1c92891c.js 1.52kb +0.00b 540.00ms -99.00ms 🚀
dist/16.07ccad90.js 1.51kb +0.00b 489.00ms -110.00ms 🚀
dist/16.00f25203.js 1.47kb +0.00b 477.00ms -105.00ms 🚀
dist/16.02056ae6.js 1.47kb +0.00b 488.00ms -100.00ms 🚀
dist/emoji.b5624cf5.js 1.46kb +0.00b 515.00ms -105.00ms 🚀
dist/16.b7da7bde.js 1.45kb +0.00b 461.00ms -102.00ms 🚀
dist/16.00100306.js 1.44kb +0.00b 505.00ms -110.00ms 🚀
dist/16.bdd61d2d.js 1.41kb +0.00b 498.00ms -110.00ms 🚀
dist/heading5.88bc6249.js 1.41kb +0.00b 551.00ms -96.00ms 🚀
dist/expand.4a483eb0.js 1.39kb +0.00b 551.00ms -98.00ms 🚀
dist/16.fa5f411c.js 1.36kb +0.00b 497.00ms -111.00ms 🚀
dist/16.2a21d3f4.js 1.34kb +0.00b 460.00ms -103.00ms 🚀
dist/heading2.22de70fe.js 1.34kb +0.00b 540.00ms -99.00ms 🚀
dist/16.d10db5a3.js 1.32kb +0.00b 497.00ms -111.00ms 🚀
dist/16.c9db7e6b.js 1.32kb +0.00b 506.00ms -111.00ms 🚀
dist/mention.c44b48ed.js 1.30kb +0.00b 523.00ms -110.00ms 🚀
dist/heading4.a2524c70.js 1.29kb +0.00b 540.00ms -107.00ms 🚀
dist/Modal.44e980e0.js 1.29kb +0.00b 468.00ms -103.00ms 🚀
dist/layout.81f2196c.js 1.28kb +0.00b 515.00ms -105.00ms 🚀
dist/16.9e97e787.js 1.28kb +0.00b 461.00ms -102.00ms 🚀
dist/16.acff901d.js 1.27kb +0.00b 489.00ms -110.00ms 🚀
dist/16.015dfb9d.js 1.26kb +0.00b 477.00ms -106.00ms 🚀
dist/16.828b958e.js 1.26kb +0.00b 489.00ms -110.00ms 🚀
dist/16.b9b1643b.js 1.26kb +0.00b 489.00ms -110.00ms 🚀
dist/16.ec84a250.js 1.26kb +0.00b 477.00ms -105.00ms 🚀
dist/divider.ed7ccd2e.js 1.26kb +0.00b 515.00ms -105.00ms 🚀
dist/quote.b6d8bffb.js 1.25kb +0.00b 531.00ms -105.00ms 🚀
dist/media-card-analytics-error-boundary.ff0177b3.js 1.25kb +0.00b 609.00ms -64.00ms 🚀
dist/action.01ec8983.js 1.24kb +0.00b 506.00ms -111.00ms 🚀
dist/panel-warning.b5f3360d.js 1.22kb +0.00b 531.00ms -104.00ms 🚀
dist/list.2dfe3f18.js 1.19kb +0.00b 523.00ms -110.00ms 🚀
dist/heading1.cc82b077.js 1.19kb +0.00b 540.00ms -99.00ms 🚀
dist/16.90358f40.js 1.18kb +0.00b 498.00ms -110.00ms 🚀
dist/panel-error.7454ebc0.js 1.12kb +0.00b 523.00ms -111.00ms 🚀
dist/panel.fc70749c.js 1.11kb +0.00b 531.00ms -105.00ms 🚀
dist/table.77df779b.js 1.10kb +0.00b 540.00ms -98.00ms 🚀
dist/panel-success.d5752da0.js 1.06kb +0.00b 531.00ms -104.00ms 🚀
dist/panel-note.6a71a1f2.js 1.05kb +0.00b 523.00ms -112.00ms 🚀
dist/media-picker-analytics-error-boundary.49b32f4d.js 1023.00b +0.00b 630.00ms -54.00ms 🚀
dist/media-card-analytics-error-boundary.9a500f44.js 1019.00b +0.00b 609.00ms -64.00ms 🚀
dist/media-card-analytics-error-boundary.b884d580.js 1019.00b +0.00b 632.00ms -61.00ms 🚀
dist/media-viewer-analytics-error-boundary.83da5db2.js 1015.00b +0.00b 630.00ms -56.00ms 🚀
dist/simpleHasher.79137cb4.js 767.00b +0.00b 591.00ms -72.00ms 🚀
dist/simpleHasher.2c5206fd.js 767.00b +0.00b 640.00ms -53.00ms 🚀
dist/index.html 119.00b +0.00b 499.00ms -50.00ms 🚀

Three.js ✅

Timings

Description Time Difference
Cold 15.12s -230.00ms
Cached 553.00ms -29.00ms 🚀

Cold Bundles

No bundle changes detected.

Cached Bundles

Bundle Size Difference Time Difference
dist/Three.js 580.60kb +0.00b 100.00ms -18.00ms 🚀

Click here to view a detailed benchmark overview.

@101arrowz
Copy link
Member

101arrowz commented Dec 29, 2020

Although I'm a fan of improving performance at any cost, the Babel ecosystem is absolutely massive. I believe that the majority of Parcel users on bigger projects will need to use a .babelrc at some point. Maybe just parsing and generation can be handled by Rust and designed for full compatibility with Babel.

Specifically, Parcel could use just SWC's parser, create a plugin/transformation interface compatible with Babel, then print the AST again with Rust. I would think that the majority of the performance hit is from parsing and generation anyway (if not, this is probably not a worthwhile endeavor).

@devongovett
Copy link
Member Author

It should be noted that this doesn't prevent you from using Babel in addition to SWC in Parcel. However, I think these days many projects might not need any custom babel stuff that SWC doesn't also provide. Preset env + JSX + TypeScript + a couple proposals (decorators, optional chaining, etc.) is all most projects need, and other popular Babel plugins can be ported to Rust for performance if needed as well. In the meantime, the fewer Babel plugins you run the faster your build will go, so offloading as much as possible to SWC will help even if you still have a couple custom plugins.

As for doing only parsing and codegen in Rust but transformation in Babel, I don't think this will help as much as you think. Specifically, because SWC's AST is represented by Rust structures and not JS objects, you essentially have to pay the cost of converting between these, which is very slow. In fact, SWC has a JS-based plugin API that does just this, and it was only slightly faster than Babel the last time I tried it. Doing everything in Rust is MUCH faster.

Additionally, parsing and codegen are actually not the slowest parts of Babel as evidenced by my recent performance related PRs (#4366, #4815, #4826), all of which replace various transformations with faster alternatives (still in JS) that don't rely on Babel. We've found the slowest part to be due to Babel's mutable AST, which makes traversal and scope tracking very slow.

This PR is the next step - if we're going to be rewriting pieces of our infrastructure for better performance, why not also do so in a language that is more performance oriented? As I said above, I'm not totally sure this is the right path forward though so I appreciate the feedback.

@101arrowz
Copy link
Member

I agree that performance is a big part of any bundler, but personally, I don't care if my app takes 2 seconds or 30 seconds or even 2 minutes to build so much as I care that the build works reliably and supports any language features or extensions I may need. I suggest the default remain as-is as long as there are other parts of bundling that can be optimized more easily, simply because the JS transformer is arguably the most important part of a bundler and there will inevitably be bugs with SWC compared to Babel, at least initially. However, I will definitely test this once added, and it's absolutely a good path forward. Slowly moving away from pure JavaScript solutions is a good idea when optimizing for performance, and performance is always useful for a good developer experience.

@devongovett
Copy link
Member Author

Babel is currently the focus because it is the slowest part of Parcel at the moment, by far. Whatever we do, Babel will of course still be supported automatically when a .babelrc or other Babel config is detected. This is only replacing Parcel's built in transforms. Also, if merged this would likely remain an experimental opt-in alternative plugin for some time while we test it out in the real world.

…ormer

# Conflicts:
#	packages/core/package-manager/src/NodeResolverBase.js
#	packages/core/package-manager/src/NodeResolverSync.js
@devongovett devongovett reopened this Jan 2, 2021
@devongovett devongovett changed the base branch from realpath to v2 January 2, 2021 03:42
@devongovett devongovett closed this May 3, 2021
@devongovett devongovett deleted the swc-transformer branch May 3, 2021 19:44
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.

None yet

6 participants