Caffe2 - C++ API
A deep learning, cross platform ML framework
blob_serialization.h
1 #ifndef CAFFE2_CORE_BLOB_SERIALIZATION_H_
2 #define CAFFE2_CORE_BLOB_SERIALIZATION_H_
3 
4 #include <limits>
5 #include <future>
6 
7 #include <google/protobuf/repeated_field.h>
8 
9 #include "caffe2/core/blob.h"
10 #include "caffe2/core/blob_serializer_base.h"
11 #include "caffe2/core/tensor.h"
12 #include <c10/util/typeid.h>
13 #include "caffe2/core/types.h"
14 #include "caffe2/utils/simple_queue.h"
15 
16 C10_DECLARE_int(caffe2_tensor_chunk_size);
17 C10_DECLARE_int(caffe2_max_tensor_serializer_threads);
18 C10_DECLARE_bool(caffe2_serialize_fp16_as_bytes);
19 
20 namespace caffe2 {
21 
22 constexpr auto kTensorBlobType = "Tensor";
23 // String used to separate chunk id from the blob name when storing in DB
24 constexpr auto kChunkIdSeparator = "#%";
25 
32 CAFFE2_API void SerializeBlob(
33  const Blob& blob,
34  const string& name,
35  BlobSerializerBase::SerializationAcceptor acceptor,
36  int chunk_size = kDefaultChunkSize);
37 
48 CAFFE2_API string SerializeBlob(const Blob& blob, const string& name);
49 
55 CAFFE2_API void DeserializeBlob(const string& content, Blob* result);
56 CAFFE2_API void DeserializeBlob(const BlobProto& proto, Blob* result);
57 
58 /*
59  * Get an empty Tensor from the TensorProto given the meta data in proto (data
60  * type and size of the Tensor) without actually filling in the data.
61  *
62  * We need this function because we want to construct a fully initialized Tensor
63  * in the beginning instead of keeping partially initialized Tensor around the
64  * process. Consider the case when we have a Tensor that is split into multiple
65  * protos during serialization, in deserialization, we have to fill the Tensor
66  * in multiple calls to Deserialize, therefore we need to create a new Tensor
67  * with the correct size and data type before the call to Deserialize, because
68  * otherwise we will have to check whether the function call is the first call
69  * to initialize the underlying Tensor, which makes the function stateful and
70  * complicated.
71  *
72  * The legacy code get away with this problem by passing in a partially
73  * initialized Tensor and use Resize and mutable_data to set the correct size,
74  * data type and allocate memory for the Tensor, so the state is encoded in
75  * these function calls. e.g. mutable_data will allocate memory on the first
76  * call and it will return a pointer to the allocated memory on later calls.
77  */
78 CAFFE2_API Tensor EmptyTensorFromProto(const TensorProto& proto);
79 
86 class CAFFE2_API TensorSerializer : public BlobSerializerBase {
87  public:
88  TensorSerializer() {}
89  ~TensorSerializer() override {}
94  void Serialize(
95  const void* pointer,
96  TypeMeta typeMeta,
97  const string& name,
98  SerializationAcceptor acceptor) override;
99  void SerializeWithChunkSize(
100  const void* pointer,
101  TypeMeta typeMeta,
102  const string& name,
103  SerializationAcceptor acceptor,
104  int chunk_size) override;
105 
106  void Serialize(
107  const Tensor& tensor,
108  const string& name,
109  TensorProto* proto,
110  size_t chunkBegin,
111  int32_t chunkSize);
112 
113  private:
114  // A utility function to store the device context detauls.
115  void StoreDeviceDetail(const Tensor& input, TensorProto* proto);
116  unique_ptr<BaseContext> context_;
117 };
118 
119 
128 class CAFFE2_API TensorDeserializer : public BlobDeserializerBase {
129  public:
130  void Deserialize(const BlobProto& proto, Blob* blob) override;
131 
132  /* There are cases when a Tensor is split into multiple protos and
133  * we have to call Deserialize multiple times to get the complete deserialized
134  * Tensor, each call will fill part of the Tensor given the segment begin and
135  * end information in proto, therefore we have to pass in the Tensor pointer
136  * rather than create a new Tensor everytime.
137  *
138  * Precondition: Tensor must be initialized
139  */
140  void DeserializeToTensor(const TensorProto& proto, Tensor* tensor);
141 
142  /* Deserialize the proto and return a new Tensor
143  * This is a utility function that combines EmptyTensorFromProto and
144  * Deserialize(const TensorProto&, Tensor*);
145  */
146  Tensor Deserialize(const TensorProto& proto);
147 };
148 
150 // Implementations
152 
153 namespace detail {
154 template <typename SrcType, typename DstType>
155 inline void CopyToProtoAsIs(
156  const size_t size,
157  const SrcType* src,
158  google::protobuf::RepeatedField<DstType>* field,
159  BaseContext* context) {
160  static_assert(
161  sizeof(SrcType) == sizeof(DstType),
162  "The source type and dest type cannot be copied as-is. Did "
163  "you mean CopyToProtoWithCast?");
164  field->Reserve(size);
165  for (size_t i = 0; i < size; ++i) {
166  field->Add(0);
167  }
168  context->template CopyToCPU<SrcType>(
169  size, src, reinterpret_cast<SrcType*>(field->mutable_data()));
170  // Make sure that we finish the copy into the protobuf.
171  context->FinishDeviceComputation();
172 }
173 
174 template <typename SrcType, typename DstType>
175 inline void CopyToProtoWithCast(
176  const size_t size,
177  const SrcType* src,
178  google::protobuf::RepeatedField<DstType>* field,
179  BaseContext* context) {
180  // TODO: we are having one unnecessary copy here if the context is already
181  // CPUContext. Remove it if it is performance critical.
182  unique_ptr<SrcType[]> buffer(new SrcType[size]);
183  context->template CopyToCPU<SrcType>(size, src, buffer.get());
184  context->FinishDeviceComputation();
185  field->Reserve(size);
186  for (size_t i = 0; i < size; ++i) {
187  field->Add(static_cast<DstType>(buffer[i]));
188  }
189 }
190 
191 template <typename SrcType, typename DstType>
192 inline void CopyFromProtoAsIs(
193  const size_t size,
194  const google::protobuf::RepeatedField<SrcType>& field,
195  DstType* dst,
196  BaseContext* context) {
197  static_assert(
198  sizeof(SrcType) == sizeof(DstType),
199  "The source type and dest type cannot be copied as-is. Did "
200  "you mean CopyFromProtoWithCast?");
201  CAFFE_ENFORCE_EQ(size, field.size(), "Incorrect proto field size.");
202  context->template CopyFromCPU<DstType>(
203  size, reinterpret_cast<const DstType*>(field.data()), dst);
204 }
205 
206 template <typename SrcType, typename DstType>
207 inline void CopyFromProtoWithCast(
208  const size_t size,
209  const google::protobuf::RepeatedField<SrcType>& field,
210  DstType* dst,
211  BaseContext* context) {
212  CAFFE_ENFORCE_EQ(size, field.size(), "Incorrect proto field size.");
213  // TODO: we are having one unnecessary copy here if the context is already
214  // CPUContext. Remove it if it is performance critical.
215  unique_ptr<DstType[]> buffer(new DstType[size]);
216  const SrcType* src = field.data();
217  for (size_t i = 0; i < size; ++i) {
218  buffer[i] = static_cast<DstType>(src[i]);
219  }
220  context->template CopyFromCPU<DstType>(size, buffer.get(), dst);
221 }
222 
223 } // namespace detail
224 
226 // Serialization Helpers
228 
229 // Converts MessageLite to string while also checking that SerializeAsString
230 // succeeds. Pass description of class/function of the call if you'd
231 // like it appended to the error message.
232 CAFFE2_API std::string SerializeAsString_EnforceCheck(
233  const google::protobuf::MessageLite&,
234  const char* error_location = nullptr);
235 
236 // Convert BlobProto to string with success checks.
237 inline std::string SerializeBlobProtoAsString_EnforceCheck(
238  const BlobProto& blob) {
239  return SerializeAsString_EnforceCheck(blob, blob.name().c_str());
240 }
241 
242 } // namespace caffe2
243 
244 #endif // CAFFE2_CORE_BLOB_SERIALIZATION_H_
Blob is a general container that hosts a typed pointer.
Definition: blob.h:24
TensorSerializer is the serializer for Tensors.
BlobDeserializerBase is an abstract class that deserializes a blob from a BlobProto or a TensorProto...
void DeserializeBlob(const string &content, Blob *result)
Deserializes from a string containing either BlobProto or TensorProto.
Virtual interface for the Context class in Caffe2.
Definition: context_base.h:32
TensorDeserializer is the deserializer for Tensors.
A global dictionary that holds information about what Caffe2 modules have been loaded in the current ...
Definition: blob.h:13
TypeMeta is a thin class that allows us to store the type of a container such as a blob...
Definition: typeid.h:324
void SerializeBlob(const Blob &blob, const string &name, BlobSerializerBase::SerializationAcceptor acceptor, int chunk_size)
Serializes the given blob, if possible.
BlobSerializerBase is an abstract class that serializes a blob to a string.