我们使用 protobuf-net 在服务之间发送日志消息。在分析压力测试时,在高并发下,我们看到非常高的 CPU 使用率,而 RuntimeTypeModel 中的 TakeLock 是罪魁祸首。热调用堆栈看起来像:
*Our code...*
ProtoBuf.Serializer.SerializeWithLengthPrefix(class System.IO.Stream,!!0,valuetype ProtoBuf.PrefixStyle)
ProtoBuf.Serializer.SerializeWithLengthPrefix(class System.IO.Stream,!!0,valuetype ProtoBuf.PrefixStyle,int32)
ProtoBuf.Meta.TypeModel.SerializeWithLengthPrefix(class System.IO.Stream,object,class System.Type,valuetype ProtoBuf.PrefixStyle,int32)
ProtoBuf.Meta.TypeModel.SerializeWithLengthPrefix(class System.IO.Stream,object,class System.Type,valuetype ProtoBuf.PrefixStyle,int32,class ProtoBuf.SerializationContext)
ProtoBuf.ProtoWriter.WriteObject(object,int32,class ProtoBuf.ProtoWriter,valuetype ProtoBuf.PrefixStyle,int32)
ProtoBuf.BclHelpers.WriteNetObject(object,class ProtoBuf.ProtoWriter,int32,valuetype
ProtoBuf.BclHelpers/NetObjectOptions)
ProtoBuf.Meta.TypeModel.GetKey(class System.Type&)
ProtoBuf.Meta.RuntimeTypeModel.GetKey(class System.Type,bool,bool)
ProtoBuf.Meta.RuntimeTypeModel.FindOrAddAuto(class System.Type,bool,bool,bool)
ProtoBuf.Meta.RuntimeTypeModel.TakeLock(int32&)
[clr.dll]
我看到我们可以使用新的预编译器来提高速度,但我想知道这是否会解决这个问题(听起来它不使用反射);整合它对我来说有点工作,所以我还没有测试它。我还看到了调用 Serializer.PrepareSerializer 的选项。我最初的(小规模)测试并没有使准备工作看起来很有希望。
关于我们正在序列化的类型的更多信息:
[ProtoContract]
public class SomeMessage
{
[ProtoMember(1)]
public SomeEnumType SomeEnum { get; set; }
[ProtoMember(2)]
public long SomeId{ get; set; }
[ProtoMember(3)]
public string SomeString{ get; set; }
[ProtoMember(4)]
public DateTime SomeDate { get; set; }
[ProtoMember(5, DynamicType = true, OverwriteList = true)]
public Collection<object> SomeArguments
}
谢谢你的帮助!
更新 9/17
感谢您的答复!我们将尝试您建议的解决方法,看看是否可以解决问题。
这段代码存在于我们的日志系统中,因此在 SomeMessage 示例中,SomeString 实际上是一个格式字符串(例如“Hello {0}”),而 SomeArguments 集合是用于填充格式字符串的对象列表,就像 String 一样。格式。在我们序列化之前,我们查看每个参数并调用DynamicSerializer.IsKnownType(argument.GetType())
,如果不知道,我们首先将其转换为字符串。我没有查看数据的比率,但我很确定我们有很多不同的字符串作为参数传入。
让我知道这是否有帮助。如果您需要,我会尝试获取更多详细信息。