Skip to content

Improve deserialization performance by ~22%. #97

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

Closed
wants to merge 1 commit into from
Closed
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
1 change: 1 addition & 0 deletions Bson/Bson.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
<Link>Properties\GlobalAssemblyInfo.cs</Link>
</Compile>
<Compile Include="BsonExtensionMethods.cs" />
<Compile Include="FastSingleton.cs" />
<Compile Include="IO\BsonDocumentReaderSettings.cs" />
<Compile Include="IO\BsonDocumentWriterSettings.cs" />
<Compile Include="IO\BsonReaderSettings.cs" />
Expand Down
251 changes: 251 additions & 0 deletions Bson/FastSingleton.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
/* Copyright 2010-2012 10gen Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Threading;

namespace MongoDB.Bson
{
/// <summary>
/// Fast singleton abstract base class. Enables runtime, static, and lazy binding.
/// </summary>
/// <typeparam name="TValue">The singleton value type.</typeparam>
internal abstract class FastSingleton<TValue> where TValue : class
{
// private static fields
private static ReaderWriterLockSlim __readerWriterLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
private static Dictionary<Type, FastSingleton<TValue>> __dictionary = new Dictionary<Type, FastSingleton<TValue>>();

// public properties
/// <summary>
/// Gets the singleton value.
/// </summary>
public abstract TValue Value
{
get;
}

/// <summary>
/// Gets An optional user-defined object that contains information about the singleton value.
/// </summary>
public abstract object State
{
get;
}

// public static methods
/// <summary>
/// Resolves a nominal type to a singleton instance.
/// </summary>
/// <param name="nominalType">The nominal type.</param>
/// <returns>A singleton instance.</returns>
public static FastSingleton<TValue> Lookup(Type nominalType)
{
FastSingleton<TValue> value;

__readerWriterLock.EnterUpgradeableReadLock();
try
{
if (__dictionary.TryGetValue(nominalType, out value))
{
return value;
}

__readerWriterLock.EnterWriteLock();
try
{
if (__dictionary.TryGetValue(nominalType, out value))
{
return value;
}

var genericType = typeof(FastSingleton<,>).MakeGenericType(typeof(TValue), nominalType);

var instancePropertyInfo = genericType.GetProperty(
"Instance",
BindingFlags.Static | BindingFlags.Public);

value = (FastSingleton<TValue>)instancePropertyInfo.GetValue(null, null);

__dictionary.Add(nominalType, value);

return value;
}
finally
{
__readerWriterLock.ExitWriteLock();
}
}
finally
{
__readerWriterLock.ExitUpgradeableReadLock();
}
}

// public methods
/// <summary>
/// Sets the singleton value.
/// </summary>
/// <param name="value">The singleton value</param>
/// <param name="state">An optional user-defined object that contains information about the singleton value.</param>
/// <remarks>Function is atomic.</remarks>
/// <returns>true if the singleton value was set; otherwise false.</returns>
public abstract bool TrySetValue(TValue value, object state);

// protected classes
/// <summary>
/// Groups a singleton value and user-defined object so they can be set atomically.
/// </summary>
protected class ValueStateTuple
{
// private fields
private readonly TValue value;
private readonly object state;

// constructors
/// <summary>
/// Initializes a new instance of the Tuple class.
/// </summary>
/// <param name="value">The singleton value</param>
/// <param name="state">An optional user-defined object that contains information about the singleton value.</param>
public ValueStateTuple(TValue value, object state)
{
if (value == null)
{
throw new ArgumentNullException("value");
}

this.value = value;
this.state = state;
}

// public properties
/// <summary>
/// Gets the singleton value.
/// </summary>
public TValue Value
{
get
{
return this.value;
}
}

/// <summary>
/// Gets an optional user-defined object that contains information about the singleton value.
/// </summary>
public object State
{
get
{
return this.state;
}
}
}
}

/// <summary>
/// Fast singleton implementation class
/// </summary>
/// <typeparam name="TValue">The singleton value type.</typeparam>
/// <typeparam name="TNominal">The nominal type associated with the singleton value.</typeparam>
internal sealed class FastSingleton<TValue, TNominal> : FastSingleton<TValue> where TValue : class
{
// private static fields
private static readonly FastSingleton<TValue, TNominal> __instance = new FastSingleton<TValue, TNominal>();
private static ValueStateTuple __tuple; // volatile accesses

// constructors
/// <summary>
/// Initializes a new instance of the FastSingleton&lt;TValue, TNominal&gt; class.
/// </summary>
/// <remarks>Private because FastSingleton&lt;TValue, TNominal&gt; is a singleton.</remarks>
private FastSingleton()
{
}

/// <summary>
/// Gets the singleton instance of the FastSingleton&lt;TValue, TNominal&gt; type.
/// </summary>
/// <remarks>Accessed by FastSingleton.Create</remarks>
public static FastSingleton<TValue, TNominal> Instance
{
get
{
return __instance;
}
}

/// <summary>
/// Gets the singleton value.
/// </summary>
public override TValue Value
{
get
{
// see implementation of Thread.VolatileRead();
var tuple = __tuple;

Thread.MemoryBarrier();

if (tuple == null)
{
return null;
}

return tuple.Value;
}
}

/// <summary>
/// Gets An optional user-defined object that contains information about the singleton value.
/// </summary>
public override object State
{
get
{
// see implementation of Thread.VolatileRead();
var tuple = __tuple;

Thread.MemoryBarrier();

if (tuple == null)
{
return null;
}

return tuple.State;
}
}

/// <summary>
/// Sets the singleton value.
/// </summary>
/// <param name="value">The singleton value</param>
/// <param name="State">An optional user-defined object that contains information about the singleton value.</param>
/// <remarks>Function is atomic.</remarks>
/// <returns>true if the singleton value was set; otherwise false.</returns>
public override bool TrySetValue(TValue value, object State)
{
var tuple = new ValueStateTuple(value, State);

var originalTuple = Interlocked.CompareExchange(ref __tuple, tuple, null);

return originalTuple == null;
}
}
}
30 changes: 29 additions & 1 deletion Bson/Serialization/BsonClassMap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ public abstract class BsonClassMap
private bool _ignoreExtraElementsIsInherited = false;
private BsonMemberMap _extraElementsMemberMap;
private List<Type> _knownTypes = new List<Type>();
private readonly FastSingleton<IDiscriminatorConvention> _discriminatorConvention;

// constructors
/// <summary>
Expand All @@ -74,6 +75,7 @@ protected BsonClassMap(Type classType)
_conventions = LookupConventions(classType);
_discriminator = classType.Name;
_isAnonymous = IsAnonymousType(classType);
_discriminatorConvention = FastSingleton<IDiscriminatorConvention>.Lookup(classType);
}

// public properties
Expand Down Expand Up @@ -581,9 +583,35 @@ public BsonClassMap Freeze()
return this;
}

/// <summary>
/// Gets the discriminator convention for the member type.
/// </summary>
/// <returns>The discriminator convention for the member type.</returns>
internal IDiscriminatorConvention GetDiscriminatorConvention()
{
if (_discriminatorConvention.Value == null)
{
// LookupDiscriminatorConvention will populate _discriminatorConvention
BsonDefaultSerializer.LookupDiscriminatorConvention(_classType);
}

return _discriminatorConvention.Value;
}

/// <summary>
/// Gets a member map (only considers members declared in this class).
/// </summary>
/// <param name="memberInfo">The MemberInfo.</param>
/// <returns>The member map.</returns>
public BsonMemberMap GetMemberMap(MemberInfo memberInfo)
{
// can be called whether frozen or not
return _declaredMemberMaps.Find(m => m.MemberInfo == memberInfo);
}

/// <summary>
/// Gets a member map.
/// </summary>
/// <param name="memberName">The member name.</param>
/// <returns>The member map (or null if the member was not found).</returns>
public BsonMemberMap GetMemberMap(string memberName)
Expand Down Expand Up @@ -1485,7 +1513,7 @@ public void UnmapProperty<TMember>(Expression<Func<TClass, TMember>> propertyLam
UnmapMember(propertyLambda);
}

// private methods
// private static methods
private static MemberInfo GetMemberInfoFromLambda<TMember>(Expression<Func<TClass, TMember>> memberLambda)
{
var body = memberLambda.Body;
Expand Down
13 changes: 11 additions & 2 deletions Bson/Serialization/BsonClassMapSerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
using System.Text.RegularExpressions;

using MongoDB.Bson.IO;
using MongoDB.Bson.Serialization.Conventions;
using MongoDB.Bson.Serialization.Options;

namespace MongoDB.Bson.Serialization
Expand Down Expand Up @@ -131,7 +132,15 @@ public object Deserialize(

bsonReader.ReadStartDocument();
var missingElementMemberMaps = new HashSet<BsonMemberMap>(classMap.AllMemberMaps); // make a copy!
var discriminatorConvention = BsonDefaultSerializer.LookupDiscriminatorConvention(nominalType);
IDiscriminatorConvention discriminatorConvention;
if (actualType == nominalType)
{
discriminatorConvention = classMap.GetDiscriminatorConvention();
}
else
{
discriminatorConvention = BsonDefaultSerializer.LookupDiscriminatorConvention(nominalType);
}
while (bsonReader.ReadBsonType() != BsonType.EndOfDocument)
{
var elementName = bsonReader.ReadName();
Expand Down Expand Up @@ -443,7 +452,7 @@ private void DeserializeMember(BsonReader bsonReader, object obj, BsonMemberMap
}
else
{
var discriminatorConvention = BsonDefaultSerializer.LookupDiscriminatorConvention(nominalType);
var discriminatorConvention = memberMap.GetDiscriminatorConvention();
actualType = discriminatorConvention.GetActualType(bsonReader, nominalType); // returns nominalType if no discriminator found
}
var serializer = memberMap.GetSerializer(actualType);
Expand Down
Loading