stdweb icon indicating copy to clipboard operation
stdweb copied to clipboard

Js! macro performance in wasm32-unknown-unknown

Open edwin0cheng opened this issue 7 years ago • 7 comments

I have some performance issues of stdweb (wasm32-unknown-unknown) while i am working on my webgl project.

I found that the overhead of the js! macro about 3 times compare of internal __js_raw_asm (in firefox) :

https://github.com/edwin0cheng/stdweb-performance-test

js! macro pcounter = 500000
js! macro ptime = 36

__js_raw_asm! macro pcounter = 500000
__js_raw_asm! macro ptime = 13.320000000000

So I want to add support in stdweb, using wasm native types (i32,u32,f32, etc) for arguments in js! macro without serialization.

As i understand, it is how js! works:

stdwb side:

  • declare __js_n as export functions
  • using a list of amazing macro to expand and stringify :
js!{@(no_return) console.log(@{x});}

became:

::private::__js_1(a0 as i32, "$0 = Module.STDWEB.to_js($0);console.log (($0));" as *const _ as *const u8)

cargo-web:

  • Deserialize wasm binary
  • Search all export functions starts with __js
  • Search all actual calls of these functions and gets its first argument (js snippet)
  • Create these snippet as functions and redirect old function calls to these new one
  • And remove the first argument (the js str) of all calls and functype
  • Serialized back the wasm binary

However, the exported __js_n functions need to be predetermined types (export func do not support generic type).

So here is my idea:

  • Instead of export function for __js_n, we use a normal generic function with [inline(never)], and the type parameters of this function have trait constraint of wasm supported types (i32,u32,etc)
  • In the stdweb side, we search these functions and make new export functions with same arguments.
  • other stuffs are same as before

Do you think its possible? or do you think there are better solution to implement this feature?

edwin0cheng avatar Jan 29 '18 16:01 edwin0cheng

Yes, the performance of the js! macro is something that I would like to improve in the future.

And I do have quite a few ideas on how to do it, however doing that with the current macro would be a nightmare, which is why I'm mostly waiting for Rust's procedural macros to stabilize.

For example, with a procedural macro it would be easy for a call like this to be optimized:

js!( window.pcounter += @{inc as i32}; );

The procedural macro would be able to autodetect that there is no return there, so it wouldn't add any return value serialization, and it would see that you did as i32, so since the argument would always be an i32 it would be able to pass it super cheap using native WASM types.

Technically even without the as i32 it should be possible to detect the type using traits, but, again, without procedural macros it would be super painful. And I definitely want to support compilation on stable Rust, so I can't just switch to procedural macros just yet. (Although if we're talking about the wasm32-unknown-unknown target I would probably be willing to make an exception, since it's highly unstable and requires nightly anyway, so technically we could switch to a procedural macro there for the js! macro and then do crazy optimization tricks.)

koute avatar Jan 29 '18 22:01 koute

I totally understand how painful without proc-macro after reading current js! Implementation. 😂

But how about the export functions parts? Even with proc-macro, it seem to not helping a lot to declare export functions? Or do i miss something ?

edwin0cheng avatar Jan 30 '18 06:01 edwin0cheng

I'm sorry, I don't really understand the question? Are you asking whenever a procedular macro would help in declaring the imports to which the JS snippets get attached? In which case - yes, it would. We could then, for example, generate a unique function import per JS snippet and clean that up in postprocessing. We can't do that with macro_rules! as you can't generate unique identifiers with them, among other problems.

koute avatar Feb 01 '18 17:02 koute

@koute What do you think about using the proc-macro-hack crate, rather than waiting for proc-macro? I haven't used it yet, but it seems reasonably straightforward.

scottjmaddox avatar Feb 14 '18 04:02 scottjmaddox

@scottjmaddox Unfortunately that's not feasible. The biggest drawback of using the proc-macro-hack crate is (quoting from their README) "An item macro cannot be invoked multiple times within the same scope". Being able to use the js! macro only once in a scope is a non-starter.

koute avatar Feb 14 '18 18:02 koute

What's the status of this since procedural macro are now stable? I saw that there are procedural macros in stdweb-private-macros, but how are they performance-wise?

totorigolo avatar Jan 31 '19 21:01 totorigolo

@totorigolo They're the same as they are a direct port of the old code. It is now possible to implement optimizations; nobody has just gotten around to doing that yet. (It shouldn't be too hard though.)

koute avatar Jan 31 '19 21:01 koute