Emscripten: NODE_CODE_CACHING with dynamic libraries

Created on 10 Nov 2020  路  6Comments  路  Source: emscripten-core/emscripten

Hi there,

Here is a feature request:

I am working on a NodeJS application, which starts up a relatively large Emscripten / WASM project, which uses several dynamically linked libraries (via the dynamicLibraries entry in the Module object). Currently, startup of the application takes several minutes. To speed things up, I imagine it would be very helpful to use NODE_CODE_CACHING=1. However, this only seems to work with the MAIN_MODULE (I get a *.cache.xxxxx file only for that type of module) and not with any SIDE_MODULE (there are no *.cache.xxxxx files).

help wanted

All 6 comments

Several minutes! Wow. How big are your module?

Are you sure this is the compile phase that is taking so long, or maybe your are hitting some pathological case in the way we link the dynamic module.

Maybe NODE_CODE_CACHING work for side module sounds like a good thing. If you have time then patches to enable that would certainly be welcome.

Several minutes! Wow. How big are your module?

Here is a list of all SIDE_MODULEs that I have built. For the current project, I'm only trying to load a subset of them (17 in total, with a combined size of 73MB).

total 118M
594K TKBinL.wasm
127K TKBinTObj.wasm
201K TKBin.wasm
259K TKBinXCAF.wasm
5.0M TKBool.wasm
3.2M TKBO.wasm
2.1M TKBRep.wasm
1.1M TKCAF.wasm
786K TKCDF.wasm
5.1M TKernel.wasm
1.2M TKFeat.wasm
3.3M TKFillet.wasm
1.3M TKG2d.wasm
2.8M TKG3d.wasm
7.5M TKGeomAlgo.wasm
6.4M TKGeomBase.wasm
1.8M TKHLR.wasm
6.1M TKIGES.wasm
2.5M TKLCAF.wasm
5.8M TKMath.wasm
806K TKMeshVS.wasm
1.3M TKMesh.wasm
1.8M TKOffset.wasm
2.0M TKOpenGl.wasm
553K TKPrim.wasm
423K TKRWMesh.wasm
3.3M TKService.wasm
3.5M TKShHealing.wasm
548K TKStdL.wasm
1.3M TKStd.wasm
3.1M TKSTEP209.wasm
4.8M TKSTEPAttr.wasm
 11M TKSTEPBase.wasm
5.3M TKSTEP.wasm
 93K TKSTL.wasm
574K TKTObj.wasm
3.9M TKTopAlgo.wasm
6.1M TKV3d.wasm
239K TKVCAF.wasm
1.3M TKVRML.wasm
1.7M TKXCAF.wasm
 53K TKXDEIGES.wasm
612K TKXDESTEP.wasm
 741 TKXMesh.wasm
894K TKXmlL.wasm
167K TKXmlTObj.wasm
321K TKXml.wasm
371K TKXmlXCAF.wasm
6.4M TKXSBase.wasm

Link to repo.

Are you sure this is the compile phase that is taking so long, or maybe your are hitting some pathological case in the way we link the dynamic module.

After you mentioned this, I actually wrote two simple test applications (links below), which just start up the emscripten project and then exit. I have two versions of this Emscripten project. A current / stable version, which is creating one large non-dynamic (i.e. no SIDE_MODULEs) Emscripten module and a work-in-progress version, which is creating the dynamic Emscripten modules, shown above. Both libraries are --embind libraries, which expose the OpenCascade C++ API to JavaScript (similar to what AmmoJS is doing with Bullet). The contents of both the dynamic and non-dynamic versions are not exactly identical (the non-dynamic library actually has a bit more bound functions), but are close in terms of total file size of the WASM modules.

With those different library versions, I achieve the following startup-times in NodeJS (v12.9.1 - but I think they are very similar in newer versions):

  1. Non-dynamic: Startup-time is roughly 60s
  2. Dynamic: Startup-time is roughly 450s - 500s.

...my machine has an Intel i7-9750H.

(If you want to test the example projects, remove node_modules and reinstall all dependencies when switching branches. Have a look at the README, since both versions need to be started slightly differently)

Maybe NODE_CODE_CACHING work for side module sounds like a good thing. If you have time then patches to enable that would certainly be welcome.

Do you think this might fix the issue that I'm facing? I will certainly have a look and give it a try :slightly_smiling_face:.

Thanks for looking into this!

That is a lot of code! Are those modules build with -O2 or -Oz? Do they include debug info?

What is your goal with splitting up your program into modules? It seems like 60s is going to be the lower limit on load time. If you load all your modules up front the startup time can only be slower than a monolithic app.

That is a lot of code!

Yeah, it is... I am auto-generating the Embind code by analyzing all C++ header-files using libclang in Python. The embind-code of the entire library currently has more than 150k lines of code...

Are those modules build with -O2 or -Oz? Do they include debug info

The example app is using the release builds of the library. Those are build using -O3 and no debug flags, see here.

EDIT:

What is your goal with splitting up your program into modules?

My goal is to create a (more or less) easy to use port of the OpenCascade CAD library, that can be run from JavaScript. Since the library is quite large, I was hoping that splitting everything up into modules would make it easier for developers to pick only the parts which are required for a project, without having to re-compile the library for each project or use-case.
Regarding this NodeJS issue: I was actually hoping that I could improve the slow startup performance by using dynamic libraries and therefore having less WASM-compilation going on during the startup-phase of the library...

It seems like 60s is going to be the lower limit on load time

Hmm, yeah, that's what I thought... I guess I will take a closer look at NODE_CODE_CACHING=1 - maybe that flag can bring down the startup time a bit further.

...a good starting point for implementing this is in #9948.

In case anyone else has performance issues with loading large dynamic libraries: Try upgrading to one of the latest versions of NodeJS.

Today, I tested the code in Chrome. The startup time was only 25 seconds. After some more research, I tried more different NodeJS versions.

  • NodeJS v14.15.0 starts in around 90 seconds
  • "Current" NodeJS v15.3.0 starts the same code in around 10-12 seconds

12 seconds is absolutely acceptable for my use case. So for the moment, implementing NODE_CODE_CACHING for SIDE_MODULEs is not a big priority any more for me, as the performance seems already to be "good enough" for what I do and also this feature has regressed (been deprecated?) in recent NodeJS versions.

Anyways, thanks a lot @sbc100 (and everyone else from the team) for your help and your great work on Emscripten!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

phraemer picture phraemer  路  3Comments

hcomere picture hcomere  路  3Comments

surma picture surma  路  4Comments

HolgerStrauss picture HolgerStrauss  路  4Comments

nemequ picture nemequ  路  4Comments