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

Faster instance creation for ValuesJsonConverter #119

Merged
merged 3 commits into from Dec 31, 2019
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion Benchmarks/Schema.NET.Benchmarks/SchemaBenchmarkBase.cs
Expand Up @@ -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; }
Expand Down
37 changes: 33 additions & 4 deletions 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;
Expand All @@ -19,6 +19,8 @@ public class ValuesJsonConverter : JsonConverter
{
private static readonly TypeInfo ThingInterfaceTypeInfo = typeof(IThing).GetTypeInfo();
private static readonly Dictionary<string, Type> BuiltInThingTypeLookup = new Dictionary<string, Type>(StringComparer.Ordinal);
private static readonly ConcurrentDictionary<Type, Func<IEnumerable<object>, object>> TypeInstantiationMap = new ConcurrentDictionary<Type, Func<IEnumerable<object>, object>>();
private static readonly ParameterExpression[] ConstructorParameterExpression = new[] { Expression.Parameter(typeof(IEnumerable<object>)) };

static ValuesJsonConverter()
{
Expand Down Expand Up @@ -71,6 +73,8 @@ static ValuesJsonConverter()
throw new ArgumentNullException(nameof(serializer));
}

var dynamicConstructor = GetDynamicConstructor(objectType);

if (reader.TokenType == JsonToken.StartArray)
{
var items = new List<object>();
Expand All @@ -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;
Expand Down Expand Up @@ -169,6 +173,31 @@ public virtual void WriteObject(JsonWriter writer, object value, JsonSerializer
serializer.Serialize(writer, value);
}

private static Func<IEnumerable<object>, object> GetDynamicConstructor(Type objectType)
Turnerj marked this conversation as resolved.
Show resolved Hide resolved
{
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<object>))
{
var newExpression = Expression.Lambda(
typeof(Func<IEnumerable<object>, object>),
Expression.Convert(Expression.New(constructor, ConstructorParameterExpression), typeof(object)),
ConstructorParameterExpression);

var constructorDelegate = newExpression.Compile();
dynamicConstructor = (Func<IEnumerable<object>, object>)constructorDelegate;
TypeInstantiationMap.TryAdd(objectType, dynamicConstructor);
break;
}
}
}

return dynamicConstructor;
}

private static object ProcessToken(JsonReader reader, Type[] targetTypes, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.StartObject)
Expand Down