1 #ifndef CAFFE2_CORE_TENSOR_H_ 2 #define CAFFE2_CORE_TENSOR_H_ 12 #include "caffe2/core/common.h" 14 #include "caffe2/core/context.h" 15 #include "caffe2/core/typeid.h" 16 #include "caffe2/core/logging.h" 21 CAFFE2_DECLARE_bool(caffe2_keep_on_shrink);
26 CAFFE2_DECLARE_int64(caffe2_max_keep_on_shrink_memory);
34 return vector<TIndex>(src.begin(), src.end());
42 for (
int i = k; i < dims.size(); ++i) {
49 inline TIndex size_to_dim_(
int k,
const vector<TIndex>& dims) {
50 CAFFE_ENFORCE(k <= dims.size());
52 for (
int i = 0; i < k; ++i) {
59 inline TIndex size_between_dim_(
int k,
int l,
const vector<TIndex>& dims) {
60 CAFFE_ENFORCE(l < dims.size());
63 for (
int i = k + 1; i < l; ++i) {
67 for (
int i = l + 1; i < k; ++i) {
74 inline int canonical_axis_index_(
int axis_index,
int ndims) {
75 CAFFE_ENFORCE_GE(axis_index, -ndims);
76 CAFFE_ENFORCE_LT(axis_index, ndims);
78 return axis_index + ndims;
92 template <
class Context>
107 explicit Tensor(
const vector<int>& dims) {
Resize(dims); }
119 template <
class SrcContext,
class ContextForCopy>
135 template <
class SrcContext>
143 template <
typename T>
144 Tensor(
const vector<TIndex>& dims,
const vector<T>& values, Context* context)
147 CAFFE_ENFORCE_EQ_WITH_CALLER(values.size(), size_);
148 context->template Copy<T, CPUContext, Context>(size_, values.data(), mutable_data<T>());
154 template <
typename T,
155 typename =
typename std::enable_if<std::is_scalar<T>::value>::type>
156 Tensor(
const T& value, Context* context) {
158 context->template Copy<T, CPUContext, Context>(size_, &value, mutable_data<T>());
165 template <
class SrcContext,
class ContextForCopy>
167 if ((
void*)&src == (
void*)
this) {
176 context->template CopyBytes<SrcContext, Context>(
189 template <
class SrcContext>
191 SrcContext tmp_context;
195 virtual ~
Tensor() noexcept {}
206 template <
class ContextForCopy>
207 void Extend(TIndex num,
float growthPct, ContextForCopy* context) {
208 CAFFE_ENFORCE_GE_WITH_CALLER(dims_.size(), 1);
209 auto newDims = dims_;
215 auto newSize = std::accumulate(
218 static_cast<TIndex
>(1),
219 std::multiplies<TIndex>());
220 if (newSize * meta_.
itemsize() <= capacity_) {
225 auto newCapacity = dims_;
226 newCapacity[0] = std::max<size_t>(
227 newDims[0], std::ceil(dims_[0] * (growthPct + 100) / 100));
228 Reserve(newCapacity, context);
233 template <
class T,
class ContextForCopy>
234 void Reserve(
const std::vector<T>& newCapacity, ContextForCopy* context) {
235 auto newSize = std::accumulate(
238 static_cast<TIndex
>(1),
239 std::multiplies<TIndex>());
240 if (newSize * meta_.
itemsize() <= capacity_) {
243 auto oldData = std::move(data_);
244 auto oldSize = size_;
245 auto oldDims = dims_;
248 context->template CopyItems<ContextForCopy, ContextForCopy>(
249 meta_, oldSize, oldData.get(), newData);
262 CAFFE_ENFORCE_WITH_CALLER(dims_.size() >= 1,
"Tensor must be at least 1D");
263 CAFFE_ENFORCE_WITH_CALLER(
264 outer_dim <= dims_[0],
265 "New outer dimension must be smaller than current.");
266 dims_[0] = outer_dim;
267 size_ = std::accumulate(
270 static_cast<TIndex
>(1),
271 std::multiplies<TIndex>());
287 template <
typename... Ts>
289 bool size_changed = SetDims(dim_source...);
293 int64_t new_size = size_ * meta_.
itemsize();
294 bool reset_tensor =
false;
298 reset_tensor = capacity_ < new_size;
300 reset_tensor = capacity_ < new_size || !FLAGS_caffe2_keep_on_shrink ||
301 capacity_ - new_size > FLAGS_caffe2_max_keep_on_shrink_memory;
314 template <
class OtherContext>
317 if (static_cast<void*>(
this) != static_cast<const void*>(&src_tensor)) {
326 inline void Reshape(
const vector<TIndex>& dims) {
328 for (
auto d : dims) {
329 CAFFE_ENFORCE_GE_WITH_CALLER(d, 0);
332 CAFFE_ENFORCE_WITH_CALLER(
334 "New size and old size are not equal. You cannot use Reshape, " 335 "but should use Resize." 338 " The old caffe2 mixes Reshape and Resize but this behavior has " 339 "been changed. If you find this error, most likely you will need " 340 "to change corresponding code from Reshape to Resize.");
344 inline void Reshape(
const vector<int>& dims) {
368 std::stringstream ss;
369 ss <<
"A Tensor of item size " <<
itemsize() <<
" and type " 370 << meta_.
name() <<
" and dimension (";
371 for (
int d : dims_) {
379 std::swap(dims_, other.dims_);
380 std::swap(size_, other.size_);
381 std::swap(meta_, other.meta_);
382 std::swap(data_, other.data_);
383 std::swap(shares_data_, other.shares_data_);
384 std::swap(capacity_, other.capacity_);
385 std::swap(reserved_, other.reserved_);
402 CAFFE_ENFORCE_EQ_WITH_CALLER(
405 "Size mismatch - did you call reshape before sharing the data?");
409 CAFFE_ENFORCE_WITH_CALLER(
410 src.data_.get() || src.size_ == 0,
411 "Source tensor has no content and has size > 0");
414 capacity_ = src.capacity_;
427 template <
typename T,
typename Deleter = MemoryDeleter>
432 template <
typename Deleter = MemoryDeleter>
437 Deleter d =
nullptr) {
439 CAFFE_ENFORCE_WITH_CALLER(
441 "To share with a raw external pointer you need to have meta " 443 CAFFE_ENFORCE_WITH_CALLER(
445 "To share data with a raw pointer, you need to set shape first.");
447 if (std::is_same<MemoryDeleter, Deleter>::value &&
448 reinterpret_cast<MemoryDeleter*>(&d)[0] ==
nullptr) {
450 data_ = std::shared_ptr<void>(std::shared_ptr<void>(), src);
457 capacity_ = capacity;
464 bool shares_data()
const {
473 CAFFE_ENFORCE_WITH_CALLER(data_.get() || size_ == 0);
483 template <
typename T>
485 CAFFE_ENFORCE_WITH_CALLER(
486 data_.get() || size_ == 0,
487 "The tensor is of non-zero shape, but its data is not allocated yet. " 488 "Caffe2 uses a lazy allocation, so you will need to call " 489 "mutable_data() or raw_mutable_data() to actually allocate memory.");
490 CAFFE_ENFORCE_WITH_CALLER(
492 "Tensor type mismatch, caller expects elements to be ",
493 TypeMeta::TypeName<T>(),
494 " while tensor contains ",
496 return static_cast<T*
>(data_.get());
512 if (meta_ == meta && (data_.get() || size_ == 0)) {
515 bool had_special_dtor = meta_.
dtor() !=
nullptr;
517 CAFFE_ENFORCE_WITH_CALLER(
519 "Tensor is not initialized. You probably need to call Resize() " 520 "before calling mutable_data()");
526 (meta.
ctor() ==
nullptr && !had_special_dtor &&
527 capacity_ >= size_ * meta_.
itemsize())) {
535 auto dtor = meta_.
dtor();
536 auto ptr_and_deleter = Context::New(size_ * meta_.
itemsize());
537 auto deleter = ptr_and_deleter.second;
539 ptr_and_deleter.first, [
size, dtor, deleter](
void* ptr) ->
void {
543 meta_.
ctor()(data_.get(), size_);
546 auto ptr_and_deleter = Context::New(size_ * meta_.
itemsize());
547 data_.reset(ptr_and_deleter.first, ptr_and_deleter.second);
549 capacity_ = size_ * meta_.
itemsize();
564 CAFFE_ENFORCE_WITH_CALLER(
566 "Calling raw_mutable_data() without meta, but the current meta is " 577 template <
typename T>
579 if ((size_ == 0 || data_.get()) && IsType<T>()) {
580 return static_cast<T*
>(data_.get());
589 inline int ndim()
const {
return dims_.size(); }
593 inline TIndex
size()
const {
return size_; }
605 inline size_t capacity_nbytes()
const {
611 inline const vector<TIndex>&
dims()
const {
return dims_; }
613 inline TIndex size_from_dim(
int k)
const {
617 inline TIndex size_to_dim(
int k)
const {
618 return size_to_dim_(k, dims_);
621 inline TIndex size_between_dim(
int k,
int l)
const {
622 return size_between_dim_(k, l, dims_);
637 return canonical_axis_index_(axis_index,
ndim());
643 template <
typename T>
644 inline bool IsType()
const {
return meta_.Match<T>(); }
657 inline int dim32(
const int i)
const {
659 CAFFE_ENFORCE_LT_WITH_CALLER(i, dims_.size(),
"Exceeding ndim limit");
660 CAFFE_ENFORCE_GE_WITH_CALLER(i, 0,
"Cannot have negative dimension index");
662 CAFFE_ENFORCE_LT_WITH_CALLER(dims_[i], std::numeric_limits<int>::max());
663 return static_cast<int>(dims_[i]);
671 inline TIndex
dim(
const int i)
const {
673 CAFFE_ENFORCE_LT_WITH_CALLER(i, dims_.size(),
"Exceeding ndim limit");
674 CAFFE_ENFORCE_GE_WITH_CALLER(i, 0,
"Cannot have negative dimension index");
680 vector<TIndex> dims_;
683 std::shared_ptr<void> data_;
684 bool shares_data_ =
false;
685 size_t capacity_ = 0;
686 bool reserved_ =
false;
692 typename =
typename std::enable_if<std::is_integral<T>::value>::type>
693 bool SetDims(
const vector<T>& src) {
694 auto old_size = size_;
695 dims_.resize(src.size());
697 for (
unsigned int i = 0; i < src.size(); ++i) {
702 return size_ != old_size;
706 auto old_size = size_;
709 return size_ != old_size;
715 bool SetDims(
const TIndex d0) {
716 auto old_size = size_;
720 return size_ != old_size;
723 bool SetDims(
const TIndex d0,
const TIndex d1) {
724 auto old_size = size_;
729 return size_ != old_size;
732 bool SetDims(
const TIndex d0,
const TIndex d1,
const TIndex d2) {
733 auto old_size = size_;
738 size_ = d0 * d1 * d2;
739 return size_ != old_size;
743 SetDims(
const TIndex d0,
const TIndex d1,
const TIndex d2,
const TIndex d3) {
744 auto old_size = size_;
750 size_ = d0 * d1 * d2 * d3;
751 return size_ != old_size;
762 constexpr
int k_limit_default_ = 1000;
765 typedef TypeMeta (*TypeCall)(
const void*);
766 TypeCall GetTypeCallFunction(CaffeTypeId
id);
767 void RegisterTypeCallFunction(CaffeTypeId
id, TypeCall c);
769 template <
class Context>
770 TypeMeta GetTensorType(
const void* c) {
776 typedef vector<TIndex> (*TensorInfoCall)(
780 DeviceOption* device);
781 TensorInfoCall GetTensorInfoFunction(CaffeTypeId
id);
782 void RegisterTensorInfoFunction(CaffeTypeId
id, TensorInfoCall c);
784 template <
class Context>
785 vector<TIndex> GetTensorInfo(
789 DeviceOption* device) {
791 *shares_data = tc->shares_data();
792 *capacity = tc->capacity_nbytes();
793 device->set_device_type(CPU);
794 device->set_cuda_gpu_id(0);
801 const std::string& tensor_name =
"",
802 const std::string& file_name =
"",
803 int limit = k_limit_default_);
809 template <
class Context>
812 template <
class Context>
818 std::unique_ptr<std::ofstream> log_file_;
819 std::string tensor_name_;
824 std::stringstream values_stream;
827 int total_count =
static_cast<int>(
828 std::min(tensor.
size(), TIndex(limit_)));
829 const T* tensor_data = tensor.template data<T>();
830 for (
int i = 0; i < total_count - 1; ++i) {
831 values_stream << tensor_data[i] <<
",";
834 values_stream << tensor_data[total_count - 1];
836 (*log_file_) << MetaStr(tensor) << values_stream.str() << std::endl;
839 LOG(INFO) << MetaStr(tensor) << values_stream.str();
843 template <
class Context>
846 (*log_file_) << MetaStr(tensor) << std::endl;
848 LOG(INFO) << MetaStr(tensor);
852 template <
class Context>
854 std::stringstream meta_stream;
855 meta_stream <<
"Tensor " << tensor_name_ <<
" of type " 856 << tensor.
meta().name() <<
". Dims: (";
857 for (
const auto dim : tensor.
dims()) {
858 meta_stream <<
dim <<
",";
860 meta_stream <<
"): ";
861 return meta_stream.str();
865 #endif // CAFFE2_CORE_TENSOR_H_ void Extend(TIndex num, float growthPct, ContextForCopy *context)
Extends the outer-most dimension of this tensor by num elements, preserving the existing data...
const T * data() const
Returns a typed pointer of the underlying storage.
size_t itemsize() const
Return the number of bytes each item takes in the tensor.
const TypeMeta & meta() const
Returns the TypeMeta object associated with the current data type.
Tensor(const vector< TIndex > &dims, const vector< T > &values, Context *context)
Creates a tensor, and fills its contents with the given values.
TIndex dim(const int i) const
Returns the i-th dimension of the tensor.
void Shrink(TIndex outer_dim)
Shrinks the outer-most dimension to given size, keeping the data.
int canonical_axis_index(int axis_index) const
Returns the 'canonical' version of a (usually) user-specified axis, allowing for negative indexing (e...
void ShareExternalPointer(T *src, size_t capacity=0, Deleter d=nullptr)
Shares the data with an externally managed pointer.
Tensor is the basic class in Caffe2 that stores a contiguous memory with its shape information...
int dim32(const int i) const
Returns the i-th dimension of the tensor in int.
void * raw_mutable_data()
Returns a mutable raw pointer of the underlying storage.
void CopyFrom(const Tensor< SrcContext > &src, ContextForCopy *context)
Copies the data from a source tensor, with a contex provided to carry out the underlying memcpy opera...
Tensor(const Tensor< SrcContext > &src, ContextForCopy *context)
Creates a tensor from a source tensor, copying over the content.
TIndex size() const
Returns the size (i.e.
T * mutable_data()
Returns a typed pointer of the underlying storage.
void FreeMemory()
Release whatever memory the tensor was holding but keep size and type information.
const vector< TIndex > & dims() const
Returns the dimensions of the tensor as a vector.
void CopyFrom(const Tensor< SrcContext > &src)
Copies the data from a source tensor.
void Resize(Ts...dim_source)
Resizes a tensor.
TIndex size_from_dim_(int k, const vector< TIndex > &dims)
Return product of all dimensions starting from K.
A global dictionary that holds information about what Caffe2 modules have been loaded in the current ...
Tensor()
Initializes an empty tensor.
const void * raw_data() const
Returns a const raw void* pointer of the underlying storage.
void Reshape(const vector< TIndex > &dims)
Resizes the tensor without touching underlying storage.
string DebugString() const
A utility function to print the debug string for the tensor.
size_t nbytes() const
Returns the total number of bytes of the storage.
void ShareData(const Tensor &src)
Shares the data with another tensor.
Tensor(const T &value, Context *context)
Creates a scalar tensor, and fills its content with the given value.
Tensor(const vector< TIndex > &dims)
Creates a tensor of the given dimension.
void ResizeLike(const Tensor< OtherContext > &src_tensor)
Resize the tensor like the source tensor.
vector< TIndex > ToVectorTIndex(const std::vector< int > &src)
A utility function to convert vector<int> to vector<TIndex>.
Commandline flags support for Caffe2.
bool IsType() const
Checks if the tensor content is of the given data type.
int ndim() const
Returns the number of dimensions of the data.
Tensor(const Tensor< SrcContext > &src)
Creates a tensor from a source tensor, copying over the content.
void * raw_mutable_data(const TypeMeta &meta)
Returns a mutable raw pointer of the underlying storage.