8

XMLSerializer、BinaryFormatter、ProtoBufを使用して「Person」クラスをシリアル化するプログラムを作成しました。protobuf-netは他の2つよりも速いはずだと思いました。Protobufのシリアル化はXMLSerializationよりも高速でしたが、バイナリのシリアル化よりもはるかに低速でした。私の理解は間違っていますか?これを理解させてください。お手伝いありがとう。

編集:-コードを変更して(以下で更新)、シリアル化の時間を測定し、ストリームを作成せずに、違いを確認しました。理由を教えてもらえますか?

以下は出力です:-

人は347ミリ秒でプロトコルバッファを使用して作成されました

人は1462ミリ秒でXMLを使用して作成されました

人は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

4 に答える 4

25

あなたのメールに返信しました。ここにも投稿されていたとは知りませんでした。最初の質問は、protobuf-net のどのバージョンですか? 私が質問する理由は、"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

もう 1 つの要因は繰り返しです。3 ~ 10 ミリ秒は率直に言って何でもありません。このレベルの数値を比較することはできません。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 に)、(すでに小さい) スピンアップ コストさえも除去できることに注意してください。

于 2010-06-03T23:03:09.233 に答える
5

私はマークされた答えとは少し異なる意見を持っています。これらのテストの数値は、バイナリ フォーマッタのメタデータ オーバーヘッドを反映していると思います。BinaryFormatter は、データを書き込む前にクラスに関するメタデータを最初に書き込みますが、protobuf はデータのみを書き込みます。

テストの非常に小さなオブジェクト(1人のオブジェクト)の場合、データよりもメタデータを書いているため、バイナリフォーマッタのメタデータコストは実際のケースよりも重量があります。したがって、繰り返しカウントを増やすと、極端な場合にXMLシリアル化と同じレベルまで、メタデータコストが誇張されています。

人アレイをシリアル化し、アレイが十分に大きい場合、メタデータコストは総コストに対して些細なものになります。次に、バイナリフォーマッタは、極端な繰り返しテストのためにProtoBufと同様に実行する必要があります。

PS: さまざまなシリアライザーを評価しているため、このページを見つけました。DataContractSerializer + バイナリ XmlDictionaryWriter が実行するテスト結果を示すブログhttp://blogs.msdn.com/b/youssefm/archive/2009/07/10/comparing-the-performance-of-net-serializers.aspxも見つけましたバイナリフォーマッタよりも数倍優れています。また、非常に小さなデータでテストしました。大きなデータを使って自分でテストを行ったところ、結果が大きく異なることに驚きました。したがって、実際に使用する実際のデータでテストしてください。

于 2010-09-30T08:18:23.393 に答える
4

非常に大きなオブジェクト (約 50 個のプロパティ) を常にシリアル化するため、BinaryFormatter と protobuf-net を比較する小さなテストを作成しました。

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

それは明らかに非常に顕著な違いです。また、最初の実行よりも 2 回目の実行 (テストはまったく同じ) の方がはるかに高速です。

アップデート。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
于 2011-06-03T04:52:53.203 に答える
0

メモリ内で比較すると、ハードコードされたシリアル化は状況によってはかなり高速になります。クラスが単純な場合は、独自のシリアライザーを作成する方がよいでしょう...

少し変更されたコード:

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
于 2015-06-19T18:05:57.027 に答える