csharp protobuf-net / MessagePack / Json.NET序列化测试

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了csharp protobuf-net / MessagePack / Json.NET序列化测试相关的知识,希望对你有一定的参考价值。

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Security.Cryptography;
using System.Text;
using Newtonsoft.Json;

namespace MessagePackVsProtobuf
{
    [Serializable]
    [DataContract]
    public class Person
    {
        [DataMember(Order = 0)]
        public virtual int Age { get; set; }

        [DataMember(Order = 1)]
        public virtual string FirstName { get; set; }

        [DataMember(Order = 2)]
        public virtual string LastName { get; set; }

        [DataMember(Order = 3)]
        public virtual Sex Sex { get; set; }

        [DataMember(Order = 4)]
        public virtual IDictionary<string, string> Items { get; set; }
    }

    public class Person2
    {
        public virtual int Age { get; set; }

        public virtual string FirstName { get; set; }

        public virtual string LastName { get; set; }

        public virtual Sex Sex { get; set; }

        public virtual IDictionary<string, string> Items { get; set; }
    }

    public enum Sex
    {
        Unknown,

        Male,

        Female,
    }

    public class Report
    {
        public string Name { get; set; }

        public string TestType { get; set; }

        public double SerializeTime { get; set; }

        public double DeserializeTime { get; set; }

        public double ReSerializeTime { get; set; }

        public long Size { get; set; }
    }

    internal class Program
    {
        private const int _iteration = 10000;

        private const int _times = 3;

        private static bool _dryRun = true;

        private static List<Report> _reports = new List<Report>();

        private const string _filename = "report.json";

