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

Use WebAssembly.instantiateStreaming when available #1036

Closed

Conversation

Jarred-Sumner
Copy link
Contributor

@Jarred-Sumner Jarred-Sumner commented Mar 23, 2021

This makes esbuild.initialize automatically use WebAssembly.instantiateStreaming in supported browsers by default.

For browsers that support WebAssembly.instantiateStreaming and if the wasmURL is same-origin, it adds an additional (default true) option, verifyWasmURL which sends a HEAD request to the wasmUrl to check that the Content-Type header is application/wasm and that the response is ok. If not, it fallsback to WebAssembly.instantiate and logs to console.info. This makes it a backwards-compatible change, incase a developer's webserver is not configured to correctly send the application/wasm header such as nginx or if the wasmUrl is under a different origin (like a CDN).

To do this, it inlines wasmUrl into global.ESBUILD_WASM_URL. If global.ESBUILD_WASM_URL is truthy and WebAssembly.instantiateStreaming is defined, it uses that instead.

I have not manually tested this, but the browser test failed until I made it check for HEAD requests to /esbuild.wasm, which is a good sign. I had some build environment issues I think due to having a later version of Go installed

I added tests that cover:

  • No WebAssembly.instantiateStreaming support
  • WebAssembly.instantiateStreaming support, verifyWasmURL false
  • WebAssembly.instantiateStreaming support, verifyWasmURL true
  • WebAssembly.instantiateStreaming support, verifyWasmURL true, return wrong content type. It should fallback to WebAssembly.instantiate

@Jarred-Sumner
Copy link
Contributor Author

Jarred-Sumner commented Mar 23, 2021

Here are timings from running the tests:

[console.timeEnd] iife: 2943.001220703125 ms
[console.timeEnd] iifeStreaming: 2432.276123046875 ms
[console.timeEnd] iifeStreamingVerifyWasmURL: 2445.62109375 ms
[console.timeEnd] iifeStreamingVerifyWasmURLFallback: 2900.329833984375 ms
[console.timeEnd] iifeAsync: 2798.52294921875 ms
[console.timeEnd] iifeAsyncStreaming: 594.915771484375 ms
[console.timeEnd] iifeAsyncStreamingVerifyWasmURL: 643.916015625 ms
[console.timeEnd] iifeAsyncStreamingVerifyWasmURLFallback: 2827.716796875 ms
[console.timeEnd] iifeMin: 2899.09423828125 ms
[console.timeEnd] iifeMinStreaming: 2420.141845703125 ms
[console.timeEnd] iifeMinStreamingVerifyWasmURL: 2440.100341796875 ms
[console.timeEnd] iifeMinStreamingVerifyWasmURLFallback: 2905.22509765625 ms
[console.timeEnd] iifeMinAsync: 2837.287109375 ms
[console.timeEnd] iifeMinAsyncStreaming: 633.72802734375 ms
[console.timeEnd] iifeMinAsyncStreamingVerifyWasmURL: 632.817138671875 ms
[console.timeEnd] iifeMinAsyncStreamingVerifyWasmURLFallback: 2830.69580078125 ms
[console.timeEnd] esm: 2863.778076171875 ms
[console.timeEnd] esmStreaming: 2400.2158203125 ms
[console.timeEnd] esmStreamingVerifyWasmURL: 2437.08984375 ms
[console.timeEnd] esmStreamingVerifyWasmURLFallback: 2897.178955078125 ms
[console.timeEnd] esmAsync: 2815.09912109375 ms
[console.timeEnd] esmAsyncStreaming: 619.460205078125 ms
[console.timeEnd] esmAsyncStreamingVerifyWasmURL: 613.30810546875 ms
[console.timeEnd] esmAsyncStreamingVerifyWasmURLFallback: 2831.27587890625 ms
[console.timeEnd] esmMin: 2890.48291015625 ms
[console.timeEnd] esmMinStreaming: 2478.572998046875 ms
[console.timeEnd] esmMinStreamingVerifyWasmURL: 2432.202880859375 ms
[console.timeEnd] esmMinStreamingVerifyWasmURLFallback: 2888.947021484375 ms
[console.timeEnd] esmMinAsync: 2788.493896484375 ms
[console.timeEnd] esmMinAsyncStreaming: 622.496337890625 ms
[console.timeEnd] esmMinAsyncStreamingVerifyWasmURL: 697.15185546875 ms
[console.timeEnd] esmMinAsyncStreamingVerifyWasmURLFallback: 2822.542724609375 ms

