lerouxrgd / ngt-rs

Rust wrappers for NGT approximate nearest neighbor search
Apache License 2.0
36 stars 6 forks source link

Statically link ngt? #7

Closed cjrh closed 2 years ago

cjrh commented 2 years ago

The idea

Looking in /lib after a build, I see libngt.a:

$ fd --hidden --no-ignore --glob libngt*
...
target/debug/build/ngt-sys-a9e71d1b7f68537e/out/lib/libngt.a
target/debug/build/ngt-sys-a9e71d1b7f68537e/out/lib/libngt.so
target/debug/build/ngt-sys-a9e71d1b7f68537e/out/lib/libngt.so.1
target/debug/build/ngt-sys-a9e71d1b7f68537e/out/lib/libngt.so.1.14.7

So I was wondering whether it would be possible to link ngt statically. This would remove the need to have to put libngt.so in a place where executables can find it.

Naively, I changed a line in build.rs from this:

    println!("cargo:rustc-link-lib=dylib=ngt");

to this:

    println!("cargo:rustc-link-lib=static=ngt");

This fails

After making the change to build.rs, this error occurs when running cargo build:

   <snip>
   Compiling proc-macro-crate v1.2.1
   Compiling num_enum_derive v0.5.7
   Compiling ngt-sys v1.14.8 (/home/caleb/tmp/ngt-rs/ngt-sys)
   Compiling num_enum v0.5.7
   Compiling ngt v0.4.4 (/home/caleb/tmp/ngt-rs)
error[E0425]: cannot find function `ngt_get_number_of_objects` in crate `sys`
   --> src/index.rs:326:23
    |
326 |         unsafe { sys::ngt_get_number_of_objects(self.index, self.ebuf) }
    |                       ^^^^^^^^^^^^^^^^^^^^^^^^^ not found in `sys`

error[E0425]: cannot find function `ngt_get_number_of_indexed_objects` in crate `sys`
   --> src/index.rs:331:23
    |
331 |         unsafe { sys::ngt_get_number_of_indexed_objects(self.index, self.ebuf) }
    |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not found in `sys`

For more information about this error, try `rustc --explain E0425`.
error: could not compile `ngt` due to 2 previous errors
warning: build failed, waiting for other jobs to finish...

Further investigation

Checking the file sizes of the ngt build artifacts:

$ ls -lah target/debug/build/ngt-sys-a9e71d1b7f68537e/out/lib/
Permissions Size User  Date Modified Name
.rw-r--r--   56M caleb  4 Sep 19:17  libngt.a
.rw-r--r--   22M caleb  4 Sep 19:18  libngt.so.1.14.7
lrwxrwxrwx    16 caleb  4 Sep 19:18  libngt.so.1 -> libngt.so.1.14.7
lrwxrwxrwx    11 caleb  4 Sep 19:18  libngt.so -> libngt.so.1

We see libngt.a is ~ 56 M. Checking for the rlib libraries produced by rust:

$ ll target/debug/deps/ | rg ngt
.rw-rw-r--   720 caleb  4 Sep 19:18  ngt_sys-88de9e97cd094267.d
.rw-rw-r--  206k caleb  4 Sep 19:18  libngt_sys-88de9e97cd094267.rmeta
.rw-rw-r--   485 caleb  4 Sep 19:18  ngt-1421cbd3da3a38b1.d
.rw-rw-r--   57M caleb  4 Sep 19:18  libngt_sys-88de9e97cd094267.rlib

We see that libngt_sys-88de9e97cd094267.rlib size is around ~ 57 MB, suggesting that libngt.a has been linked into it?

That's as far as I can go for now, but hopefully we can figure out a way to link ngt statically?

lerouxrgd commented 2 years ago

I think the error is because you didn't run git submodule update --init in your local clone ?

As for the statically linking, I think it wasn't possible before, I'll have a look at it again.

lerouxrgd commented 2 years ago

The tricky dep for statically linking is openmp if I remember correctly.

cjrh commented 2 years ago

didn't run git submodule update --init in your local clone ?

Well spotted, I forgot to update after the new release :)

cjrh commented 2 years ago

So when I was playing with static on the previous release, what I found was that everything "builds" fine (cargo build), but cargo test would fail with a bunch of missing symbols.

I updated the submodule and I'm building again on the new release...

cjrh commented 2 years ago

Yup cargo build succeeds but cargo test fails:

   Compiling ngt v0.4.4 (/home/caleb/tmp/ngt-rs)
error: linking with `cc` failed: exit status: 1

