【问题标题】:Protobuf-net Vs BinarySerializer Serialized List<T> SizeProtobuf-net Vs BinarySerializer 序列化列表<T> 大小
【发布时间】:2013-01-28 21:27:06
【问题描述】:

我已经开始使用 protobuf-net 库。它将序列化速度提高了 30%,但我对生成的文件大小有疑问。

我的数据模型是:

    [Serializable]
    [ProtoContract(SkipConstructor = true)]
    private class ReportDataItem
    {
        [ProtoMember(1)]
        public Int32 C11 { get; set; }
        [ProtoMember(2)]
        public Int32 C12 { get; set; }
        [ProtoMember(3)]
        public Int32 C13 { get; set; }
        [ProtoMember(4)]
        public Int32 C14 { get; set; }
        [ProtoMember(5)]
        public Int32 C15 { get; set; }
        [ProtoMember(6)]
        public Int32 C16 { get; set; }
        [ProtoMember(7)]
        public Int32 C17 { get; set; }
        [ProtoMember(8)]
        public Int32 C18 { get; set; }
        [ProtoMember(9)]
        public Int32 C19 { get; set; }
        [ProtoMember(10)]
        public Int32 C110 { get; set; }

        [ProtoMember(11)]
        public Int64 C21 { get; set; }
        [ProtoMember(12)]
        public Int64 C22 { get; set; }
        [ProtoMember(13)]
        public Int64 C23 { get; set; }
        [ProtoMember(14)]
        public Int64 C24 { get; set; }
        [ProtoMember(15)]
        public Int64 C25 { get; set; }
        [ProtoMember(16)]
        public Int64 C26 { get; set; }
        [ProtoMember(17)]
        public Int64 C27 { get; set; }
        [ProtoMember(18)]
        public Int64 C28 { get; set; }
        [ProtoMember(19)]
        public Int64 C29 { get; set; }
        [ProtoMember(20)]
        public Int64 C210 { get; set; }

        [ProtoMember(21)]
        public String C31 { get; set; }
        [ProtoMember(22)]
        public String C32 { get; set; }
        [ProtoMember(23)]
        public String C33 { get; set; }
        [ProtoMember(24)]
        public String C34 { get; set; }
        [ProtoMember(25)]
        public String C35 { get; set; }
        [ProtoMember(26)]
        public String C36 { get; set; }
        [ProtoMember(27)]
        public String C37 { get; set; }
        [ProtoMember(28)]
        public String C38 { get; set; }
        [ProtoMember(29)]
        public String C39 { get; set; }
        [ProtoMember(30)]
        public String C310 { get; set; }
    }

    [Serializable]
    [ProtoContract()]
    private class ReportData
    {
        [ProtoMember(1, DataFormat = DataFormat.Group)]
        public List<ReportDataItem> ReportDataItems { get; set; }
    }

    [Serializable]
    [ProtoContract()]
    private class Report
    {
        [ProtoMember(1)]
        public ReportData ReportData { get; set; }
    }

所以当我尝试序列化时:

    private static void ObjectSerialization()
    {

const string someData = @"qtwretyfsjdabvfsjdlfudspogds;kfg;lkfdsl;gkl;dsfkgl;kdfsgr;iweprpo\z\xlvcfmxzcbvjiorsdifdlf\jl;dsa";

            Report report = new Report();
            report.ReportData = new ReportData {ReportDataItems = new List<ReportDataItem>()};

            for (int j = 0; j < 10; j++)
            {
                ReportDataItem reportDataItem = new ReportDataItem();

                reportDataItem.C11 = j;
                reportDataItem.C12 = j;
                reportDataItem.C13 = j;
                reportDataItem.C14 = j;
                reportDataItem.C15 = j;
                reportDataItem.C16 = j;
                reportDataItem.C17 = j;
                reportDataItem.C18 = j;
                reportDataItem.C19 = j;
                reportDataItem.C110 = j;

                reportDataItem.C21 = j;
                reportDataItem.C22 = j;
                reportDataItem.C23 = j;
                reportDataItem.C24 = j;
                reportDataItem.C25 = j;
                reportDataItem.C26 = j;
                reportDataItem.C27 = j;
                reportDataItem.C28 = j;
                reportDataItem.C29 = j;
                reportDataItem.C210 = j;

                reportDataItem.C31 =someData;
                reportDataItem.C32 = someData;
                reportDataItem.C33 = someData;
                reportDataItem.C34 = someData;
                reportDataItem.C35 = someData;
                reportDataItem.C36 = someData;
                reportDataItem.C37 = someData;
                reportDataItem.C38 = someData;
                reportDataItem.C39 = someData;
                reportDataItem.C310 = someData;

                report.ReportData.ReportDataItems.Add(reportDataItem);
            }

            using (Stream stream = new FileStream(@"c:\Test\Object\0.bin", FileMode.Create, FileAccess.Write, FileShare.Write))
            {
                Serializer.Serialize(stream, report);
            }

            using (Stream stream = new FileStream(@"c:\Test\Object\bf_0.bin", FileMode.Create, FileAccess.Write, FileShare.Write))
            {
                BinaryFormatter formatter = new BinaryFormatter();
                formatter.Serialize(stream, report);
            }
}

我在下面提供了结果:

  • protobuf-net 文件大小 10428 字节
  • BinaryFormatter 文件大小3458 字节

您能帮我找到减小结果 protobuf-net 文件大小的正确解决方案吗? Protobuf-net 我从 VS 包管理器作为包安装。

【问题讨论】:

  • 通过压缩流运行它怎么样?哦,使用这样的重复值对我来说似乎不是一个有代表性的测试。
  • 我刚刚运行了你的测试,我从 protobuf-net 获得了 1628 个字节;请验证
  • 没有压缩的标准 .NET BinaryFormatter 的主要思想是运行速度较慢,但​​文件比 protobuf-net 库更小。我正在寻找如何通过 protobuf-net 库中的功能改进代码的解决方案。也许是某些属性或其他特征。执行速度对我来说非常重要。这也是我尝试使用 protobuf-net 的原因之一。
  • @Puh 我 认为 我知道发生了什么 - 您的实际测试使用的字符串比“一些数据”更长,对吧?查看我的答案的更新。
  • @Marc Gravell 是的,谢谢你的评论。你说的对。很抱歉造成混乱。

标签: .net protobuf-net


【解决方案1】:

我把最后几行改成:

using (Stream stream = new FileStream(@"pb.bin", FileMode.Create,
     FileAccess.Write, FileShare.Write))
{
    Serializer.Serialize(stream, report);
    Console.WriteLine(stream.Length);
}
Console.WriteLine(new FileInfo("pb.bin").Length);

using (Stream stream = new FileStream(@"bf.bin", FileMode.Create,
     FileAccess.Write, FileShare.Write))
{
    BinaryFormatter formatter = new BinaryFormatter();
    formatter.Serialize(stream, report);
    Console.WriteLine(stream.Length);
}
Console.WriteLine(new FileInfo("bf.bin").Length);

获取写入流的数据量,以及文件的最终大小。我的结果:

1628
1628
3144
3144

这对我来说看起来不错。请验证您的数据。

您使用的字符串是否可能比“某些数据”大得多?如果是这样,那么有一个重要的问题:您是否可能在实际代码中复制字符串?如果你不是,那么 BF 测试是无效的,因为默认情况下它将使用 reference tracking,所以只存储一次字符串 - 但你的真实数据会表现得非常不同。如果您要多次使用相同的字符串,那么您可以在 protobuf-net 中模仿这种重复使用:

[ProtoMember(21, AsReference=true)]
public String C31 { get; set; }
[ProtoMember(22, AsReference = true)]
public String C32 { get; set; }
[ProtoMember(23, AsReference = true)]
public String C33 { get; set; }
[ProtoMember(24, AsReference = true)]
public String C34 { get; set; }
[ProtoMember(25, AsReference = true)]
public String C35 { get; set; }
[ProtoMember(26, AsReference = true)]
public String C36 { get; set; }
[ProtoMember(27, AsReference = true)]
public String C37 { get; set; }
[ProtoMember(28, AsReference = true)]
public String C38 { get; set; }
[ProtoMember(29, AsReference = true)]
public String C39 { get; set; }
[ProtoMember(30, AsReference = true)]
public String C310 { get; set; }

现在的输出:

939
939
3144
3144

但是!如果字符串通常重复,这将略微增加输出,并且会使其他 protobuf 实现难以使用它(它是有效的 protobuf 数据,但通过一些巫术)。

例如,如果您有自定义名称/国家/地区名称/状态等以字符串表示但重复批次的内容,则上述内容很有用。

【讨论】:

  • 感谢您的回答。这真的是帮帮我! [ProtoMember(30, AsReference = true)] public String C310 { get;放; }
  • @Puh 再次,只有当值可能重复时才会有所帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-12-30
  • 2011-12-04
  • 2021-01-14
  • 1970-01-01
  • 2012-04-26
  • 2011-01-23
  • 1970-01-01
相关资源
最近更新 更多