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

Cache char[] instances #178

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 6 additions & 16 deletions Benchmark/ExtensionMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,12 @@ public interface IGenericEquality<T>

static class ExtensionMethods
{
static readonly char[] ASCII;

static ExtensionMethods()
{
var cs = new List<char>();

for (var i = 0; i <= byte.MaxValue; i++)
{
var c = (char)i;
if (char.IsControl(c)) continue;

cs.Add(c);
}

ASCII = cs.ToArray();
}
static readonly char[] ASCII =
Enumerable
.Range(0, byte.MaxValue)
.Select(b => (char)b)
.Where(c => !Char.IsControl(c))
.ToArray();

public static bool TrueEqualsDictionary<K, V>(this Dictionary<K, V> a, Dictionary<K, V> b)
where V : class, IGenericEquality<V>
Expand Down
1 change: 1 addition & 0 deletions Benchmark/Result.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ class Result
public string Serializer { get; set; }
public string TypeName { get; set; }
public TimeSpan Elapsed { get; set; }
public int[] GCCounts { get; set; }
}
}
198 changes: 148 additions & 50 deletions Jil/Serialize/InlineSerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,24 @@

namespace Jil.Serialize
{
class InlineSerializer<ForType>
{
public static bool ReorderMembers = true;
public static bool UseCustomIntegerToString = true;
public static bool SkipDateTimeMathMethods = true;
public static bool UseCustomISODateFormatting = true;
public static bool UseFastLists = true;
public static bool UseFastArrays = true;
public static bool UseFastGuids = true;
public static bool AllocationlessDictionaries = true;
public static bool PropagateConstants = true;
public static bool UseCustomWriteIntUnrolled = true;
public static bool UseCustomRFC1123DateTimeFormatting = true;

static string CharBuffer = "char_buffer";
internal const int CharBufferSize = 36;
internal const int RecursionLimit = 50;

static Dictionary<char, string> CharacterEscapes =
internal static class InlineSerializer
{
/// <summary>
/// Name used for character buffer local variables.
/// </summary>
public const string CharBuffer = "char_buffer";

/// <summary>
/// The maximum length of the character buffer needed to serialize any primitive type.
/// </summary>
public const int CharBufferSize = 36;

public const int RecursionLimit = 50;

/// <summary>
/// Escape sequences for special characters.
/// </summary>
public static readonly Dictionary<char, string> CharacterEscapes =
new Dictionary<char, string>
{
{ '\\', @"\\" },
Expand Down Expand Up @@ -69,6 +68,22 @@ class InlineSerializer<ForType>
{ '\u001E', @"\u001E" },
{ '\u001F', @"\u001F" }
};
}

class InlineSerializer<ForType>
{
public static bool ReorderMembers = true;
public static bool UseCustomIntegerToString = true;
public static bool SkipDateTimeMathMethods = true;
public static bool UseCustomISODateFormatting = true;
public static bool UseFastLists = true;
public static bool UseFastArrays = true;
public static bool UseFastGuids = true;
public static bool AllocationlessDictionaries = true;
public static bool PropagateConstants = true;
public static bool UseCustomWriteIntUnrolled = true;
public static bool UseCustomRFC1123DateTimeFormatting = true;
public static bool UseCachedCharBuffers = true;

private readonly Type RecursionLookupOptionsType; // This is a type that implements ISerializeOptions and has an empty, public constructor
private readonly bool ExcludeNulls;
Expand Down Expand Up @@ -791,8 +806,16 @@ void WriteISO8601StyleDateTime()
return;
}

Emit.LoadLocal(CharBuffer); // TextWriter DateTime char[]
Emit.Call(Methods.GetCustomISO8601ToString(BuildingToString)); // --empty--
if (UseCachedCharBuffers)
{
Emit.Call(Methods.GetCachedCharBuffer()); // TextWriter DateTime char[]
}
else
{
Emit.LoadLocal(InlineSerializer.CharBuffer); // TextWriter DateTime char[]
}

Emit.Call(Methods.GetCustomISO8601ToString(BuildingToString)); // --empty--
}

static readonly MethodInfo DateTime_ToString = typeof(DateTime).GetMethod("ToString", new[] { typeof(string) });
Expand Down Expand Up @@ -866,7 +889,14 @@ void WriteDateTimeOffset()
Emit.Call(TimeSpan_Minutes); // TextWriter long int int
}

