// Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved. // // 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 "paddle/fluid/framework/dlpack_tensor.h" #include "paddle/fluid/framework/convert_utils.h" #include "paddle/fluid/framework/data_type.h" #include "paddle/phi/common/data_type.h" #include "paddle/phi/common/place.h" #include "paddle/phi/core/utils/visit_place.h" namespace paddle { namespace framework { namespace internal { class PaddleDeleterManager { public: static PaddleDeleterManager &Instance() { static PaddleDeleterManager instance; return instance; } void AddDeleter(void *ptr, std::function deleter) { std::lock_guard lock(mutex_); ptr_to_deleter_[ptr] = deleter; } static void DeleterBridge(phi::Allocation *alloc) { std::lock_guard lock(PaddleDeleterManager::Instance().mutex_); auto &ptr_to_deleter = PaddleDeleterManager::Instance().ptr_to_deleter_; auto it = ptr_to_deleter.find(static_cast(alloc->ptr())); if (it != ptr_to_deleter.end()) { it->second(alloc); // call the deleter ptr_to_deleter.erase(it); // remove the entry from the map safely } } private: std::unordered_map> ptr_to_deleter_; std::mutex mutex_; }; template DenseTensor from_blob(void *data, T *src, const DDim &shape, const DDim &strides, DataType dtype, const Place &place, const Deleter &deleter) { auto meta = phi::DenseTensorMeta(dtype, shape, strides); phi::Allocation::DeleterFnPtr f = nullptr; if (deleter) { auto g = [deleter, src](phi::Allocation *p) { if (src->manager_ctx) { deleter(src); } }; PaddleDeleterManager::Instance().AddDeleter(data, std::move(g)); f = PaddleDeleterManager::DeleterBridge; } // Calculate the number of elements of underlying storage size_t size = 1; for (auto i = 0; i < shape.size(); ++i) { if (shape[i] == 0) { size = 0; break; } size += strides[i] * (shape[i] - 1); } auto alloc = std::make_shared(data, size * SizeOf(dtype), f, place); return DenseTensor(alloc, meta); } template ::DLDataType GetDLDataTypeCode() { ::DLDataType dtype; if (std::is_same>::value || std::is_same>::value) { dtype.code = kDLComplex; } else if (std::is_same::value) { dtype.code = kDLFloat8_e4m3fn; } else if (std::is_same::value) { dtype.code = kDLFloat8_e5m2; } else if (std::is_same::value) { dtype.code = kDLBfloat; } else if (std::is_same::value || std::is_floating_point::value) { dtype.code = kDLFloat; } else if (std::is_same::value) { // Since std::is_unsigned::value is True, // it is necessary to evaluate bool before std::is_unsigned. dtype.code = kDLBool; } else if (std::is_unsigned::value) { dtype.code = kDLUInt; } else if (std::is_integral::value) { dtype.code = kDLInt; } else { PADDLE_THROW(common::errors::Unavailable( "Unsupported data type (%s), only supports float16, float, unsigned " "int and int.", common::demangle(typeid(T).name()))); } dtype.bits = 8 * sizeof(T); dtype.lanes = 1; return dtype; } template <> ::DLDataType GetDLDataTypeCode() { ::DLDataType dtype = {}; // pstring is not supported in DLPack return dtype; } static std::unordered_map CreateDLDataTypeMap() { static std::unordered_map result; #define REG_DL_DATA_TYPE(cpp_type, data_type) \ result[static_cast(data_type)] = GetDLDataTypeCode(); PD_FOR_EACH_DATA_TYPE(REG_DL_DATA_TYPE); #undef REG_DL_DATA_TYPE return result; } static ::DLDataType GetDLDataTypeFromTypeIndex(DataType type) { static auto type_to_dtype_map = CreateDLDataTypeMap(); static auto type_to_dtype_map_end_it = type_to_dtype_map.end(); auto it = type_to_dtype_map.find(static_cast(type)); PADDLE_ENFORCE_NE(it, type_to_dtype_map_end_it, common::errors::InvalidArgument( "Unsupported data type (%s).", DataTypeToString(type))); return it->second; } struct DLDeviceVisitor { using argument_type = const Place &; using result_type = ::DLDevice; inline ::DLDevice operator()(const CPUPlace &place) const { ::DLDevice device; device.device_type = kDLCPU; device.device_id = 0; return device; } inline ::DLDevice operator()(const phi::IPUPlace &place) const { PADDLE_THROW( common::errors::Unimplemented("phi::IPUPlace is not supported")); } inline ::DLDevice operator()(const XPUPlace &place) const { PADDLE_THROW(common::errors::Unimplemented("XPUPlace is not supported")); } inline ::DLDevice operator()(const phi::XPUPinnedPlace &place) const { #if defined(PADDLE_WITH_XPU) ::DLDevice device; device.device_type = kDLCUDAHost; device.device_id = 0; return device; #else PADDLE_THROW(common::errors::Unavailable( "phi::XPUPinnedPlace is not supported in CPU only version.")); #endif } inline ::DLDevice operator()(const phi::CustomPlace &place) const { PADDLE_THROW( common::errors::Unimplemented("phi::CustomPlace is not supported")); } inline ::DLDevice operator()(const GPUPlace &place) const { #if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP) ::DLDevice device; device.device_type = kDLCUDA; device.device_id = place.device; // NOLINT return device; #else PADDLE_THROW(common::errors::Unavailable( "GPUPlace is not supported in CPU only version.")); #endif } inline ::DLDevice operator()(const GPUPinnedPlace &place) const { #if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP) ::DLDevice device; device.device_type = kDLCUDAHost; device.device_id = 0; return device; #else PADDLE_THROW(common::errors::Unavailable( "GPUPinnedPlace is not supported in CPU only version.")); #endif } }; } // namespace internal DataType DLDataTypeToPhiDataType(::DLDataType type) { // vector types not currently supported PADDLE_ENFORCE_LE( type.lanes, 1, common::errors::Unimplemented("Vector type is not supported currently.")); switch (type.bits) { case 8: if (type.code == kDLBool) return DataType::BOOL; if (type.code == kDLInt) return DataType::INT8; if (type.code == kDLUInt) return DataType::UINT8; if (type.code == kDLFloat8_e4m3fn) return DataType::FLOAT8_E4M3FN; if (type.code == kDLFloat8_e5m2) return DataType::FLOAT8_E5M2; PADDLE_THROW(common::errors::Unimplemented( "DLDataType code <%d> is illegal when DLDataType.bits is <%d>.", type.code, type.bits)); case 16: if (type.code == kDLInt) return DataType::INT16; if (type.code == kDLUInt) return DataType::UINT16; if (type.code == kDLFloat) return DataType::FLOAT16; if (type.code == kDLBfloat) return DataType::BFLOAT16; PADDLE_THROW(common::errors::Unimplemented( "DLDataType code <%d> is illegal when DLDataType.bits is <%d>.", type.code, type.bits)); case 32: if (type.code == kDLInt) return DataType::INT32; if (type.code == kDLUInt) return DataType::UINT32; if (type.code == kDLFloat) return DataType::FLOAT32; PADDLE_THROW(common::errors::Unimplemented( "DLDataType code <%d> is illegal when DLDataType.bits is <%d>.", type.code, type.bits)); case 64: if (type.code == kDLInt) return DataType::INT64; if (type.code == kDLUInt) return DataType::UINT64; if (type.code == kDLFloat) return DataType::FLOAT64; if (type.code == kDLComplex) return DataType::COMPLEX64; PADDLE_THROW(common::errors::Unimplemented( "DLDataType code <%d> is illegal when DLDataType.bits is <%d>.", type.code, type.bits)); case 128: if (type.code == kDLComplex) return DataType::COMPLEX128; PADDLE_THROW(common::errors::Unimplemented( "DLDataType code <%d> is illegal when DLDataType.bits is <%d>.", type.code, type.bits)); default: PADDLE_THROW(common::errors::Unimplemented( "Unsupported DLDataType.bits %d.", type.bits)); } } ::DLDataType PhiDataTypeToDLDataType(DataType dtype) { return internal::GetDLDataTypeFromTypeIndex(dtype); } Place DLDeviceToPlace(const ::DLDevice &dl_device) { Place place; if (dl_device.device_type == kDLCPU) { place = CPUPlace(); } else if (dl_device.device_type == kDLCUDA) { place = GPUPlace(dl_device.device_id); } else if (dl_device.device_type == kDLCUDAHost) { place = GPUPinnedPlace(); } else { PADDLE_THROW(common::errors::Unimplemented("Given Place is not supported")); } return place; } ::DLDevice PlaceToDLDevice(const Place &place) { return phi::VisitPlace(place, internal::DLDeviceVisitor()); } template struct PaddleDLMTensor { DenseTensor handle; T tensor; }; template static void deleter(T *self) { if (self && self->manager_ctx) { delete static_cast *>(self->manager_ctx); } } template void FillVersionInfo(T *tensor, uint64_t flags) {} template <> void FillVersionInfo(DLManagedTensorVersioned *tensor, uint64_t flags) { tensor->flags = flags; tensor->version.major = DLPACK_MAJOR_VERSION; tensor->version.minor = DLPACK_MINOR_VERSION; } template T *ToDLPackImpl(const DenseTensor &src, uint64_t flags) { PaddleDLMTensor *pdDLMTensor(new PaddleDLMTensor); pdDLMTensor->handle = const_cast(src); pdDLMTensor->tensor.manager_ctx = pdDLMTensor; pdDLMTensor->tensor.deleter = &deleter; using DimType = decltype(pdDLMTensor->tensor.dl_tensor.ndim); // int32_t pdDLMTensor->tensor.dl_tensor.ndim = static_cast(src.dims().size()); pdDLMTensor->tensor.dl_tensor.data = const_cast(src.data()); pdDLMTensor->tensor.dl_tensor.shape = const_cast(pdDLMTensor->handle.dims().Get()); pdDLMTensor->tensor.dl_tensor.strides = const_cast(pdDLMTensor->handle.strides().Get()); pdDLMTensor->tensor.dl_tensor.device = PlaceToDLDevice(src.place()); pdDLMTensor->tensor.dl_tensor.dtype = PhiDataTypeToDLDataType(src.dtype()); pdDLMTensor->tensor.dl_tensor.byte_offset = 0; FillVersionInfo(&(pdDLMTensor->tensor), flags); return &(pdDLMTensor->tensor); } DLManagedTensor *ToDLPack(const DenseTensor &src, uint64_t flags) { return ToDLPackImpl(src, flags); } DLManagedTensorVersioned *ToDLPackVersioned(const DenseTensor &src, uint64_t flags) { return ToDLPackImpl(src, flags); } void ToDLPackNonOwningImpl(const DenseTensor &tensor, ::DLTensor *out) { // Fill in the pre-allocated DLTensor struct with direct pointers // This is a non-owning conversion - the caller owns the tensor // and must keep it alive for the duration of DLTensor usage out->data = const_cast(tensor.data()); out->device = PlaceToDLDevice(tensor.place()); out->ndim = static_cast(tensor.dims().size()); out->dtype = PhiDataTypeToDLDataType(tensor.dtype()); // sizes() and strides() return pointers to TensorImpl's stable storage // which remains valid as long as the tensor is alive out->shape = const_cast(tensor.dims().Get()); out->strides = const_cast(tensor.strides().Get()); out->byte_offset = 0; } template DenseTensor FromDLPackImpl(T *src, Deleter deleter) { std::vector shape_vec; std::copy(src->dl_tensor.shape, src->dl_tensor.shape + src->dl_tensor.ndim, std::back_inserter(shape_vec)); Place place = DLDeviceToPlace(src->dl_tensor.device); DataType dtype = DLDataTypeToPhiDataType(src->dl_tensor.dtype); if (!src->dl_tensor.strides) { return internal::from_blob( src->dl_tensor.data, src, common::make_ddim(shape_vec), phi::DenseTensorMeta::calc_strides(common::make_ddim(shape_vec)), dtype, place, std::move(deleter)); } else { std::vector strides_vec; std::copy(src->dl_tensor.strides, src->dl_tensor.strides + src->dl_tensor.ndim, std::back_inserter(strides_vec)); return internal::from_blob(src->dl_tensor.data, src, common::make_ddim(shape_vec), common::make_ddim(strides_vec), dtype, place, deleter); } } template DenseTensor FromDLPackImpl(T *src) { auto deleter = [src](void *self [[maybe_unused]]) { if (src->deleter) { src->deleter(src); } }; return FromDLPackImpl(src, std::move(deleter)); } DenseTensor FromDLPack(DLManagedTensor *src) { return FromDLPackImpl(src); } DenseTensor FromDLPackVersioned(DLManagedTensorVersioned *src) { return FromDLPackImpl(src); } } // namespace framework } // namespace paddle