Caffe2 - C++ API
A deep learning, cross platform ML framework
blob_serialization.cc
1 #include "caffe2/core/blob_serialization.h"
2 
3 #include <sstream>
4 #include <mutex>
5 
6 #include "caffe2/core/blob.h"
7 #include "caffe2/utils/proto_utils.h"
8 
9 C10_DEFINE_int(
10  caffe2_tensor_chunk_size,
11  1000000,
12  "Chunk size to split tensor data into");
13 
14 C10_DEFINE_int(
15  caffe2_max_tensor_serializer_threads,
16  16,
17  "Maximal number of threads that can be used for tensor serialization");
18 
19 C10_DEFINE_bool(
20  caffe2_serialize_fp16_as_bytes,
21  false,
22  "Serialize FLOAT16 tensors using byte_data field");
23 
24 namespace caffe2 {
32  public:
33  StringSerializer() {}
34  ~StringSerializer() override {}
39  void Serialize(
40  const void* pointer,
41  TypeMeta typeMeta,
42  const string& name,
43  SerializationAcceptor acceptor) override {
44  CAFFE_ENFORCE(typeMeta.Match<std::string>());
45 
46  BlobProto blob_proto;
47  blob_proto.set_name(name);
48  blob_proto.set_type("std::string");
49  blob_proto.set_content(*static_cast<const std::string*>(pointer));
50  acceptor(name, SerializeBlobProtoAsString_EnforceCheck(blob_proto));
51  }
52 };
53 
59  public:
60  void Deserialize(const BlobProto& proto, Blob* blob) override {
61  *blob->GetMutable<std::string>() = proto.content();
62  }
63 };
64 
65 namespace {
66 void SerializeBlob(
67  const void* pointer,
68  TypeMeta typeMeta,
69  const string& name,
70  BlobSerializerBase::SerializationAcceptor acceptor,
71  int chunk_size) {
72  std::unique_ptr<BlobSerializerBase> serializer(
73  CreateSerializer(typeMeta.id()));
74  CAFFE_ENFORCE(serializer, "No known serializer for ", typeMeta.name());
75  serializer->SerializeWithChunkSize(
76  pointer, typeMeta, name, acceptor, chunk_size);
77 }
78 
79 std::string
80 SerializeBlob(const void* pointer, TypeMeta typeMeta, const string& name) {
81  std::string data;
82  BlobSerializerBase::SerializationAcceptor acceptor =
83  [&data](const std::string&, const std::string& blob_str) {
84  DCHECK(data.empty()); // should be called once with kNoChunking
85  data = blob_str;
86  };
87  SerializeBlob(pointer, typeMeta, name, acceptor, kNoChunking);
88  return data;
89 }
90 } // namespace
91 
93  const Blob& blob,
94  const string& name,
95  BlobSerializerBase::SerializationAcceptor acceptor,
96  int chunk_size) {
97  SerializeBlob(blob.GetRaw(), blob.meta(), name, acceptor, chunk_size);
98 }
99 
100 std::string SerializeBlob(const Blob& blob, const string& name) {
101  return SerializeBlob(blob.GetRaw(), blob.meta(), name);
102 }
103 
105  const void* pointer,
106  TypeMeta typeMeta,
107  const string& name,
108  BlobSerializerBase::SerializationAcceptor acceptor) {
109  this->SerializeWithChunkSize(
110  pointer, typeMeta, name, acceptor, kDefaultChunkSize);
111 }
112 
113 void TensorSerializer::SerializeWithChunkSize(
114  const void* pointer,
115  TypeMeta typeMeta,
116  const string& name,
117  BlobSerializerBase::SerializationAcceptor acceptor,
118  int chunk_size) {
119  CAFFE_ENFORCE(typeMeta.Match<Tensor>());
120  const auto& tensor = *static_cast<const Tensor*>(pointer);
121  if (chunk_size == kNoChunking) {
122  chunk_size = tensor.numel() + 1; // to account for empty tensors
123  } else if (chunk_size == kDefaultChunkSize) {
124  chunk_size = FLAGS_caffe2_tensor_chunk_size;
125  }
126 
127  auto processChunk = [&](int64_t chunkStart) {
128  BlobProto blob_proto;
129  blob_proto.set_name(name);
130  blob_proto.set_type(kTensorBlobType);
131  TensorProto& proto = *blob_proto.mutable_tensor();
132  proto.set_name(name);
133  this->Serialize(
134  tensor, name, blob_proto.mutable_tensor(), chunkStart, chunk_size);
135  acceptor(
136  c10::str(name, kChunkIdSeparator, chunkStart / chunk_size),
137  SerializeBlobProtoAsString_EnforceCheck(blob_proto));
138  };
139 
140 #ifndef __ANDROID__
141  // Poorman's IOBound ThreadPool
142  SimpleQueue<size_t> chunkQueue;
143  auto task = [&]() {
144  size_t chunkStart;
145  while (chunkQueue.Pop(&chunkStart)) {
146  processChunk(chunkStart);
147  }
148  };
149  std::vector<std::future<void>> futures;
150  if (tensor.numel() > chunk_size) {
151  futures.reserve(FLAGS_caffe2_max_tensor_serializer_threads);
152  for (int i = 0; i < FLAGS_caffe2_max_tensor_serializer_threads; ++i) {
153  futures.emplace_back(std::async(std::launch::async, task));
154  }
155  }
156 #endif
157 
158  VLOG(1) << "Serializing blob " << name;
159  // Serialize whole vector. If vector is empty, it's shape still needs to be
160  // serialized in empty proto
161  for (size_t chunkBegin = 0;
162  chunkBegin < std::max(tensor.numel(), static_cast<int64_t>(1));
163  chunkBegin += chunk_size) {
164  VLOG(2) << "Starting a chunk at " << chunkBegin;
165 #ifndef __ANDROID__
166  if (tensor.numel() > chunk_size) {
167  chunkQueue.Push(chunkBegin);
168  } else {
169  // Sync mode for small tensors
170  processChunk(chunkBegin);
171  }
172 #else
173  // Since Android does not have std::future, we will always do sync mode
174  processChunk(chunkBegin);
175 #endif
176  }
177 
178 #ifndef __ANDROID__
179  chunkQueue.NoMoreJobs();
180  for (auto& fut : futures) {
181  fut.get();
182  }
183 #endif
184 }
185 
187  const Tensor& input,
188  const string& name,
189  TensorProto* proto_ptr,
190  size_t chunkBegin,
191  int32_t chunkSize) {
192  CAFFE_ENFORCE(
193  chunkBegin <= input.numel(),
194  "Chunk begin is out of tensor: ",
195  chunkBegin,
196  ' ',
197  input.numel());
198  if (chunkBegin + chunkSize > input.numel()) {
199  chunkSize = input.numel() - chunkBegin;
200  }
201 
202  if (chunkSize != 0) {
203  CAFFE_ENFORCE(
204  input.raw_data(),
205  "The input does not have data input yet. This is probably because you "
206  "created a tensor of non-zero shape but never filled its data via "
207  "mutable_data() calls. This means that it makes no sense to serialize "
208  "the tensor content.");
209  } else if (!input.dtype_initialized()) {
210  C10_LOG_EVERY_MS(WARNING, 1000)
211  << "You're trying to serialize tensor with zero numel and no dtype. "
212  << "This is a legacy behavior and it WILL BREAK. Contact PyTorch team "
213  << "for details. Offending blob name: " << name;
214  }
215 
216  TensorProto& proto = *proto_ptr;
217  proto.mutable_segment()->set_begin(chunkBegin);
218  proto.mutable_segment()->set_end(chunkBegin + chunkSize);
219 
220  for (int i = 0; i < input.dim(); ++i) {
221  proto.add_dims(input.size(i));
222  }
223  const TensorProto::DataType data_type = TypeMetaToDataType(input.dtype());
224  proto.set_data_type(data_type);
225  StoreDeviceDetail(input, &proto);
226  // TODO: use CUDAGuard here instead of context and employ explicit sync
227  // copy
228  auto uniq_ptr = CreateContext(input.GetDevice());
229  // A lot of copypaste is error prone. Should we create a macro for this?
230  switch (data_type) {
231  case TensorProto_DataType_FLOAT:
232  detail::CopyToProtoAsIs(
233  chunkSize,
234  input.template data<float>() + chunkBegin,
235  proto.mutable_float_data(),
236  uniq_ptr.get());
237  break;
238  case TensorProto_DataType_INT32:
239  detail::CopyToProtoAsIs(
240  chunkSize,
241  input.template data<int>() + chunkBegin,
242  proto.mutable_int32_data(),
243  uniq_ptr.get());
244  break;
245  case TensorProto_DataType_BYTE:
246  LOG(FATAL) << "This should not happen. When serializing, "
247  "BYTE is deprecated and moved to UINT8.";
248  break;
249  case TensorProto_DataType_STRING: {
250  proto.mutable_string_data()->Reserve(chunkSize);
251  const string* content = input.template data<string>();
252  for (int i = chunkBegin; i < chunkBegin + chunkSize; ++i) {
253  proto.add_string_data(content[i]);
254  }
255  break;
256  }
257  case TensorProto_DataType_BOOL:
258  detail::CopyToProtoWithCast(
259  chunkSize,
260  input.template data<bool>() + chunkBegin,
261  proto.mutable_int32_data(),
262  uniq_ptr.get());
263  break;
264  case TensorProto_DataType_UINT8:
265  detail::CopyToProtoWithCast(
266  chunkSize,
267  input.template data<uint8_t>() + chunkBegin,
268  proto.mutable_int32_data(),
269  uniq_ptr.get());
270  break;
271  case TensorProto_DataType_INT8:
272  detail::CopyToProtoWithCast(
273  chunkSize,
274  input.template data<int8_t>() + chunkBegin,
275  proto.mutable_int32_data(),
276  uniq_ptr.get());
277  break;
278  case TensorProto_DataType_UINT16:
279  detail::CopyToProtoWithCast(
280  chunkSize,
281  input.template data<uint16_t>() + chunkBegin,
282  proto.mutable_int32_data(),
283  uniq_ptr.get());
284  break;
285  case TensorProto_DataType_INT16:
286  detail::CopyToProtoWithCast(
287  chunkSize,
288  input.template data<int16_t>() + chunkBegin,
289  proto.mutable_int32_data(),
290  uniq_ptr.get());
291  break;
292  case TensorProto_DataType_INT64:
293  detail::CopyToProtoAsIs(
294  chunkSize,
295  input.template data<int64_t>() + chunkBegin,
296  proto.mutable_int64_data(),
297  uniq_ptr.get());
298  break;
299  case TensorProto_DataType_FLOAT16: {
300  if (FLAGS_caffe2_serialize_fp16_as_bytes) {
301  const int kValue = 1;
302  CAFFE_ENFORCE_EQ(
303  reinterpret_cast<const char*>(&kValue)[0],
304  1,
305  "Serialization of FLOAT16 on big endian platform "
306  "is not written yet.");
307  unique_ptr<char[]> buffer(new char[2 * chunkSize]);
308  this->context_->template CopyToCPU<char>(
309  2 * chunkSize,
310  reinterpret_cast<const char*>(
311  input.template data<at::Half>() + chunkBegin),
312  buffer.get());
313  this->context_->FinishDeviceComputation();
314  proto.set_byte_data(buffer.release(), 2 * chunkSize);
315  } else {
316  detail::CopyToProtoWithCast(
317  chunkSize,
318  reinterpret_cast<const uint16_t*>(input.template data<at::Half>()) +
319  chunkBegin,
320  proto.mutable_int32_data(),
321  uniq_ptr.get());
322  }
323  } break;
324  case TensorProto_DataType_DOUBLE:
325  detail::CopyToProtoAsIs(
326  chunkSize,
327  input.template data<double>() + chunkBegin,
328  proto.mutable_double_data(),
329  uniq_ptr.get());
330  break;
331  case TensorProto_DataType_UNDEFINED: {
332  proto.mutable_string_data()->Reserve(chunkSize);
333  if (chunkSize > 0) {
334  const char* raw_data = static_cast<const char*>(input.raw_data());
335  for (int i = chunkBegin; i < chunkBegin + chunkSize; ++i) {
336  proto.add_string_data(SerializeBlob(
337  raw_data + i * input.itemsize(), input.dtype(), ""));
338  }
339  }
340  } break;
341  // Note: we intentially do not provide "default:" so if any new data types
342  // are added, the compiler should warn the user to add the case here.
343  }
344 }
345 
346 int GetGPUIDForPointer(const void* ptr);
347 
348 void TensorSerializer::StoreDeviceDetail(
349  const Tensor& input,
350  TensorProto* proto) {
351  ExtractDeviceOption(proto->mutable_device_detail(), input.GetDevice());
352 }
353 // The actual serialization registry objects.
354 C10_DEFINE_TYPED_REGISTRY(
355  BlobSerializerRegistry,
358  std::unique_ptr);
359 
360 C10_DEFINE_REGISTRY(BlobDeserializerRegistry, BlobDeserializerBase);
361 
362 void DeserializeBlob(const string& content, Blob* result) {
363  BlobProto blob_proto;
364  CAFFE_ENFORCE(
365  blob_proto.ParseFromString(content),
366  "Cannot parse content into a BlobProto.");
367  DeserializeBlob(blob_proto, result);
368 }
369 
370 void DeserializeBlob(const BlobProto& blob_proto, Blob* result) {
371  if (blob_proto.type() == kTensorBlobType) {
372  // This is a tensor object. Depending on the device type, we will
373  // use the corresponding TensorDeserializer.
374  auto deserializer = CreateDeserializer(
375  "Tensor" +
376  DeviceTypeName(blob_proto.tensor().device_detail().device_type()));
377  // Tensor's deserializer should always be registered, but we will double
378  // check if it is not null anyway.
379  CAFFE_ENFORCE(deserializer.get());
380  deserializer->Deserialize(blob_proto, result);
381  } else {
382  auto deserializer = CreateDeserializer(blob_proto.type());
383  CAFFE_ENFORCE(
384  deserializer.get(),
385  "No registered deserializer for type ",
386  blob_proto.type());
387  deserializer->Deserialize(blob_proto, result);
388  }
389 }
390 
391 // === Local helper functions ===
392 // Get dimensions from Tensor proto
393 static std::vector<int64_t> DimsFromTensorProto(const TensorProto& proto) {
394  std::vector<int64_t> dims;
395  dims.reserve(proto.dims().size());
396  for (const int64_t d : proto.dims()) {
397  dims.push_back(d);
398  }
399  return dims;
400 }
401 
402 // Get number of elements from Tensor proto
403 static int64_t NumelFromTensorProto(const TensorProto& tensor_proto) {
404  int64_t numel = 1;
405  for (const int64_t d : tensor_proto.dims()) {
406  numel *= d;
407  }
408  return numel;
409 }
410 
411 // Get data type from Tensor proto
412 static TypeMeta GetDataType(const TensorProto& tensor_proto) {
413  TypeMeta dtype;
414  if (tensor_proto.data_type() != TensorProto_DataType_UNDEFINED) {
415  dtype = DataTypeToTypeMeta(tensor_proto.data_type());
416  } else {
417  Blob temp_blob;
418  DeserializeBlob(tensor_proto.string_data(0), &temp_blob);
419  dtype = temp_blob.meta();
420  }
421  return dtype;
422 }
423 
424 // Get TensorOptions from Tensor proto
425 // Assumes TensorProto is not empty
426 static at::TensorOptions TensorOptionsFromProto(
427  const TensorProto& tensor_proto) {
428  return at::dtype(GetDataType(tensor_proto))
429  .device(OptionToDevice(tensor_proto.device_detail()));
430 }
431 
432 static std::unique_ptr<BaseContext> ContextFromProto(
433  const TensorProto& tensor_proto) {
434  auto device = OptionToDevice(tensor_proto.device_detail());
435  return CreateContext(device);
436 }
437 
438 // === Local helper functions ===
439 
440 Tensor EmptyTensorFromProto(const TensorProto& tensor_proto) {
441  auto context = ContextFromProto(tensor_proto);
442  context->SwitchToDevice();
443  if (NumelFromTensorProto(tensor_proto) == 0 &&
444  tensor_proto.data_type() == TensorProto_DataType_UNDEFINED) {
445  // TODO: remove when serialization of dtype uninitialized tensor is removed
446  return caffe2::empty(
447  {0},
448  at::dtype<float>().device(
449  OptionToDevice(tensor_proto.device_detail())));
450  } else {
451  return caffe2::empty(
452  DimsFromTensorProto(tensor_proto),
453  TensorOptionsFromProto(tensor_proto));
454  }
455 }
456 
457 void TensorDeserializer::Deserialize(const BlobProto& blob_proto, Blob* blob) {
458  auto tensor_proto = blob_proto.tensor();
459  auto context = ContextFromProto(tensor_proto);
460  context->SwitchToDevice();
461  if (NumelFromTensorProto(tensor_proto) == 0 &&
462  tensor_proto.data_type() == TensorProto_DataType_UNDEFINED) {
463  // TODO: remove after empty Tensor serialization is forbidden
464  VLOG(1) << "Deseriralizing an empty Tensor.";
465  BlobGetMutableTensor(
466  blob,
467  {0},
468  at::dtype<float>().device(
469  OptionToDevice(tensor_proto.device_detail())));
470  } else {
471  DeserializeToTensor(
472  tensor_proto,
473  BlobGetMutableTensor(
474  blob,
475  DimsFromTensorProto(tensor_proto),
476  TensorOptionsFromProto(tensor_proto)));
477  }
478 }
479 
480 void TensorDeserializer::DeserializeToTensor(
481  const TensorProto& tensor_proto,
482  Tensor* tensor) {
483  CAFFE_ENFORCE(
484  tensor->storage_initialized() && tensor->dtype_initialized(),
485  "Tensor must be initialized before passed into Deserialize function.");
486  // We create a local context for deserializing. Since Caffe2 contexts are
487  // usually lightweight, this should not involve too much overhead.
488  auto uniq_ptr = ContextFromProto(tensor_proto);
489  // since CopyFromProtoAsIs accepts BaseContext*
490  auto context = uniq_ptr.get();
491  context->SwitchToDevice();
492 
493  int64_t chunkBegin = 0;
494  auto chunkEnd = tensor->numel();
495  if (tensor_proto.has_segment()) {
496  chunkBegin = tensor_proto.segment().begin();
497  chunkEnd = tensor_proto.segment().end();
498  }
499  CAFFE_ENFORCE(
500  0 <= chunkBegin && chunkBegin <= chunkEnd && chunkEnd <= tensor->numel(),
501  "Invalid chunk ",
502  chunkBegin,
503  ' ',
504  chunkEnd,
505  " with total tensor size ",
506  tensor->numel());
507  auto chunkSize = chunkEnd - chunkBegin;
508 
509  switch (tensor_proto.data_type()) {
510  case TensorProto_DataType_FLOAT:
511  detail::CopyFromProtoAsIs(
512  chunkSize,
513  tensor_proto.float_data(),
514  tensor->template mutable_data<float>() + chunkBegin,
515  context);
516  break;
517  case TensorProto_DataType_INT32:
518  detail::CopyFromProtoAsIs(
519  chunkSize,
520  tensor_proto.int32_data(),
521  tensor->template mutable_data<int>() + chunkBegin,
522  context);
523  break;
524  case TensorProto_DataType_BYTE:
525  // Since BYTE stores the data in a string field instead of a repreated
526  // field we will have it special cased.
527  CAFFE_ENFORCE_EQ(
528  chunkSize,
529  tensor_proto.byte_data().size(),
530  "Incorrect proto field size.");
531  context->template CopyToCPU<uint8_t>(
532  chunkSize,
533  reinterpret_cast<const uint8_t*>(tensor_proto.byte_data().data()),
534  tensor->template mutable_data<uint8_t>() + chunkBegin);
535  break;
536  case TensorProto_DataType_STRING:
537  // Special handing of string because it is a non-fundamental type.
538  {
539  string* content = tensor->template mutable_data<string>();
540  for (int i = 0; i < chunkSize; ++i) {
541  content[i + chunkBegin] = tensor_proto.string_data(i);
542  }
543  }
544  break;
545  case TensorProto_DataType_BOOL:
546  detail::CopyFromProtoWithCast(
547  chunkSize,
548  tensor_proto.int32_data(),
549  tensor->template mutable_data<bool>() + chunkBegin,
550  context);
551  break;
552  case TensorProto_DataType_UINT8:
553  detail::CopyFromProtoWithCast(
554  chunkSize,
555  tensor_proto.int32_data(),
556  tensor->template mutable_data<uint8_t>() + chunkBegin,
557  context);
558  break;
559  case TensorProto_DataType_INT8:
560  detail::CopyFromProtoWithCast(
561  chunkSize,
562  tensor_proto.int32_data(),
563  tensor->template mutable_data<int8_t>() + chunkBegin,
564  context);
565  break;
566  case TensorProto_DataType_UINT16:
567  detail::CopyFromProtoWithCast(
568  chunkSize,
569  tensor_proto.int32_data(),
570  tensor->template mutable_data<uint16_t>() + chunkBegin,
571  context);
572  break;
573  case TensorProto_DataType_INT16:
574  detail::CopyFromProtoWithCast(
575  chunkSize,
576  tensor_proto.int32_data(),
577  tensor->template mutable_data<int16_t>() + chunkBegin,
578  context);
579  break;
580  case TensorProto_DataType_INT64:
581  detail::CopyFromProtoAsIs(
582  chunkSize,
583  tensor_proto.int64_data(),
584  tensor->template mutable_data<int64_t>() + chunkBegin,
585  context);
586  break;
587  case TensorProto_DataType_FLOAT16:
588  if (tensor_proto.has_byte_data()) {
589  const int kValue = 1;
590  CAFFE_ENFORCE_EQ(
591  reinterpret_cast<const char*>(&kValue)[0],
592  1,
593  "Serialization of FLOAT16 on big endian platform "
594  "is not written yet.");
595  CAFFE_ENFORCE_EQ(
596  2 * chunkSize,
597  tensor_proto.byte_data().size(),
598  "Incorrect proto field size.");
599  context->template CopyToCPU<at::Half>(
600  chunkSize,
601  reinterpret_cast<const at::Half*>(tensor_proto.byte_data().data()),
602  tensor->template mutable_data<at::Half>() + chunkBegin);
603  } else {
604  // Backward compatibility with models which used int32_data field
605  detail::CopyFromProtoWithCast(
606  chunkSize,
607  tensor_proto.int32_data(),
608  reinterpret_cast<uint16_t*>(
609  tensor->template mutable_data<at::Half>()) +
610  chunkBegin,
611  context);
612  }
613  break;
614  case TensorProto_DataType_DOUBLE:
615  detail::CopyFromProtoAsIs(
616  chunkSize,
617  tensor_proto.double_data(),
618  tensor->template mutable_data<double>() + chunkBegin,
619  context);
620  break;
621  case TensorProto_DataType_UNDEFINED: {
622  Blob temp_blob;
623  void* raw_ptr = nullptr;
624  for (int i = 0; i < chunkSize; ++i) {
625  DeserializeBlob(tensor_proto.string_data(i), &temp_blob);
626  if (i == 0) {
627  raw_ptr = tensor->raw_mutable_data(temp_blob.meta());
628  }
629  temp_blob.meta().copy()(
630  temp_blob.GetRaw(),
631  static_cast<char*>(raw_ptr) +
632  (i + chunkBegin) * temp_blob.meta().itemsize(),
633  1);
634  }
635  } break;
636  // Note: we intentially do not provide "default:" so if any new data types
637  }
638  context->FinishDeviceComputation();
639 }
640 
641 Tensor TensorDeserializer::Deserialize(const TensorProto& tensor_proto) {
642  auto tensor = EmptyTensorFromProto(tensor_proto);
643  DeserializeToTensor(tensor_proto, &tensor);
644  return tensor;
645 }
646 
648 // Serialization Helpers
650 
651 std::string SerializeAsString_EnforceCheck(
652  const google::protobuf::MessageLite& msg,
653  const char* error_location) {
654  std::string serialize_output;
655  bool result = msg.SerializeToString(&serialize_output);
656  if (!error_location) {
657  CAFFE_ENFORCE(result, "protobuf::SerializeToString failed");
658  } else {
659  CAFFE_ENFORCE(result,
660  "protobuf::SerializeToString failed for ", error_location);
661  }
662  return serialize_output;
663 }
664 
665 
666 namespace {
667 // Serialize Tensor
668 REGISTER_BLOB_SERIALIZER((TypeMeta::Id<Tensor>()), TensorSerializer);
669 REGISTER_BLOB_DESERIALIZER(TensorCPU, TensorDeserializer);
670 // Serialize std::string
671 REGISTER_BLOB_SERIALIZER((TypeMeta::Id<std::string>()), StringSerializer);
672 REGISTER_BLOB_DESERIALIZER(std::string, StringDeserializer);
673 } // namespace
674 } // namespace caffe2
Blob is a general container that hosts a typed pointer.
Definition: blob.h:24
C10_NODISCARD TensorOptions device(c10::optional< Device > device) const noexcept
Return a copy of TensorOptions with device set to the given one, or cleared if device is nullopt...
constexpr size_t itemsize() const noexcept
Returns the size of the item.
Definition: typeid.h:365
TensorSerializer is the serializer for Tensors.
BlobDeserializerBase is an abstract class that deserializes a blob from a BlobProto or a TensorProto...
void Serialize(const void *pointer, TypeMeta typeMeta, const string &name, SerializationAcceptor acceptor) override
Serializes a Blob.
void DeserializeBlob(const string &content, Blob *result)
Deserializes from a string containing either BlobProto or TensorProto.
StringSerializer is the serializer for String.
A type id is a unique id for a given C++ type.
Definition: typeid.h:60
constexpr const char * name() const noexcept
Returns a printable name for the type.
Definition: typeid.h:395
void Serialize(const void *pointer, TypeMeta typeMeta, const string &name, SerializationAcceptor acceptor) override
Serializes a Blob.
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
int GetGPUIDForPointer(const void *ptr)
Gets the GPU id that the current pointer is located at.
Definition: common_gpu.cc:106
const TypeMeta & meta() const noexcept
Returns the meta info of the blob.
Definition: blob.h:54
constexpr TypeIdentifier id() const noexcept
Returns the type id.
Definition: typeid.h:359
constexpr Copy * copy() const noexcept
Returns the typed copy function pointer for individual iterms.
Definition: typeid.h:380
T * GetMutable()
Gets a mutable pointer to the stored object.
Definition: blob.h:100
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.
StringDeserializer is the deserializer for Strings.
BlobSerializerBase is an abstract class that serializes a blob to a string.