Emit.LoadLocal(CharBuffer); // TextWriter long int int char[]
if (UseCachedCharBuffers)
{
Emit.Call(Methods.GetCachedCharBuffer()); // TextWriter long int int char[]
}
else
{
Emit.LoadLocal(InlineSerializer.CharBuffer); // TextWriter long int int char[]
}
Emit.Call(Methods.GetCustomWriteMicrosoftStyleWithOffset(BuildingToString)); // --empty--
return;
}
Expand All @@ -888,7 +918,15 @@ void WriteDateTimeOffset()
Emit.LoadLocalAddress(tsLoc); // TextWriter DateTime int TimeSpan*
Emit.Call(TimeSpan_Minutes); // TextWriter DateTime int int
}
Emit.LoadLocal(CharBuffer); // TextWriter DateTime int int char[]

if (UseCachedCharBuffers)
{
Emit.Call(Methods.GetCachedCharBuffer()); // TextWriter DateTime int int char[]
}
else
{
Emit.LoadLocal(InlineSerializer.CharBuffer); // TextWriter DateTime int int char[]
}
Emit.Call(Methods.GetCustomISO8601WithOffsetToString(BuildingToString)); // --empty--
return;
}
Expand Down Expand Up @@ -930,7 +968,14 @@ void WriteTimeSpan()
return;
}

Emit.LoadLocal(CharBuffer); // TextWriter TimeSpan char[]
if (UseCachedCharBuffers)
{
Emit.Call(Methods.GetCachedCharBuffer()); // TextWriter TimeSpan char[]
}
else
{
Emit.LoadLocal(InlineSerializer.CharBuffer); // TextWriter TimeSpan char[]
}

switch(DateFormat)
{
Expand Down Expand Up @@ -1114,32 +1159,60 @@ void WritePrimitive(Type primitiveType, bool quotesNeedHandling)
{
if (primitiveType == typeof(int))
{
Emit.LoadLocal(CharBuffer); // TextWriter int char[]
CallWriteInt(); // --empty--
if (UseCachedCharBuffers)
{
Emit.Call(Methods.GetCachedCharBuffer()); // TextWriter int char[]
}
else
{
Emit.LoadLocal(InlineSerializer.CharBuffer); // TextWriter int char[]
}
CallWriteInt(); // --empty--

return;
}

if (primitiveType == typeof(uint))
{
Emit.LoadLocal(CharBuffer); // TextWriter int char[]
CallWriteUInt(); // --empty--
if (UseCachedCharBuffers)
{
Emit.Call(Methods.GetCachedCharBuffer()); // TextWriter uint char[]
}
else
{
Emit.LoadLocal(InlineSerializer.CharBuffer); // TextWriter uint char[]
}
CallWriteUInt(); // --empty--

return;
}

if (primitiveType == typeof(long))
{
Emit.LoadLocal(CharBuffer); // TextWriter int char[]
CallWriteLong(); // --empty--
if (UseCachedCharBuffers)
{
Emit.Call(Methods.GetCachedCharBuffer()); // TextWriter long char[]
}
else
{
Emit.LoadLocal(InlineSerializer.CharBuffer); // TextWriter long char[]
}
CallWriteLong(); // --empty--

return;
}

if (primitiveType == typeof(ulong))
{
Emit.LoadLocal(CharBuffer); // TextWriter int char[]
CallWriteULong(); // --empty--
if (UseCachedCharBuffers)
{
Emit.Call(Methods.GetCachedCharBuffer()); // TextWriter ulong char[]
}
else
{
Emit.LoadLocal(InlineSerializer.CharBuffer); // TextWriter ulong char[]
}
CallWriteULong(); // --empty--

return;
}
Expand Down Expand Up @@ -1213,15 +1286,22 @@ void WriteGuidFast(bool quotesNeedHandling)
{
if (quotesNeedHandling)
{
WriteString("\""); // TextWriter Guid
WriteString("\""); // TextWriter Guid
}

Emit.LoadLocal(CharBuffer); // TextWriter Guid char[]
Emit.Call(Methods.GetWriteGuid(BuildingToString)); // --empty--
if (UseCachedCharBuffers)
{
Emit.Call(Methods.GetCachedCharBuffer()); // TextWriter Guid char[]
}
else
{
Emit.LoadLocal(InlineSerializer.CharBuffer); // TextWriter Guid char[]
}
Emit.Call(Methods.GetWriteGuid(BuildingToString)); // --empty--

if (quotesNeedHandling)
{
WriteString("\""); // --empty--
WriteString("\""); // --empty--
}
}

Expand Down Expand Up @@ -1284,9 +1364,9 @@ void WriteEncodedChar(bool quotesNeedHandling)
writeChar = typeof(TextWriter).GetMethod("Write", new[] { typeof(char) });
}

var lowestCharNeedingEncoding = (int)CharacterEscapes.Keys.OrderBy(c => (int)c).First();
var lowestCharNeedingEncoding = (int)InlineSerializer.CharacterEscapes.Keys.OrderBy(c => (int)c).First();

var needLabels = CharacterEscapes.OrderBy(kv => kv.Key).Select(kv => Tuple.Create(kv.Key - lowestCharNeedingEncoding, kv.Value)).ToList();
var needLabels = InlineSerializer.CharacterEscapes.OrderBy(kv => kv.Key).Select(kv => Tuple.Create(kv.Key - lowestCharNeedingEncoding, kv.Value)).ToList();

var labels = new List<Tuple<Sigil.Label, string>>();

Expand Down Expand Up @@ -3508,10 +3588,10 @@ void AddCharBuffer(Type serializingType)
return;
}

