protobuf-net НЕ быстрее, чем двоичная сериализация? - PullRequest
8 голосов
/ 03 июня 2010

Я написал программу для сериализации класса Person с использованием XMLSerializer, BinaryFormatter и ProtoBuf. Я думал, что protobuf-net должен быть быстрее двух других. Сериализация Protobuf была быстрее, чем XMLSerialization, но намного медленнее, чем двоичная сериализация. Мое понимание неверно? Пожалуйста, дайте мне понять это. Спасибо за помощь.

РЕДАКТИРОВАТЬ: - Я изменил код (обновлен ниже), чтобы измерить время только для сериализации, а не создания потоков и все еще вижу разницу. Можно мне сказать, почему?

Ниже выводится : -

Человек создан с использованием буфера протокола за 347 миллисекунд

Человек был создан с использованием XML за 1462 миллисекунды

Человек был создан с использованием двоичного файла за 2 миллисекунды

Код ниже

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ProtoBuf;
using System.IO;
using System.Diagnostics;
using System.Runtime.Serialization.Formatters.Binary;
namespace ProtocolBuffers
{
    class Program
    {
        static void Main(string[] args)
        {

            string folderPath  = @"E:\Ashish\Research\VS Solutions\ProtocolBuffers\ProtocolBuffer1\bin\Debug";
            string XMLSerializedFileName = Path.Combine(folderPath,"PersonXMLSerialized.xml");
            string ProtocolBufferFileName = Path.Combine(folderPath,"PersonProtocalBuffer.bin");
            string BinarySerializedFileName = Path.Combine(folderPath,"PersonBinary.bin");

            if (File.Exists(XMLSerializedFileName))
            {
                File.Delete(XMLSerializedFileName);
                Console.WriteLine(XMLSerializedFileName + " deleted");
            }
            if (File.Exists(ProtocolBufferFileName))
            {
                File.Delete(ProtocolBufferFileName);
                Console.WriteLine(ProtocolBufferFileName + " deleted");
            }
            if (File.Exists(BinarySerializedFileName))
            {
                File.Delete(BinarySerializedFileName);
                Console.WriteLine(BinarySerializedFileName + " deleted");
            }

            var person = new Person
            {
                Id = 12345,
                Name = "Fred",
                Address = new Address
                {
                    Line1 = "Flat 1",
                    Line2 = "The Meadows"
                }
            };

            Stopwatch watch = Stopwatch.StartNew();

            using (var file = File.Create(ProtocolBufferFileName))
            {
                watch.Start();
                Serializer.Serialize(file, person);
                watch.Stop();
            }

            //Console.WriteLine(watch.ElapsedMilliseconds.ToString());
            Console.WriteLine("Person got created using protocol buffer in " + watch.ElapsedMilliseconds.ToString() + " milliseconds ");

            watch.Reset();

            System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(person.GetType());
            using (TextWriter w = new StreamWriter(XMLSerializedFileName))
            {
                watch.Start();
                x.Serialize(w, person);
                watch.Stop();
            }

            //Console.WriteLine(watch.ElapsedMilliseconds.ToString());
            Console.WriteLine("Person got created using XML in " + watch.ElapsedMilliseconds.ToString() + " milliseconds");

            watch.Reset();

            using (Stream stream = File.Open(BinarySerializedFileName, FileMode.Create))
            {
                BinaryFormatter bformatter = new BinaryFormatter();
                //Console.WriteLine("Writing Employee Information");
                watch.Start();
                bformatter.Serialize(stream, person);
                watch.Stop();
            }

            //Console.WriteLine(watch.ElapsedMilliseconds.ToString());
            Console.WriteLine("Person got created using binary in " + watch.ElapsedMilliseconds.ToString() + " milliseconds");

            Console.ReadLine();



        }
    }


    [ProtoContract]
    [Serializable]
    public class Person
    {
        [ProtoMember(1)]
        public int Id { get; set; }
        [ProtoMember(2)]
        public string Name { get; set; }
        [ProtoMember(3)]
        public Address Address { get; set; }
    }
    [ProtoContract]
    [Serializable]
    public class Address
    {
        [ProtoMember(1)]
        public string Line1 { get; set; }
        [ProtoMember(2)]
        public string Line2 { get; set; }
    }
}

Ответы [ 4 ]

25 голосов
/ 04 июня 2010

я ответил на твой e-mail; Я не знала, что вы также разместили это здесь. Первый вопрос, который у меня есть: какая версия protobuf-net? Причина, по которой я спрашиваю, состоит в том, что в стволе development"v2" намеренно отключена автоматическая компиляция, поэтому я могу использовать свои модульные тесты для тестирования как во время выполнения, так и в предварительно скомпилированных версиях. Поэтому, если вы используете «v2» (доступно только в исходном коде), вам нужно указать это для компиляции модели - в противном случае он работает на 100% отражении.

В «v1» или «v2» вы можете сделать это с помощью:

