rustwasm / team

A point of coordination for all things Rust and WebAssembly
MIT License
1.45k stars 59 forks source link

Splitting and lazy loading wasm modules #52

Open sendilkumarn opened 6 years ago

sendilkumarn commented 6 years ago

This issue arises based on this discussion.

When converting an entire library into a wasm module, it is quite often we end up in huge size wasm output (~equal to the binary size). This will surely affect the wide usage of wasm.

One important usage of wasm is to use native libraries in the web application straightaway for the speed and other benefits. Splitting of wasm into chunks / modules will have its own benefits. This will probably followed by lazy-loading of the binaries as and when needed.

While, streaming compiler looks promising for libraries that are medium sized. It is better to provide an option to chunk the wasm and load them easily.

Pauan commented 6 years ago

JavaScript has an official way to lazy-load modules: the import() expression. This allows it to delay loading the module until it is needed. Naturally this allows for lazy-loading .wasm files as well, because .wasm files are ES6 modules.

I'm not sure if there are plans to support import() directly in WebAssembly, but it should be possible to accomplish something similar by having WebAssembly call into JavaScript, and then JavaScript can do the actual import() call.

However, it is important to note that the import() expression always loads modules relative to the file which contains the import(). So if you have a WebAssembly file foo.wasm, and a JavaScript file bar/qux.js, and the JavaScript file uses import(), then it will always import the modules relative to the bar directory. So when doing the lazy-loading, it will have to take that into account and compensate for it.

est31 commented 6 years ago

Wasm modules don't support splitting up well yet, as shared components like allocators must be included in every component. For this to work well, we'd need dynamic linking support.... right now apparently they want you to go through js but I doubt that this is fast enough for usage in e.g. an allocator. IDK though, maybe js engines can inline such a layer.

linclark commented 6 years ago

I'm not sure if there are plans to support import() directly in WebAssembly, but it should be possible to accomplish something similar by having WebAssembly call into JavaScript, and then JavaScript can do the actual import() call.

The plan is to eventually have WASM modules just be ES modules. Once that's in place, import() should just work for WASM modules (unless there's some complication I'm missing).

sendilkumarn commented 6 years ago

unless there's some complication I'm missing

Well I am not sure, as @est31 points out there is an ambiguity in sharing allocators and dynamic linking of the wasm modules.

using them as ES Modules is clear. But how can we split one giant wasm file into separate modules

fitzgen commented 6 years ago

I think we could split at the crate boundary, assuming one of

Exported generics, if allowed, would have to be monomorphised into the downstream crate, I guess.

Basically, it is the same issues we run into when splitting an existing binary across multiple dylibs... Which Rust doesn't have a great story for.

najamelan commented 5 years ago

I wonder if we can make any progress on this. wasm-bindgen now supports wasm-pack --target web which exports wasm as a ES module.

There is documentation on the webassembly website about dynamic loading using module imports/exports.

It would be nice to be able to compile libraries as dylibs that can be linked against for other components.

giacomocariello commented 4 years ago

Any news on this?

tlively commented 4 years ago

FYI I have plans to implement profile-guided module splitting functionality in Binaryen to facilitate lazy loading of code that is not needed for startup. Once that work is farther along, perhaps we can coordinate on a way to expose that functionality ergonomically in wasm-pack.

wdanilo commented 2 years ago

Hi! Is there any progress in this regards? :)

tlively commented 2 years ago

FWIW Binaryen now has a wasm-split tool that does what I described back in 2020. It's integrated into Emscripten and documented here: https://emscripten.org/docs/optimizing/Module-Splitting.html#module-splitting.

This should work out of the box with Rust when compiling to the wasm32-unknown-emscripten target, but that's unfortunately not compatible with wasm-pack.

wdanilo commented 2 years ago

Oh, that's super interesting! Thanks for letting me know. I'm so sad that wasm-pack does not support it though :/

lukechu10 commented 2 years ago

I'm not familiar with how the wasm-split tool works. Any chance something similar could be built for the wasm32-unknown-unknown target?

tlively commented 2 years ago

wasm-split itself isn't specific to Emscripten in any way, so it could be used with wasm32-unknown-unknown. The only tricky part is that it requires some special new JS to be provided to the split module as an import, so whatever tool generates the JS would need to be told how to generate that extra code. Alternatively, you could maintain your own JS and add the extra code yourself.

jbms commented 6 months ago

I've built a prototype that addresses this issue in what I think is quite an elegant way. See https://github.com/rustwasm/wasm-bindgen/issues/3939