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

Exporting the WebAssembly.Module object #14

Open
alexcrichton opened this issue Aug 28, 2018 · 11 comments
Open

Exporting the WebAssembly.Module object #14

alexcrichton opened this issue Aug 28, 2018 · 11 comments

Comments

@alexcrichton
Copy link
Contributor

One of the really nifty things about a WebAssembly.Module object is that it can be posted to web workers in a cheap fashion, avoiding unnecessary recompilation of the internals. One of the primary use cases for this seems to be to help support the upcoming threads proposal where a module can be instantiated in multiple threads.

It seems, though, that if a world is envisioned where wasm modules are all ES modules, this may be difficult to do currently! As proposed I think that there's no way for an wasm-module-as-ES-module to export its own WebAssembly.Module instance. That means it's not possible, when using native ES module integration for wasm, to cheaply instantiate the module on multiple workers.

Would it be possible to extend this proposal to allow a module to sort of export itself? It'd be cool if as part of wat we could do something like:

(export "module" (module))

(or something like that).

By providing the ability to hook into it that should be enough for various other tools I think to use the natively compiled module as part of ESM integration to send to other threads and such.

Now there's still a lot of weird questions specifically with the threading-related proposal about things like imports, but allowing a handle to the WebAssembly.Module itself would also allow modules to introspect their own custom sections (I believe) even when integrated with native ESM integration.

@lukewagner
Copy link
Member

Agreed on the utility here! Once we have references to memories, tables, functions, instances, etc, it follows we'd also want references to modules, and then a way to export "this module" makes sense (modules would be no different than the other definition kinds except that that there is exactly 1 local definition).

Given all that, adding a "module" export kind could be considered just a subset of the later full feature (like we've done with, e.g., limiting memory indices to 0, etc) that makes sense to add in the context of esm-integration.

@rossberg
Copy link
Member

Hm, is this addressing a real problem? A module that's gonna be distributed to workers won't typically be compiled as an ESM module I think, since you won't need an instance of it in the main thread. And on the worker side you'll need to instantiate it manually anyway. So this scenario is going to remain a non-ESM use case.

@guybedford
Copy link
Collaborator

In the scenario where there are modules used by both the main thread and by the module workers, it would be nice to have ways of sharing the compilation work, but yes the approach here seems to assume some custom worker instantiation code that is able to do this work, instead of using module workers, while using module scripts on the main page.

Perhaps this is something that needs to be thought about from a module workers spec perspective rather?

@lukewagner
Copy link
Member

@rossberg I think one might well need an instance on the main thread to access the shared memory to both input data into the threaded computation and extract results. To be a bit more concrete, imagine a package that provides a (main-thread) ESM with the interface and it's purely an impl detail that the ESM spins up some workers, does a computation, they finish, and then the main thread ESM resolves a promise and extracts the results from shared memory. Since you wouldn't want/need to write distinct main-thread logic to interpret the shared memory bytes, there's a good reason to share a single .wasm (and thus, ideally, it's machine code) between window and worker threads.

It's true that the postMessage()ed Module would require some custom instantiation mechanism in the worker, but this could just be some generic glue code provided by the toolchain. Certain imports would likely be main-thread-only and be instantiated with throwing stubs in workers.

Extrapolating out a bit, if N different packages want to spin up threads to do their internal computation, it would be rather ineffficient if all N created M workers; you'd want some sort of thread pool. This could take the form of a main-thread thread-pool ESM provided by the toolchain and imported by the N thread-using ESMs. The workers it spawned by thread-pool would then contain some generic instantiation glue code to instantiate the various modules that wanted to run jobs.

Before pushing forward, though, I agree we should have some more concrete use cases / examples; this feature can also be polyfilled/prototyped pretty easily by bundlers (which are already desugaring wasm-esms into WebAssembly.instantiate).

@alexcrichton
Copy link
Contributor Author

