Skip to content

Commit

Permalink
Support not inlining nested structs in Serializer
Browse files Browse the repository at this point in the history
Add support to force generated serializer expressions
to always generate separate delegates for nested structs.
This will significantly reduce creation time of serializers
for complex type graphs.
  • Loading branch information
Ran Raviv authored and sapek committed Dec 16, 2015
1 parent d94bb4a commit cb95fdb
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 22 deletions.
11 changes: 9 additions & 2 deletions cs/src/core/Serializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,18 @@ public class Serializer<W>
/// Create a serializer for specified type
/// </summary>
/// <param name="type">Type representing a Bond schema</param>
public Serializer(Type type)
public Serializer(Type type) : this(type, inlineNested: true) { }

/// <summary>
/// Create a serializer for specified type
/// </summary>
/// <param name="type">Type representing a Bond schema</param>
/// <param name="inlineNested">Indicates whether nested struct serialization code may be inlined</param>
public Serializer(Type type, bool inlineNested)
{
var parser = new ObjectParser(type);
serialize = SerializerGeneratorFactory<object, W>.Create(
(o, w, i) => serialize[i](o, w), type)
(o, w, i) => serialize[i](o, w), type, inlineNested)
.Generate(parser)
.Select(lambda => lambda.Compile()).ToArray();
}
Expand Down
2 changes: 1 addition & 1 deletion cs/src/core/expressions/ObjectParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ public Expression Bonded(ValueHandler handler)
{
if (schemaType.IsBonded())
{
return handler(PrunedExpression.Convert(value, typeof(IBonded)));
return handler(value);
}

var bondedType = typeof(Bonded<>).MakeGenericType(objectType);
Expand Down
36 changes: 24 additions & 12 deletions cs/src/core/expressions/SerializerGeneratorFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,25 @@ namespace Bond.Expressions
using System;
using System.Globalization;
using System.Linq.Expressions;

internal static class SerializerGeneratorFactory<R, W>
{
public static ISerializerGenerator<R, W> Create<S>(
Expression<Action<R, W, int>> deferredSerialize, S schema)
Expression<Action<R, W, int>> deferredSerialize, S schema, bool inlineNested = true)
{
return Cache<S>.Create(deferredSerialize, schema);
return Cache<S>.Create(deferredSerialize, schema, inlineNested);
}

static class Cache<S>
{
public static readonly Func<Expression<Action<R, W, int>>, S, ISerializerGenerator<R, W>> Create;
public static readonly Func<Expression<Action<R, W, int>>, S, bool, ISerializerGenerator<R, W>> Create;

[System.Diagnostics.CodeAnalysis.SuppressMessage(
"Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")]
static Cache()
{
Type generator;

var attribute = typeof(W).GetAttribute<SerializerAttribute>();
if (attribute == null)
{
Expand All @@ -39,19 +39,21 @@ static Cache()
}

generator = attribute.Type.MakeGenericType(typeof(R), typeof(W));

if (!typeof(ISerializerGenerator<R, W>).IsAssignableFrom(generator))
{
throw new InvalidOperationException(
string.Format(
CultureInfo.InvariantCulture,
"Serializer generator {0} specified for writer {1} is not an ISerializerGenerator.",
generator,
typeof(W)));
generator, typeof(W)));
}
}

var ctor = generator.GetConstructor(typeof(Expression<Action<R, W, int>>), typeof(S));
var ctor =
generator.GetConstructor(typeof(Expression<Action<R, W, int>>), typeof(S), typeof(bool)) ??
generator.GetConstructor(typeof(Expression<Action<R, W, int>>), typeof(S));

if (ctor == null)
{
throw new InvalidOperationException(
Expand All @@ -63,9 +65,19 @@ static Cache()

var deferredSerialize = Expression.Parameter(typeof(Expression<Action<R, W, int>>));
var schema = Expression.Parameter(typeof(S));
Create = Expression.Lambda<Func<Expression<Action<R, W, int>>, S, ISerializerGenerator<R, W>>>(
Expression.New(ctor, deferredSerialize, schema), deferredSerialize, schema)
.Compile();
var inlineNested = Expression.Parameter(typeof(bool));

var newExpression =
ctor.GetParameters().Length == 3
? Expression.New(ctor, deferredSerialize, schema, inlineNested)
: Expression.New(ctor, deferredSerialize, schema);

Create =
Expression.Lambda<Func<Expression<Action<R, W, int>>, S, bool, ISerializerGenerator<R, W>>>(
newExpression,
deferredSerialize,
schema,
inlineNested).Compile();
}
}
}
Expand Down
22 changes: 15 additions & 7 deletions cs/src/core/expressions/SerializerTransform.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ protected Expression GenerateSerialize(Serialize serialize, IParser parser, Para

body = Expression.Invoke(
deferredSerialize,
parser.ReaderValue,
PrunedExpression.Convert(parser.ReaderValue, parser.ReaderParam.Type),
writer,
Expression.Constant(index));
}
Expand All @@ -100,19 +100,21 @@ internal class SerializerTransform<R, W> : SerializerGenerator<R, W>
readonly ProtocolWriter<W> writer = new ProtocolWriter<W>();
readonly Dictionary<RuntimeSchema, Serialize> serializeDelegates =
new Dictionary<RuntimeSchema, Serialize>(new TypeDefComparer());
readonly bool inlineNested;
static readonly bool untaggedWriter =
typeof (IUntaggedProtocolReader).IsAssignableFrom(typeof (W).GetAttribute<ReaderAttribute>().ReaderType);
static readonly bool binaryWriter = untaggedWriter
|| typeof(ITaggedProtocolReader).IsAssignableFrom(typeof(W).GetAttribute<ReaderAttribute>().ReaderType);