Serializer.PrepareSerializer<Person>();

Сделав это, числа, которые я получаю (из кода в вашем электронном письме; я не проверял, является ли приведенный выше образец того же самого):

10
Person got created using protocol buffer in 10 milliseconds
197
Person got created using XML in 197 milliseconds
3
Person got created using binary in 3 milliseconds

Другим фактором являются повторы; Откровенно ничего не 3; Вы не можете сравнивать числа вокруг этого уровня. Подняв его, чтобы повторить 5000 раз (повторное использование экземпляров XmlSerializer / BinaryFormatter; ложные затраты не введены), я получаю:

110
Person got created using protocol buffer in 110 milliseconds
329
Person got created using XML in 329 milliseconds
133
Person got created using binary in 133 milliseconds

Принимая это к более глупым крайностям (100000):

1544
Person got created using protocol buffer in 1544 milliseconds
3009
Person got created using XML in 3009 milliseconds
3087
Person got created using binary in 3087 milliseconds

Итак, в конечном итоге:

  • когда у вас практически нет данных для сериализации, большинство подходов будут очень быстрыми (включая protobuf-net)
  • при добавлении данных различия становятся более очевидными; Как правило, protobuf здесь превосходен, как для отдельных больших графов, так и для множества маленьких графов

Также обратите внимание, что в "v2" скомпилированная модель может быть полностью статически скомпилирована (в dll, который вы можете развернуть), что устраняет даже (и без того небольшие) затраты на ускорение.

5 голосов
/ 30 сентября 2010

У меня немного другое мнение, чем отмеченный ответ. Я думаю, что числа из этих тестов отражают издержки метаданных двоичного форматера. BinaryFormatter записывает метаданные о классе сначала перед записью данных, а protobuf записывает только данные.

Для очень маленького объекта (одного объекта Person) в вашем тесте стоимость метаданных двоичного форматера весит больше, чем реальные случаи, потому что он записывает больше метаданных, чем данных. Таким образом, при увеличении количества повторений стоимость метаданных преувеличивается до уровня, аналогичного сериализации xml в крайнем случае.

Если вы сериализуете массив Person, и этот массив достаточно велик, тогда стоимость метаданных будет тривиальной к общей стоимости. Тогда двоичный форматер должен выполнить аналогично protobuf для вашего экстремального повторного теста.

PS: я нашел эту страницу, потому что оцениваю разные сериализаторы. Я также нашел блог http://blogs.msdn.com/b/youssefm/archive/2009/07/10/comparing-the-performance-of-net-serializers.aspx, в котором показан результат теста, который DataContractSerializer + двоичный XmlDictionaryWriter работает в несколько раз лучше, чем двоичный форматер. Это также проверено с очень маленькими данными. Когда я сам провел тест с большими данными, я с удивлением обнаружил, что результат сильно отличается. Поэтому проведите тестирование с реальными данными, которые вы фактически будете использовать.

4 голосов
/ 03 июня 2011

Мы постоянно сериализуем довольно большие объекты (около 50 свойств), поэтому я написал небольшой тест для сравнения BinaryFormatter и protobuf-net, как вы сделали, и вот мои результаты (10000 объектов):

BinaryFormatter serialize: 316
BinaryFormatter deserialize: 279
protobuf serialize: 243
protobuf deserialize: 139
BinaryFormatter serialize: 315
BinaryFormatter deserialize: 281
protobuf serialize: 127
protobuf deserialize: 110

Это, очевидно, очень заметная разница. Он также намного быстрее при втором запуске (тесты точно такие же), чем при первом.

Обновление. Doing RuntimeTypeModel.Add..Compile генерирует следующие результаты:

BinaryFormatter serialize: 303
BinaryFormatter deserialize: 282
protobuf serialize: 113
protobuf deserialize: 50
BinaryFormatter serialize: 317
BinaryFormatter deserialize: 266
protobuf serialize: 126
protobuf deserialize: 49
0 голосов
/ 19 июня 2015

Если мы сравниваем в памяти, жестко запрограммированная сериализация будет в некоторых ситуациях более быстрой. Если ваш класс простой, возможно, лучше напишет собственный сериализатор ...

Слегка измененный код:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ProtoBuf;
using System.IO;
using System.Diagnostics;
using System.Runtime.Serialization.Formatters.Binary;

