How can the "linking build.js (wasm)" time be accellerated

In our WebGL project with 2022.1.x the webgl building time was < 15 minutes.
With 2022.2.x it takes 1 h 20 min. (I tried serveral versions)
“linking build.js (wasm)” is what takes so long now.

How can that be reduced?

The simple, official answer from Unity is that you can’t and should switch to development builds.

More complex answer here; WebGL build "linking build.js (wasm)" takes forever page-2#post-8910037

Good luck.

1 Like

Ah thx.

Switching to “faster build time” as mentioned somewhere in the thread helped.

Mine spent <40s building my project and 17 minutes linking build.js … part of the reason I would never use unitys we can build it for you system because after all you get some free cpu but it then charges you - for what, for time doing what? how do you link a javascript file? its text.

Currently if doing a Release build and Code Optimization is set to “Runtime Speed” or “Disk Size”, Unity will enable LLVM Link-Time Optimizations (LTO) to get the best optimizations possible.
9260664--1295652--upload_2023-8-30_14-17-8.png

However it turns out that these optimizations are very costly in terms of time. In a future update, we are looking to add a control to configure LTO optimizations separately. (i.e. “Runtime Speed” vs “Runtime Speed with LTO”). That way users can do Runtime Speed or Disk Size optimized builds without enabling the slow LLVM LTO pass.

Those new LTO-disabled passes will still not be as fast as the “Shorter Build Time” option. That option does not have LTO enabled. So if build speeds are of biggest concern, it is best recommended to enable “Shorter Build Time” option.

Additionally, in Unity 2023.2 and newer, enabling both WebAssembly.Table and BigInt options will also improve build times.
9260664--1295655--upload_2023-8-30_14-20-25.png

2 Likes

@jukka_j out of interest then are you able to explain what “linking build.js” is doing? even at a general wafty level? cos 17 mins to generate a <8mb file for build.wasm.* or the wholel 71k of build.framework.js (as it does mention js as build.js) my whole thing clocks in <12mb including the html/images how is that a 17 minute build?

“Linking build.js” stage is when the build is out of Unity’s hands, and the final Emscripten compiler is run, which, roughly,

  1. runs the Emscripten front-end to find all necessary inputs to the build (.o, .a, .bc), and configures the needed build flags ( https://emscripten.org/ ),
  2. runs LLVM optimization passes on them ( LLVM’s Analysis and Transform Passes — LLVM 20.0.0git documentation ),
  3. then runs LTO optimizations on the code ( LLVM Link Time Optimization: Design and Implementation — LLVM 20.0.0git documentation ) ,
  4. then runs LLVM WebAssembly lowering backend on the result to generate the .wasm output ( llvm-project/llvm/lib/Target/WebAssembly at main · llvm/llvm-project · GitHub ),
  5. then runs the Binaryen optimizer to perform wasm-specific code optimization passes ( GitHub - WebAssembly/binaryen: Optimizer and compiler/toolchain library for WebAssembly ),
  6. and the needed WebAssembly backwards compatibility passes ( e.g. binaryen/src/passes/GenerateDynCalls.cpp at main · WebAssembly/binaryen · GitHub , and others ),
  7. then Emscripten back-end is run to figure out the needed system libraries to link with (WebAudio, WebGL, input, HTML5 DOM) and other JS libraries to link ( emscripten/tools/system_libs.py at main · emscripten-core/emscripten · GitHub ),
  8. and a cross-language linking between JS and Wasm is performed ( ( https://github.com/emscripten-core/emscripten/blob/main/src/jsifier.js )
  9. a metadce optimization pass that performs dead code elimination across JS+Wasm together is run ( emscripten/tools/acorn-optimizer.js at c47ab8d7f644c6a501a0fc0eeb91f60c5b6682cb · emscripten-core/emscripten · GitHub )
  10. and then the final build.js is written by Emscripten, which Unity takes and mixes with the user’s WebGL Template to generate the final output files

If you are seeing a 17 minute long build, it is probably the phase 3) above that has been identified as the slowest part of the build, and like mentioned above, choosing “Shorter Build Time” can help with that.

If you tick the WebAssembly.Table and BigInt Language Features checkboxes during the build, then stage 6) is avoided.

I do not have any kind of “megabytes/minute” standard to give on these types of processes of what limit would be reasonable. If you would like to dig in deeper, there exists documentation for a toolchain profiler for Emscripten at Profiling the Toolchain — Emscripten 3.1.65-git (dev) documentation that can be used to generate a detailed swimlane of the above processes, and figure out how long each of them take.

4 Likes

We also noticed that it’s the only non-multithreaded stage of the build process. We tried to raise the issue in here ( WebGL build "linking build.js (wasm)" takes forever page-4) with @JustBeanUnity but to no avail.

Might also only happen on AMD CPUs vs Intels but again, hard to submit a bug report on that.

1 Like

We tried to raise the issue in here (…) with @JustBeanUnity but to no avail.

Oh no, totally the opposite. It was @JustBeanUnity in the first place who internally raised this in the web team, which got the team looking into investigating the change in the LTO build times. So big thanks for doing so.

1 Like

Good to hear, thank you.

Do you know if the multithreading aspect (AMD vs Intel, etc.) of this issue has been investigated internally and if there is an issue # we can use to track?

I believe the multithreading aspect (build config/machine X seems to run singlethreaded vs build config/machine Y seems to run parallelized) was a red herring.

The build process consists of several stages like was illustrated above. Some of those stages are parallelized (steps 5,6 above come to mind at least), and others are not.

So it would not give a correct conclusion to look at task manager for a short period of time during the link and reason if the link was parallelized or not, that depends on which exact stage the link process was going on when taking a peek.

In particular, the LLVM LTO procedure (step 3) is not parallelized. So if that was running slow, then it would look like most of the link overall was running singlethreaded.

Overall, independent of the upcoming added option to disable LTO when targeting Runtime Speed and Disk Size, the fastest current build setting that we know of is to

a) do a Development build with WebAssembly.Table and BigInt language features enabled,

or if that is not feasible (maybe some feature requires iterating on Release builds), the second fastest build is to do

b) a Release build with Code Optimization set to “Build Times”, and with WebAssembly.Table and BigInt language features enabled.

The LTO disabled variant of Runtime Speed and Disk Size builds will still be expected run slower than both a) and b) above, since Runtime Speed and Disk Size builds both activate more optimizations than the Build Times setting.

2 Likes