2

我在我正在处理的 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 生成的源代码,但没有发现任何适用的。

4

1 回答 1

4

当涉及到大型stringbytes对象时,Protobuf 似乎可以很好地计算情况。以下传递,这意味着在Bar创建新对象时,二进制数组通过引用复制(Pythonbytes是不可变的,因此有意义):

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)或set_allocated_...()Python protobuf 的东西,那将解决它,但是我不知道有这样的功能。

于 2018-02-23T23:59:09.890 回答