gpt4 book ai didi

protocol-buffers - TakeLock 中的 protobuf-net 并发性能问题

转载 作者:行者123 更新时间:2023-12-04 05:32:36 32 4
gpt4 key购买 nike

我们使用 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()) ,如果它不知道,我们首先将其转换为字符串。我没有看过数据的比率,但我很确定我们有很多不同的字符串作为参数传入。

如果这有帮助,请告诉我。如果您需要,我会尝试获取更多详细信息。

最佳答案

TakeLock仅在更改模型时使用,例如因为它第一次看到类型。您通常不会看到 TakeLock在第一次使用特定类型之后。大多数情况下,使用 Serializaer.PrepareSerializer<SomeMessage>()应该执行所有必要的初始化(对于您正在使用的任何其他合约也是如此)。

然而!我想知道这是否也与您使用DynamicType有关。 ;这里使用的实际对象是什么?可能是我需要在这里调整逻辑,这样它就不会在这一步上花费任何时间。如果您让我知道实际对象(以便我可以重现),我将尝试运行一些测试。

至于预编译器是否会改变这一点;是的。完全编译的静态模型具有完全不同的 ProtoBuf.Meta.TypeModel.GetKey 实现。方法,所以它永远不会调用 TakeLock (您不需要保护永远不会改变的模型!)。但是您实际上可以做一些非常相似的事情而无需使用预编译。考虑以下情况,作为应用程序初始化的一部分运行:

static readonly TypeModel serializer;
...
var model = TypeModel.Create();
model.Add(typeof(SomeMessage), true);
// TODO add other contracts you use here
serializer = model.Compile();

这将在内存中创建一个完全静态编译的序列化程序程序集(而不是一个编译了单个操作的可变模型)。如果您现在使用 serializer.Serialize(...)而不是 Serializer.Serialize (即您存储的 TypeModel 上的实例方法而不是 Serializer 上的静态方法)那么它本质上会做一些与“预编译器”非常相似的事情,但不需要实际预编译它(显然这只会可用在“完整”.NET 上)。这将永远不会调用 TakeLock ,因为它运行的是固定模型,而不是灵活模型。但是,它确实要求您知道您使用的契约(Contract)类型。您可以使用反射来查找这些,通过查找具有给定属性的所有类型:
static readonly TypeModel serializer;
...
var model = TypeModel.Create();
Type attributeType = typeof(ProtoContractAttribute);
foreach (var type in typeof(SomeMessage).Assembly.GetTypes()) {
if (Attribute.IsDefined(type, attributeType)) {
model.Add(type, true);
}
}
serializer = model.Compile();

但强调:以上是一种解决方法;听起来好像有一个小故障,如果我能看到一个实际发生的例子,我会很高兴地进行调查;最重要的是: SomeArguments 中的对象是什么? ?

关于protocol-buffers - TakeLock 中的 protobuf-net 并发性能问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12396742/

32 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com