LLamaSharp icon indicating copy to clipboard operation
LLamaSharp copied to clipboard

feat: support dynamic native library loading in .NET standard 2.0.

Open AsakusaRinne opened this issue 9 months ago • 8 comments

This PR should only be merged after #688.

It supports dynamic native library loading but needs more test.

Checks

  • [x] Windows .NET framework 4.8 console app
  • [ ] Windows Mono (Unity)
  • [x] Linux .NET standard console app
  • [ ] Linux Mono (Unity)
  • [ ] MAC

TODO (not necessary in this PR)

  • Update the documentation.
  • Add unit test targeting .NET standard2.0.

I'll appreciate it if you'd like to help to test it!

The way to test it:

  1. Clone the repo and checkout to this PR.
  2. modify the LLamaSharp.csproj, replacing the target frameworks with <TargetFramework>netstandard2.0</TargetFramework>.
  3. Run the NetAppTest project.

If you want to test on other runtimes such as .NET framework and Mono, please replace the target framework yourself.


P.S. The NetAppTest project will be removed before it get merged.

AsakusaRinne avatar May 13 '24 23:05 AsakusaRinne

Using this PR, I was able to switch between AVX2 and CUDA in an Unity game. 👍

m0nsky avatar May 19 '24 11:05 m0nsky

@m0nsky Thank you for your feedback. In which system did you test it? I've only tested this feature on Windows11 and Ubuntu22.04 yet. I'll appreciate it if you could help to test on other platforms. :)

AsakusaRinne avatar May 19 '24 14:05 AsakusaRinne

My tests were done on Windows 10. Unfortunately I don't have any other platforms to test on.

m0nsky avatar May 19 '24 15:05 m0nsky

Thank you. That's enough. At least now I know it works properly on windows 10. :)

AsakusaRinne avatar May 19 '24 15:05 AsakusaRinne

My main concern with this PR is the massive amount of extra complexity involved in importing functions. If every single function definition goes from:

static extern LLamaContextParams llama_context_default_params();

To something like:

#if NETSTANDARD
        [DllImport(NativeApi.libraryName, CallingConvention = CallingConvention.Cdecl)]
        static extern LLamaContextParams llama_context_default_params_r();
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        delegate LLamaContextParams llama_context_default_params_t();
        static LLamaContextParams llama_context_default_params() => NativeLibraryConfig.DynamicLoadingDisabled ?
            llama_context_default_params_r() : NativeApi.GetLLamaExport<llama_context_default_params_t>("llama_context_default_params")();
#else
        [DllImport(NativeApi.libraryName, CallingConvention = CallingConvention.Cdecl)]
        static extern LLamaContextParams llama_context_default_params();
#endif

That's going to make upgrading the binaries a huge amount of work (more than I want to do every month, personally).

I think if we want to go this path we need to find a way to reduce the boilerplate involved, or to autogenerate it all from the C header files (or some other config file).

martindevans avatar May 23 '24 02:05 martindevans

Code generation is a good way to handle with it but the source generator is not supported in .NET standard2.0. If we want to do so, maybe we need to write a script to manually run it every time we're going to update the binaries. In fact most of the code transforms in this PR are completed by copilot. :)

AsakusaRinne avatar May 23 '24 05:05 AsakusaRinne

We could use t4 templates, they're supported in every version of dotnet (since they're a stage that runs before the compiler, not as part of it).

I've used them extensively in another project of mine, here's an example of one alongside the code it generates.

  • https://github.com/martindevans/Myriad.ECS/blob/master/Myriad.ECS/Queries/CachedQueries.tt
  • https://github.com/martindevans/Myriad.ECS/blob/master/Myriad.ECS/Queries/CachedQueries.cs

We could write one t4 file which contains all of the generation code, and then import that into another which just acts as a "config file" - i.e. that's what gets hand edited every time a binary update is done, specifying the function signatures that need to be generated.

martindevans avatar May 23 '24 13:05 martindevans

After some investigation I found I was wrong. The source generator supports .NET standard2.0. t4 template is also a good option. I'll try to use them to generate the code.

Besides, I will put the code of NativeLibrary.NetStandard inside our repo instead in this PR. The code of that repo is of small size and not likely to increase in the future. It will reduce the dependency number of LLamaSharp. What's more, though it might be out-of-scope, I wonder if we should remove the dependency for Microsoft.Logging in the future because we have had a self-defined log callback.

AsakusaRinne avatar May 24 '24 17:05 AsakusaRinne

@AsakusaRinne I think there is an even better use case for this now, since Vulkan support was introduced to LLamaSharp in 0.14 (July 16).

Is there any chance you could update this PR to be compatible with master? This will allow for deploying Unity games on both AMD & NVIDIA GPUs until a better solution is ready.

m0nsky avatar Oct 15 '24 15:10 m0nsky