Caffe2::Tensor - PaddlePaddle/Paddle GitHub Wiki
Caffe2 has caffe2::Tensor.  The purpose of caffe2::Tensor is to manage the memory of a tensor.  Users calls data() or mutable_data() to access the managed memory.  data and mutable_data can return a big enough block of any typed elements -- please go on reading for details.
caffe2::Tensor has the following data members:
- 
vector<TIndex> dims_records the size of each dimension.
- 
TIndex size_ = -1is the total number of elements in this tensor.
- 
TypeMeta meta_contains constructor, destructor, and copy lamda of a certain element type.
- 
std::shared_ptr<void> data_holds the data.
- 
bool shares_data_ = falsemarks if this tensor shares data with another tensor.
- 
size_t capacity_ = 0in case of chunk load we store how much data was already loaded.
We can see that caffe2::Tensor uses a vector<TIndex> to represent the size of tensors, where typedef int64_t TIndex;.
In addition to data members, caffe2::Tensor takes a template parameter, Context, which could be GPUContext or CPUContext, that provides methods that allocate and delete memory.
Please be aware that the memory is owned by std::shared_ptr<void> data_, and shared_ptr takes not only a pointer to the data, and optionally a lambda that frees the memory.  shared_ptr calls the memory free lambda when the reference count goes down to zero.
As caffe2::Tensor uses void-typed shared_ptr (shared_ptr<void> data_), it requires the customized memory deletion functions -- the template parameter  Context provides them.  Also, MetaType provides ways to initialize allocated memory.
meta_ either comes from other tensors copied by copy-from-other-tensors constructors, or is created by TypeMeta::Make<T> from element type T by copy-from-vector constructors.
caffe2::Tensor doesn't have destructors.  shared_ptr is in charge of deleting the memory.
caffe2::Tensor's constructor calls Resize, which records the size into dims_ and size_ by calling SetDims, but doesn't allocate the memory.  The memory allocation is delayed until caffe2::Tensor::mutable_data() is called.
To be more specific,  Resize checks if capacity_ < size_ * meta_.itemize().
  void Resize(Ts... dim_source) {
    bool size_changed = SetDims(dim_source...);
    // If needed, we will free the data. the next mutable_data() call
    // will create the data storage.
    if (size_changed && (capacity_ < size_ * meta_.itemsize() ||
                         !FLAGS_caffe2_keep_on_shrink)) {
      data_.reset();
      capacity_ = 0;
    }
  }If so, it resets data_, and a later call to mutable_data will re-allocate the memory; otherwise, Resize does nothing, as there is sufficient memory and data accessors could work.
It seems it doesn't hurt anyway if Resize re-allocate memory in case capacity is not enough.  Why does it leave the work to mutable_data?
The answer lies in the implementation of mutable_data:
 template <typename T>
 inline T* mutable_data() {
   if ((size_ == 0 || data_.get()) && IsType<T>()) {
     return static_cast<T*>(data_.get());
   }
   return static_cast<T*>(raw_mutable_data(TypeMeta::Make<T>()));
 }We can see that as a function template, mutable_data can return an array of any typed elements.  As a public method, mutable_data is supposed to be call by the client directly; it makes Tensor looks can hold any typed elements.
In above code, Tensor::IsType<T>() checks if the expected return type T is the current type recorded by mata_:
 template <typename T>
 inline bool IsType() const { return meta_.Match<T>(); }If they are the same, mutable_data returns the data pointer; otherwise, mutable_data calls raw_mutable_data to reallocate the memory by calling Context::New and meta_.ctor.
In addition to constructors and data accessors, all other methods of caffe2::Tensor generally define the type and size of memory data accessors return:
- 
Resize,
- 
Reshape, a variant ofResizethat requires the total number of elements doesn't change,
- 
ResizeLike, another variant ofResizethat make the tensor the same size as another one,
- CopyFrom
- 
Extend, which extends the out-most dimension,
- 
Shrink, which shrinks the out-most dimension,
- 
ShareData, which marks the current tensor shares another tensor's data, sodata()andmutable_datareturns (and mutate) another tensor's memory.
The vector<TIndex> Tensor::dims_ reads straightforward, and only some variants of SetDims are required to construct it conveniently.
Caffe2 throw exceptions when errors happen.  It has a set of macros sharing the prefix CAFFE_ENFORCE.   For example:
    CAFFE_ENFORCE(
        outer_dim <= dims_[0],
        "New outer dimension must be smaller than current.");where the definition of CAFFE_ENFORCE throws an exception:
#define CAFFE_ENFORCE(condition, ...)                                         \
  do {                                                                        \
    if (!(condition)) {                                                       \
      throw ::caffe2::EnforceNotMet(                                          \
          __FILE__, __LINE__, #condition, ::caffe2::MakeString(__VA_ARGS__)); \
    }                                                                         \
  } while (false)