/** * Copyright 2020 Huawei Technologies Co., Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ir/tensor.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "abstract/abstract_value.h" namespace mindspore { namespace tensor { constexpr auto kEllipsis = "..."; constexpr auto kThreshold = 6; constexpr auto kThreshold1DFloat = kThreshold * 2; constexpr auto kThreshold1DInt = kThreshold * 4; constexpr auto kThreshold1DBool = kThreshold * 2; static std::string MakeId() { // Use atomic to make id generator thread safe. static std::atomic last_id{1}; return "T" + std::to_string(last_id.fetch_add(1, std::memory_order_relaxed)); } static TypeId TypeIdOf(const TypePtr &data_type, TypeId defaultTypeId) { return data_type ? data_type->type_id() : defaultTypeId; } static size_t SizeOf(const ShapeVector &shape) { return std::accumulate(shape.begin(), shape.end(), size_t(1), std::multiplies()); } static std::string ShapeToString(const ShapeVector &shape) { std::string str = "["; const size_t count = shape.size(); for (size_t i = 0; i < count; ++i) { if (i > 0) { str.append(", "); } str.append(std::to_string(shape[i])); } return str.append("]"); } template std::unique_ptr NewData(const U *input, size_t size) { if (input == nullptr || size == 0) { return nullptr; } auto data = std::make_unique(size); if constexpr (!std::is_same::value && (std::is_same::value || std::is_same::value)) { // Because float16 do not support implicit cast from/to other types, // We can not use std::copy() on array of float16, use a loop here. for (size_t i = 0; i < size; ++i) { data[i] = static_cast(input[i]); } } else { // otherwise, use std::copy for better performance. std::copy(input, input + size, data.get()); } return data; } template std::unique_ptr NewData(Scalar scalar) { auto data = std::make_unique(1); data[0] = static_cast(scalar); return data; } template std::unique_ptr CopyData(const ShapeVector &shape, void *const data, TypeId data_type) { const size_t size = SizeOf(shape); switch (data_type) { case kNumberTypeBool: { auto buf = static_cast(data); return NewData(buf, size); } case kNumberTypeUInt8: { auto buf = static_cast(data); return NewData(buf, size); } case kNumberTypeInt8: { auto buf = static_cast(data); return NewData(buf, size); } case kNumberTypeInt16: { auto buf = static_cast(data); return NewData(buf, size); } case kNumberTypeInt32: { auto buf = static_cast(data); return NewData(buf, size); } case kNumberTypeInt64: { auto buf = static_cast(data); return NewData(buf, size); } case kNumberTypeUInt16: { auto buf = static_cast(data); return NewData(buf, size); } case kNumberTypeUInt32: { auto buf = static_cast(data); return NewData(buf, size); } case kNumberTypeUInt64: { auto buf = static_cast(data); return NewData(buf, size); } case kNumberTypeFloat16: { auto buf = static_cast(data); return NewData(buf, size); } case kNumberTypeFloat32: { auto buf = static_cast(data); return NewData(buf, size); } case kNumberTypeFloat64: { auto buf = static_cast(data); return NewData(buf, size); } default: break; } MS_LOG(EXCEPTION) << "Cannot construct Tensor because of unsupported data type: " << data_type << "."; } template std::unique_ptr CopyData(const ShapeVector &shape, void *const data, size_t data_len) { size_t size = SizeOf(shape); if (size * sizeof(T) != data_len) { MS_LOG(EXCEPTION) << "Incorrect tensor input data length " << data_len << ", expect " << size * sizeof(T) << " item size " << sizeof(T); } auto buf = static_cast(data); return NewData(buf, size); } // Tensor data implementation. template class TensorDataImpl : public TensorData { public: explicit TensorDataImpl(const ShapeVector &shape) : ndim_(shape.size()), data_size_(SizeOf(shape)) {} ~TensorDataImpl() = default; TensorDataImpl(const ShapeVector &shape, void *data, size_t data_len) : ndim_(shape.size()), data_size_(SizeOf(shape)), data_(CopyData(shape, data, data_len)) {} TensorDataImpl(const ShapeVector &shape, void *data, TypeId data_type) : ndim_(shape.size()), data_size_(SizeOf(shape)), data_(CopyData(shape, data, data_type)) {} template TensorDataImpl(const ShapeVector &shape, const U *input, size_t size) : ndim_(shape.size()), data_size_(SizeOf(shape)), data_(NewData(input, size)) {} template TensorDataImpl(const ShapeVector &shape, Scalar scalar) : ndim_(shape.size()), data_size_(SizeOf(shape)), data_(NewData(scalar)) {} ssize_t size() const override { return static_cast(data_size_); } ssize_t itemsize() const override { return static_cast(sizeof(T)); } ssize_t nbytes() const override { return size() * itemsize(); } ssize_t ndim() const override { return static_cast(ndim_); } void *data() override { if (data_ == nullptr) { // Lazy allocation. data_ = std::make_unique(data_size_); } return data_.get(); } const void *const_data() const override { // May return nullptr if data not initialized. return data_.get(); } bool equals(const TensorData &other) const override { auto ptr = dynamic_cast *>(&other); if (ptr == nullptr) { // Not same type, compare data byte by byte. return TensorData::equals(other); } if (ptr == this) { return true; } if (data_ == nullptr || ptr->data_ == nullptr) { return false; } return (ndim_ == ptr->ndim_) && (data_size_ == ptr->data_size_) && std::equal(data_.get(), data_.get() + data_size_, ptr->data_.get()); } std::string ToString(const TypeId type, const ShapeVector &shape, bool use_comma) const override { constexpr auto valid = std::is_same::value || std::is_same::value || std::is_same::value || std::is_same::value || std::is_same::value || std::is_same::value || std::is_same::value || std::is_same::value || std::is_same::value || std::is_same::value || std::is_same::value || std::is_same::value; static_assert(valid, "Type is invalid"); if (data_size_ == 0) { return ""; } if (data_ == nullptr) { return ""; } std::ostringstream ss; if (data_size_ == 1 && ndim_ == 0) { // Scalar OutputDataString(ss, 0, 0, 1, false); return ss.str(); } ssize_t cursor = 0; SummaryStringRecursive(ss, shape, &cursor, 0, use_comma); return ss.str(); } private: void OutputDataString(std::ostringstream &ss, ssize_t cursor, ssize_t start, ssize_t end, bool use_comma) const { const bool isScalar = ndim_ == 0 && end - start == 1; constexpr auto isFloat = std::is_same::value || std::is_same::value || std::is_same::value; constexpr auto isBool = std::is_same::value; constexpr int linefeedThreshold = isFloat ? kThreshold1DFloat : (isBool ? kThreshold1DBool : kThreshold1DInt); for (ssize_t i = start; i < end && (cursor + i) < static_cast(data_size_); i++) { const auto value = data_[cursor + i]; if constexpr (isFloat) { if (isScalar) { ss << value; } else { if constexpr (std::is_same::value) { ss << std::setw(11) << std::setprecision(4) << std::setiosflags(std::ios::scientific | std::ios::right) << value; } else { ss << std::setw(15) << std::setprecision(8) << std::setiosflags(std::ios::scientific | std::ios::right) << value; } } } else if (std::is_same::value) { if (isScalar) { ss << (value ? "True" : "False"); } else { ss << std::setw(5) << std::setiosflags(std::ios::right) << (value ? "True" : "False"); } } else { constexpr auto isSigned = std::is_same::value; if constexpr (isSigned) { if (!isScalar && static_cast(value) >= 0) { ss << ' '; } } // Set width and indent for different int type with signed position. // // uint8 width: 3, [0, 255] // int8 width: 4, [-128, 127] // uint16 width: 5, [0, 65535] // int16 width: 6, [-32768, 32767] // uint32 width: 10, [0, 4294967295] // int32 width: 11, [-2147483648, 2147483647] // uint64 width: NOT SET (20, [0, 18446744073709551615]) // int64 width: NOT SET (20, [-9223372036854775808, 9223372036854775807]) if constexpr (std::is_same::value) { ss << std::setw(3) << std::setiosflags(std::ios::right) << static_cast(value); } else if constexpr (std::is_same::value) { ss << std::setw(4) << std::setiosflags(std::ios::right) << static_cast(value); } else if constexpr (std::is_same::value) { ss << std::setw(5) << std::setiosflags(std::ios::right) << value; } else if constexpr (std::is_same::value) { ss << std::setw(6) << std::setiosflags(std::ios::right) << value; } else if constexpr (std::is_same::value) { ss << std::setw(10) << std::setiosflags(std::ios::right) << value; } else if constexpr (std::is_same::value) { ss << std::setw(11) << std::setiosflags(std::ios::right) << value; } else { ss << value; } } if (!isScalar && i != end - 1) { if (use_comma) { ss << ','; } ss << ' '; } if (!isScalar && ndim_ == 1 && (i + 1) % linefeedThreshold == 0) { // Add a line feed every {threshold of type} for 1D tensor. ss << '\n' << ' '; } } } void SummaryStringRecursive(std::ostringstream &ss, const ShapeVector &shape, ssize_t *cursor, ssize_t depth, bool use_comma) const { if (depth >= static_cast(ndim_)) { return; } ss << '['; if (depth == static_cast(ndim_) - 1) { // Bottom dimension ssize_t num = shape[depth]; if (num > kThreshold && ndim_ > 1) { OutputDataString(ss, *cursor, 0, kThreshold / 2, use_comma); ss << ' ' << kEllipsis << ' '; OutputDataString(ss, *cursor, num - kThreshold / 2, num, use_comma); } else { OutputDataString(ss, *cursor, 0, num, use_comma); } *cursor += num; } else { // Middle dimension ssize_t num = shape[depth]; // Handle the first half. for (ssize_t i = 0; i < std::min(static_cast(kThreshold / 2), num); i++) { if (i > 0) { if (use_comma) { ss << ','; } ss << '\n'; ss << std::setw(depth + 1) << ' '; // Add the indent. } SummaryStringRecursive(ss, shape, cursor, depth + 1, use_comma); } // Handle the ignored part. if (num > kThreshold) { if (use_comma) { ss << ','; } ss << '\n'; ss << std::setw(depth + 1) << ' '; // Add the indent. ss << kEllipsis; // Ignored at this layer. ssize_t ignored = shape[depth + 1]; for (ssize_t i = depth + 2; i < static_cast(ndim_); i++) { ignored *= shape[i]; } // Multiple with ignored layers number. ignored *= num - kThreshold; *cursor += ignored; } // Handle the second half. if (num > kThreshold / 2) { auto continue_pos = num - kThreshold / 2; for (ssize_t i = continue_pos; i < num; i++) { if (use_comma && i != continue_pos) { ss << ','; } ss << '\n'; ss << std::setw(depth + 1) << ' '; // Add the indent. SummaryStringRecursive(ss, shape, cursor, depth + 1, use_comma); } } } ss << ']'; } size_t ndim_{0}; size_t data_size_{0}; std::unique_ptr data_; }; template TensorDataPtr MakeTensorData(TypeId data_type, const ShapeVector &shape, const Args... args) { switch (data_type) { case kNumberTypeBool: return std::make_shared>(shape, args...); case kNumberTypeUInt8: return std::make_shared>(shape, args...); case kNumberTypeInt8: return std::make_shared>(shape, args...); case kNumberTypeInt16: return std::make_shared>(shape, args...); case kNumberTypeInt32: return std::make_shared>(shape, args...); case kNumberTypeInt64: return std::make_shared>(shape, args...); case kNumberTypeUInt16: return std::make_shared>(shape, args...); case kNumberTypeUInt32: return std::make_shared>(shape, args...); case kNumberTypeUInt64: return std::make_shared>(shape, args...); case kNumberTypeFloat16: return std::make_shared>(shape, args...); case kNumberTypeFloat32: return std::make_shared>(shape, args...); case kNumberTypeFloat64: return std::make_shared>(shape, args...); default: break; } MS_LOG(EXCEPTION) << "Cannot construct Tensor because of unsupported data type: " << data_type << "."; } Tensor::Tensor(const Tensor &tensor) : MetaTensor(tensor), init_flag_(tensor.init_flag_), data_(tensor.data_), id_(tensor.id_), event_(tensor.event_), sync_status_(tensor.sync_status_), device_sync_(tensor.device_sync_), padding_type_(tensor.padding_type()) {} Tensor::Tensor(const Tensor &tensor, TypeId data_type) : MetaTensor(data_type, tensor.shape_), init_flag_(tensor.init_flag_), data_(MakeTensorData(data_type, tensor.shape_, tensor.data_->data(), tensor.data_type_)), id_(tensor.id_), event_(tensor.event_), sync_status_(tensor.sync_status_), device_sync_(tensor.device_sync_), padding_type_(tensor.padding_type()) {} Tensor::Tensor(TypeId data_type, const ShapeVector &shape, TensorDataPtr data) : MetaTensor(data_type, shape), data_(std::move(data)), id_(MakeId()) {} Tensor::Tensor(TypeId data_type, const ShapeVector &shape) : Tensor(data_type, shape, MakeTensorData(data_type, shape)) {} Tensor::Tensor(TypeId data_type, const ShapeVector &shape, void *data, size_t data_len) : Tensor(data_type, shape, MakeTensorData(data_type, shape, data, data_len)) {} Tensor::Tensor(TypeId data_type, const ShapeVector &shape, void *data, TypeId src_data_type) : Tensor(data_type, shape, MakeTensorData(data_type, shape, data, src_data_type)) {} Tensor::Tensor(const std::vector &input, const TypePtr &data_type) : MetaTensor(TypeIdOf(data_type, kNumberTypeInt32), {static_cast(input.size())}), data_(MakeTensorData(data_type_, shape_, input.data(), input.size())), id_(MakeId()) {} Tensor::Tensor(const std::vector &input, const TypePtr &data_type) : MetaTensor(TypeIdOf(data_type, kNumberTypeFloat32), {static_cast(input.size())}), data_(MakeTensorData(data_type_, shape_, input.data(), input.size())), id_(MakeId()) {} Tensor::Tensor(int64_t input, const TypePtr &data_type) : MetaTensor(TypeIdOf(data_type, kNumberTypeInt32), {}), data_(MakeTensorData(data_type_, {}, input)), id_(MakeId()) {} Tensor::Tensor(double input, const TypePtr &data_type) : MetaTensor(TypeIdOf(data_type, kNumberTypeFloat32), {}), data_(MakeTensorData(data_type_, {}, input)), id_(MakeId()) {} bool Tensor::operator==(const Tensor &tensor) const { return (&tensor == this || (MetaTensor::operator==(tensor) && data_ == tensor.data_)); } bool Tensor::ValueEqual(const Tensor &tensor) const { return (&tensor == this || (MetaTensor::operator==(tensor) && data_->equals(*tensor.data_))); } // assgin value to this tensor Tensor &Tensor::AssignValue(const Tensor &tensor) { if (this != &tensor) { MetaTensor::operator=(tensor); device_sync_ = tensor.device_sync_; data_ = tensor.data_; id_ = tensor.id_; event_ = tensor.event_; sync_status_ = tensor.sync_status_; padding_type_ = tensor.padding_type_; } return *this; } abstract::AbstractBasePtr Tensor::ToAbstract() { auto tens = shared_from_base(); auto dtype = tens->Dtype(); if (!IsSubType(dtype, kNumber)) { MS_LOG(EXCEPTION) << "Expect tensor type kNumber but got: " << dtype->ToString() << "."; } auto tensor_shape = tens->shape(); auto abs_tensor = std::make_shared(dtype, tensor_shape); // if is parameter always no value. if (is_parameter_) { auto param_name = param_info_->name(); auto ref_key = std::make_shared(param_name); auto abs_ref_key = ref_key->ToAbstract(); abs_tensor = std::make_shared(abs_ref_key, abs_tensor); } else { abs_tensor->set_value(shared_from_base()); } return abs_tensor; } std::string Tensor::GetShapeAndDataTypeInfo() const { std::ostringstream buf; buf << "Tensor shape:[" << shape() << "]" << this->Dtype()->ToString(); return buf.str(); } std::string Tensor::ToStringInternal(int limit_size) const { std::ostringstream buf; auto dtype = Dtype(); MS_EXCEPTION_IF_NULL(dtype); data_sync(); buf << "Tensor(shape=" << ShapeToString(shape_) << ", dtype=" << dtype->ToString() << ','; if (limit_size <= 0 || DataSize() < limit_size) { // Only print data for small tensor. buf << ((data().ndim() > 1) ? '\n' : ' ') << data().ToString(data_type_, shape_, false) << ')'; } else { buf << " [...])"; } return buf.str(); } std::string Tensor::ToString() const { constexpr int small_tensor_size = 30; return ToStringInternal(small_tensor_size); } std::string Tensor::ToStringNoLimit() const { return ToStringInternal(0); } std::string Tensor::ToStringRepr() const { std::ostringstream buf; auto dtype = Dtype(); MS_EXCEPTION_IF_NULL(dtype); data_sync(); buf << "Tensor(shape=" << ShapeToString(shape_) << ", dtype=" << dtype->ToString() << ',' << ((data().ndim() > 1) ? '\n' : ' ') << data().ToString(data_type_, shape_, true) << ')'; return buf.str(); } void Tensor::data_sync() const { Wait(); if (device_sync_ == nullptr) { return; } if (!device_sync_->SyncDeviceToHost(shape(), static_cast(data().nbytes()), data_type(), data_c())) { MS_LOG(EXCEPTION) << "SyncDeviceToHost failed."; } } TypeId Tensor::set_data_type(const TypeId data_type) { if (data_type != data_type_) { data_ = MakeTensorData(data_type, shape_, data_->data(), data_type_); return MetaTensor::set_data_type(data_type); } return data_type; } } // namespace tensor } // namespace mindspore