TensorFlow源码分析(9):Proto Buffer生成的C++类

半世苍凉 提交于 2020-02-06 13:24:37

文章(8)中提到,Proto Buffer提供了一个工具,可以为不同编程语言,自动生成支持Proto Buffer协议的数据类型。本文继续以DeviceAttributes类为例,介绍C++的Proto Buffer类。

默认情况下,Proto Buffer生成的C++类都定义在.pb.h文件中。TensorFlow也沿用了这一规则。DeviceAttributes类定义在core/framework/device_attributes.pb.h文件中。

class DeviceAttributes : public ::google::protobuf::Message

上面的代码告诉我们,任何Proto Buffer类必须继承自protobuf::Message类。

DeviceAttributes类定义的方法特别多。要了解这些方法的意义,首先需要回顾DeviceAttributes消息类型的定义。

message DeviceAttributes {
  string name = 1;
  string device_type = 2;
  int64 memory_limit = 4;
  DeviceLocality locality = 5;
  fixed64 incarnation = 6;
  string physical_device_desc = 7;
}

 

DeviceAttributes类的许多方法与消息类型的字段相关,例如,针对name字段,我们有如下方法。

void clear_name();
const ::std::string& name() const;
void set_name(const ::std::string& value);
void set_name(const char* value);
void set_name(const char* value, size_t size);
::std::string* mutable_name();
::std::string* release_name();
void set_allocated_name(::std::string* name);
::std::string* unsafe_arena_release_name();
void unsafe_arena_set_allocated_name(
     ::std::string* name);

C++用string类型来实现消息类型的string字段,因此可以看到与name相关的方法都大量使用了string类。

  • name()方法返回一个name字段的值,它返回的是常量字符串。与此相对应的mutable_name()方法返回的是一个可修改的字符串。release_name()方法也返回可修改的字符串。但是,这个函数被调用以后,DeviceAttributes对象不再管理这个返回的字符串,而DeviceAttributes对象的name变量被重置为默认值。如果用栈数据结构做一个类比,那么name()和mutable_name()相当于栈的top()方法,而release_name()相当于pop()方法。
  • set_name()方法设置name字段的值。另外,还有一个set_allocated_name()方法,也是设置name字段的值。这两个方法的不同之处在于,set_name()方法会重新开辟空间保存字符串的值,而set_allocated_name()方法直接使用传入的string类型指针参数所指定的存储空间。set_allocated_name()方法被调用以后,在DeviceAttributes对象外部不应该再使用传入的指针变量的值来操作字符串。
  • clear_name()方法重置name字段的值。

上面代码中,还有两个unsafe开头的方法,它们已经不再被推荐使用了。但是其中涉及的Arena的概念,却有必要提一下。Arena是一块预先开辟的大内存区域。如果没有Arena,那么DeviceAttributes对象的所有变量都是保存在堆上面。程序需要为每个字段申请特定的内存空间。当字段的值发生更改,可能需要执行内存空间的释放和重新申请操作,这显然会增加运行的时间。但有了Arena以后,所有的字段都保存在Arena之上。而Arena在对象创建之初申请,对象销毁之后释放,这样就减少了申请和释放内存空间的开销。另一方面,由于Arena是一块连续的内存区域,因此可以更好地利用系统的高速缓存。

可以在proto文件中设置是否使用Arena,参见tensorflow/core/framework/device_attributes.proto.

option cc_enable_arenas = true;

DeviceAtrributes消息类型还有device_type和physical_device_desc字段都是字符串类型,因此与name字段对应的方法类似。

 

DeviceAtrributes消息类型的memory_limit字段相应的方法如下。

void clear_memory_limit();
::google::protobuf::int64 memory_limit() const;
void set_memory_limit(::google::protobuf::int64 value);

上面的方法简单明了,唯一值得一提的是google::protobuf::int64是C++ int64_t的别名。

 

DeviceAtrributes消息类型的locality字段是一个自定义类型的变量。

  void clear_locality();
  const ::tensorflow::DeviceLocality& locality() const;
  ::tensorflow::DeviceLocality* release_locality();
  ::tensorflow::DeviceLocality* mutable_locality();
  void set_allocated_locality(::tensorflow::DeviceLocality* locality);

注意上面代码中没有set_locality()方法,也就是说Proto Buffer类不会申请自定义类型字段的内存空间。这样做,当然是为了效率的考量。因为自定义类型字段内部可能还有自定义类型字段,如此递归下去,再与Arena结合,会有诸多不便。

 

 

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!