dss-extensions / dss_capi

A multi-platform, customized C-API library and alternative implementation of OpenDSS (EPRI Distribution System Simulator) aiming for full COM compatibility. Unofficial/unsupported by EPRI, but extensively tested.
https://dss-extensions.org/
BSD 3-Clause "New" or "Revised" License
33 stars 13 forks source link

Compiling as static library #110

Open felipemarkson opened 2 years ago

felipemarkson commented 2 years ago

Hi Paulo!

Is there a way to compile the API as a static library?

Or, at less, compiling using KLUSolver as a static library?

I've tried to do this, but I'm not familiar with fpc or any related to Pascal, actually.

I'm trying to create a version of this API (without COM compatible needs) in Rust for thread-safe operations, but the dynamic library is causing some memory issues, especially with the API KLUSolver dependency (I've been using your version).

I'm trying to avoid using a mutex. But if the static library is not possible, maybe it is the only solution...

PMeira commented 2 years ago

I'm assuming you're using the 0.12.x branch, right? The 0.10.x branch is not suited for that and should happily segfault.

Pretty sure to get a static library, you just need to adjust the compiler and linker flags (for FPC, the src/*.cfg files), maybe change something in https://github.com/dss-extensions/dss_capi/blob/0.12.x/src/Common/KLUSolve.pas

For the KLUSolveX part, most can be adjusted at https://github.com/dss-extensions/klusolve/blob/master/CMakeLists.txt

Some of the aux build scripts (.sh, .bat) assume dynamic libs too.

Pascal is a dead-end and I rather not complicate the build process any further (also considering freezing some new/untested features from the offcial repo), so I personally won't look into this now. Feel free to send PRs though -- as long as nothing in the usual build breaks, I'm fine with changes.

Note that KLU and KLUSolve(X) are LGPL and OpenDSS is BSD-3. By linking them together, the whole blob is basically LGPL. We'll probably merge KLUSolveX into DSS C-API anyway soon, changing it to LGPL, but it's worth mentioning.

felipemarkson commented 2 years ago

Ok, thanks! I've been testing in 0.10.x branch, but I will test in 0.12.x.

But probably I will use mutex anyway, cause the license can be an issue.

Thanks!

PMeira commented 2 years ago

@felipemarkson Part of the issues was that 0.10.x used the default memory allocator for FPC. In 0.12.x, cmem is used. This one already uses thread sync objs, and it's a bit slower on Linux.

Also in 0.12.x we're fully using KLUSolveX, which rewrites and extends the upstream KLUSolve project, which is moribund.

But probably I will use mutex anyway

I'm still not sure why that would be required, but you might be able to just recompile KLUSolveX by tweaking the CMake file to use a different allocator, or remove some of the current build flags (like using the static runtime). A lot would depend on the rest of your system/libs.

felipemarkson commented 2 years ago

Yeah, building the KLUSolveX as static I think is not the problem.

But I need to investigate how to link this to the API. On my last try, for some reason, the API doesn't recognize the KLUSolveX but compiled with no problem.

felipemarkson commented 2 years ago

Do you prefer that I keep this issue open? If I make it, I can create a PR.

PMeira commented 2 years ago

On my last try, for some reason, the API doesn't recognize the KLUSolveX but compiled with no problem.

You may need to edit KLUSolve.pas and adjust the linker flags (see https://www.freepascal.org/docs-html/user/userap1.html ). For experimenting, maybe using a custom link step like we used to on Linux for 0.10.x may help. See https://github.com/dss-extensions/dss_capi/commit/031a9b203cd49cd2af2781f452fcb17f9d74d386 for the commit that removed it. Basically:

  1. we used -sh to generate a link script instead of letting the compiler run everything by itself
  2. then our custom_link.sh was used to adjust a few things, like adding aliases, and producing the final library

After 1, you could inspect the resulting objs/libs/scripts, and see better what failed and maybe why.

Nowadays, FPC 3.2.2 adds the process PID to the link script file name, but otherwise the approach should work.

Do you prefer that I keep this issue open? If I make it, I can create a PR.

Up to you, feel free to reopen whenever.

felipemarkson commented 2 years ago

I'm reopening this issue due to https://github.com/dss-extensions/klusolve/pull/16. I will create another PR with instructions to build DSS C-API with KLUSolveX as a static library when this PR is approved.

PMeira commented 2 years ago

@felipemarkson I looked into this and my conclusion is that it's not worth the trouble with the Pascal version, FPC does not support generating static libs in the current version anymore, and linking the objects manually is cumbersome. We should have a full C++ port soon and that will be trivial to build statically -- effectively we will merge the klusolvex into this repo and the build process will be nearly the same with CMake, including the static lib option added by your PR.

Also of note, I've ran a bunch of multi-threaded tests this past month, including multiple different memory allocators (currently I recommend jemalloc or mimalloc). Through those tests, I found some minor issues and restructured some of the Pascal code. Notably, it is a good idea to force the Pascal side to create a single thread to initialize the thread local stuff of the memory allocator (this seems required on Linux, on Windows it doesn't seem so). So, running a DSS_Start(0); will now do that if required. After that, you can create new OpenDSS instances with ctx_New() and use the ctx_* functions with different contexts in different threads without issue.

I'm trying to create a version of this API (without COM compatible needs) in Rust for thread-safe operations, but the dynamic library is causing some memory issues, especially with the API KLUSolver dependency (I've been using your version).

I'm trying to avoid using a mutex. But if the static library is not possible, maybe it is the only solution...

If you still find issues, please try to provide some minimal sample code so I could try to check it. Note that using a single OpenDSS instance across threads would not work since it's not thread-safe -- the whole point of creating the new ctx_*/DSSContext API was to sidestep this limitation.

(pinging @keegit since he mentioned Rust too)

felipemarkson commented 2 years ago

I looked into this and my conclusion is that it's not worth the trouble with the Pascal version, FPC does not support generating static libs in the current version anymore, and linking the objects manually is cumbersome.

What version do you refer to? The lasted commits made this impossible? Because I already have to compile it as a static library, I do not have time to create a PR yet.

We should have a full C++ port soon and that will be trivial to build statically -- effectively we will merge the klusolvex into this repo and the build process will be nearly the same with CMake, including the static lib option added by your PR.

When this merge occurs, will you guys change the license? I thought that it's possible to use another solver than klusolvex to avoid the GPL, like Eigen's Sparse LU that uses a more permissive license (MPL V2) which can allow a fully static compilation without the GPL issues.

Maybe, it could be done by creating an interface between the OpenDSS and KLU that other solvers can use to replace KLU. I could implement that, but my knowledge of Pascal don't help me a lot when I tried to change this file src/Common/KLUSolve.pas.

PMeira commented 2 years ago

What version do you refer to? The lasted commits made this impossible? Because I already have to compile it as a static library, I do not have time to create a PR yet.

I mean linking the objects (.o) from DSS C-API (+ the dependencies from FreePascal) + the objects/static lib from KLUSolve into a new static lib (let's say "libdss_capi.a"). That doesn't seem feasible for all platforms, and would require the final binary to call some FPC functions that are called automatically when loading a .dll/.so.

Just linking KLUSolve into DSS C-API is simpler but doesn't add much value right now, but doesn't hurt either. For the official releases, KLUSolve was kept as a separate DLL for a few reasons:

When this merge occurs, will you guys change the license?

Maybe not, but the final DLL will probably be inherently LGPL'd if we link KLU itself statically. If that has performance advantages (think link-time or profile-guided optimization), we will default to that. I think LGPL is fine, the GPL is the one that presents more issues.

On KLUSolve, it's mostly a wrapper to KLU. It becomes unnecessary, since most of the code is trivial things to interface C++ with C, or redundant implementations (dfs used in FindIslands) since Eigen already contains that. The rest of the original KLUSolve was removed in our fork in 2020 or earlier.

KLU itself is very important for good numerical compatibility with the official OpenDSS. We can and will add other solvers, but not remove KLU for the general version for a while. If we can show that other solvers are acceptable, maybe even publish a paper about that to list as a reference, we can finally mark KLU as optional.