Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Linking (ld) consumes 3GB RAM. #171

Open
aerosayan opened this issue May 12, 2024 · 7 comments
Open

Linking (ld) consumes 3GB RAM. #171

aerosayan opened this issue May 12, 2024 · 7 comments

Comments

@aerosayan
Copy link

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.

  • The linker ld on Linux, consumes 3 GB RAM to bulid this small example code.

  • The bottle executable built is also 270 MB in size.

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
Copy link
Owner

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
Copy link
Author

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
Copy link
Owner

bschwind commented May 13, 2024

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
Copy link
Author

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
Copy link
Author

@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");
 
-    let occt_config = OcctConfig::detect();
+    let occt_config = OcctConfig{include_dir: "/home/ares/x/occt/build/include/opencascade/".into(), library_dir: "/home/ares/x/occt/build/lin64/gcc/lib".into(), is_dynamic: true};
 
     println!("cargo:rustc-link-search=native={}", occt_config.library_dir.to_str().unwrap());
  1. It will not run because rust on Linux doesn't trust shared libraries from random locations on your computer. Thus we have to modify /etc/ld.so.conf to point to the directory that contains your newly built OCCT shared libraries, then run sudo ldconfig. For me, /etc/ld.so.conf looks like this now. [Source for more Information]
$ cat /etc/ld.so.conf        
include /etc/ld.so.conf.d/*.conf

# 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.

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
Copy link
Owner

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
Copy link
Author

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants