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

C#: add experimental NativeAOT-LLVM support #713

Merged
merged 3 commits into from Apr 3, 2024
Merged

Conversation

RReverser
Copy link
Contributor

@RReverser RReverser commented Jan 10, 2024

Description of Changes

Adds alternative experimental mode for building C# modules. For now, it requires user to:

  1. Use Windows.
  2. Manually install Emscripten SDK (https://github.com/emscripten-core/emsdk).
  3. Opt-in to experimental compilation mode via EXPERIMENTAL_WASM_AOT=1 environment variable.

The development experience will be improved in the future, but for now this should be enough to get some more testing.

Initial numbers suggest it's roughly 15-20x faster in simple cases (where module language overhead dominates over DB operations).

I already published those packages as prerelease versions, so, once this PR is merged and STDB can correctly find Wasm file produced by this mode, users should be able to test via

<PackageReference Include="SpacetimeDB.Codegen" Version="0.8.1-naot-experimental" />
 <PackageReference Include="SpacetimeDB.Runtime" Version="0.8.2-naot-experimental" />

in their .csproj projects.

API and ABI breaking changes

If this is an API or ABI breaking change, please apply the
corresponding GitHub label.

Expected complexity level and risk

3 - this is somewhat risky because, in order to add conditional compilation, I had to modify paths that affect regular compilation too. It seems to work correctly, but testing by more people would be appreciated.

How complicated do you think these changes are? Grade on a scale from 1 to 5,
where 1 is a trivial change, and 5 is a deep-reaching and complex change.

This complexity rating applies not only to the complexity apparent in the diff,
but also to its interactions with existing and future code.

If you answered more than a 2, explain what is complex about the PR,
and what other components it interacts with in potentially concerning ways.

@RReverser RReverser marked this pull request as draft January 10, 2024 17:32
@RReverser RReverser marked this pull request as ready for review January 16, 2024 18:57
_console_log_imp((LogLevel){fd == STDERR_FILENO ? /*WARN*/ 1 : /*INFO*/ 2},
CSTR("wasi"), CSTR(__FILE__), __LINE__, iovs[i].buf,
iovs[i].buf_len);
// _console_log_imp((LogLevel){fd == STDERR_FILENO ? /*WARN*/ 1 : /*INFO*/
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to self: uncomment this back and verify it works with NAOT (I remember commenting it out because there were some issues).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(is this a thing that should happen now?)

@bfops bfops added the verify labels Internal, for tracking purposes label Feb 8, 2024
@bfops
Copy link
Contributor

bfops commented Feb 16, 2024

@RReverser I just want to double-check - this change looks backwards-compatible, is that right?

@RReverser
Copy link
Contributor Author

It's meant to be, yes, but it's waiting for review as I'd like someone else to double-check that no existing functionality with existing backend was accidentally broken in the process.

@jdetter jdetter requested a review from bfops March 4, 2024 19:23
@bfops bfops added release-any To be landed in any release window and removed verify labels Internal, for tracking purposes performance labels Mar 4, 2024
Copy link
Contributor

@bfops bfops left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

left a few questions!

I was able to test that I can create & build a C# module on Linux (i.e. a flow without the fancy new stuff).

But I was only able to test up to the point of creating a multipart dotnet.wasm, because I apparently don't have wasi-sdk installed, and that seems to require a big installation rabbit hole of clang etc. as well.

I have not tested under Windows - do you mainly want test coverage on the "old" build path, or the "new" one?

_console_log_imp((LogLevel){fd == STDERR_FILENO ? /*WARN*/ 1 : /*INFO*/ 2},
CSTR("wasi"), CSTR(__FILE__), __LINE__, iovs[i].buf,
iovs[i].buf_len);
// _console_log_imp((LogLevel){fd == STDERR_FILENO ? /*WARN*/ 1 : /*INFO*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(is this a thing that should happen now?)

crates/bindings-csharp/Runtime/RawBindings.cs Show resolved Hide resolved
@RReverser
Copy link
Contributor Author

But I was only able to test up to the point of creating a multipart dotnet.wasm, because I apparently don't have wasi-sdk installed, and that seems to require a big installation rabbit hole of clang etc. as well.

It should download it for you behind the scenes, you don't need to install WASI SDK yourself. Are you saying it didn't?

@bfops
Copy link
Contributor

bfops commented Mar 11, 2024

But I was only able to test up to the point of creating a multipart dotnet.wasm, because I apparently don't have wasi-sdk installed, and that seems to require a big installation rabbit hole of clang etc. as well.

It should download it for you behind the scenes, you don't need to install WASI SDK yourself. Are you saying it didn't?

I only ran under Linux, but yes that seems to be the case. My build outputs were dotnet.wasm and some other files. This link says it's because I don't have the proper WASI SDK stuff?

@bfops bfops self-requested a review March 12, 2024 00:24
@RReverser
Copy link
Contributor Author

No, we definitely download it for Linux too - you can see conditions like IsOSPlatform('Linux')) and, besides, building C# modules works on our website which also runs Linux.