Emit.DeclareLocal<char[]>(CharBuffer);
Emit.LoadConstant(CharBufferSize);
Emit.DeclareLocal<char[]>(InlineSerializer.CharBuffer);
Emit.LoadConstant(InlineSerializer.CharBufferSize);
Emit.NewArray<char>();
Emit.StoreLocal(CharBuffer);
Emit.StoreLocal(InlineSerializer.CharBuffer);
}

Emit MakeEmit(Type forType)
Expand All @@ -3538,17 +3618,20 @@ void BuildObjectWithNewImpl()
{
var goOn = Emit.DefineLabel();

Emit.LoadArgument(2); // int
Emit.LoadConstant(RecursionLimit); // int int
Emit.BranchIfLess(goOn); // --empty--
Emit.LoadArgument(2); // int
Emit.LoadConstant(InlineSerializer.RecursionLimit); // int int
Emit.BranchIfLess(goOn); // --empty--

Emit.NewObject(typeof(InfiniteRecursionException)); // infiniteRecursionException
Emit.Throw(); // --empty--

Emit.MarkLabel(goOn); // --empty--
}

AddCharBuffer(typeof(ForType));
if (!UseCachedCharBuffers)
{
AddCharBuffer(typeof(ForType));
}

RecursiveTypes = PreloadRecursiveTypes(recursiveTypes);

Expand Down Expand Up @@ -3605,7 +3688,10 @@ void BuildListWithNewImpl(MemberInfo dynamicMember)

Emit = MakeEmit(typeof(ForType));

AddCharBuffer(typeof(ForType));
if (!UseCachedCharBuffers)
{
AddCharBuffer(typeof(ForType));
}

RecursiveTypes = PreloadRecursiveTypes(recursiveTypes);

Expand Down Expand Up @@ -3633,7 +3719,10 @@ void BuildEnumerableWithNewImpl(MemberInfo dynamicMember)

Emit = MakeEmit(typeof(ForType));

AddCharBuffer(typeof(ForType));
if (!UseCachedCharBuffers)
{
AddCharBuffer(typeof(ForType));
}

RecursiveTypes = PreloadRecursiveTypes(recursiveTypes);

Expand Down Expand Up @@ -3661,7 +3750,10 @@ void BuildDictionaryWithNewImpl(MemberInfo dynamicMember)

Emit = MakeEmit(typeof(ForType));

AddCharBuffer(typeof(ForType));
if (!UseCachedCharBuffers)
{
AddCharBuffer(typeof(ForType));
}

RecursiveTypes = PreloadRecursiveTypes(recursiveTypes);

Expand All @@ -3687,7 +3779,10 @@ void BuildPrimitiveWithNewImpl()
{
Emit = MakeEmit(typeof(ForType));

AddCharBuffer(typeof(ForType));
if (!UseCachedCharBuffers)
{
AddCharBuffer(typeof(ForType));
}

Emit.LoadArgument(0);
Emit.LoadArgument(1);
Expand Down Expand Up @@ -3717,7 +3812,10 @@ void BuildNullableWithNewImpl(MemberInfo dynamicMember)

Emit = MakeEmit(typeof(ForType));

AddCharBuffer(typeof(ForType));
if (!UseCachedCharBuffers)
{
AddCharBuffer(typeof(ForType));
}

RecursiveTypes = PreloadRecursiveTypes(recursiveTypes);

Expand Down
6 changes: 6 additions & 0 deletions Jil/Serialize/Methods.Get.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ namespace Jil.Serialize
{
static partial class Methods
{
public static MethodInfo GetCachedCharBuffer()
{
// no difference depending on thunk writer here
return GetThreadLocalCharBuffer;
}

public static MethodInfo GetValidateDouble()
{
// no difference depending on thunk writer here
Expand Down
Loading