namespace ProtocolBuffers
{
    class Program
    {
        static void Main(string[] args)
        {

            string folderPath = @"../Debug";
            string XMLSerializedFileName = Path.Combine(folderPath, "PersonXMLSerialized.xml");
            string ProtocolBufferFileName = Path.Combine(folderPath, "PersonProtocalBuffer.bin");
            string BinarySerializedFileName = Path.Combine(folderPath, "PersonBinary.bin");
            string BinarySerialized2FileName = Path.Combine(folderPath, "PersonBinary2.bin");

            if (File.Exists(XMLSerializedFileName))
            {
                File.Delete(XMLSerializedFileName);
                Console.WriteLine(XMLSerializedFileName + " deleted");
            }
            if (File.Exists(ProtocolBufferFileName))
            {
                File.Delete(ProtocolBufferFileName);
                Console.WriteLine(ProtocolBufferFileName + " deleted");
            }
            if (File.Exists(BinarySerializedFileName))
            {
                File.Delete(BinarySerializedFileName);
                Console.WriteLine(BinarySerializedFileName + " deleted");
            }
            if (File.Exists(BinarySerialized2FileName))
            {
                File.Delete(BinarySerialized2FileName);
                Console.WriteLine(BinarySerialized2FileName + " deleted");
            }

            var person = new Person
            {
                Id = 12345,
                Name = "Fred",
                Address = new Address
                {
                    Line1 = "Flat 1",
                    Line2 = "The Meadows"
                }
            };

            Stopwatch watch = Stopwatch.StartNew();

            using (var file = new MemoryStream())
            //using (var file = File.Create(ProtocolBufferFileName))
            {
                watch.Start();
                for (int i = 0; i < 100000; i++)
                    Serializer.Serialize(file, person);
                watch.Stop();
            }

            Console.WriteLine("Person got created using protocol buffer in " + watch.ElapsedMilliseconds.ToString() + " milliseconds ");

            watch.Reset();

            System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(person.GetType());
            using (var w = new MemoryStream())
            //using (TextWriter w = new StreamWriter(XMLSerializedFileName))
            {
                watch.Start();
                for (int i = 0; i < 100000; i++)
                    x.Serialize(w, person);
                watch.Stop();
            }

            Console.WriteLine("Person got created using XML in " + watch.ElapsedMilliseconds.ToString() + " milliseconds");

            watch.Reset();

            using (var stream = new MemoryStream())
            //using (Stream stream = File.Open(BinarySerializedFileName, FileMode.Create))
            {
                BinaryFormatter bformatter = new BinaryFormatter();
                watch.Start();
                for (int i = 0; i < 100000; i++)
                    bformatter.Serialize(stream, person);
                watch.Stop();
            }

            Console.WriteLine("Person got created using binary in " + watch.ElapsedMilliseconds.ToString() + " milliseconds");

            watch.Reset();

            using (var stream = new MemoryStream())
            //using (Stream stream = File.Open(BinarySerialized2FileName, FileMode.Create))
            {
                BinaryWriter writer = new BinaryWriter(stream);
                watch.Start();
                for (int i = 0; i < 100000; i++)
                    writer.Write(person.GetBytes());
                watch.Stop();
            }

            Console.WriteLine("Person got created using binary2 in " + watch.ElapsedMilliseconds.ToString() + " milliseconds");

            Console.ReadLine();
        }
    }


    [ProtoContract]
    [Serializable]
    public class Person
    {
        [ProtoMember(1)]
        public int Id { get; set; }
        [ProtoMember(2)]
        public string Name { get; set; }
        [ProtoMember(3)]
        public Address Address { get; set; }

        public byte[] GetBytes()
        {
            using (var stream = new MemoryStream())
            {
                BinaryWriter writer = new BinaryWriter(stream);

                writer.Write(this.Id);
                writer.Write(this.Name);
                writer.Write(this.Address.GetBytes());

                return stream.ToArray();
            }
        }

        public Person()
        {
        }

        public Person(byte[] bytes)
        {
            using (var stream = new MemoryStream(bytes))
            {
                BinaryReader reader = new BinaryReader(stream);

                Id = reader.ReadInt32();
                Name = reader.ReadString();

                int bytesForAddressLenght = (int)(stream.Length - stream.Position);
                byte[] bytesForAddress = new byte[bytesForAddressLenght];
                Array.Copy(bytes, (int)stream.Position, bytesForAddress, 0, bytesForAddressLenght);
                Address = new Address(bytesForAddress);
            }
        }
    }
    [ProtoContract]
    [Serializable]
    public class Address
    {
        [ProtoMember(1)]
        public string Line1 { get; set; }
        [ProtoMember(2)]
        public string Line2 { get; set; }

        public byte[] GetBytes()
        {
            using(var stream = new MemoryStream())
            {
                BinaryWriter writer = new BinaryWriter(stream);

                writer.Write(this.Line1);
                writer.Write(this.Line2);

                return stream.ToArray();
            }
        }

        public Address()
        {

        }

        public Address(byte[] bytes)
        {
            using(var stream = new MemoryStream(bytes))
            {
                BinaryReader reader = new BinaryReader(stream);

                Line1 = reader.ReadString();
                Line2 = reader.ReadString();
            }
        }
    }
}

и мои результаты:

Person got created using protocol buffer in 141 milliseconds
Person got created using XML in 676 milliseconds
Person got created using binary in 525 milliseconds
Person got created using binary2 in 79 milliseconds
...