        private static void Main(string[] args)
        {
            var p = new Person
            {
                Age = 99,
                FirstName = NextString(6),
                LastName = NextString(6),
                Sex = Sex.Male,
                Items = new Dictionary<string, string> {{NextString(6), NextString(6)}}
            };
            IList<Person> l = Enumerable.Range(1, 1000)
                                        .Select(
                                            x => new Person
                                            {
                                                Age = x,
                                                FirstName = NextString(6),
                                                LastName = NextString(6),
                                                Sex = Sex.Female,
                                                Items = new Dictionary<string, string> {{NextString(6), NextString(6)}}
                                            })
                                        .ToArray();

            #region protobuf-net single

            {
                _dryRun = true;
                SerializeProtobuf(p, null);
                _dryRun = false;
                var reports = new List<Report>();
                for (int i = 0; i < _times; i++)
                {
                    var report = new Report()
                    {
                        TestType = "Single Object",
                        Name = "mgravell/protobuf-net",
                    };
                    var b = SerializeProtobuf(p, report);
                    reports.Add(report);
                }

                var averageReport = new Report
                {
                    TestType = "Single Object",
                    Name = "mgravell/protobuf-net",
                    SerializeTime = reports.Average(report => report.SerializeTime),
                    DeserializeTime = reports.Average(report => report.DeserializeTime),
                    ReSerializeTime = reports.Average(report => report.ReSerializeTime),
                    Size = reports.FirstOrDefault()?.Size ?? 0
                };
                _reports.Add(averageReport);
            }

            #endregion

            #region Official MsgPack-Cli single

            {
                _dryRun = true;
                SerializeMsgPack(p, null);
                _dryRun = false;
                var reports = new List<Report>();
                for (int i = 0; i < _times; i++)
                {
                    var report = new Report()
                    {
                        TestType = "Single Object",
                        Name = "Official MsgPack-Cli",
                    };
                    var b = SerializeMsgPack(p, report);
                    reports.Add(report);
                }

                var averageReport = new Report
                {
                    TestType = "Single Object",
                    Name = "Official MsgPack-Cli",
                    SerializeTime = reports.Average(report => report.SerializeTime),
                    DeserializeTime = reports.Average(report => report.DeserializeTime),
                    ReSerializeTime = reports.Average(report => report.ReSerializeTime),
                    Size = reports.FirstOrDefault()?.Size ?? 0
                };

                _reports.Add(averageReport);
            }

            #endregion

            #region neuecc/MessagePack-CSharp single

            {
                _dryRun = true;
                SerializeMessagePack(p, null);
                _dryRun = false;
                var reports = new List<Report>();
                for (int i = 0; i < _times; i++)
                {
                    var report = new Report()
                    {
                        TestType = "Single Object",
                        Name = "neuecc/MessagePack-CSharp",
                    };
                    var b = SerializeMessagePack(p, report);
                    reports.Add(report);
                }

                var averageReport = new Report
                {
                    TestType = "Single Object",
                    Name = "neuecc/MessagePack-CSharp",
                    SerializeTime = reports.Average(report => report.SerializeTime),
                    DeserializeTime = reports.Average(report => report.DeserializeTime),
                    ReSerializeTime = reports.Average(report => report.ReSerializeTime),
                    Size = reports.FirstOrDefault()?.Size ?? 0
                };

                _reports.Add(averageReport);
            }

            #endregion

            #region Newtonsoft.Json single

            {
                _dryRun = true;
                SerializeNewtonsoftJson(p, null);
                _dryRun = false;
                var reports = new List<Report>();
                for (int i = 0; i < _times; i++)
                {
                    var report = new Report()
                    {
                        TestType = "Single Object",
                        Name = "Newtonsoft.Json",
                    };
                    var b = SerializeNewtonsoftJson(p, report);
                    reports.Add(report);
                }

                var averageReport = new Report
                {
                    TestType = "Single Object",
                    Name = "Newtonsoft.Json",
                    SerializeTime = reports.Average(report => report.SerializeTime),
                    DeserializeTime = reports.Average(report => report.DeserializeTime),
                    ReSerializeTime = reports.Average(report => report.ReSerializeTime),
                    Size = reports.FirstOrDefault()?.Size ?? 0
                };

                _reports.Add(averageReport);
            }

            #endregion

            #region protobuf-net array

            {
                _dryRun = true;
                SerializeProtobuf(l, null);
                _dryRun = false;
                var reports = new List<Report>();
                for (int i = 0; i < _times; i++)
                {
                    var report = new Report()
                    {
                        TestType = "Large Array",
                        Name = "mgravell/protobuf-net",
                    };
                    var b = SerializeProtobuf(l, report);
                    reports.Add(report);
                }

                var averageReport = new Report
                {
                    TestType = "Large Array",
                    Name = "mgravell/protobuf-net",
                    SerializeTime = reports.Average(report => report.SerializeTime),
                    DeserializeTime = reports.Average(report => report.DeserializeTime),
                    ReSerializeTime = reports.Average(report => report.ReSerializeTime),
                    Size = reports.FirstOrDefault()?.Size ?? 0
                };

                _reports.Add(averageReport);
            }

            #endregion

            #region Official MsgPack-Cli array

            {
                _dryRun = true;
                SerializeMsgPack(l, null);
                _dryRun = false;
                var reports = new List<Report>();
                for (int i = 0; i < _times; i++)
                {
                    var report = new Report()
                    {
                        TestType = "Large Array",
                        Name = "Official MsgPack-Cli",
                    };
                    var b = SerializeMsgPack(l, report);
                    reports.Add(report);
                }

                var averageReport = new Report
                {
                    TestType = "Large Array",
                    Name = "Official MsgPack-Cli",
                    SerializeTime = reports.Average(report => report.SerializeTime),
                    DeserializeTime = reports.Average(report => report.DeserializeTime),
                    ReSerializeTime = reports.Average(report => report.ReSerializeTime),
                    Size = reports.FirstOrDefault()?.Size ?? 0
                };

                _reports.Add(averageReport);
            }

            #endregion

            #region neuecc/MessagePack-CSharp array

            {
                _dryRun = true;
                SerializeMessagePack(l, null);
                _dryRun = false;
                var reports = new List<Report>();
                for (int i = 0; i < _times; i++)
                {
                    var report = new Report()
                    {
                        TestType = "Large Array",
                        Name = "neuecc/MessagePack-CSharp",
                    };
                    var b = SerializeMessagePack(l, report);
                    reports.Add(report);
                }

                var averageReport = new Report
                {
                    TestType = "Large Array",
                    Name = "neuecc/MessagePack-CSharp",
                    SerializeTime = reports.Average(report => report.SerializeTime),
                    DeserializeTime = reports.Average(report => report.DeserializeTime),
                    ReSerializeTime = reports.Average(report => report.ReSerializeTime),
                    Size = reports.FirstOrDefault()?.Size ?? 0
                };

                _reports.Add(averageReport);
            }

            #endregion

            #region Newtonsoft.Json array

            {
                _dryRun = true;
                SerializeNewtonsoftJson(l, null);
                _dryRun = false;
                var reports = new List<Report>();
                for (int i = 0; i < _times; i++)
                {
                    var report = new Report()
                    {
                        TestType = "Large Array",
                        Name = "Newtonsoft.Json",
                    };
                    var b = SerializeNewtonsoftJson(l, report);
                    reports.Add(report);
                }

                var averageReport = new Report
                {
                    TestType = "Large Array",
                    Name = "Newtonsoft.Json",
                    SerializeTime = reports.Average(report => report.SerializeTime),
                    DeserializeTime = reports.Average(report => report.DeserializeTime),
                    ReSerializeTime = reports.Average(report => report.ReSerializeTime),
                    Size = reports.FirstOrDefault()?.Size ?? 0
                };

                _reports.Add(averageReport);
            }

            #endregion

            // Without Attribute

            global::MessagePack.MessagePackSerializer.SetDefaultResolver(
                MessagePack.Resolvers.ContractlessStandardResolver.Instance);

            var p2 = new Person2
            {
                Age = 99,
                FirstName = NextString(6),
                LastName = NextString(6),
                Sex = Sex.Male,
                Items = new Dictionary<string, string> {{NextString(6), NextString(6)}}
            };
            IList<Person2> l2 = Enumerable.Range(1, 1000)
                                          .Select(
                                              x => new Person2
                                              {
                                                  Age = x,
                                                  FirstName = NextString(6),
                                                  LastName = NextString(6),
                                                  Sex = Sex.Female,
                                                  Items = new Dictionary<string, string>
                                                  {
                                                      {NextString(6), NextString(6)}
                                                  }
                                              })
                                          .ToArray();

            #region neuecc/MessagePack-CSharp single

            {
                _dryRun = true;
                SerializeMessagePack(p2, null);
                _dryRun = false;
                var reports = new List<Report>();
                for (int i = 0; i < _times; i++)
                {
                    var report = new Report()
                    {
                        TestType = "Single Object",
                        Name = "neuecc/MessagePack-CSharp without Attribute",
                    };
                    var b = SerializeMessagePack(p2, report);
                    reports.Add(report);
                }

                var averageReport = new Report
                {
                    TestType = "Single Object",
                    Name = "neuecc/MessagePack-CSharp without Attribute",
                    SerializeTime = reports.Average(report => report.SerializeTime),
                    DeserializeTime = reports.Average(report => report.DeserializeTime),
                    ReSerializeTime = reports.Average(report => report.ReSerializeTime),
                    Size = reports.FirstOrDefault()?.Size ?? 0
                };

                _reports.Add(averageReport);
            }

            #endregion

            #region neuecc/MessagePack-CSharp array

            {
                _dryRun = true;
                SerializeMessagePack(l2, null);
                _dryRun = false;
                var reports = new List<Report>();
                for (int i = 0; i < _times; i++)
                {
                    var report = new Report()
                    {
                        TestType = "Large Array",
                        Name = "neuecc/MessagePack-CSharp without Attribute",
                    };
                    var b = SerializeMessagePack(l2, report);
                    reports.Add(report);
                }

                var averageReport = new Report
                {
                    TestType = "Large Array",
                    Name = "neuecc/MessagePack-CSharp without Attribute",
                    SerializeTime = reports.Average(report => report.SerializeTime),
                    DeserializeTime = reports.Average(report => report.DeserializeTime),
                    ReSerializeTime = reports.Average(report => report.ReSerializeTime),
                    Size = reports.FirstOrDefault()?.Size ?? 0
                };

                _reports.Add(averageReport);
            }

            #endregion

            File.WriteAllText(_filename, JsonConvert.SerializeObject(_reports));
            Console.WriteLine("Test done");
        }

