Support async instantiation in Node.js - #27730
jeremy-code wants to merge 12 commits into
Conversation
This is an automatic change generated by tools/maint/rebaseline_tests.py. The following (13) test expectation files were updated by running the tests with `--rebaseline`: ``` codesize/test_codesize_cxx_except_wasm.json: 168663 => 168976 [+313 bytes / +0.19%] codesize/test_codesize_cxx_except_wasm_legacy.json: 166523 => 166836 [+313 bytes / +0.19%] codesize/test_codesize_cxx_lto.json: 119632 => 119945 [+313 bytes / +0.26%] test/codesize/test_codesize_file_preload.expected.js updated codesize/test_codesize_mem_O3_grow_standalone.json: 9306 => 9619 [+313 bytes / +3.36%] codesize/test_codesize_mem_O3_standalone.json: 9140 => 9453 [+313 bytes / +3.42%] codesize/test_codesize_mem_O3_standalone_lib.json: 8334 => 8647 [+313 bytes / +3.76%] codesize/test_codesize_mem_O3_standalone_narg.json: 8455 => 8768 [+313 bytes / +3.70%] codesize/test_codesize_mem_O3_standalone_narg_flto.json: 7386 => 7699 [+313 bytes / +4.24%] codesize/test_codesize_minimal_64.json: 2439 => 2751 [+312 bytes / +12.79%] test/codesize/test_codesize_minimal_O0.expected.js updated codesize/test_codesize_minimal_pthreads_memgrowth.json: 26506 => 26819 [+313 bytes / +1.18%] codesize/test_unoptimized_code_size.json: 173189 => 172730 [-459 bytes / -0.27%] Average change: +2.98% (-0.27% - +12.79%) ```
This is an automatic change generated by tools/maint/rebaseline_tests.py. The following (46) test expectation files were updated by running the tests with `--rebaseline`: ``` codesize/test_codesize_cxx_ctors1.json: 153508 => 153517 [+9 bytes / +0.01%] codesize/test_codesize_cxx_ctors2.json: 152917 => 152926 [+9 bytes / +0.01%] codesize/test_codesize_cxx_except.json: 199612 => 199621 [+9 bytes / +0.00%] codesize/test_codesize_cxx_except_wasm.json: 168976 => 168985 [+9 bytes / +0.01%] codesize/test_codesize_cxx_except_wasm_legacy.json: 166836 => 166845 [+9 bytes / +0.01%] codesize/test_codesize_cxx_lto.json: 119945 => 119954 [+9 bytes / +0.01%] codesize/test_codesize_cxx_mangle.json: 265942 => 265951 [+9 bytes / +0.00%] codesize/test_codesize_cxx_noexcept.json: 155419 => 155428 [+9 bytes / +0.01%] codesize/test_codesize_cxx_wasmfs.json: 180524 => 180533 [+9 bytes / +0.00%] test/codesize/test_codesize_file_preload.expected.js updated codesize/test_codesize_file_preload.json: 23714 => 23723 [+9 bytes / +0.04%] codesize/test_codesize_files_js_fs.json: 18565 => 18574 [+9 bytes / +0.05%] codesize/test_codesize_files_wasmfs.json: 63544 => 63553 [+9 bytes / +0.01%] codesize/test_codesize_hello_O0.json: 38265 => 38274 [+9 bytes / +0.02%] codesize/test_codesize_hello_O1.json: 7956 => 7965 [+9 bytes / +0.11%] codesize/test_codesize_hello_O2.json: 5611 => 5620 [+9 bytes / +0.16%] codesize/test_codesize_hello_O3.json: 5307 => 5316 [+9 bytes / +0.17%] codesize/test_codesize_hello_Os.json: 5297 => 5306 [+9 bytes / +0.17%] codesize/test_codesize_hello_Oz.json: 4935 => 4944 [+9 bytes / +0.18%] codesize/test_codesize_hello_dylink.json: 43494 => 43503 [+9 bytes / +0.02%] codesize/test_codesize_hello_dylink_all.json: 859299 => 859308 [+9 bytes / +0.00%] codesize/test_codesize_hello_export_nothing.json: 2998 => 3007 [+9 bytes / +0.30%] codesize/test_codesize_hello_wasmfs.json: 5307 => 5316 [+9 bytes / +0.17%] codesize/test_codesize_libcxxabi_message_O3.json: 3411 => 3420 [+9 bytes / +0.26%] codesize/test_codesize_libcxxabi_message_O3_standalone.json: 3628 => 3637 [+9 bytes / +0.25%] codesize/test_codesize_mem_O3.json: 9455 => 9464 [+9 bytes / +0.10%] codesize/test_codesize_mem_O3_grow.json: 9791 => 9800 [+9 bytes / +0.09%] codesize/test_codesize_mem_O3_grow_standalone.json: 9619 => 9628 [+9 bytes / +0.09%] codesize/test_codesize_mem_O3_standalone.json: 9453 => 9462 [+9 bytes / +0.10%] codesize/test_codesize_mem_O3_standalone_lib.json: 8647 => 8656 [+9 bytes / +0.10%] codesize/test_codesize_mem_O3_standalone_narg.json: 8768 => 8777 [+9 bytes / +0.10%] codesize/test_codesize_mem_O3_standalone_narg_flto.json: 7699 => 7708 [+9 bytes / +0.12%] codesize/test_codesize_minimal_64.json: 2751 => 2760 [+9 bytes / +0.33%] test/codesize/test_codesize_minimal_O0.expected.js updated codesize/test_codesize_minimal_O0.json: 20262 => 20271 [+9 bytes / +0.04%] codesize/test_codesize_minimal_O1.json: 3565 => 3574 [+9 bytes / +0.25%] codesize/test_codesize_minimal_O2.json: 2691 => 2700 [+9 bytes / +0.33%] codesize/test_codesize_minimal_O3.json: 2386 => 2395 [+9 bytes / +0.38%] codesize/test_codesize_minimal_Os.json: 2386 => 2395 [+9 bytes / +0.38%] codesize/test_codesize_minimal_Oz-ctors.json: 2357 => 2366 [+9 bytes / +0.38%] codesize/test_codesize_minimal_Oz.json: 2386 => 2395 [+9 bytes / +0.38%] codesize/test_codesize_minimal_esm.json: 2517 => 2526 [+9 bytes / +0.36%] codesize/test_codesize_minimal_pthreads.json: 26360 => 26369 [+9 bytes / +0.03%] codesize/test_codesize_minimal_pthreads_memgrowth.json: 26819 => 26828 [+9 bytes / +0.03%] codesize/test_codesize_minimal_wasmfs.json: 2386 => 2395 [+9 bytes / +0.38%] codesize/test_unoptimized_code_size.json: 172730 => 172757 [+27 bytes / +0.02%] Average change: +0.14% (+0.00% - +0.38%) ```
|
Regarding the Bun test failures, I am guessing it may be related to this issue: oven-sh/bun#33220. |
|
Are you sure that the caching is working under node and using streaming instantiation? Where is the cached code stored? How can we show that the caching is working? We do already have an existing How does this compare to that? |
|
Here's a quick test repo https://github.com/jeremy-code/emscripten-node-test, and the Node.js trace logs do show that the events "wasm.StartStreamingCompilation" and "wasm.InitializeStreaming" are occurring. I'm not sure how to verify the code caching part works as intended, but at the very least, the aspect of "compil[ing] the WebAssembly module while the module bytes are still downloading" is definitely improving performance (screenshots below, no stream on top, streaming bottom).
It's not easy to test the code caching part since the bottleneck is Node.js already storing the file in memory (which is not much of a concern since the Wasm binary is only 151.589 KB), but in practice, this should be GC'd eventually. As for where the cache is stored, I believe it is in memory. Per node:module documentation, it only stores CJS/MJS/TS files and in practice, with NODE_COMPILE_CACHE set, I don't see any Wasm files. Per the link you provided, NODE_CODE_CACHING is a legacy setting and has no effect, is that true? |
This is an automatic change generated by tools/maint/rebaseline_tests.py. The following (46) test expectation files were updated by running the tests with `--rebaseline`: ``` codesize/test_codesize_cxx_ctors1.json: 153517 => 153546 [+29 bytes / +0.02%] codesize/test_codesize_cxx_ctors2.json: 152926 => 152955 [+29 bytes / +0.02%] codesize/test_codesize_cxx_except.json: 199621 => 199650 [+29 bytes / +0.01%] codesize/test_codesize_cxx_except_wasm.json: 168985 => 169014 [+29 bytes / +0.02%] codesize/test_codesize_cxx_except_wasm_legacy.json: 166845 => 166874 [+29 bytes / +0.02%] codesize/test_codesize_cxx_lto.json: 119954 => 119983 [+29 bytes / +0.02%] codesize/test_codesize_cxx_mangle.json: 265951 => 265980 [+29 bytes / +0.01%] codesize/test_codesize_cxx_noexcept.json: 155428 => 155457 [+29 bytes / +0.02%] codesize/test_codesize_cxx_wasmfs.json: 180533 => 180562 [+29 bytes / +0.02%] test/codesize/test_codesize_file_preload.expected.js updated codesize/test_codesize_file_preload.json: 23723 => 23752 [+29 bytes / +0.12%] codesize/test_codesize_files_js_fs.json: 18574 => 18603 [+29 bytes / +0.16%] codesize/test_codesize_files_wasmfs.json: 63553 => 63584 [+31 bytes / +0.05%] codesize/test_codesize_hello_O0.json: 38274 => 38304 [+30 bytes / +0.08%] codesize/test_codesize_hello_O1.json: 7965 => 7995 [+30 bytes / +0.38%] codesize/test_codesize_hello_O2.json: 5620 => 5649 [+29 bytes / +0.52%] codesize/test_codesize_hello_O3.json: 5316 => 5345 [+29 bytes / +0.55%] codesize/test_codesize_hello_Os.json: 5306 => 5335 [+29 bytes / +0.55%] codesize/test_codesize_hello_Oz.json: 4944 => 4973 [+29 bytes / +0.59%] codesize/test_codesize_hello_dylink.json: 43503 => 43532 [+29 bytes / +0.07%] codesize/test_codesize_hello_dylink_all.json: 859308 => 859337 [+29 bytes / +0.00%] codesize/test_codesize_hello_export_nothing.json: 3007 => 3036 [+29 bytes / +0.96%] codesize/test_codesize_hello_wasmfs.json: 5316 => 5345 [+29 bytes / +0.55%] codesize/test_codesize_libcxxabi_message_O3.json: 3420 => 3449 [+29 bytes / +0.85%] codesize/test_codesize_libcxxabi_message_O3_standalone.json: 3637 => 3666 [+29 bytes / +0.80%] codesize/test_codesize_mem_O3.json: 9464 => 9493 [+29 bytes / +0.31%] codesize/test_codesize_mem_O3_grow.json: 9800 => 9829 [+29 bytes / +0.30%] codesize/test_codesize_mem_O3_grow_standalone.json: 9628 => 9657 [+29 bytes / +0.30%] codesize/test_codesize_mem_O3_standalone.json: 9462 => 9491 [+29 bytes / +0.31%] codesize/test_codesize_mem_O3_standalone_lib.json: 8656 => 8685 [+29 bytes / +0.34%] codesize/test_codesize_mem_O3_standalone_narg.json: 8777 => 8806 [+29 bytes / +0.33%] codesize/test_codesize_mem_O3_standalone_narg_flto.json: 7708 => 7737 [+29 bytes / +0.38%] codesize/test_codesize_minimal_64.json: 2760 => 2789 [+29 bytes / +1.05%] test/codesize/test_codesize_minimal_O0.expected.js updated codesize/test_codesize_minimal_O0.json: 20271 => 20301 [+30 bytes / +0.15%] codesize/test_codesize_minimal_O1.json: 3574 => 3604 [+30 bytes / +0.84%] codesize/test_codesize_minimal_O2.json: 2700 => 2729 [+29 bytes / +1.07%] codesize/test_codesize_minimal_O3.json: 2395 => 2424 [+29 bytes / +1.21%] codesize/test_codesize_minimal_Os.json: 2395 => 2424 [+29 bytes / +1.21%] codesize/test_codesize_minimal_Oz-ctors.json: 2366 => 2395 [+29 bytes / +1.23%] codesize/test_codesize_minimal_Oz.json: 2395 => 2424 [+29 bytes / +1.21%] codesize/test_codesize_minimal_esm.json: 2526 => 2549 [+23 bytes / +0.91%] codesize/test_codesize_minimal_pthreads.json: 26369 => 26398 [+29 bytes / +0.11%] codesize/test_codesize_minimal_pthreads_memgrowth.json: 26828 => 26857 [+29 bytes / +0.11%] codesize/test_codesize_minimal_wasmfs.json: 2395 => 2424 [+29 bytes / +1.21%] codesize/test_unoptimized_code_size.json: 172757 => 172775 [+18 bytes / +0.01%] Average change: +0.43% (+0.00% - +1.23%) ```
|
I switched it from using openAsBlob to createReadStream (converted to a web stream) since Node.js seemed to handle backpressure strangely with blob.stream() (see node_trace.1.stream-blob.log, node_trace.1.stream-web.log). I think this also makes more sense since openAsBlob is a slightly newer API (Node v19.8.0) compared to createReadStream (Node v0.1.31) and Readable.toWeb (Node v17.0.0). Unfortunately, it looks like there is no easy fix to the Bun issue. I'm going to file a bug report and see if a fix is possible. |


Since Node.js is on v8, which optimizes
instantiateStreamingsignificantly due to caching,123 async instantiation should be preferred. While Node.js doesn't support file URIs, a file system read stream can still be used as an input forinstantiateStreaming, so as long as the MIME type is set correctly (this same technique is used incosInstantiateStream).Unidici does support using a Node.js read stream in the Response constructor (see here). I believe this means this should be valid in all Node.js environments. If there somehow is an issue (maybe in an edge environment where some Node polyfill exists, but the native web Response is still used...?), this could be fixed either by:
Readable.toWeb()fromnode:streamfs.openAsBlob()instead (might have some weird issues on older Node.js versions since blobs used to be stored in memory in Node File-based Blob implementation nodejs/node#45188)Let me know what you think.
Footnotes
https://v8.dev/blog/wasm-code-caching ↩
https://web.dev/articles/loading-wasm ↩
MDN warns "This method is not the most efficient way of fetching and instantiating Wasm modules. If at all possible, you should use the newer WebAssembly.instantiateStreaming() method instead": https://developer.mozilla.org/en-US/docs/WebAssembly/Reference/JavaScript_interface/instantiate_static ↩