我发现 ProtoBuf 性能在大量复杂对象序列化方面存在一个非常奇怪的问题。让我们有这两种情况:
A) 将对象列表一一序列化
B) 将列表作为一个整体进行序列化
根据直觉,这应该具有类似的性能。然而,在我的应用程序中,仅通过将对象放入列表并序列化列表,反序列化就有 10 倍的差异。 您可以在下面找到代码来测试这一点。在此示例中,结果在 2 倍到 5 倍加速之间变化,但在我的代码中,其加速非常一致,为 10 倍。
是什么导致了这个问题?我有一个应用程序,我需要将对象一一序列化,它的性能确实下降了,有什么方法可以提高一一序列化的性能?
谢谢
以下代码的输出
One by one serialization = 329204 ; deserialization = 41342
List serialization = 19531 ; deserialization = 27716
代码
[ProtoContract]
class TestObject
{
[ProtoMember(1)]public string str1;
[ProtoMember(2)]public string str2;
[ProtoMember(3)]public int i1;
[ProtoMember(4)]public int i2;
[ProtoMember(5)]public double d1;
[ProtoMember(6)]public double d2;
public TestObject(int cnt)
{
str1 = $"Hello World {cnt}";
str2 = $"Lorem ipsum {cnt}";
for (int i = 0; i < 2 ; i++) str1 = str1 + str1;
d1 = i1 = cnt;
d2 = i2 = cnt * 2;
}
public TestObject() { }
}
private void ProtoBufTest()
{
//init test data
List<TestObject> objects = new List<TestObject>();
int numObjects = 1000;
for(int i = 0; i < numObjects;i++)
{
objects.Add(new TestObject(i));
}
Stopwatch sw = new Stopwatch();
MemoryStream memStream = new MemoryStream();
//test 1
sw.Restart();
for (int i = 0; i < numObjects; i++)
{
ProtoBuf.Serializer.SerializeWithLengthPrefix<TestObject>(memStream, objects[i], ProtoBuf.PrefixStyle.Base128);
}
long timeToSerializeSeparately = sw.ElapsedTicks;
memStream.Position = 0;
sw.Restart();
for (int i = 0; i < numObjects; i++)
{
ProtoBuf.Serializer.DeserializeWithLengthPrefix<TestObject>(memStream, ProtoBuf.PrefixStyle.Base128);
}
long timeToDeserializeSeparately = sw.ElapsedTicks;
//test 2
memStream.Position = 0;
sw.Restart();
ProtoBuf.Serializer.SerializeWithLengthPrefix<List<TestObject>>(memStream, objects, ProtoBuf.PrefixStyle.Base128);
long timeToSerializeList = sw.ElapsedTicks;
memStream.Position = 0;
sw.Restart();
ProtoBuf.Serializer.DeserializeWithLengthPrefix<List<TestObject>>(memStream, ProtoBuf.PrefixStyle.Base128);
long timeToDeserializeList = sw.ElapsedTicks;
Console.WriteLine($"One by one serialization = {timeToSerializeSeparately} ; deserialization = {timeToDeserializeSeparately}");
Console.WriteLine($"List serialization = {timeToSerializeList} ; deserialization = {timeToDeserializeList}");
}
最佳答案
我认为您歪曲了初始反射预处理和 JIT 成本;如果我们更改它,以便它多次运行测试:
static void Main()
{
ProtoBufTest(1);
for (int i = 0; i < 10; i++)
{
ProtoBufTest(1000);
}
}
private static void ProtoBufTest(int numObjects)
{
...
然后我就得到了我期望的结果,其中单个对象代码更快。
基本上,它在第一次需要时做了很多工作,基本上正是您在这里所要求的:
is there some way to force ProtoBuf to cache reflection data between calls ? That would help a lot probably
已经发生了。作为旁注,您还可以执行以下操作:
Serializer.PrepareSerializer<TestObject>();
一旦在您的应用程序开始时,它就会尽可能多地执行操作。不过,我无法强制 JIT 发生 - 为此,您需要调用一次代码。
关于c# - 通过序列化对象列表来奇怪的 Protobuf 加速,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43209521/