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

Cache wasm module instantiations across macro invocations #16

Closed
alexcrichton opened this issue Oct 30, 2019 · 4 comments · Fixed by #21
Closed

Cache wasm module instantiations across macro invocations #16

alexcrichton opened this issue Oct 30, 2019 · 4 comments · Fixed by #21

Comments

@alexcrichton
Copy link
Collaborator

I'm extracting this as an actionable item from #2 where one thing for speed optimization that we'll need to do is to cache the instantiation of a wasm module across macro invocations. This'll shave about 50ms off each macro invocation in the case of using the JIT.

I'm not really sure how best to key this cache though in an efficient manner. We may be able to get by with a simple hash table and comparing sizes of wasm blobs, and then memcmp should be a well optimized rountine located in libstd, even for debug builds, so that may be fast enough to just key off wasm blobs directly. (they're all pretty unlikely to be the same length anyway)

@mystor
Copy link
Contributor

mystor commented Oct 31, 2019

We might also be able to get away with asking people to call into watt using a macro_rules! macro instead of a method, and perform instantiation caching with the macro, perhaps even performing the include! ourself so we know the module isn't dynamically provided. Might not be worth the reduction in flexibility & extra complexity, though.

Hashing off of the size of wasm blobs also sounds perfectly reasonable as an option, and is probably similarly performant due to size being unlikely to match.

@alexcrichton
Copy link
Collaborator Author

That's not a bad idea I think actually, I could imagine something like:

extern crate proc_macro;

use proc_macro::TokenStream;

static MODULE: watt::Module = watt::Module::new(include_bytes!("foo.wasm"));

#[proc_macro]
pub fn my_macro(input: TokenStream) -> TokenStream {
    MODULE.call1("my_macro", input)
}

#[proc_macro_attribute]
pub fn my_attribute(attr: TokenStream, input: TokenStream) -> TokenStream {
    MODULE.call2("my_attribute", attr, input)
}

We could even forgo the macros and just use a bunch of lazily initialized atomics and such internally!

@alexcrichton
Copy link
Collaborator Author

alexcrichton commented Oct 31, 2019

And if you wanted to get really fancy we could honestly replace that entire file with:

watt::instantiate_proc_macro!("foo.wasm");

That could be a procedural macro which parses the wasm file and generates all the #[proc_macro] functions appropriately. The original watt macros could perhaps serialize information out to a custom section of the wasm file which instantiate_proc_macro! would read.

Edit: er, but maybe scratch this since this is happening on the consumer which needs to be super fast to compile, and compiling a whole wasm parser and/or another proc macro may add too much compile time.

alexcrichton added a commit to alexcrichton/watt that referenced this issue Oct 31, 2019
This commit continues to rejigger the API of `watt` by having a
top-level `Instance` type now instead of a suite of top-level functions.
By using a top-level `struct` we can store an internal identifier
(`usize`) which is used to key a thread-local cache for wasm blobs. This
should allow us to share resources like module instantiations across
macro invocations, ensuring that we only instantiate modules
once-per-process.

Closes dtolnay#16
@mystor
Copy link
Contributor

mystor commented Oct 31, 2019

Edit: er, but maybe scratch this since this is happening on the consumer which needs to be super fast to compile, and compiling a whole wasm parser and/or another proc macro may add too much compile time.

If we require the caller to list supported macros, we can probably still provide a helper macro like that. It'll also give the host a place to write doc-comments for their macro, as those will be lost when building the wasm module.

watt::instantiate_proc_macro! {
    wasm "foo.wasm";

    /// My doc-comment goes here
    #[proc_macro_derive(Bar)]
    pub fn bar_derive;

    /// Another comment
    #[proc_macro_attribute]
    pub fn my_attribute;
}

alexcrichton added a commit to alexcrichton/watt that referenced this issue Oct 31, 2019
This commit continues to rejigger the API of `watt` by having a
top-level `Instance` type now instead of a suite of top-level functions.
By using a top-level `struct` we can store an internal identifier
(`usize`) which is used to key a thread-local cache for wasm blobs. This
should allow us to share resources like module instantiations across
macro invocations, ensuring that we only instantiate modules
once-per-process.

Closes dtolnay#16
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 a pull request may close this issue.

2 participants