Skip to content

Conversation

@NinoFloris
Copy link
Member

@NinoFloris NinoFloris commented Jun 5, 2021

Tadah, as discussed.

Checks off Array reader delegates in #3300 and I also threw in reflectionless nullable for free 😄

I searched the codebase for System.Reflection and System.Linq.Expressions and the only ones left are for composites. Seems unavoidable, though we could expose some apis for users to hand npgsql the required composite code as an alternative to reflection.

@NinoFloris NinoFloris requested a review from roji June 5, 2021 14:07
@NinoFloris NinoFloris requested a review from vonzshik as a code owner June 5, 2021 14:07
@NinoFloris
Copy link
Member Author

NinoFloris commented Jun 5, 2021

Just pushed another commit completely splitting ArrayTypeInfo and ListTypeInfo. We only access ListTypeInfo after any checks on ArrayTypeInfo do not match. This enables array reading for aot without reflection, for example dotnet/aspnetcore#31561.

EDIT: It would be good to actually tests this.

Copy link
Member

@roji roji left a comment

Choose a reason for hiding this comment

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

Here's a partial review... Just came across a MakeGenericType in the new code, is there some reason that's OK?

@NinoFloris
Copy link
Member Author

NinoFloris commented Jun 6, 2021

I tested this against NativeAOT, you can read some of my findings below. As it stands, due to missing ado.net apis and our own dynamic lookup of handlers an illink descriptor file or rd.xml is a necessity.

Testing this immediately prompted me to move some of the classes a bit up in the hiarchy, all the nesting really isn't nice. But for an example of what it would look like after the latest commit see:

<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
<Application>
    <Assembly Name="Npgsql">
        <Type Name="Npgsql.Internal.TypeHandling.NullableHandler`2[[System.Nullable`1[[System.Int64, System.Runtime]], System.Runtime],[System.Int64, System.Runtime]]" Dynamic="Required All" />
        <Type Name="Npgsql.Internal.TypeHandlers.ArrayHandler`2[[System.Nullable`1[[System.Int64, System.Runtime]][], System.Runtime],[System.Nullable`1[[System.Int64, System.Runtime]], System.Runtime]]" Dynamic="Required All" />
    </Assembly>
</Application>
</Directives>

I had hoped we could keep these entries at Activation instead of Dynamic which would reduce size but the MakeGenericType (even though the type that will be made was fully specified) still needed the full type info. I expect things like this to improve over time but it works, and allows for successful execution of a sample like:

using System;
using Npgsql;

await using var conn = new NpgsqlConnection("Server=localhost;User ID=postgres;Password=postgres123;Database=postgres");
await conn.OpenAsync();
await using var reader = await new NpgsqlCommand("SELECT '{1,2,3}'::bigint[]", conn).ExecuteReaderAsync();
while (await reader.ReadAsync())
{
    var value = reader.GetFieldValue<long?[]>(0);
    Console.WriteLine(string.Join(", ", value));
}

Next up I tried to force <TrimMode>link</TrimMode>, today due to how DefaultTypeHandlerFactory resolves internal handlers we lose all the constructors (and probably more) for the handlers so the first step was to fix that. You could specify all of them in the linker xml but in my mind we should think of a better - code first - way to have users communicate their intent w.r.t. the handlers they want. Anyway my weapon of choice to get past this hurdle was :')
Screenshot 2021-06-06 at 02 24 15

so this is an important one to tackle in a nice way.

After doing that link trimming 'just worked and for my RID osx-x64 I got a 100MB executable out (as opposed to ~150MB). Stripping the binary of debug info moved it down to a 'respectable' 44MB and playing with the feature switches https://docs.microsoft.com/en-us/dotnet/core/deploying/trimming-options#trimming-framework-library-features brought another ~5/3MB respectively. Contrast this with a hello world app which takes up around 10MB and 5MB stripped, or even 3.5MB for reflection free and 2MB stripped.

If you're determined you can get Npgsql working with NativeAOT. We're quite far out before reflection free mode is really an option though and regardless of which mode you try we have improvements to make before it feels like it mostly just works.

