From aa17f85d91c9e79b874a07ca917fa756d56b1a59 Mon Sep 17 00:00:00 2001 From: Turnerj Date: Tue, 24 Dec 2019 23:31:35 +1030 Subject: [PATCH 1/3] Update benchmark to use .NET Core 3.1 --- Benchmarks/Schema.NET.Benchmarks/SchemaBenchmarkBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Benchmarks/Schema.NET.Benchmarks/SchemaBenchmarkBase.cs b/Benchmarks/Schema.NET.Benchmarks/SchemaBenchmarkBase.cs index ffb36788..2f8ef5a7 100644 --- a/Benchmarks/Schema.NET.Benchmarks/SchemaBenchmarkBase.cs +++ b/Benchmarks/Schema.NET.Benchmarks/SchemaBenchmarkBase.cs @@ -13,7 +13,7 @@ namespace Schema.NET.Benchmarks [CsvMeasurementsExporter] [RPlotExporter] [SimpleJob(RuntimeMoniker.Net472)] - [SimpleJob(RuntimeMoniker.NetCoreApp30)] + [SimpleJob(RuntimeMoniker.NetCoreApp31)] public abstract class SchemaBenchmarkBase { protected Thing Thing { get; set; } From 1e184c24d90952a7d311a884e3835dcc9e5fbae1 Mon Sep 17 00:00:00 2001 From: Turnerj Date: Tue, 24 Dec 2019 23:57:07 +1030 Subject: [PATCH 2/3] Use cached constructor delegate --- Source/Schema.NET/ValuesJsonConverter.cs | 37 +++++++++++++++++++++--- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/Source/Schema.NET/ValuesJsonConverter.cs b/Source/Schema.NET/ValuesJsonConverter.cs index b11f4934..46052edc 100644 --- a/Source/Schema.NET/ValuesJsonConverter.cs +++ b/Source/Schema.NET/ValuesJsonConverter.cs @@ -1,11 +1,11 @@ namespace Schema.NET { using System; - using System.Collections; + using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; - using System.Linq; + using System.Linq.Expressions; using System.Reflection; using System.Xml; using Newtonsoft.Json; @@ -19,6 +19,8 @@ public class ValuesJsonConverter : JsonConverter { private static readonly TypeInfo ThingInterfaceTypeInfo = typeof(IThing).GetTypeInfo(); private static readonly Dictionary BuiltInThingTypeLookup = new Dictionary(StringComparer.Ordinal); + private static readonly ConcurrentDictionary, object>> TypeInstantiationMap = new ConcurrentDictionary, object>>(); + private static readonly ParameterExpression[] ConstructorParameterExpression = new[] { Expression.Parameter(typeof(IEnumerable)) }; static ValuesJsonConverter() { @@ -71,6 +73,8 @@ static ValuesJsonConverter() throw new ArgumentNullException(nameof(serializer)); } + var dynamicConstructor = GetDynamicConstructor(objectType); + if (reader.TokenType == JsonToken.StartArray) { var items = new List(); @@ -91,12 +95,12 @@ static ValuesJsonConverter() items.Add(item); } - return Activator.CreateInstance(objectType, items); + return dynamicConstructor(items); } else if (reader.TokenType != JsonToken.Null) { var item = ProcessToken(reader, objectType.GenericTypeArguments, serializer); - return Activator.CreateInstance(objectType, (IEnumerable)new[] { item }); + return dynamicConstructor(new[] { item }); } return default; @@ -169,6 +173,31 @@ public virtual void WriteObject(JsonWriter writer, object value, JsonSerializer serializer.Serialize(writer, value); } + private static Func, object> GetDynamicConstructor(Type objectType) + { + if (!TypeInstantiationMap.TryGetValue(objectType, out var dynamicConstructor)) + { + foreach (var constructor in objectType.GetTypeInfo().DeclaredConstructors) + { + var paramters = constructor.GetParameters(); + if (constructor.IsPublic && paramters.Length == 1 && paramters[0].ParameterType == typeof(IEnumerable)) + { + var newExpression = Expression.Lambda( + typeof(Func, object>), + Expression.Convert(Expression.New(constructor, ConstructorParameterExpression), typeof(object)), + ConstructorParameterExpression); + + var constructorDelegate = newExpression.Compile(); + dynamicConstructor = (Func, object>)constructorDelegate; + TypeInstantiationMap.TryAdd(objectType, dynamicConstructor); + break; + } + } + } + + return dynamicConstructor; + } + private static object ProcessToken(JsonReader reader, Type[] targetTypes, JsonSerializer serializer) { if (reader.TokenType == JsonToken.StartObject) From 6ec3ccbb66de31a510f6d2baba3b9433b117cb8e Mon Sep 17 00:00:00 2001 From: Turnerj Date: Tue, 31 Dec 2019 14:18:36 +1030 Subject: [PATCH 3/3] Moved faster object activation to separate class --- Source/Schema.NET/FastActivator.cs | 61 ++++++++++++++++++++++++ Source/Schema.NET/Schema.NET.csproj | 4 ++ Source/Schema.NET/ValuesJsonConverter.cs | 29 +---------- 3 files changed, 66 insertions(+), 28 deletions(-) create mode 100644 Source/Schema.NET/FastActivator.cs diff --git a/Source/Schema.NET/FastActivator.cs b/Source/Schema.NET/FastActivator.cs new file mode 100644 index 00000000..e969a408 --- /dev/null +++ b/Source/Schema.NET/FastActivator.cs @@ -0,0 +1,61 @@ +namespace Schema.NET +{ + using System; + using System.Collections.Concurrent; + using System.Collections.Generic; + using System.Linq.Expressions; + using System.Reflection; + using System.Text; + + /// + /// A faster version of by providing constructor delegates. + /// + internal static class FastActivator + { + private static readonly ConcurrentDictionary<(Type, Type), Delegate> ConstructorDelegateLookup = new ConcurrentDictionary<(Type, Type), Delegate>(); + + /// + /// Creates a constructor delegate for the specified type. + /// + /// Type of first argument for constructor. + /// The object to find the constructor. + /// The constructor delegate. + public static Func GetDynamicConstructor(Type objectType) + { + var constructorKey = (objectType, typeof(T1)); + if (!ConstructorDelegateLookup.TryGetValue(constructorKey, out var constructorDelegate)) + { + var constructor = GetConstructorInfo(objectType, typeof(T1)); + constructorDelegate = CreateConstructorDelegate(constructor); + ConstructorDelegateLookup.TryAdd(constructorKey, constructorDelegate); + } + + return constructorDelegate as Func; + } + + private static Func CreateConstructorDelegate(ConstructorInfo constructor) => Expression.Lambda>( + Expression.Convert( + Expression.New(constructor, ConstructorParameter.SingleParameter), + typeof(object)), + ConstructorParameter.SingleParameter).Compile(); + + private static ConstructorInfo GetConstructorInfo(Type objectType, Type parameter1) + { + foreach (var constructor in objectType.GetTypeInfo().DeclaredConstructors) + { + var parameters = constructor.GetParameters(); + if (constructor.IsPublic && parameters.Length == 1 && parameters[0].ParameterType == parameter1) + { + return constructor; + } + } + + return null; + } + + private static class ConstructorParameter + { + public static readonly ParameterExpression[] SingleParameter = new[] { Expression.Parameter(typeof(T1)) }; + } + } +} diff --git a/Source/Schema.NET/Schema.NET.csproj b/Source/Schema.NET/Schema.NET.csproj index 42108cb0..47b818da 100644 --- a/Source/Schema.NET/Schema.NET.csproj +++ b/Source/Schema.NET/Schema.NET.csproj @@ -45,6 +45,10 @@ + + + + diff --git a/Source/Schema.NET/ValuesJsonConverter.cs b/Source/Schema.NET/ValuesJsonConverter.cs index 46052edc..83899677 100644 --- a/Source/Schema.NET/ValuesJsonConverter.cs +++ b/Source/Schema.NET/ValuesJsonConverter.cs @@ -19,8 +19,6 @@ public class ValuesJsonConverter : JsonConverter { private static readonly TypeInfo ThingInterfaceTypeInfo = typeof(IThing).GetTypeInfo(); private static readonly Dictionary BuiltInThingTypeLookup = new Dictionary(StringComparer.Ordinal); - private static readonly ConcurrentDictionary, object>> TypeInstantiationMap = new ConcurrentDictionary, object>>(); - private static readonly ParameterExpression[] ConstructorParameterExpression = new[] { Expression.Parameter(typeof(IEnumerable)) }; static ValuesJsonConverter() { @@ -73,7 +71,7 @@ static ValuesJsonConverter() throw new ArgumentNullException(nameof(serializer)); } - var dynamicConstructor = GetDynamicConstructor(objectType); + var dynamicConstructor = FastActivator.GetDynamicConstructor>(objectType); if (reader.TokenType == JsonToken.StartArray) { @@ -173,31 +171,6 @@ public virtual void WriteObject(JsonWriter writer, object value, JsonSerializer serializer.Serialize(writer, value); } - private static Func, object> GetDynamicConstructor(Type objectType) - { - if (!TypeInstantiationMap.TryGetValue(objectType, out var dynamicConstructor)) - { - foreach (var constructor in objectType.GetTypeInfo().DeclaredConstructors) - { - var paramters = constructor.GetParameters(); - if (constructor.IsPublic && paramters.Length == 1 && paramters[0].ParameterType == typeof(IEnumerable)) - { - var newExpression = Expression.Lambda( - typeof(Func, object>), - Expression.Convert(Expression.New(constructor, ConstructorParameterExpression), typeof(object)), - ConstructorParameterExpression); - - var constructorDelegate = newExpression.Compile(); - dynamicConstructor = (Func, object>)constructorDelegate; - TypeInstantiationMap.TryAdd(objectType, dynamicConstructor); - break; - } - } - } - - return dynamicConstructor; - } - private static object ProcessToken(JsonReader reader, Type[] targetTypes, JsonSerializer serializer) { if (reader.TokenType == JsonToken.StartObject)