donalffons / opencascade.js

Port of the OpenCascade CAD library to JavaScript and WebAssembly via Emscripten.
https://ocjs.org/
GNU Lesser General Public License v2.1
617 stars 88 forks source link

initOpenCascade very slow with node.js #18

Closed huzaifah0x00 closed 3 years ago

huzaifah0x00 commented 3 years ago

index.mjs

import opencascade from "opencascade.js/dist/opencascade.wasm.js";

new opencascade(
    {
        locateFile(path) {
            console.log(path);
            if (path.endsWith('.wasm')) {
                return "./node_modules/opencascade.js/dist/opencascade.wasm.wasm";
            }
            return path;
        }
    }
).then((openCascade) => {
    // Register the "OpenCascade" WebAssembly Module under the shorthand "oc"
    var oc = openCascade;
    console.log("oc ready")
});

It takes about 5 minutes for the code to print "oc ready"

To reproduce:

mkdir slowinit_bug; cd slowinit_bug
npm init -y
npm install --save opencascade.js

# edit node_modules/opencascade.js/dist/opencascade.wasm.js
# and change the last line 
# from "export default opencascade" to "module.exports = opencascade"
# I don't know much about ES6 but if I don't make this change to opencascade.wasm.js,
# node.js complains about "export" statement being a syntax error.
# (maybe someone can also tell me the correct way to do this )

sed -i  "s/export\sdefault\sopencascade/module\.exports = opencascade/g" node_modules/opencascade.js/dist/opencascade.wasm.js```

node index.mjs

EDIT: Also my CPU usage is 300% until the code prints "oc ready", I don't know anything about WebAssembly but I think this issue is related to it.

But it works perfectly when running in a browser ( opencascade.js-examples )

donalffons commented 3 years ago

Hi there, I also noticed similar issues with a NodeJS application recently. I was using NodeJS 12 initially and encountered initialization times of almost 10 minutes. After trying many methods to improve the startup-time, I eventually tried newer NodeJS versions and the startup time dropped to only (roughly) 12 seconds in NodeJS v15.3.0. I opened a related issue in the Emscripten repo.

Could you try to update your NodeJS (ideally to v15.5.0, released just a few days ago) and report back if this improves your situation?

huzaifah0x00 commented 3 years ago

Hi, sorry for the late response. I tried running it with the latest NodeJS(v15.5.0) and also v15.3.0 but both versions had the same behavior ( It still took about 5 minutes to initialize opencascade )

I tried running opencascade.js-emscripten-dynamic-library that you mentioned in your comment here, and it loads within 20 seconds for me, maybe I can use this since I don't really need all the opencascade modules?

( Only the ones that are enough to read a .step file and provide some Brep properties of the Solid inside ) Though the download size seemed too big ( it took a while to download dependencies through yarn and a lot of RAM (It crashed my 1GB ram VirtualMachine) ) I'm not sure why that is, but I'm planning to use this library inside a Google Cloud Function, so I can't have long dependencies install times.

Anyway, any ideas about why it still takes me a long time to initialize on NodeJS v15.3.0, but it worked for you? or anything else I can try?

donalffons commented 3 years ago

The long instantiation times are an issue, that I am currently investigating (and have been, for a while). When I look at a profile of what happens during startup, most of the time is spent inside Emscripten's __post_instantiate methods (each WASM module has one of those). It seems like there are probably two contributing factors to those long startup times:

  1. The library is just very large
  2. The are a lot of exported symbol (probably 10ths of thousands)

I tried running opencascade.js-emscripten-dynamic-library that you mentioned in your comment here, and it loads within 20 seconds for me, maybe I can use this since I don't really need all the opencascade modules?

Please don't use the modularization branch used in the repo you mentioned :slightly_smiling_face:. In the current master branch (the modularization branch has been merged), the entire OpenCascade.js library has been split up into Modules (the same modules that are defined in OpenCascade). The intent behind using modules is to reduce the total amount of code that needs to be downloaded (when running in a browser) and instantiated. This already works and improves the situation a little bit. However, startup times are still too long and module sizes too big for many applications.

If you want to test your application with the current master branch: New releases are published under the beta tag on npm (i.e. npm install opencascade.js@beta should work). I also just uploaded a new example on how you would use this: opencascadejs-tessellation-test, preview. Please note, that instantiation must use Emscripten's loadDynamicLibrary method. Using the dynamicLibraries array of the Module object does not work due to random errors. The example should give you an idea on how to do this. This example loads the minimal amount of libraries / modules required to load this particular step file, tessellate it and convert it to GLB (which is then used by ThreeJS).

This beta version will most certainly receiving breaking updates in the future.

It crashed my 1GB ram VirtualMachine

In my experience, with the current version of the library you need at least 3-4GB for any meaningful application (involving tessellation of STEP files). I hope this will improve in the future...

donalffons commented 3 years ago

...I'm currently experimenting with different ideas on how to solve the underlying problem (library is too large / number of exports too big). The only solution that I have tested to work successfully, is to create a custom build of the library, which contains (more or less) the exact parts of the library needed by each respective project. Of course, this doesn't make for a great development experience, because you have to mess around with the build system and producing optimized builds can take several hours.

donalffons commented 3 years ago

...it took a while to figure this one out, but I think the current solution solves this issue and achieves load times which are probably reasonably close to what is ideally possible using Emscripten + Embind.

I have re-written large parts of the build system to allow for efficiently building a "full" version of the library without using dynamic modules. One of the main reason for slowness in previous versions, was the use of WASM modules, which are therefore not used any longer.

In addition, you can now choose between using the NPM-published build or creating a custom builds.

The build is currently running. And given that it finished without issues (because there are never any :expressionless:), the new package should be available on NPM within 5 hours or so.

Let me know if you have questions or issues.