The changes here are strictly an improvement even though they can't solve the problem of missing apis. It's much easier to opt into a few types you need, there is no unnecessary bloat from Linq.Expression use and the error messages if you run into a type that you missed are ok. (though the linked configurator shows misleading 'dotnet native' type name syntax ... I should probably make a PR to change those links)

An example error:

Unhandled Exception: System.Reflection.MissingMetadataException: 'Npgsql.Internal.TypeHandlers.ArrayHandler<System.Nullable<System.Int64>[],System.Nullable<System.Int64>>' is missing metadata. For more information, please visit http://go.microsoft.com/fwlink/?LinkID=392859

All the warnings for Npgsql from ilc when referenced in a simple aot console app.
/_/npgsql/src/Npgsql/NpgsqlSchema.cs(573): AOT analysis warning IL9700: Npgsql.NpgsqlSchema.GetDataTypes(NpgsqlConnection): Calling 'System.Type.MakeArrayType()' which has `RequiresDynamicCodeAttribute` can break functionality when compiled fully ahead of time. The native code for the array might not be available at runtime.
/_/npgsql/src/Npgsql/NpgsqlSchema.cs(594): AOT analysis warning IL9700: Npgsql.NpgsqlSchema.GetDataTypes(NpgsqlConnection): Calling 'System.Type.MakeGenericType(Type[])' which has `RequiresDynamicCodeAttribute` can break functionality when compiled fully ahead of time. The native code for this instantiation might not be available at runtime.
/_/npgsql/src/Npgsql/Internal/TypeHandling/NullableHandler.cs(23): AOT analysis warning IL9700: Npgsql.Internal.TypeHandling.NullableHandler`1.<get_DerivedInstance>g__CreateInstance|5_0(): Calling 'System.Type.MakeGenericType(Type[])' which has `RequiresDynamicCodeAttribute` can break functionality when compiled fully ahead of time. The native code for this instantiation might not be available at runtime.
/_/npgsql/src/Npgsql/Internal/TypeHandlers/CompositeHandlers/CompositeConstructorHandler.cs(51): AOT analysis warning IL9700: Npgsql.Internal.TypeHandlers.CompositeHandlers.CompositeConstructorHandler`1.Create(PostgresType,ConstructorInfo,CompositeParameterHandler[]): Calling 'System.Type.MakeGenericType(Type[])' which has `RequiresDynamicCodeAttribute` can break functionality when compiled fully ahead of time. The native code for this instantiation might not be available at runtime. 
/_/npgsql/src/Npgsql/Internal/TypeHandlers/CompositeHandlers/CompositeHandler.cs(180): AOT analysis warning IL9700: Npgsql.Internal.TypeHandlers.CompositeHandlers.CompositeHandler`1.CreateConstructorHandler(PostgresCompositeType,ConnectorTypeMapper,INpgsqlNameTranslator): Calling 'System.Type.MakeGenericType(Type[])' which has `RequiresDynamicCodeAttribute` can break functionality when compiled fully ahead of time. The native code for this instantiation might not be available at runtime.
/_/npgsql/src/Npgsql/TypeMapping/TypeMapperBase.cs(86): AOT analysis warning IL9700: Npgsql.TypeMapping.TypeMapperBase.<>c__DisplayClass15_0.<MapComposite>b__0(INpgsqlNameTranslator): Calling 'System.Type.MakeGenericType(Type[])' which has `RequiresDynamicCodeAttribute` can break functionality when compiled fully ahead of time. The native code for this instantiation might not be available at runtime.
/_/npgsql/src/Npgsql/Internal/TypeHandlers/ArrayHandler.cs(225): AOT analysis warning IL9700: Npgsql.Internal.TypeHandlers.ArrayHandler`2.<get_DerivedInstance>g__CreateInstance|5_0(): Calling 'System.Type.MakeGenericType(Type[])' which has `RequiresDynamicCodeAttribute` can break functionality when compiled fully ahead of time. The native code for this instantiation might not be available at runtime.
/_/npgsql/src/Npgsql/Internal/TypeHandlers/ArrayHandler.cs(267): AOT analysis warning IL9700: Npgsql.Internal.TypeHandlers.ListHandler`2.<get_DerivedInstance>g__CreateInstance|5_0(): Calling 'System.Type.MakeGenericType(Type[])' which has `RequiresDynamicCodeAttribute` can break functionality when compiled fully ahead of time. The native code for this instantiation might not be available at runtime.
/_/npgsql/src/Npgsql/Internal/TypeHandlers/ArrayHandler.cs(95): AOT analysis warning IL9700: Npgsql.Internal.TypeHandlers.ArrayHandler.<ReadArray>d__15`1.MoveNext(): Calling 'System.Array.CreateInstance(Type,Int32)' which has `RequiresDynamicCodeAttribute` can break functionality when compiled fully ahead of time. The native code for the array might not be available at runtime.
/_/npgsql/src/Npgsql/Internal/TypeHandlers/ArrayHandler.cs(95): AOT analysis warning IL9700: Npgsql.Internal.TypeHandlers.ArrayHandler.<ReadArray>d__15`1.MoveNext(): Calling 'System.Array.CreateInstance(Type,Int32[])' which has `RequiresDynamicCodeAttribute` can break functionality when compiled fully ahead of time. The native code for the array might not be available at runtime. 
/_/npgsql/src/Npgsql/Internal/TypeHandlers/ArrayHandler.cs(127): AOT analysis warning IL9700: Npgsql.Internal.TypeHandlers.ArrayHandler.<ReadArray>d__15`1.MoveNext(): Calling 'System.Array.CreateInstance(Type,Int32[])' which has `RequiresDynamicCodeAttribute` can break functionality when compiled fully ahead of time. The native code for the array might not be available at runtime.
/_/npgsql/src/Npgsql/Internal/TypeHandlers/CompositeHandlers/CompositeHandler.cs(245): AOT analysis warning IL9700: Npgsql.Internal.TypeHandlers.CompositeHandlers.CompositeHandler`1.<>c__DisplayClass13_0.<CreateMemberHandlers>g__CreateMemberHandler|0(MemberInfo,Type): Calling 'System.Type.MakeGenericType(Type[])' which has `RequiresDynamicCodeAttribute` can break functionality when compiled fully ahead of time. The native code for this instantiation might not be available at runtime.
/_/npgsql/src/Npgsql/Internal/TypeHandlers/ArrayHandler.cs(201): AOT analysis warning IL9700: Npgsql.Internal.TypeHandlers.ArrayHandler.ElementTypeInfo`1..cctor(): Calling 'System.Type.MakeGenericType(Type[])' which has `RequiresDynamicCodeAttribute` can break functionality when compiled fully ahead of time. The native code for this instantiation might not be available at runtime.

