Python Protobuf 中有效的消息字段设置

3

我正在一个Python项目中使用Protobuf(v3.5.1)。我的情况可以简化为以下内容:

// Proto file

syntax = "proto3";

message Foo {
    Bar bar = 1;
}

message Bar {
    bytes lotta_bytes_here = 1;
}

# Python excerpt
def MakeFooUsingBar(bar):
    foo = Foo()
    foo.bar.CopyFrom(bar)

我担心.CopyFrom()的内存性能(如果我没记错,它是在复制内容而不是引用)。在C++中,我可以使用类似以下的语句:

Foo foo;
Bar* bar = new Bar();
bar->set_lotta_bytes_here("abcd");
foo.set_allocated_bar(bar);

从生成的源代码来看,似乎不需要复制任何内容:

inline void Foo::set_allocated_bar(::Bar* bar) {
  ::google::protobuf::Arena* message_arena = GetArenaNoVirtual();
  if (message_arena == NULL) {
    delete bar_;
  }
  if (bar) {
    ::google::protobuf::Arena* submessage_arena = NULL;
    if (message_arena != submessage_arena) {
      bar = ::google::protobuf::internal::GetOwnedMessage(
          message_arena, bar, submessage_arena);
    }

  } else {

  }
  bar_ = bar;
  // @@protoc_insertion_point(field_set_allocated:Foo.bar)
}

有没有类似的Python库可用?我已经查看了Python生成的源代码,但没有找到适用的内容。

你只是担心性能问题吗?你有没有测量过,看看它是否会对你的应用程序造成问题? - Greg Hewgill
@GregHewgill 我知道在复制资源时,内存中存在两个实例。我的应用程序使用大量资源(以十兆字节或百兆字节为单位),我想避免额外的开销。特别是因为我的意图不是复制资源,而只是将其移动。我理解这可能被看作是过早优化,但如果有可以使用的内置功能,我不明白为什么不使用它。 - Michał
哦,如果你要复制几百兆的东西,那么当然值得研究一下性能方面的问题。 :) - Greg Hewgill
1个回答

5

当涉及到大的stringbytes对象时,Protobuf似乎处理得相当不错。以下代码可通过测试,这意味着虽然创建了一个新的Bar对象,但二进制数组是通过引用复制的(Python的bytes是不可变的,所以这是有道理的):

def test_copy_from_with_large_bytes_field(self):
    bar = Bar()
    bar.val = b'12345'
    foo = Foo()
    foo.bar.CopyFrom(bar)

    self.assertIsNot(bar, foo.bar)
    self.assertIs(bar.val, foo.bar.val)

这解决了我的大bytes对象问题。但如果有人的问题在于嵌套或重复的字段,那么这不能帮助他们——这些字段是逐个字段复制的。这是有道理的——如果一个消息被复制,他们希望两者是独立的。如果它们不是,对原始消息进行更改将修改复制的消息(反之亦然)。

如果有类似于C++移动语义 (https://github.com/google/protobuf/issues/2791) 或Python Protobuf中的set_allocated_...(),那就可以解决问题,但我不知道是否有此功能。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接