It's possible some distros or architectures will need manual installation and/or building though. You are not on ARM by any chance? (like M1)

and that seems to require a big installation rabbit hole of clang etc. as well.

FWIW unless you are building yourself, this shouldn't be the case, WASI SDK is self-contained and has its own special version of Clang and so on.

@bfops
Copy link
Contributor

bfops commented Mar 14, 2024

No, we definitely download it for Linux too - you can see conditions like IsOSPlatform('Linux')) and, besides, building C# modules works on our website which also runs Linux.

It's possible some distros or architectures will need manual installation and/or building though. You are not on ARM by any chance? (like M1)

and that seems to require a big installation rabbit hole of clang etc. as well.

FWIW unless you are building yourself, this shouldn't be the case, WASI SDK is self-contained and has its own special version of Clang and so on.

I'm on a pretty standard Ubuntu Linux, x86-64, etc.

I do have the wasi-experimental workload installed, if that helps.

Maybe I'm making bad assumptions. Does this look correct/expected? Successful build, dotnet.wasm+[ModuleName].dll?

$ dotnet build StdbModule.csproj 
MSBuild version 17.8.5+b5265ef37 for .NET
  Determining projects to restore...
  Restored /home/lead/work/clockwork/SpacetimeDBPrivate/public/crates/bindings-csharp/Codegen/Codegen.csproj (in 248 ms).
  Restored /mnt/nutera/work/project-spacetime-csharp/StdbModule.csproj (in 273 ms).
  2 of 4 projects are up-to-date for restore.
  Codegen -> /home/lead/work/clockwork/SpacetimeDBPrivate/public/crates/bindings-csharp/Codegen/bin/Debug/netstandard2.0/SpacetimeDB.Codegen.dll
  Runtime -> /home/lead/work/clockwork/SpacetimeDBPrivate/public/crates/bindings-csharp/Runtime/bin/Debug/net8.0/SpacetimeDB.Runtime.dll
  StdbModule -> /mnt/nutera/work/project-spacetime-csharp/bin/Debug/net8.0/wasi-wasm/StdbModule.dll

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:01.85
$ find bin/ obj/ -name '*.wasm'
bin/Debug/net8.0/wasi-wasm/dotnet.wasm
bin/Debug/net8.0/wasi-wasm/AppBundle/dotnet.wasm
$ find bin/Debug/net8.0/wasi-wasm/AppBundle/ | grep -v '/System.*dll$'
bin/Debug/net8.0/wasi-wasm/AppBundle/
bin/Debug/net8.0/wasi-wasm/AppBundle/tmp
bin/Debug/net8.0/wasi-wasm/AppBundle/dotnet.wasm
bin/Debug/net8.0/wasi-wasm/AppBundle/icudt.dat
bin/Debug/net8.0/wasi-wasm/AppBundle/managed
bin/Debug/net8.0/wasi-wasm/AppBundle/managed/netstandard.dll
bin/Debug/net8.0/wasi-wasm/AppBundle/managed/Microsoft.Win32.Primitives.dll
bin/Debug/net8.0/wasi-wasm/AppBundle/managed/Microsoft.CSharp.dll
bin/Debug/net8.0/wasi-wasm/AppBundle/managed/FSharp.Core.dll
bin/Debug/net8.0/wasi-wasm/AppBundle/managed/SpacetimeDB.Runtime.pdb
bin/Debug/net8.0/wasi-wasm/AppBundle/managed/Linq.Expression.Optimizer.dll
bin/Debug/net8.0/wasi-wasm/AppBundle/managed/Microsoft.VisualBasic.Core.dll
bin/Debug/net8.0/wasi-wasm/AppBundle/managed/Microsoft.Win32.Registry.dll
bin/Debug/net8.0/wasi-wasm/AppBundle/managed/Microsoft.VisualBasic.dll
bin/Debug/net8.0/wasi-wasm/AppBundle/managed/WindowsBase.dll
bin/Debug/net8.0/wasi-wasm/AppBundle/managed/mscorlib.dll
bin/Debug/net8.0/wasi-wasm/AppBundle/managed/SpacetimeDB.Codegen.pdb
bin/Debug/net8.0/wasi-wasm/AppBundle/managed/StdbModule.dll
bin/Debug/net8.0/wasi-wasm/AppBundle/managed/StdbModule.pdb
bin/Debug/net8.0/wasi-wasm/AppBundle/managed/SpacetimeDB.Codegen.dll
bin/Debug/net8.0/wasi-wasm/AppBundle/managed/SpacetimeDB.Runtime.dll
bin/Debug/net8.0/wasi-wasm/AppBundle/run-wasmtime.sh
bin/Debug/net8.0/wasi-wasm/AppBundle/runtimeconfig.bin
bin/Debug/net8.0/wasi-wasm/AppBundle/StdbModule.runtimeconfig.json

@RReverser
Copy link
Contributor Author

Does this look correct/expected? Successful build, dotnet.wasm+[ModuleName].dll?

Ah you need to do dotnet publish if you're doing this manually. That's the step thag collect all those libraries into standalone Wasm file.

But, as a user, you shouldn't do this for STDB modules anyway - just use spacetime build / spacetime publish like you would for Rust modules, which wraps dotnet commands under the hood.

@bfops
Copy link
Contributor

bfops commented Mar 18, 2024

Does this look correct/expected? Successful build, dotnet.wasm+[ModuleName].dll?

Ah you need to do dotnet publish if you're doing this manually. That's the step thag collect all those libraries into standalone Wasm file.

But, as a user, you shouldn't do this for STDB modules anyway - just use spacetime build / spacetime publish like you would for Rust modules, which wraps dotnet commands under the hood.

Hm, this is what I'm getting:

$ spacetime publish -c -S -s local
MSBuild version 17.8.5+b5265ef37 for .NET
/home/lead/.nuget/packages/fsharp.core/4.1.18/lib/netstandard1.6/FSharp.Core.dll : warning IL2104: Assembly 'FSharp.Core' produced trim warnings. For more information see https://aka.ms/dotnet-illink/libraries [/mnt/nutera/work/project-spacetime-csharp/StdbModule.csproj]
/home/lead/.nuget/packages/linq.expression.optimizer/1.0.15/lib/netstandard1.6/Linq.Expression.Optimizer.dll : warning IL2104: Assembly 'Linq.Expression.Optimizer' produced trim warnings. For more information see https://aka.ms/dotnet-illink/libraries [/mnt/nutera/work/project-spacetime-csharp/StdbModule.csproj]
Error: Built project successfully but couldn't find the output file.
$ dotnet publish StdbModule.csproj
MSBuild version 17.8.5+b5265ef37 for .NET
  Determining projects to restore...
  Restored /home/lead/work/clockwork/SpacetimeDBPrivate/public/crates/bindings-csharp/Codegen/Codegen.csproj (in 591 ms).
  3 of 4 projects are up-to-date for restore.
  Codegen -> /home/lead/work/clockwork/SpacetimeDBPrivate/public/crates/bindings-csharp/Codegen/bin/Release/netstandard2.0/SpacetimeDB.Codegen.dll
  Runtime -> /home/lead/work/clockwork/SpacetimeDBPrivate/public/crates/bindings-csharp/Runtime/bin/Release/net8.0/SpacetimeDB.Runtime.dll
  StdbModule -> /mnt/nutera/work/project-spacetime-csharp/bin/Release/net8.0/wasi-wasm/StdbModule.dll
  StdbModule -> /mnt/nutera/work/project-spacetime-csharp/bin/Release/net8.0/wasi-wasm/publish/
  Codegen -> /home/lead/work/clockwork/SpacetimeDBPrivate/public/crates/bindings-csharp/Codegen/bin/Release/netstandard2.0/SpacetimeDB.Codegen.dll
  Runtime -> /home/lead/work/clockwork/SpacetimeDBPrivate/public/crates/bindings-csharp/Runtime/bin/Release/net8.0/SpacetimeDB.Runtime.dll
  StdbModule -> /mnt/nutera/work/project-spacetime-csharp/bin/Release/net8.0/wasi-wasm/StdbModule.dll
  StdbModule -> /mnt/nutera/work/project-spacetime-csharp/bin/Release/net8.0/wasi-wasm/publish/
$ find bin/ -name '*.wasm'
bin/Release/net8.0/wasi-wasm/dotnet.wasm
bin/Release/net8.0/wasi-wasm/publish/dotnet.wasm
bin/Release/net8.0/wasi-wasm/AppBundle/dotnet.wasm
$ spacetime version
Path: /home/lead/.cargo/bin/spacetime
Commit: 47cd946cc8370abc29ab5354ddc4d272cb3059c1
spacetimedb tool version 0.8.0; spacetimedb-lib version 0.8.0;