public SerializerTransform(Expression<Action<R, W, int>> deferredSerialize, RuntimeSchema schema)
public SerializerTransform(Expression<Action<R, W, int>> deferredSerialize, RuntimeSchema schema, bool inlineNested = true)
: base(deferredSerialize)
{
runtimeSchema = schema;
this.inlineNested = inlineNested;
}

public SerializerTransform(Expression<Action<R, W, int>> deferredSerialize, Type type)
: this(deferredSerialize, Schema.GetRuntimeSchema(type))
public SerializerTransform(Expression<Action<R, W, int>> deferredSerialize, Type type, bool inlineNested = true)
: this(deferredSerialize, Schema.GetRuntimeSchema(type), inlineNested)
{}

public override IEnumerable<Expression<Action<R, W>>> Generate(IParser parser)
Expand Down Expand Up @@ -146,6 +148,9 @@ Expression GenerateSerialize(SerializeWithSchema serializeWithSchema, IParser pa
// and for large schemas JIT fails to compile resulting lambda (InvalidProgramException).
// As a workaround we don't inline nested serialize expressions in this case.
var inline = !typeof(ITaggedProtocolReader).IsAssignableFrom(parser.ReaderParam.Type);

inline = inline && (this.inlineNested || !schema.IsStruct);

return GenerateSerialize(serialize, parser, writer.Param, inline);
}

Expand Down Expand Up @@ -266,7 +271,8 @@ Expression Value(IParser parser, Expression valueType)
{
if (parser.IsBonded)
{
return parser.Bonded(writer.WriteBonded);
return parser.Bonded(value =>
writer.WriteBonded(PrunedExpression.Convert(value, typeof(IBonded))));
}

var switchCases = new List<DeferredSwitchCase>
Expand Down Expand Up @@ -317,7 +323,9 @@ Expression Value(IParser parser, Expression valueType, RuntimeSchema schema)
Debug.Assert(schema.HasValue);

if (parser.IsBonded || (untaggedWriter && schema.IsBonded))
return parser.Bonded(writer.WriteBonded);
return parser.Bonded(value =>
writer.WriteBonded(PrunedExpression.Convert(value, typeof(IBonded))));


if (schema.IsStruct)
return GenerateSerialize(Struct, parser, schema);
Expand Down
11 changes: 11 additions & 0 deletions cs/test/core/Util.cs
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,16 @@ public static ArraySegment<byte> SerializeSafeCB<T>(T obj)
return output.Data;
}

public static ArraySegment<byte> SerializeSafeCBNoInlining<T>(T obj)
{
var output = new Bond.IO.Safe.OutputBuffer(new byte[11]);
var writer = new CompactBinaryWriter<Bond.IO.Safe.OutputBuffer>(output);

var serializer = new Serializer<CompactBinaryWriter<Bond.IO.Safe.OutputBuffer>>(typeof(T), false);
serializer.Serialize(obj, writer);
return output.Data;
}

public static T DeserializeCB<T>(Stream stream)
{
var input = new InputStream(stream);
Expand Down Expand Up @@ -721,6 +731,7 @@ public static void AllSerializeDeserialize<From, To>(From from, bool noTranscodi
memoryRoundtrip(SerializeSafeCB, DeserializeSafeCB<To>);
memoryRoundtrip(SerializeSafeCB, DeserializeUnsafeCB<To>);
memoryPointerRoundtrip(SerializeSafeCB, DeserializePointerCB<To>);
memoryRoundtrip(SerializeSafeCBNoInlining, DeserializeSafeCB<To>);

streamMarshal(MarshalCB);
streamMarshal(SerializerMarshalCB);
Expand Down

0 comments on commit cb95fdb

Please sign in to comment.