        private static T SerializeProtobuf<T>(T original, Report report)
        {
            Console.WriteLine(report?.Name);

            T copy = default(T);
            MemoryStream stream = null;

            using (new Measure(Measure.MeasureType.Serialize, report))
            {
                for (int i = 0; i < _iteration; i++)
                {
                    ProtoBuf.Serializer.Serialize<T>(stream = new MemoryStream(), original);
                }
            }

            using (new Measure(Measure.MeasureType.Deserialize, report))
            {
                for (int i = 0; i < _iteration; i++)
                {
                    stream.Position = 0;
                    copy = ProtoBuf.Serializer.Deserialize<T>(stream);
                }
            }

            using (new Measure(Measure.MeasureType.ReSerialize, report))
            {
                for (int i = 0; i < _iteration; i++)
                {
                    ProtoBuf.Serializer.Serialize<T>(stream = new MemoryStream(), copy);
                }
            }

            if (!_dryRun)
            {
                Console.WriteLine(string.Format("{0,15} {1}", "Size of Binary", ToHumanReadableSize(stream.Position)));
                report.Size = stream.Position;
            }

            return copy;
        }

        private static T SerializeMsgPack<T>(T original, Report report)
        {
            Console.WriteLine(report?.Name);

            T copy = default(T);

            MemoryStream stream = null;
            using (new Measure(Measure.MeasureType.Serialize, report))
            {
                for (int i = 0; i < _iteration; i++)
                {
                    MsgPack.Serialization.MessagePackSerializer.Get<T>().Pack(stream = new MemoryStream(), original);
                }
            }

            using (new Measure(Measure.MeasureType.Deserialize, report))
            {
                for (int i = 0; i < _iteration; i++)
                {
                    stream.Position = 0;
                    copy = MsgPack.Serialization.MessagePackSerializer.Get<T>().Unpack(stream);
                }
            }

            using (new Measure(Measure.MeasureType.ReSerialize, report))
            {
                for (int i = 0; i < _iteration; i++)
                {
                    MsgPack.Serialization.MessagePackSerializer.Get<T>().Pack(stream = new MemoryStream(), copy);
                }
            }

            if (!_dryRun)
            {
                Console.WriteLine(string.Format("{0,15} {1}", "Size of Binary", ToHumanReadableSize(stream.Position)));
                report.Size = stream.Position;
            }
            return copy;
        }

