Skip to content

Commit

Permalink
Optimize codec initialization (#71)
Browse files Browse the repository at this point in the history
  • Loading branch information
quinchs authored Oct 2, 2023
1 parent 33ff79d commit 392e69f
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 81 deletions.
88 changes: 42 additions & 46 deletions src/EdgeDB.Net.Driver/Binary/Builders/ObjectBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,67 +10,63 @@ namespace EdgeDB
{
internal sealed class ObjectBuilder
{
private static readonly Dictionary<Type, (int Version, ICodec Codec)> _codecVisitorStateTable = new();
private static readonly ConcurrentDictionary<Type, (int Version, ICodec Codec)> _codecVisitorStateTable = new();
private static readonly object _visitorLock = new();

public static TType? BuildResult<TType>(EdgeDBBinaryClient client, ICodec codec, in ReadOnlyMemory<byte> data)
public readonly struct PreheatedCodec
{
// TO INVESTIGATE: since a codec can only be "visited" or "mutated" for
// one type at a time, we have to ensure that the codec is ready to deserialize
// TType, we can store the states of the codecs here for building result
// to achieve this.
var typeCodec = codec;

bool wasSkipped = false;
lock(_visitorLock)
{
// Since the supplied codec could be a template, we walk the codec and cache based on the supplied type
// if the codec hasn't been walked OR the result type is an object we walk it and cache it if its not an
// object codec.
if (typeof(TType) != typeof(object) && _codecVisitorStateTable.TryGetValue(typeof(TType), out var info))
{
if(codec.GetHashCode() == info.Version)
{
typeCodec = info.Codec;
wasSkipped = true;
}
}

if (!wasSkipped)
{
var version = codec.GetHashCode();

var visitor = new TypeVisitor(client);
public readonly ICodec Codec;

visitor.SetTargetType(typeof(TType));
public PreheatedCodec(ICodec codec)
{
Codec = codec;
}
}

visitor.Visit(ref codec);
public static PreheatedCodec PreheatCodec<T>(EdgeDBBinaryClient client, ICodec codec)
{
// if the codec has been visited before and we have the most up-to-date version, return it.
if (
typeof(T) != typeof(object) &&
_codecVisitorStateTable.TryGetValue(typeof(T), out var info) &&
codec.GetHashCode() == info.Version)
{
client.Logger.SkippingCodecVisiting(typeof(T), codec);
return new(info.Codec);
}

if (typeof(TType) != typeof(object))
_codecVisitorStateTable[typeof(TType)] = (version, codec);
var version = codec.GetHashCode();

typeCodec = codec;
var visitor = new TypeVisitor(client);
visitor.SetTargetType(typeof(T));
visitor.Visit(ref codec);

client.Logger.ObjectDeserializationPrep(CodecFormatter.Format(typeCodec).ToString());
}
}
if (typeof(T) != typeof(object))
_codecVisitorStateTable[typeof(T)] = (version, codec);

if(wasSkipped)
if (client.Logger.IsEnabled(LogLevel.Debug))
{
client.Logger.SkippingCodecVisiting(typeof(TType), codec);
client.Logger.ObjectDeserializationPrep(CodecFormatter.Format(codec).ToString());
}


if (typeCodec is ObjectCodec objectCodec)
return new(codec);
}

public static T? BuildResult<T>(EdgeDBBinaryClient client, in PreheatedCodec preheated, in ReadOnlyMemory<byte> data)
{
if (preheated.Codec is ObjectCodec objectCodec)
{
return (TType?)TypeBuilder.BuildObject(client, typeof(TType), objectCodec, in data);
return (T?)TypeBuilder.BuildObject(client, typeof(T), objectCodec, in data);
}

var value = typeCodec.Deserialize(client, in data);
var value = preheated.Codec.Deserialize(client, in data);

return (TType?)ConvertTo(typeof(TType), value);
return (T?)ConvertTo(typeof(T), value);
}

public static T? BuildResult<T>(EdgeDBBinaryClient client, ICodec codec, in ReadOnlyMemory<byte> data)
=> BuildResult<T>(client, PreheatCodec<T>(client, codec), data);

public static object? ConvertTo(Type type, object? value)
{
if (value is null)
Expand Down Expand Up @@ -102,7 +98,7 @@ internal sealed class ObjectBuilder
{
return ConvertCollection(type, valueType, value);
}

// check for edgeql types
//if (TypeBuilder.IsValidObjectType(type) && value is IDictionary<string, object?> dict)
// return TypeBuilder.BuildObject(type, dict);
Expand Down Expand Up @@ -162,13 +158,13 @@ internal sealed class ObjectBuilder
foreach (var val in (IEnumerable)value)
{
converted.Add(strongInnerType is not null ? ConvertTo(strongInnerType, val) : val);

//if (val is IDictionary<string, object?> raw)
//{
// converted.Add(strongInnerType is not null ? TypeBuilder.BuildObject(strongInnerType, raw) : val);
//}
//else


}

Expand Down
18 changes: 9 additions & 9 deletions src/EdgeDB.Net.Driver/Binary/Builders/TypeBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ public static class TypeBuilder
/// using this naming strategy, the naming convention of the dotnet type will be preserved.
/// </remarks>
/// <remarks>
/// If the naming strategy doesn't find a match, the
/// If the naming strategy doesn't find a match, the
/// <see cref="AttributeNamingStrategy"/> will be used.
/// </remarks>
public static INamingStrategy SchemaNamingStrategy { get; set; }

internal readonly static ConcurrentDictionary<Type, EdgeDBTypeDeserializeInfo> TypeInfo = new();
internal readonly static ConcurrentDictionary<Type, IEdgeDBTypeConverter> TypeConverters = new();
internal static readonly INamingStrategy AttributeNamingStrategy;
Expand All @@ -56,7 +56,7 @@ public static void AddOrUpdateTypeBuilder<TType>(
if(!EdgeDBTypeConstructorInfo.TryGetConstructorInfo(typeof(TType), out var ctorInfo) || ctorInfo.EmptyConstructor is null)
throw new TargetInvocationException($"Cannot create an instance of {typeof(TType).Name}: no empty constructor found", null);

object Factory(ref ObjectEnumerator enumerator)
object? Factory(ref ObjectEnumerator enumerator)
{
var instance = (TType)ctorInfo.EmptyConstructor.Invoke(Array.Empty<object>());

Expand Down Expand Up @@ -127,7 +127,7 @@ public static bool TryRemoveTypeFactory<TType>([MaybeNullWhen(false)]out TypeDes
internal static bool TryGetTypeDeserializerInfo(Type type, [MaybeNullWhen(false)] out EdgeDBTypeDeserializeInfo info)
{
info = null;

if (!IsValidObjectType(type))
return false;

Expand All @@ -138,15 +138,15 @@ internal static bool TryGetTypeDeserializerInfo(Type type, [MaybeNullWhen(false)
}
else
info = typeInfo;

return info is not null;
}

internal static object? BuildObject(EdgeDBBinaryClient client, Type type, Binary.Codecs.ObjectCodec codec, in ReadOnlyMemory<byte> data)
{
if (!IsValidObjectType(type))
throw new InvalidOperationException($"Cannot deserialize data to {type.Name}");

if (!TypeInfo.TryGetValue(type, out EdgeDBTypeDeserializeInfo? info))
{
info = TypeInfo.AddOrUpdate(type, new EdgeDBTypeDeserializeInfo(type), (_, v) => v);
Expand Down Expand Up @@ -197,7 +197,7 @@ internal static bool IsValidObjectType(Type type)
}

return
type == typeof(object) ||
type == typeof(object) ||
type.IsAssignableTo(typeof(ITuple)) ||
type.IsAbstract ||
type.IsRecord() ||
Expand Down Expand Up @@ -226,7 +226,7 @@ private static object CreateDynamicList(Array arr, Type elementType)

return inst;
}

internal static bool TryGetCustomBuilder(this Type objectType, out MethodInfo? info)
{
info = null;
Expand Down
Loading

0 comments on commit 392e69f

Please sign in to comment.