This is with the cache disabled, 30mbps down, 15mbps up, and 20ms latency: (my home internet is 100mbps down)

     // Connect to Chrome DevTools
          const client = await page.target().createCDPSession()

          // Set throttling property
          await client.send('Network.emulateNetworkConditions', {
            'offline': false,
            // 30 mbps down
            'downloadThroughput': 3932160,
            // 15 mbps up
            'uploadThroughput': 15728640,
            // Little latency
            'latency': 20,
            
          })
          await client.send("Network.setCacheDisabled", {cacheDisabled: true});

I'm a little suspicious of whether or not the Network.setCacheDisabled actually works for this case.

If you'd like, I can commit the code that adds these timings (its wrapped in a boolean so it can be easily turned off).

Without emulateNetworkConditions and without setting the cache to disabled:

[console.timeEnd] iife: 742.485107421875 ms
[console.timeEnd] iifeStreaming: 730.726806640625 ms
[console.timeEnd] iifeStreamingVerifyWasmURL: 798.177978515625 ms
[console.timeEnd] iifeStreamingVerifyWasmURLFallback: 735.27734375 ms
[console.timeEnd] iifeAsync: 646.009033203125 ms
[console.timeEnd] iifeAsyncStreaming: 636.798828125 ms
[console.timeEnd] iifeAsyncStreamingVerifyWasmURL: 599.933837890625 ms
[console.timeEnd] iifeAsyncStreamingVerifyWasmURLFallback: 655.170166015625 ms
[console.timeEnd] iifeMin: 722.64794921875 ms
[console.timeEnd] iifeMinStreaming: 758.51904296875 ms
[console.timeEnd] iifeMinStreamingVerifyWasmURL: 806.5048828125 ms
[console.timeEnd] iifeMinStreamingVerifyWasmURLFallback: 741.68408203125 ms
[console.timeEnd] iifeMinAsync: 653.8447265625 ms
[console.timeEnd] iifeMinAsyncStreaming: 606.351806640625 ms
[console.timeEnd] iifeMinAsyncStreamingVerifyWasmURL: 621.1298828125 ms
[console.timeEnd] iifeMinAsyncStreamingVerifyWasmURLFallback: 637.239990234375 ms
[console.timeEnd] esm: 706.704833984375 ms
[console.timeEnd] esmStreaming: 783.910888671875 ms
[console.timeEnd] esmStreamingVerifyWasmURL: 771.71728515625 ms
[console.timeEnd] esmStreamingVerifyWasmURLFallback: 721.199951171875 ms
[console.timeEnd] esmAsync: 636.990966796875 ms
[console.timeEnd] esmAsyncStreaming: 617.01318359375 ms
[console.timeEnd] esmAsyncStreamingVerifyWasmURL: 612.56103515625 ms
[console.timeEnd] esmAsyncStreamingVerifyWasmURLFallback: 639.93115234375 ms
[console.timeEnd] esmMin: 702.072021484375 ms
[console.timeEnd] esmMinStreaming: 734.986083984375 ms
[console.timeEnd] esmMinStreamingVerifyWasmURL: 733.179931640625 ms
[console.timeEnd] esmMinStreamingVerifyWasmURLFallback: 711.146240234375 ms
[console.timeEnd] esmMinAsync: 634.615966796875 ms
[console.timeEnd] esmMinAsyncStreaming: 622.924072265625 ms
[console.timeEnd] esmMinAsyncStreamingVerifyWasmURL: 630.2548828125 ms
[console.timeEnd] esmMinAsyncStreamingVerifyWasmURLFallback: 652.715087890625 ms

@evanw
Copy link
Owner

evanw commented Mar 25, 2021

I do think this change should be done (I even have a draft of this change somewhere), and I appreciate the thoughtfulness that went into this. However, I've been thinking that this should be done by instead just trying WebAssembly.instantiateStreaming and then automatically falling back to WebAssembly.instantiate if that fails before finally giving up.

My thinking is that not serving WebAssembly with the right MIME type is really a bug on the server, albeit one with a graceful fallback, and is somewhat of an exceptional situation that shouldn't be catered to for performance. The default behavior should assume that everything is set up correctly for maximum speed.

So the way I'd like to see this implemented is a) there is no configuration option to change behavior and b) WebAssembly.instantiateStreaming is automatically tried first. In that case I think the main thread doesn't need to perform the fetch at all. The worker thread will need to handle errors and post any error back to the main thread, however.

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 this pull request may close these issues.

None yet

2 participants