@roji
Copy link
Member

roji commented Jun 6, 2021

Thanks @NinoFloris, this is awesome work. We obviously have some refactoring work to do before things work seamlessly/out-of-the-box without any hacks.

FYI Opened #3815 to track the DefaultTypeHandlerFactory stuff and linked from #3300.

@NinoFloris NinoFloris changed the title Remove nullable and array reflection and code generation Reduce nullable and array reflection and code generation Jun 6, 2021
Copy link
Member

@roji roji left a comment

Choose a reason for hiding this comment

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

Really like it. See nits.

@roji roji mentioned this pull request Jun 7, 2021
6 tasks
@NinoFloris NinoFloris force-pushed the enhancement/no-codegen branch from a9b670a to 45647a1 Compare June 10, 2021 14:01
@roji
Copy link
Member

roji commented Jul 31, 2021

@NinoFloris this is still waiting on you right?

@roji
Copy link
Member

roji commented Jun 15, 2022

Any plans on picking this up for 7.0 @NinoFloris? Not that I have lots of time to review/discuss right at this moment, but just wondering.

@roji roji mentioned this pull request Dec 3, 2022
4 tasks
@NinoFloris NinoFloris force-pushed the enhancement/no-codegen branch from 45647a1 to 78568ac Compare February 27, 2023 16:19
@NinoFloris NinoFloris merged commit 83ab433 into npgsql:main Feb 27, 2023
@NinoFloris NinoFloris deleted the enhancement/no-codegen branch February 27, 2023 16:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants