Cysharp / MemoryPack

Zero encoding extreme performance binary serializer for C# and Unity.
MIT License
3.29k stars 193 forks source link

Deserialize Exception 'T is failed in provider at creating formatter.' at NativeAOT #189

Closed dameng324 closed 7 months ago

dameng324 commented 1 year ago

I found the main issue is The T's static constructor does not Invoke when Deserialize<T> when NativeAOT. so there is no formatter registered. If I add var r = new MemPackTestObj() { Strings = new[] { "a", "b", "c" } }; before Deserialize, It works.

Code:

using MemoryPack;
using System.Runtime.InteropServices;

Console.WriteLine($"{RuntimeInformation.FrameworkDescription}");

//var r = new MemPackTestObj() { Strings = new[] { "a", "b", "c" } };
//var bytes = MemoryPackSerializer.Serialize(r);

var bytes = Convert.FromBase64String("AwMAAAD+////AQAAAGH+////AQAAAGL+////AQAAAGMAAAAAAAAAAP////8=");
Console.WriteLine(Convert.ToBase64String(bytes));
var r2 = MemoryPackSerializer.Deserialize<MemPackTestObj>(bytes);
foreach (var s in r2!.Strings)
{
    Console.WriteLine(s);
}

[MemoryPackable]
public partial record MemPackTestObj
{
    public string[] Strings { get; set; }
    public DateTime Date { get; set; }
    public string Name { get; set; }
}

csproj

<Project Sdk="Microsoft.NET.Sdk">

    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net8.0</TargetFramework>
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>enable</Nullable>
        <IsPackable>false</IsPackable>

        <!-- AOT Options -->
        <!-- https://learn.microsoft.com/ja-jp/dotnet/core/deploying/native-aot/ -->
        <PublishAot>true</PublishAot>
        <SelfContained>true</SelfContained> 
        <IlcOptimizationPreference>Speed</IlcOptimizationPreference>
        <IlcOptimizationPreference>Size</IlcOptimizationPreference>
        <IlcFoldIdenticalMethodBodies>true</IlcFoldIdenticalMethodBodies>
    </PropertyGroup>

    <ItemGroup>
        <PackageReference Include="MemoryPack" Version="1.9.16" />
    </ItemGroup>
</Project>

When dotnet run -c Release It works as expected.

.NET 8.0.0-rc.2.23479.6
AwMAAAD+////AQAAAGH+////AQAAAGL+////AQAAAGMAAAAAAAAAAP////8=
a
b
c

When dotnet publish -c Release and .\bin\Release\net8.0\win-x64\publish\NativeAot.exe

.NET 8.0.0-rc.2.23479.6
AwMAAAD+////AQAAAGH+////AQAAAGL+////AQAAAGMAAAAAAAAAAP////8=
Unhandled Exception: MemoryPack.MemoryPackSerializationException: MemPackTestObj is failed in provider at creating formatter.
 ---> System.InvalidOperationException: Type implements IMemoryPackFormatterRegister but can not found RegisterFormatter. Type: MemPackTestObj
   at MemoryPack.MemoryPackFormatterProvider.TryInvokeRegisterFormatter(Type) + 0xe9
   at MemoryPack.MemoryPackFormatterProvider.Cache`1..cctor() + 0x51
   --- End of inner exception stack trace ---
   at MemoryPack.MemoryPackSerializationException.ThrowRegisterInProviderFailed(Type, Exception) + 0x4a
   at MemoryPack.ErrorMemoryPackFormatter`1.Throw() + 0x2f
   at MemoryPack.MemoryPackSerializer.Deserialize[T](ReadOnlySpan`1, T&, MemoryPackSerializerOptions) + 0xf0
   at Program.<Main>$(String[] args) + 0x8e
   at NativeAot!<BaseAddress>+0x172ee0

Is this a NativeAOT runtime bug? any solution to solve this?

dameng324 commented 1 year ago

https://github.com/Cysharp/MemoryPack/blob/9aa51ce1e8da24e27053bf2448c127c4263c2096/src/MemoryPack.Core/MemoryPackFormatterProvider.cs#L177-L197

Maybe the main point is here. dotnet -c Release has reflection metadata. but NativeAOT has not.

static method is not invoked at dotnet run -c Release, the TryInvokeRegisterFormatter method invoked.

It looks like a MemoryPack issue not NativeAOT runtime bug.

hadashiA commented 7 months ago

Hello. Thanks for the report.

It seems that if there is no Serialize in the source code and only Deserialize, the AOT build will trim the required code.

In #237, As a quick workaround, we have addressed this issue by adding [DynamicallyAccessedMembers].

It is difficult to completely remove reflection from a MemoryPack implementation. Therefore, we are currently unable to even turn off all the AOTwarnings that the compiler produces.

But for the time being, adding attribute seems to work in most cases.

Next release should be coming soon. Please let us know if there are any more problems. Thanks again.

dameng324 commented 7 months ago

Thanks for your work. It looks useful in the case.

I wonder Deserialize<ICollection<MemPackTestObj>> will work or not. I'll try until have some time to do it.

dameng324 commented 7 months ago

I wonder Deserialize<ICollection> will work or not. I'll try until have some time to do it.

I have tried and it works. Looks very nice.