        private static T SerializeMessagePack<T>(T original, Report report)
        {
            Console.WriteLine(report?.Name);

            T copy = default(T);
            MemoryStream stream = null;

            using (new Measure(Measure.MeasureType.Serialize, report))
            {
                for (int i = 0; i < _iteration; i++)
                {
                    global::MessagePack.MessagePackSerializer.Serialize(stream = new MemoryStream(), original);
                }
            }

            using (new Measure(Measure.MeasureType.Deserialize, report))
            {
                for (int i = 0; i < _iteration; i++)
                {
                    stream.Position = 0;
                    copy = global::MessagePack.MessagePackSerializer.Deserialize<T>(stream);
                }
            }

            using (new Measure(Measure.MeasureType.ReSerialize, report))
            {
                for (int i = 0; i < _iteration; i++)
                {
                    global::MessagePack.MessagePackSerializer.Serialize(stream = new MemoryStream(), copy);
                }
            }

            if (!_dryRun)
            {
                Console.WriteLine(string.Format("{0,15} {1}", "Size of Binary", ToHumanReadableSize(stream.Position)));
                report.Size = stream.Position;
            }
            return copy;
        }

        private static T SerializeNewtonsoftJson<T>(T original, Report report)
        {
            Console.WriteLine(report?.Name);

            var jsonSerializer = new JsonSerializer();
            T copy = default(T);
            MemoryStream stream = null;

            using (new Measure(Measure.MeasureType.Serialize, report))
            {
                for (int i = 0; i < _iteration; i++)
                {
                    stream = new MemoryStream();
                    using (var tw = new StreamWriter(stream, Encoding.UTF8, 1024, true))
                    using (var jw = new JsonTextWriter(tw))
                    {
                        jsonSerializer.Serialize(jw, original);
                    }
                }
            }

            using (new Measure(Measure.MeasureType.Deserialize, report))
            {
                for (int i = 0; i < _iteration; i++)
                {
                    stream.Position = 0;
                    using (var tr = new StreamReader(stream, Encoding.UTF8, false, 1024, true))
                    using (var jr = new JsonTextReader(tr))
                    {
                        copy = jsonSerializer.Deserialize<T>(jr);
                    }
                }
            }

            using (new Measure(Measure.MeasureType.ReSerialize, report))
            {
                for (int i = 0; i < _iteration; i++)
                {
                    stream = new MemoryStream();
                    using (var tw = new StreamWriter(stream, Encoding.UTF8, 1024, true))
                    using (var jw = new JsonTextWriter(tw))
                    {
                        jsonSerializer.Serialize(jw, copy);
                    }
                }
            }

            if (!_dryRun)
            {
                Console.WriteLine(string.Format("{0,15} {1}", "Size of Binary", ToHumanReadableSize(stream.Position)));
                report.Size = stream.Position;
            }

            return copy;
        }

        private struct Measure : IDisposable
        {
            public enum MeasureType
            {
                Serialize,

                Deserialize,

                ReSerialize
            }

            private readonly MeasureType _measureType;

            private readonly Report _report;

            private readonly Stopwatch _stopwatch;

            public Measure(MeasureType measureType, Report report)
            {
                _measureType = measureType;
                _report = report;
                _stopwatch = Stopwatch.StartNew();
            }

            public void Dispose()
            {
                _stopwatch.Stop();
                if (!_dryRun)
                {
                    Console.WriteLine($"{_measureType.ToString(),15}   {_stopwatch.Elapsed.TotalMilliseconds} ms");
                    switch (_measureType)
                    {
                    case MeasureType.Serialize:
                        _report.SerializeTime = _stopwatch.Elapsed.TotalMilliseconds;
                        break;
                    case MeasureType.Deserialize:
                        _report.DeserializeTime = _stopwatch.Elapsed.TotalMilliseconds;
                        break;
                    case MeasureType.ReSerialize:
                        _report.ReSerializeTime = _stopwatch.Elapsed.TotalMilliseconds;
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                    }
                }

                GC.Collect();
                GC.WaitForPendingFinalizers();
                GC.Collect();
            }
        }

        private static string ToHumanReadableSize(long size)
        {
            return ToHumanReadableSize(new Nullable<long>(size));
        }

        private static string ToHumanReadableSize(long? size)
        {
            if (size == null)
                return "NULL";

            double bytes = size.Value;

            if (bytes <= 1024)
                return bytes.ToString("f2") + " B";

            bytes = bytes / 1024;
            if (bytes <= 1024)
                return bytes.ToString("f2") + " KB";

            bytes = bytes / 1024;
            if (bytes <= 1024)
                return bytes.ToString("f2") + " MB";

            bytes = bytes / 1024;
            if (bytes <= 1024)
                return bytes.ToString("f2") + " GB";

            bytes = bytes / 1024;
            if (bytes <= 1024)
                return bytes.ToString("f2") + " TB";

            bytes = bytes / 1024;
            if (bytes <= 1024)
                return bytes.ToString("f2") + " PB";

            bytes = bytes / 1024;
            if (bytes <= 1024)
                return bytes.ToString("f2") + " EB";

            bytes = bytes / 1024;
            return bytes + " ZB";
        }

        /// <summary>
        /// 生成随机字符串,字典为[A-Za-z0-9]
        /// </summary>
        /// <param name="length">字符串长度</param>
        /// <returns></returns>
        public static string NextString(int length)
        {
            const string dictionary = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
            int dictCount = dictionary.Length;
            var builder = new StringBuilder(length);
            var bytes = new byte[length * 4];
            RandomNumberGenerator.Create().GetBytes(bytes);
            for (var i = 0; i < length; ++i)
            {
                builder.Append(dictionary[Math.Abs(BitConverter.ToInt32(bytes, i * 4)) % dictCount]);
            }

            return builder.ToString();
        }
    }
}

以上是关于csharp protobuf-net / MessagePack / Json.NET序列化测试的主要内容,如果未能解决你的问题,请参考以下文章

csharp 与文化串联的月份西班牙语/ Mes a partir de una Fecha en C#conculturaEspañol

如何使用 protobuf-net 扩展?

Protobuf的C#使用

如何在 Protobuf-net 中动态添加 Proto 成员

protobuf-net:日期时间的编码

Protobuf-net:嵌套的 IEnumerable 对象