<snip, lots of this:>

          /bin/ld: /usr/include/c++/11/ext/new_allocator.h:145: undefined reference to `operator delete(void*)'
          /bin/ld: /home/caleb/tmp/ngt-rs/target/debug/deps/libngt_sys-88de9e97cd094267.rlib(Capi.cpp.o): in function `NGT::SearchContainer::~SearchContainer()':
          /home/caleb/tmp/ngt-rs/ngt-sys/NGT/lib/NGT/Common.h:2035: undefined reference to `operator delete(void*)'
          /bin/ld: /home/caleb/tmp/ngt-rs/target/debug/deps/libngt_sys-88de9e97cd094267.rlib(Capi.cpp.o): in function `__gnu_cxx::new_allocator<char>::deallocate(char*, unsigned long)':
          /usr/include/c++/11/ext/new_allocator.h:145: undefined reference to `operator delete(void*)'
          /bin/ld: /home/caleb/tmp/ngt-rs/target/debug/deps/libngt_sys-88de9e97cd094267.rlib(Capi.cpp.o): in function `NGT::Exception::~Exception()':
          /home/caleb/tmp/ngt-rs/ngt-sys/NGT/lib/NGT/Common.h:69: undefined reference to `std::exception::~exception()'
          /bin/ld: /home/caleb/tmp/ngt-rs/target/debug/deps/libngt_sys-88de9e97cd094267.rlib(Capi.cpp.o): in function `NGT::LeafNode::~LeafNode()':
          /home/caleb/tmp/ngt-rs/ngt-sys/NGT/lib/NGT/Node.h:420: undefined reference to `operator delete[](void*)'
          /bin/ld: /home/caleb/tmp/ngt-rs/target/debug/deps/libngt_sys-88de9e97cd094267.rlib(Capi.cpp.o): in function `NGT::LeafNode::~LeafNode()':

<snip>
lerouxrgd commented 2 years ago

I've implemented static build in 0.4.5, note that it disables OpenMP when building NGT.

lerouxrgd commented 2 years ago

Still, I think using openmp-sys to statically link against host's OpenMP could be interesting too.

cjrh commented 2 years ago

I've implemented static build in 0.4.5, note that it disables OpenMP when building NGT.

Thank you!

cjrh commented 2 years ago

I pulled 0.4.5 (master branch actually) then did cargo build followed by cargo test. The build succeeds but the test fails:

$ cargo test
   Compiling crossbeam-utils v0.8.11
   Compiling rayon-core v1.9.3
   Compiling either v1.8.0
   Compiling remove_dir_all v0.5.3
   Compiling fastrand v1.8.0
   Compiling memoffset v0.6.5
   Compiling crossbeam-epoch v0.9.10
   Compiling rayon v1.5.3
   Compiling num_cpus v1.13.1
   Compiling tempfile v3.3.0
   Compiling crossbeam-channel v0.5.6
   Compiling crossbeam-deque v0.8.2
   Compiling ngt v0.4.5 (/home/caleb/tmp/ngt-rs)
    Finished test [unoptimized + debuginfo] target(s) in 5.73s
     Running unittests src/lib.rs (target/debug/deps/ngt-7334a1c6d8fc0434)

running 7 tests
test optim::tests::test_convert_anng_to_onng ... ignored
test optim::tests::test_optimize_anng ... ignored
test index::tests::test_batch ... ok
test index::tests::test_basics ... ok
test index::tests::test_quantize ... ok
test optim::tests::test_refine_anng ... ok
test index::tests::test_multithreaded ... ok

test result: ok. 5 passed; 0 failed; 2 ignored; 0 measured; 0 filtered out; finished in 0.29s

   Doc-tests ngt

running 2 tests
test src/lib.rs - (line 17) ... FAILED
test src/lib.rs - (line 37) ... FAILED

failures:

---- src/lib.rs - (line 17) stdout ----
Test executable failed (exit status: 127).

stderr:
/tmp/rustdoctest9cK9AQ/rust_out: error while loading shared libraries: libngt.so.1: cannot open shared object file: No such file or directory

---- src/lib.rs - (line 37) stdout ----
Test executable failed (exit status: 127).

stderr:
/tmp/rustdoctest5ep2IC/rust_out: error while loading shared libraries: libngt.so.1: cannot open shared object file: No such file or directory

failures:
    src/lib.rs - (line 17)
    src/lib.rs - (line 37)

test result: FAILED. 0 passed; 2 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.36s

error: test failed, to rerun pass '--doc'

Something is looking for libngt.so.1. I don't have time to look further into this right now but I will try a bit later this evening.

cjrh commented 2 years ago

oh nvm I see I need to add a feature.

cjrh commented 2 years ago

Perfect, this works:

$ cargo test --features static

Thanks again!