为啥protobuf序列化“oneof”消息使用if-else
Posted
技术标签:
【中文标题】为啥protobuf序列化“oneof”消息使用if-else【英文标题】:Why protobuf serialize "oneof" message use if-else为什么protobuf序列化“oneof”消息使用if-else 【发布时间】:2018-04-20 08:09:45 【问题描述】:我有一个这样的消息定义:
message Command
oneof type
Point point = 1;
Rotate rotate = 2;
Move move = 3;
... //about 100 messages
然后protoc生成SerializeWithCachedSizes函数:
void Command::SerializeWithCachedSizes(
::google::protobuf::io::CodedOutputStream* output) const
// @@protoc_insertion_point(serialize_start:coopshare.proto.Command)
::google::protobuf::uint32 cached_has_bits = 0;
(void) cached_has_bits;
// .coopshare.proto.Point point = 1;
if (has_point())
::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray(
1, *type_.point_, output);
// .coopshare.proto.Rotate rotate = 2;
if (has_rotate())
::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray(
2, *type_.rotate_, output);
// .coopshare.proto.Move move = 3;
if (has_move())
::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray(
3, *type_.move_, output);
“oneof”消息将特定类型保存在_oneof_case_中。我认为使用 switch-case 更有效。
但是为什么 protobuf 还是会生成这样的代码呢?
【问题讨论】:
编译器很聪明,所以做任何一个都可能无关紧要。编译器能够将一长串if/else
块转换为查找表。如果您尝试使用一个小的switch
块来编写程序,您会看到编译器会将其转换为几个条件跳转。生成工具可能会这样做,因为它更容易生成。
【参考方案1】:
Oneofs 在内部处理类似于可选字段。事实上,descriptor.proto 将它们表示为一组可选字段,只有一个额外的 oneof_index
表示它们属于一起。这是一个合理的选择,因为它允许在添加任何特殊支持之前立即将 oneofs 与许多库一起使用。
我假设 C++ 代码生成器对可选字段和 oneof 使用相同的结构。
switch-case 可能会生成更高效的代码,在这种情况下,建议将其作为对 protobuf 项目的改进会很有用。然而,就像 Jorge Bellón 在 cmets 中指出的那样,编译器完全有可能自动优化这个结构。必须进行测试和基准测试才能确定。
【讨论】:
以上是关于为啥protobuf序列化“oneof”消息使用if-else的主要内容,如果未能解决你的问题,请参考以下文章
用于非 protobuf 类的 protobuf `oneof` 功能的 C++ 实现
如果子消息没有字段,如何在 protobuf 消息上分配 oneof 字段?