neo
neo copied to clipboard
Fast serialization
Redesigned the object serialization scheme using reflection.
- Don't worry about performance, because all reflections will only be executed once. It will be read from the cache later.
- Object creation and property assignment use precompiled expression trees, which makes them almost the same speed as direct creation and assignment.
- We don't need to add the
Serialize()andDeserialize()methods for each class, just let it implement theISerializableinterface and add theSerializedattribute for each property that needs to be serialized. The new serializer will automatically perform serialization and deserialization.
@neo-project/core What do you think? If you think it's good, I will complete it.
Object creation benchmark:
class TestClassForConstructor
{
private delegate TestClassForConstructor ConstructorDelegate(object arg);
public TestClassForConstructor(object arg)
{
}
public static void RunTest()
{
Type t = typeof(TestClassForConstructor);
Type paramType = typeof(object);
var constructorInfo = t.GetConstructor(new[] { paramType });
var paramExpr = Expression.Parameter(paramType);
var newExpr = Expression.New(constructorInfo, paramExpr);
var constructorExpr = Expression.Lambda<ConstructorDelegate>(newExpr, paramExpr);
var constructor = constructorExpr.Compile();
byte[] memory = new byte[2];
const int count = 100_000_000;
Stopwatch stopwatch = Stopwatch.StartNew();
for (int i = 0; i < count; i++)
_ = new TestClassForConstructor(memory);
stopwatch.Stop();
Console.WriteLine($"New object: t={stopwatch.ElapsedMilliseconds}ms");
stopwatch.Restart();
for (int i = 0; i < count; i++)
_ = constructor(memory);
stopwatch.Stop();
Console.WriteLine($"Expression: t={stopwatch.ElapsedMilliseconds}ms");
}
}
Result:
New object: t=923ms
Expression: t=1203ms
Property assignment benchmark:
class TestClassForProperty
{
public string MyProperty { get; set; }
public static void RunTest()
{
TestClassForProperty obj = new TestClassForProperty();
PropertyInfo property = obj.GetType().GetProperty(nameof(MyProperty));
Action<string> setMethod = (Action<string>)property.SetMethod.CreateDelegate(typeof(Action<string>), obj);
const string data = "Hello";
const int count = 100_000_000;
Stopwatch stopwatch = Stopwatch.StartNew();
for (int i = 0; i < count; i++)
obj.MyProperty = data;
stopwatch.Stop();
Console.WriteLine($"Set property:\tt={stopwatch.ElapsedMilliseconds}ms");
stopwatch.Restart();
for (int i = 0; i < count; i++)
setMethod(data);
stopwatch.Stop();
Console.WriteLine($"Call setMethod:\tt={stopwatch.ElapsedMilliseconds}ms");
}
}
Result:
Set property: t=563ms
Call setMethod: t=564ms
Example:
public class Transaction : ISerializable
{
[Serialized(0, Serializer = typeof(UnmanagedSerializer<byte>))]
public byte Version { get; set; }
[Serialized(1, Serializer = typeof(UnmanagedSerializer<uint>))]
public uint Nonce { get; set; }
[Serialized(2, Serializer = typeof(UnmanagedSerializer<long>))]
public long Sysfee { get; set; }
[Serialized(3, Serializer = typeof(UnmanagedSerializer<long>))]
public long Netfee { get; set; }
[Serialized(4, Serializer = typeof(UnmanagedSerializer<uint>))]
public uint ValidUntilBlock { get; set; }
[Serialized(5, Max = 16)]
public Signer[] Signers { get; set; }
[Serialized(6, Serializer = typeof(PolymorphousArraySerializer<TransactionAttribute, TransactionAttributeType>), Max = 16)]
public TransactionAttribute[] Attributes { get; set; }
[Serialized(7, Max = ushort.MaxValue)]
public byte[] Script { get; set; }
[Serialized(8, Max = 16)]
public Witness[] Witnesses { get; set; }
}
In my experience, reflection always it's slower, in your benchmarks you need to increase the time spend in get the structure from the cache, i think that it will be easier, but slower.
In my experience, reflection always it's slower
It use reflection only once.
Close as inactive