I swear I saw it work at one point last week... I'm sure I've messed something up (this is the same on master spacetime too).

@bfops
Copy link
Contributor

bfops commented Mar 18, 2024

Okay, got it successfully building & publishing without any complaints!

For some reason, it works for me in the module directories under public/modules, but not in directories out side of SpacetimeDB (with the exact same module files). I suspect it's some bad interaction between local project references + symlinks.

Anyway, now it works as expected!

Copy link
Contributor

@bfops bfops left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "old" codepath works identically for me on Linux, as far as I can tell!

(Diff also LGTM, but I have no domain expertise here)

@bfops
Copy link
Contributor

bfops commented Mar 26, 2024

Successfully built with NativeAOT under Windows as well!

@bfops
Copy link
Contributor

bfops commented Apr 2, 2024

Worth calling out that this fails for me if I'm not in an administrator powershell. It fails to locate emscripten properly, so it could easily just be related to my install.
Edit: Nevermind, must be related to something else.

@bfops
Copy link
Contributor

bfops commented Apr 2, 2024

Weird, I don't know what it is , but sometimes it seems to lose track of my emscripten.. no idea why. I do sometimes get it publishing though.

The publish took a very long time (a minute or two, just for the "publishing module" step). I'm not sure if that was because of the NativeAOT stuff, or some other variable.

@bfops
Copy link
Contributor

bfops commented Apr 2, 2024

My StdbModule.wasm seems to be ~11MB.. just for the spacetimedb-quickstart-cs. I don't know how this compares to master, but seems big.

@bfops
Copy link
Contributor

bfops commented Apr 2, 2024

It looks like about 45s for the upload without native AOT (database opened -> module logs start), vs ~150s with NativeAOT!

(This does not include build time!)

@bfops
Copy link
Contributor

bfops commented Apr 2, 2024

NativeAOT does appear faster, by roughly 50%.

PS SpacetimeDBPrivate\public\modules\spacetimedb-quickstart-cs> date; spacetime.exe call -s local nativeaot-module fib 25; date

Tuesday, April 2, 2024 2:34:14 PM
Tuesday, April 2, 2024 2:34:32 PM


PS SpacetimeDBPrivate\public\modules\spacetimedb-quickstart-cs> date; spacetime.exe call -s local no-aot-module fib 25; date

Tuesday, April 2, 2024 2:34:37 PM
Tuesday, April 2, 2024 2:35:09 PM

My fib reducer:

    static Int64 BigFuncInner( Int64 i, int steps )
    {
        Log($"[{steps}] {nameof(BigFuncInner)} : {nameof(i)} = {i}");
        if ( i <= 1 )
        {
            return i;
        }
        return BigFuncInner( i - 2, steps + 1 ) + BigFuncInner( i - 1, steps + 1 );
    }

    [SpacetimeDB.Reducer("fib")]
    public static void BigFunc( DbEventArgs dbEvent, Int64 i )
    {
        Log($"Start {nameof(BigFunc)}({i})");
        var r = BigFuncInner( i, 0 );
        Log($"{nameof(BigFunc)}({i}) returned {r}");
    }

@RReverser
Copy link
Contributor Author

Just in case: are those timings with release version of STDB CLI or a debug one?

@bfops
Copy link
Contributor

bfops commented Apr 3, 2024

Just in case: are those timings with release version of STDB CLI or a debug one?

Well, that's a good call. Much faster with a release build; both uploads complete in a few seconds.

Also a significantly better runtime performance improvement:

PS SpacetimeDBPrivate\public\modules\spacetimedb-quickstart-cs> date; spacetime.exe call -s local no-aot-module fib 28; date

Wednesday, April 3, 2024 10:15:54 AM
Wednesday, April 3, 2024 10:16:48 AM

PS SpacetimeDBPrivate\public\modules\spacetimedb-quickstart-cs> date; spacetime.exe call -s local nativeaot-module fib 28; date

Wednesday, April 3, 2024 10:17:45 AM
Wednesday, April 3, 2024 10:17:50 AM

@RReverser
Copy link
Contributor Author

Well, that's a good call. Much faster with a release build; both uploads complete in a few seconds.

Heh, classic :)

@RReverser RReverser added this pull request to the merge queue Apr 3, 2024
Merged via the queue into master with commit 02d0428 Apr 3, 2024
6 checks passed
@RReverser RReverser deleted the csharp-naot-llvm-4 branch April 3, 2024 20:40
@RReverser
Copy link
Contributor Author

I'm going to submit docs separately.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
release-any To be landed in any release window
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants