改进protobuf-net序列化时间以获得大量“响应”

时间:2013-08-16 15:46:38

标签: c# performance wcf serialization protobuf-net

我正在使用Protobuf-net实现一个wcf请求/响应服务,用于序列化和tcp绑定。在运行同一台机器上运行的服务器和客户端的测试中,我看到,对于大约1.5Mb的响应对象,一个大约500ms的空转时间。

当我将同一个对象序列化为内存流时,在接收到客户端上的响应后,大约需要115毫秒,并且大约需要330.ms左右。 考虑到从数据库等查询数据的开销,这种总计往返时间加起来

我已经看到它写的这可能是消息大小限制,应该使用原型缓冲区,但这是我可以期待的那种序列化/解除时间吗? protobuf-net是否有任何尺寸/速度权衡选项? 感谢

这是目前的模型......

public class BaseResponse
{
    public bool Success {get;set;}
    public string Error {get;set;}
}

public class SourceTableResponse : BaseResponse
{
   public Dictionary<string, Dictionary<string,string>> FieldValuesByTableName {get;set;}
}

1 个答案:

答案 0 :(得分:4)

有一些技巧可以帮到这里,是的。其中最常见的是尽可能使用“分组”数据。解释一下:“群组”是protobuf规范的一个特征,谷歌不会使用太多 - 他们建议默认为子对象的长度前缀表示法 - 但长度前缀写入相对昂贵。在大多数情况下,这就像在一些注释中添加DataFormat = DataFormat.Group一样简单,但是:当您拥有Dictionary<string,Dictionary<string,string>>时,这并不是那么简单 - 因为KeyValuePair<,> protocuf-net保护,以防止好心的用户破坏它:它不会让你改变格式。我们仍然可以这个,但是:我们需要编写自己的模型而不是使用裸字典 - 有点痛苦。

其他技巧:

  • 在基元列表上使用“packed”编码 - 此处不适用;你没有任何
  • 利用实际重复的字符串值 - 查看你的模型,我猜内部字典的键是一个字段名,因此可能会在表间重复使用很多次;我们可以尝试实习那个 - 再次,不是我们可以做的Dictionary<string,string>,尽管

但从根本上讲:您的数据目前将由UTF-8 支配 - 无论是在存储和处理方面。我不能做太多,因为你把所有东西都存储为字符串。就个人而言,我会说这个非常宽松的模型并不适合充分利用protobuf-net;我所能做的就是在极限范围内尽可能地收紧它。例如,这仅适用于前向(无缓冲):

[ProtoContract]
[ProtoInclude(3, typeof(CustomSourceTableResponse), DataFormat = DataFormat.Group)]
public class CustomBaseResponse
{
    [ProtoMember(1)]
    public bool Success { get; set; }
    [ProtoMember(2)]
    public string Error { get; set; }
}
[ProtoContract]
public class CustomSourceTableResponse : CustomBaseResponse
{
    [ProtoMember(1, DataFormat = DataFormat.Group)]
    public List<FieldTable> FieldValuesByTableName { get { return fieldValuesByTableName; } }
    private readonly List<FieldTable> fieldValuesByTableName = new List<FieldTable>();
}
[ProtoContract]
public class FieldTable
{
    public FieldTable() { }
    public FieldTable(string tableName)
    {
        TableName = tableName;
    }
    [ProtoMember(1)]
    public string TableName { get; set; }
    [ProtoMember(2, DataFormat = DataFormat.Group)]
    public List<FieldValue> FieldValues { get { return fieldValues; } }
    private readonly List<FieldValue> fieldValues = new List<FieldValue>();
}
[ProtoContract]
public class FieldValue
{
    public FieldValue() { }
    public FieldValue(string name, string value)
    {
        Name = name;
        Value = value;
    }
    [ProtoMember(1)]
    public string Name { get; set; }
    [ProtoMember(2)]
    public string Value { get; set; }
}

如果你期望有很多重复的FieldValue.Name值(例如,有很多行,每行都有相同的字段),那么......好吧,坦率地说我建议使用适当的基于类型的模型,即

class SomeRow {
    public int Id {get;set;}
    public string Name {get;set;}
    public DateTime DateOfBirth {get;set;}
}

但是如果那是不可能的,那么我想你仍然可以避免在数据中使用"DateOfBirth" 200次:

[ProtoMember(1, AsReference=true)]
public string Name { get; set; }
[ProtoMember(2)]
public string Value { get; set; }

但请注意:由于多种原因,真的非常比使用打字模型贵得多:

  • 所有文字都很昂贵
  • 名称需要存储(protobuf的一部分就是避免存储名称)
  • 所有文字都很昂贵(是的,我已经说过 - 但这真的很重要)