Compiling to the web with Rust and emscripten


#22

From my experience of cross-compiling C/C++, yes, most asm.js / wasm code will be faster than most handwritten Javascript code, sometimes dramatically faster because:

  • asm.js / wasm has fixed and very simple types, a variable will never change its type and there are no JS ‘objects’
  • there is no garbage collection taking place since there are no objects to be garbage collected, the only exception is when the boundary to JS needs to be crossed to call into HTML5 APIs
  • most browsers detect asm.js now and take various shortcuts, for instance on Firefox, asm.js is AOT compiled so that the code doesn’t need to ‘warm up’ first

Handwritten Javascript is still better for simple webpage tasks or manipulating the DOM though, also C++ tends to create code bloat if not careful and the resulting asm.js / wasm blobs can be shockingly big as a result. I think the same will apply to Rust, you’ll have to avoid some patterns and techniques to keep compiled size down. I wrote a blog post about this a short while ago (this is about C++, but as I said, I think Rust will also require to be careful):

http://floooh.github.io/2016/08/27/asmjs-diet.html

I am really, really excited to see Rust supporting asm.js/wasm :smiley:

Cheers,
-Floh.


#23

Thanks. Based on this emscripten doc it looks like we need to pass EXPORTED_FUNCTIONS to emcc. This doesn’t seem to be an environment var, so I guess it requires something more clever


#24

Yes, correct.

Basically you have this boilerplate right now:

#[link_args = "-s EXPORTED_FUNCTIONS=['_hello_world']"]
extern {}

fn main() {}

#[no_mangle]
pub extern fn hello_world(n: c_int) -> c_int {
    n + 1
}

Then you can use this in your javascript to access and call the function:

var hello_world = cwrap('hello_world', 'number', ['number']);

console.log(hello_world(41));

#25

Keep in mind that whenever you actually need to interact with JavaScript or the DOM API you will face a significant performance hit.
asm.js (and wasm) are optimized for the fully separated run, but not when interacting with the native JS imlementation.


#26

Yes I know, when calling into WebGL or WebAudio this is inevitable though, this is also where the emscripten HTML5 API shims need to maintain actual Javascript objects. It’s still quite fast though, around 10k WebGL calls per frame while maintaining 60Hz is possible on desktop (not on mobile though). Still it would be nice to have some sort of ‘fast path’ in later WebAssembly versions there.


#27

This is so, so cool. I’ve been interested in Emscripten for a while now, but never actually got in and started working with it.

I spent a few hours yesterday stepping through the generated JavaScript code and was amazed at how it all worked. It’s got an emulated Filesystem, Syscalls, Stack, Heap, everything! It’s truly incredible that all of this is possible and so easy to get started with - thanks so much for this great writeup!


#28

So I followed the commands above on Windows, and everything works up to:

rustc --target=asmjs-unknown-emscripten hello.rs

Which returns:

error: Could not create LLVM TargetMachine for triple: asmjs-unknown-emscripten: No available targets are compatible with this triple.

But when I run rustup show I get:

installed targets for active toolchain
--------------------------------------

asmjs-unknown-emscripten
wasm32-unknown-emscripten
x86_64-pc-windows-msvc

So I’m not sure where to go next. (I also apologize if this isn’t the right thread to ask this, but I haven’t found much information on this online and this was the most helpful guide!)


#29

What happens is that java and javac on Sierra are stubs: they linger around and once they get called, the prompt will appear.

The problem is that this leads to misdetections of whether java is available or not.


#30

Is the shell you are calling it from configured with the emsdk environment? rustc needs to find emcc and without that it does not have any idea where.


#31

Yeah, I did eventually figure it out (see here). It ended up being that I had an old version of rustc and then a PATH issue with emcc.


#32

So I get it to compile and run in asm.js. When it comes to wasm it compiles but I get the following error. (just doing the Hello, Emscripten demo on this page).

        C:\Users\unsername\rust_test\src\hello.js:110
              throw ex;
              ^
        no binaryen method succeeded. consider enabling more options,
        like interpreting, if you want that: 
        https://github.com/kripken/emscripten/wiki/WebAssembly#binaryen-methods

does anyone have any ideas?


#33

What about source maps? is it possible to compile it for debugging?


#34

the compiled js file is so big, 1.1M here. Can it be optimized?


#35

If someone’s interested, I recently gave a talk sharing my experiments with Rust <-> JavaScript interop.

Slides: https://www.slideshare.net/RReverser/rust-javascript
Video: https://www.youtube.com/watch?v=zxIbTfsOJZE


#36

Sorry guys, what is the state of Web Workers now? I tried to write something to spawn a web worker thread but no luck.

I posted the details here: https://stackoverflow.com/questions/45248078/creating-web-worker-from-rust-with-emscripten-target


#37

no response
when execute rustc --target=wasm32-unknown-emscripten hello.rs


#38

@brson I have the same question as @eminence RE: building a library. Is there a way to do this?

In my case, I’m building a dynamic lib that will be used with a Unity project – so that means supporting ALL the platforms. I have it working for everything except asmjs. My crate-type is “cdylib” in my Cargo.toml, but when I try to build for emscripten, it gives me an error that cdylib isn’t supported.


#39

From what I understand, asm.js is basically just using JS as a middleman to pass through executable code. The only reason, per se, that JS is necessary is because it is in browsers, and everybody uses browsers. I understand the decision to make emscripten, and thus makes sense to support rust. If it gets people using Rust in place of JS, then that’s great. (I like JS and Rust both.)

It seems a little funny to me to just hijack the JS runtime to run an executable, when people can just download an .exe/.bin like normal with better performance.

I do have a heroku nodejs spun up with an emscripten project. It’s running a WebGL video game in the browser. So it’s an interesting and impressive technology, if not a novelty of virtualization.

Seems more sensical to put a JS runtime in an executable, not the other way around. It’s probably too late. I’m sure somebody has already done that and then ran it in asm so we could have emsception.

So, what kind of projects are you all doing with this? Where do you see this going?


#40

While I prefer native apps, everybody with their own preference and performance requirements.

asm.js is not passed to an “executable”, it is run by the browser’s JavaScript Engine(V8 in Chrome for example).

Running inside the browser is more portable(it is easier for a company to iterate and update the software when it is deployed on a website vs an installed binary that needs to be compiled for a specific platform and also people need to manually copy and install it), in some cases people prefer to just open a web-page instead of downloading an app(GMail is a good example here). Chromium OS, etc…

Why would somebody want to use it?: to replace JavaScript code with high-performance, cross-compiled code.
Here is one interesting, relevant to this question, older Rust experiment: https://davidmcneil.github.io/the-rusty-web/


#41

What happens if we try to use std::thread::spawn in Rust then try to compile to wasm? Will it just fail?