bschwind / opencascade-rs

Rust bindings to the OpenCascade CAD Kernel
GNU Lesser General Public License v2.1
102 stars 18 forks source link

Linking (ld) consumes 3GB RAM. #171

Open aerosayan opened 4 months ago

aerosayan commented 4 months ago

Hello,

I'm absolutely amazed by this project! Great work!

I compiled the project successfully, and am currently trying to understand it.

One particular problem I faced was, trying to compile and link the bottle and simple examples.

That doesn't seem right.

Maybe you're using static linking for building the executables (as that's the default) in Rust, and dynamic linking would be better.

I'm not sure which crate causes the issue, but I would be willing to test.

Thanks!

bschwind commented 4 months ago

Hey, thanks for trying out the library!

Yes, right now the resource consumption is absolutely massive. This is because the project will build the entire C++ OpenCascade project, and then statically link it.

You can instead install OpenCascade via your system's package manager and dynamically link to it. Use --no-default-features when invoking cargo to do so. For example:

cargo run --release --no-default-features --example bottle produces a 488K binary on my machine (MacOS).

But even this isn't a great developer experience. My larger plan is to have model code be written in Rust and compiled to WASM, where a viewer app will execute the WASM and build/display the resulting model. This should allow for super quick iteration times, low memory usage for building, and the viewer app can be installed separately so it's not constantly being built and linked.

aerosayan commented 4 months ago

Thanks!

I was able to compile and build successfully, the issue is only during development, where it takes too much time and memory to specifically link against OCCT.

I will try to do what you advised, regarding using a pre-installed OCCT library with --no-default-features.

I see from the code that you've tried to use dynamic linking if possible.

I'm new to rust, so I don't know why my code isn't. I will try to figure this out.

I tried using rust's dylib feature to create the crates as dynamic libraries.

Most of them compile with dylib, but opencascade-sys doesn't. It fails with some errors, which I don't understand right now.

Right now, the statically linked code is unfit for use. It almost crashes my small laptop.

So, I will try to fix this issue.

Apparently other projects like Bevy game engine have also faced a similar issue, and they solved it by using a "facade crate", which re-exports all dependencies, but does so as a dynamic crate using dylib flag.

This "trick" seems promising: https://crates.io/crates/bevy_dylib/ and its [Source Code]

This reddit thread also discusses more about this: https://redd.it/13vkut6

Although I'm not sure if anyone has completely solved this issue, because rust depends a lot on generics and traits, which may misbehave if used as dynamic libraries. However, Bevy seems to show promising results.

bschwind commented 4 months ago

Oh I see, you're using this as a library at the moment. In that case, in your Cargo.toml, specify it like so:

opencascade-rs = { version = "...", default-features = false }

aerosayan commented 4 months ago

Thanks, right now I'm just compiling the base project on its own.

I modified opencascade-sys's build.rs, to directly link to the include/ and lib/ directories.

It was partially successful, but failed, because my installed OCCT version was probably old, and didn't have all of the header files required by the wrapper.hxx.

Just a heads up, the include/ and lib/ directories can be under different directories, and not under DEP_OCCT_ROOT.

On Ubuntu based distros, the include is under: /usr/include/opencascade/ and lib is under: /usr/lib/x86_64-linux-gnu/ : [Source List]

So, this could be an issue for other users in future, if they try to use the default OCCT installation. Thus, it might be better to read the include and lib dirs as separate environment variables, or use "symbolic soft links" to create a custom DEP_OCCT_ROOT that links to include/ and lib/ directories.

Currently I'm trying to build OCCT from source, and link it dynamically. Let's see how it goes.

aerosayan commented 4 months ago

@bschwind Thansk for your help!

I was able to link OCCT dynamically!

Now size of bottle example binary is only 592K, and the linking now happens within few seconds, and doesn't consume too much RAM.

I will note down what I had to do, so in future other users may be able to use this amazing library.

  1. Install OCCT from the system package manager, or build it from source. I'd recommend building it from source, because system repos often have old versions of OCCT, which may not have the header files and libraries required by this library.
  2. Using the DEP_OCCT_ROOT may not work, due to some unforeseen reason. Then, manually patch/modify the crates/opencascade-sys/build.rs to directly point into the include/ and lib/ directories of your OCCT build. This is the path on my computer, and users should replace it with their own. This will allow the code to be compiled, and linked. But it will not run.
    
    diff --git a/crates/opencascade-sys/build.rs b/crates/opencascade-sys/build.rs
    index 3f9b0cf..6892405 100644
    --- a/crates/opencascade-sys/build.rs
    +++ b/crates/opencascade-sys/build.rs
    @@ -1,3 +1,5 @@
    +#![allow(dead_code)]
    +
    /// Minimum compatible version of OpenCASCADE library (major, minor)
    ///
    /// Pre-installed OpenCASCADE library will be checked for compatibility using semver rules.
    @@ -34,7 +36,8 @@ fn main() {
     let is_windows = target.to_lowercase().contains("windows");
     let is_windows_gnu = target.to_lowercase().contains("windows-gnu");

OpenCASCADE library

/home/ares/x/occt/build/lin64/gcc/lib/


This will allow rust to load the library at run time, and you can get your expected result.

```bash
cargo run --release --no-default-features --example bottle
    Finished `release` profile [optimized] target(s) in 0.16s
     Running `target/release/examples/bottle`
Done! Success = true

Thanks!

bschwind commented 4 months ago

Glad you got it working, and thanks for the notes on the steps you took to get there!

I can accept PRs which improve the build situation for more platforms, but I mostly want to invest my time into making the WASM API for it so that this becomes much easier for everyone.

aerosayan commented 4 months ago

Most of build.rs is working correctly.

To provide a failsafe alternative to DEP_OCCT_ROOT, I propose also trying to read two new environment variables : DEP_OCCT_INCLUDE_DIR, and DEP_OCCT_LIBRARY_DIR

These two new environment variables can have a higher priority than DEP_OCCT_ROOT, and create OcctConfig from them.