c# - 通过序列化对象列表来奇怪的 Protobuf 加速

标签 c# .net list serialization protobuf-net

我发现 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/

相关文章:

c# - EF核心: inserting large amounts of data

c# - COM dll 的有效 .cs 文件

c# - 在 .NET 2.0 上消除文本锯齿的更好方法吗?

.net - 循环外键。我该如何处理它们?

python - 无法弄清楚如何停止返回列表内的列表(leetcode问题)

c# - C#中的API调用结构

c# - .MakeArrayType() 和 .MakeArrayType(1) 的区别

c# - 每次在 C# 中的特殊字符之前剪切一个字符串

python - 读取包含值列表到数组中的文件

html - (新)制作水平列表