协议缓冲区 GetRepeatedField (反射) 代码优化
protocol buffer GetRepeatedField (reflection) code optimization
我正在使用协议缓冲区的反射功能在运行时读取消息字段值。
我拥有的原型:
package xapp.battle;
message BATTLE_DATA {
repeated AInfo aInfo = 1;
repeated BInfo bInfo = 2;
repeated CInfo cInfo = 3;
// a lot other repeated messages
}
message AInfo {
int32 test_field = 1;
// ......
}
message BInfo {
int32 test_field = 1;
// ......
}
message CInfo {
int32 test_field = 1;
// ......
}
现在我有的代码:
void DO_SOMETHING(messageName) {
const Descriptor* pDescriptor = BATTLE_DATA->GetDescriptor();
const FieldDescriptor* pMessageField = pDescriptor->FindFieldByName(messageName);
const Reflection* pReflection = BATTLE_DATA->GetReflection();
if (messageName == "aInfo") {
const RepeatedPtrField<::xapp::battle::AInfo> repeated_ptr_field = pReflection->GetRepeatedPtrField<::xapp::battle::AInfo>(*BATTLE_DATA, pMessageField);
for (int i = 0; i < repeated_ptr_field.size(); i ++) {
::xapp::battle::AInfo messageInfo = repeated_ptr_field.Get(i);
// continue to read the test_field value of messageInfo
}
}
else if (messageName == "bInfo") {
const RepeatedPtrField<::xapp::battle::BInfo> repeated_ptr_field = pReflection->GetRepeatedPtrField<::xapp::battle::BInfo>(*BATTLE_DATA, pMessageField);
for (int i = 0; i < repeated_ptr_field.size(); i ++) {
::xapp::battle::BInfo messageInfo = repeated_ptr_field.Get(i);
// continue to read the test_field value of messageInfo
}
}
else if (messageName == "CInfo") {
const RepeatedPtrField<::xapp::battle::CInfo> repeated_ptr_field = pReflection->GetRepeatedPtrField<::xapp::battle::CInfo>(*BATTLE_DATA, pMessageField);
for (int i = 0; i < repeated_ptr_field.size(); i ++) {
::xapp::battle::CInfo messageInfo = repeated_ptr_field.Get(i);
// continue to read the test_field value of messageInfo
}
}
// ......
else {
LOG("loadOneBin - Unknown messageName");
}
}
这段代码有效,但显然它不是最好的解决方案,因为有很多重复的"else-if"代码块。
我想要的是类似的东西(至少摆脱那些"else-if"块):
const RepeatedPtrField<::xapp::battle::MESSAGE_NAME> repeated_ptr_field = pReflection->GetRepeatedPtrField<::xapp::battle::MESSAGE_NAME>(*BATTLE_DATA, pMessageField);
for (int i = 0; i < repeated_ptr_field.size(); i ++) {
::xapp::battle::MESSAGE_NAME messageInfo = repeated_ptr_field.Get(i);
// continue to read the test_field value of messageInfo
}
GetRepeatedPtrField 的源代码:
template<typename PB>
inline const RepeatedPtrField<PB>& Reflection::GetRepeatedPtrField(
const Message& message, const FieldDescriptor* field) const {
return *static_cast<RepeatedPtrField<PB>* >(
MutableRawRepeatedField(const_cast<Message*>(&message), field,
FieldDescriptor::CPPTYPE_MESSAGE, -1,
PB::default_instance().GetDescriptor()));
}
任何建议将不胜感激,谢谢:)
正确答案
这里的正确答案是,您应该使用 Reflection::GetRepeatedMessage()
来获取重复字段的每个元素的泛const Message&
,而不是使用 Reflection::GetRepeatedPtrField<T>()
。不幸的是,您需要为每个元素调用此方法一次(使用 Reflection::FieldSize()
查找大小)。
在每个Message
,您可以使用Message::GetDescriptor()
,查找名为test_field
的字段,然后Message::GetReflection()
并使用它来读取该字段的值。作为优化,您可以放心地假设同一重复字段中的所有消息都具有相同的Descriptor
和Reflection
对象,因此您只需获取这些对象并为整个数组查找一次FieldDescriptor
。
类似的东西(未经测试):
int size = pReflection->FieldSize(*BATTLE_DATA, pMessageField);
Reflection* pInnerReflection = NULL;
FieldDescriptor* pTestFieldDesc = NULL;
for (int i = 0; i < size; i++) {
const Message& msg = pReflection->GetRepeatedMessage(
*BATTLE_DATA, pMessageField, i);
if (pInnerReflection == NULL) {
pInnerReflection = msg.GetReflection();
pTestFieldDesc = msg.GetDescriptor()
->FindFieldByName("test_field");
}
int testField = pInnerReflection->GetInt32(msg, pTestFieldDesc);
// ...
}
一个有趣但糟糕的答案
第二种解决方案的性能会稍微好一些,看起来更漂亮一些。不幸的是,它在技术上是未定义的行为。它在实践中适用于所有编译器,但除非您遇到性能问题,否则您可能不应该这样做。我把它放在这里是为了好玩,因为无论如何你都可能会弄清楚,所以很高兴知道它为什么不好。
可以调用 GetRepeatedPtrField<google::protobuf::Message>(...)
来获取泛型Message
对象的RepeatedPtrField
。然后,您可以像第一个解决方案一样获取描述符和反射,但不再需要调用Reflection::GetMessage
(虚拟调用)来读取每条消息。(不过,您仍然需要调用反射来读取test_field
。
这在技术上是未定义的行为,因为它假设了几件事:
- 该
RepeatedPtrField<T>
对所有T
具有相同的布局。事实上,这是正确的,因为RepeatedPtrField<T>
扩展RepeatedPtrFieldBase
并且不添加任何新字段,但C++标准并不要求这种情况。 - 将特定消息类型指针(如
AInfo*
)向上转换为Message*
不会更改指针的位。在实践中,只要没有多重继承,所有编译器都是如此,Protobufs 不使用多重继承(Google C++ 风格指南甚至禁止它)。 - 编译器不会根据严格的别名规则进行优化,以至于它最终会对
RepeatedPtrField<Message>
的访问重新排序,而不是对程序中其他地方发生的RepeatedPtrField<AInfo>
(或任何类型)的其他访问进行重新排序。在实践中,想象任何编译器实际上为此用例执行此操作是不合理的,但从技术上讲,C++允许这样做。
- C和C 中的代码优化
- 使用清理代码优化多个出口点
- 在 c++ 中从 txt 文件中提取条目的代码优化问题
- 为什么传递值参数经常使编译器更容易进行代码优化
- 来自MATLAB的代码优化直方图C
- C - 代码优化
- 代码优化子集总和
- 在代码优化过程中,C++11编译器是否会在可能的情况下将局部变量转换为右值
- C++-筛选Atkin代码优化
- 协议缓冲区 GetRepeatedField (反射) 代码优化
- C++代码优化
- 编译和代码优化
- C++ ARM 设备上代码优化的提示
- 我是否应该将 const 用于局部变量以获得更好的代码优化
- 用于并行计算的C++代码优化示例
- gcc/C++:如果CPU负载很低,那么代码优化用处不大,这是真的
- 反转每个单词在一个句子中使用c++需要代码优化我的代码片段
- 是我的编译器将适当的代码优化为无用的崩溃代码
- Arduino代码优化的多路复用LED矩阵
- 在代码优化中使用new运算符是否值得?