-
Notifications
You must be signed in to change notification settings - Fork 62
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
EnumerableSerializeFactory fixes #81
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is nice idea (checking for constructor with IEnumerable<>
arg) 👍 However, there are several concerns to deal with.
var instance = Activator.CreateInstance(type); | ||
if (preserveObjectReferences) | ||
var count = stream.ReadInt32(session); | ||
if (construct != null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can move those null checks outside of the ObjectReader
implementation - it's not like the constructor/addRange/add presence for the same type will change from one deserializer call to another. Instead you can make those checks outside and return one of the 3 different ObjectReader
implementations (depending if constructor/addRange/add is available).
: null; | ||
} | ||
|
||
private static Func<object, object> CompileCtorToDelegate(ConstructorInfo ctor, Type argType) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to check which is faster: using lambda Func<,>
which call the constructor, or calling ConstructorInfo.Invoke
directly and use that one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 Is there any standards for benchmarking I should follow, e.g. which benchmarking library to use, what data to test on, etc.?
return compiled; | ||
} | ||
|
||
private static Action<object, object> CompileMethodToDelegate(MethodInfo method, Type instanceType, Type argType) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as with calling ConstructorInfo.Invoke
vs Func<,>
.
} | ||
|
||
var count = stream.ReadInt32(session); | ||
|
||
var instance = Activator.CreateInstance(type); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you're going to call Activator.CreateInstance
make sure, that you've checked that in CanSerialize
you've checked that this type has default constructor. Also in hyperion we accept calling non-public constructors, so Activator.CreateInstance(type, true)
is probably better and more explicit in this context.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch on using Activator.CreateInstance(type, true)
. On a similar note, should I provide the appropriate BindingFlags
value to type.GetTypeInfo().GetConstructor
to return non-public constructors there as well?
We're going to move to BenchmarkDotNet with performance benchmarks(see #57). Also I've made initial benchmarks, you can see them here. General results are: BenchmarkDotNet=v0.10.10, OS=Windows 10 Redstone 2 [1703, Creators Update] (10.0.15063.726)
Processor=Intel Core i5-4430 CPU 3.00GHz (Haswell), ProcessorCount=4
Frequency=2929686 Hz, Resolution=341.3335 ns, Timer=TSC
.NET Core SDK=2.1.1
[Host] : .NET Core 2.0.3 (Framework 4.6.25815.02), 64bit RyuJIT
DefaultJob : .NET Core 2.0.3 (Framework 4.6.25815.02), 64bit RyuJIT
Summary:
|
…ader, allow private constructors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My mistake in benchmarks - when we are creating a new collection via constructor with IEnumerable<>
argument, we also need to add a cost of initializing an array. I'll make updated benchmarks later today.
@@ -48,6 +51,8 @@ public override bool CanSerialize(Serializer serializer, Type type) | |||
return false; | |||
} | |||
|
|||
private static readonly BindingFlags allInstanceBindings = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a BindingFlagsEx.All
member, which does exactly that ;)
I updated the benchmarks, ran them, and put the code/results here. Please let me know if everything looks OK to you. I updated the constructor and add range benchmarks to use Also, the Based on these results, it seems that calling BenchmarkDotNet=v0.10.11, OS=Windows 10 Redstone 2 [1703, Creators Update] (10.0.15063.674)
Processor=Intel Core i5-4300M CPU 2.60GHz (Haswell), ProcessorCount=4
Frequency=2533210 Hz, Resolution=394.7561 ns, Timer=TSC
[Host] : .NET Framework 4.6.1 (CLR 4.0.30319.42000), 32bit LegacyJIT-v4.7.2115.0
DefaultJob : .NET Framework 4.6.1 (CLR 4.0.30319.42000), 32bit LegacyJIT-v4.7.2115.0
|
From the benchmarks it looks like by default the best option would be an |
Just to make sure I understand your suggestion correctly, should we add a special case for Also, for other types, we should look for an |
What I meant, was to create ObjectReader based on:
What do you think about that? |
I tried using the In the current version of the code, So which of these two options do you think would be the better one:
|
Update this factory to handle classes that implement
IEnumerable<T>
and have a constructor that acceptsIEnumerable<T>
(e.g.FSharpSet<T>
andLinkedList<T>)
and to use compiled expressions instead of invoking MethodInfo for Add and AddRange.