@rossberg a good point about the problem this is addressing! I've been attempting to foray into getting Rust to play well with the threads proposal, but Rust's main focus right now is working with ESM-looking wasm modules, and I basically ended up discovering that ESMs and workers don't play super well together. To that end you're definitely right in that today there's no real story (I think? I'm very much a newb here!) for what wasm ESMs would look like when spread out to worker threads, necessitating non-ESM-looking wasm modules and custom instantiation glue.

Along the lines of what @lukewagner said though I was hoping to start prototyping out what a toolchain solution to this might look like, or some set of conventions of how a toolchain (like Rust or C++) could provide the ability to spawn worker threads as a library. It's true that my only idea for imports it to just stub them all out to throwing (as @lukewagner mentioned) and pray the worker threads don't call imported functions. That has its own problems and has a long list of other problems to solve (if you stub out all imports how in a worker does wasm call postMessage?).

My idea for this issue was to see if there was general agreement to have something like this so tools like Webpack could provide a polyfill for a possibly-future standard. I have a feeling getting tools like Webpack on board would be much easier if this were at least on the right trajectory :)


I've personally only really considered this from the perspective of threading (where the goal is to avoid redownloading/recompiling wasm when spinning up a worker). I haven't thought too much about the other applications, and racking my brain all I can really think of is the ability to introspect custom sections at runtime, but that's just a feature and I can't quite think of why one might wish to do that!

@alexcrichton
Copy link
Contributor Author

@guybedford oh forgot to mention, but interesting! Is there a module workers spec in the works somewhere? I'd love to take a look at that to see what's going on!

@rossberg
Copy link
Member

If convenience with ESMs is the only motivation then it might be more adequate and more lightweight to just add respective functionality at the ESM integration level. For example, the JS API could add such a pseudo export, on top of the module's explicit exports.

It's actually rather weird from a core Wasm perspective, especially since the module exists prior to the instance that would export it. And it might not be so easy to make sense of it in other embedding environments -- not all embeddings may want to (or be able to) expose module objects as first-class entities. Also, the classes of exports and imports ought to be symmetric somehow; various oddities would arise if they weren't.

@lukewagner
Copy link
Member

lukewagner commented Aug 29, 2018

@rossberg I assumed, insofar as reference types to all the other core entites (memories, tables, ...) are useful, that references to modules would be too. How else would you write a dynamic linker/loader in wasm or support dynamic compilation of modules from generated bytecode? I can see the whole set of features being unavailable on some hosts, though.

But I do see the point about the module being different than the other definition-kinds here in that it exists before the instance so not really an export. You could think of the module as being imported by instantiation and then optionally re-exported, but it's a bit of a stretch...

My initial stab at this was actually what you suggest: some sort of custom section that was specifically for ESM integration and said "also export the WebAssembly.Module".

@littledan
Copy link
Collaborator

Rather than forcing developers to take the low-level route of manually compiling and passing around a WebAssembly module, I imagine that the pattern within a Wasm ESM world would be that multiple workers simply import the same module, and the engine handles caching the compiled Wasm code. If this implementation technique is used, is there any particular need for dealing with these modules as first-class values to be exported? It's hard for me to understand the motivation for integrating this into the Wasm/ESM proposal, since once you get the module over to the other side, I don't see how it can be treated as an ESM--you'll just be back to the old JS API.

@Jamesernator
Copy link

I have a couple reasons for wanting to directly import a WebAssembly.Module rather than an instance (in the same module):

  1. The lack of freeing memory means I can create and discard instances to avoid leaking memory.
  2. Instantiating the module with differing imports.

I imagine other people would have other use cases (like their own custom sections).

By having it as a standard to export a Module we can avoid the associated boilerplate of different environments (as long as they implement WebAssembly esm loading), e.g. fetch vs fs.readFile etc.

@Jamesernator
Copy link

Looks like the module linking proposal allows for wasm sub-modules which can be exported so that's pretty cool.

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

No branches or pull requests

6 participants