// Copyright 2019-2020 Yan Yan // // 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. /* tv::Tensor is a lightweight header-only tensor container without template and annoying dependencies. no algorithm is implemented. it should only be used when you want a no-template simple container but dont want to link with libtorch. If you can use libtorch, dont use tv::Tensor. */ #pragma once #include "mp_helper.h" #include "tensorview.h" #include #include #include #include #ifdef TV_CUDA #include #include #include #endif namespace tv { enum DType { float32, int32, int16, int8, float64, bool_, uint8, float16, int64, uint16, uint32, uint64 }; namespace detail { using dtype_collection_t = tv::mp_list_c; using all_tensor_types_t = std::tuple; template class TensorStorage { public: TensorStorage(size_t size, int device = -1, bool managed = false, bool pinned = false) : mSize(size), device_(device), managed_(managed), pinned_(pinned) { if (size == 0) { mPtr = nullptr; } else { if (device == -1) { if (pinned_) { #ifdef TV_CUDA checkCudaErrors(cudaMallocHost(&mPtr, size * sizeof(T))); #else TV_THROW_INVALID_ARG("you need to define TV_CUDA to use pinned"); #endif } else { mPtr = new T[size]; } } else { #ifdef TV_CUDA int deviceCount; cudaGetDeviceCount(&deviceCount); if (device >= deviceCount) { TV_THROW_INVALID_ARG("you provide device ", device, " but you only have ", deviceCount, " device."); } cudaSetDevice(device); if (managed) { checkCudaErrors(cudaMallocManaged(&this->mPtr, size * sizeof(T))); } else { checkCudaErrors(cudaMalloc(&mPtr, size * sizeof(T))); } #else TV_THROW_INVALID_ARG("don't compiled with cuda"); #endif } } } TensorStorage(T *ptr, size_t size, int device) : mSize(size), mPtr(ptr), from_blob_(true), device_(device) {} virtual ~TensorStorage() { if (empty()) { return; } if (from_blob_) { return; } if (device_ == -1) { if (pinned_) { #ifdef TV_CUDA cudaFreeHost(mPtr); #endif } else { delete[] mPtr; } } else { #ifdef TV_CUDA cudaFree(mPtr); #endif } }; inline size_t size() const { return mSize; } T *data() { return mPtr; } const T *data() const { return mPtr; } bool empty() const { return mPtr == nullptr || mSize == 0; } bool managed() const { return managed_; } bool pinned() const { return pinned_; } int device() const { return device_; } void zero_() { if (device_ == -1) { std::memset(data(), 0, mSize); // std::fill(data(), data() + mSize, 0); } else { #ifdef TV_CUDA checkCudaErrors(cudaMemset(data(), 0, mSize / sizeof(T))); #else TV_THROW_INVALID_ARG("don't compiled with cuda"); #endif } } private: size_t mSize = 0; T *mPtr = nullptr; bool from_blob_ = false; int device_ = -1; bool managed_ = false; bool pinned_ = false; }; template size_t sizeof_dtype(T dtype) { switch (dtype) { case float32: return sizeof(float); case int8: return sizeof(int8_t); case int16: return sizeof(int16_t); case int32: return sizeof(int32_t); case float64: return sizeof(double); case int64: return sizeof(int64_t); case bool_: return sizeof(bool); case uint8: return sizeof(uint8_t); case uint16: return sizeof(uint16_t); case uint32: return sizeof(uint32_t); case uint64: return sizeof(uint64_t); case float16: return 2; default: TV_THROW_RT_ERR("unsupported dtype"); } return 0; } template std::string typeString(T t) { switch (t) { case DType::bool_: return "bool"; case DType::float32: return "float32"; case DType::int8: return "int8"; case DType::int16: return "int16"; case DType::int32: return "int32"; case DType::float64: return "float64"; case DType::int64: return "int64"; case DType::uint8: return "uint8"; case DType::uint16: return "uint16"; case DType::uint32: return "uint32"; case DType::uint64: return "uint64"; case DType::float16: return "half"; default: return ""; } } template struct TypeToDtypeTraits; template <> struct TypeToDtypeTraits { static constexpr DType dtype = int32; }; #ifdef TV_CUDA template <> struct TypeToDtypeTraits<__half> { static constexpr DType dtype = float16; }; #endif template <> struct TypeToDtypeTraits { static constexpr DType dtype = float32; }; template <> struct TypeToDtypeTraits { static constexpr DType dtype = float64; }; template <> struct TypeToDtypeTraits { static constexpr DType dtype = int16; }; template <> struct TypeToDtypeTraits { static constexpr DType dtype = int8; }; template <> struct TypeToDtypeTraits { static constexpr DType dtype = int64; }; template <> struct TypeToDtypeTraits { static constexpr DType dtype = uint8; }; template <> struct TypeToDtypeTraits { static constexpr DType dtype = uint16; }; template <> struct TypeToDtypeTraits { static constexpr DType dtype = uint32; }; template <> struct TypeToDtypeTraits { static constexpr DType dtype = uint64; }; template <> struct TypeToDtypeTraits { static constexpr DType dtype = bool_; }; template <> struct TypeToDtypeTraits { static constexpr DType dtype = int32; }; #ifdef TV_CUDA template <> struct TypeToDtypeTraits { static constexpr DType dtype = float16; }; #endif template <> struct TypeToDtypeTraits { static constexpr DType dtype = float32; }; template <> struct TypeToDtypeTraits { static constexpr DType dtype = float64; }; template <> struct TypeToDtypeTraits { static constexpr DType dtype = int16; }; template <> struct TypeToDtypeTraits { static constexpr DType dtype = int8; }; template <> struct TypeToDtypeTraits { static constexpr DType dtype = int64; }; template <> struct TypeToDtypeTraits { static constexpr DType dtype = uint8; }; template <> struct TypeToDtypeTraits { static constexpr DType dtype = uint16; }; template <> struct TypeToDtypeTraits { static constexpr DType dtype = uint32; }; template <> struct TypeToDtypeTraits { static constexpr DType dtype = uint64; }; template <> struct TypeToDtypeTraits { static constexpr DType dtype = bool_; }; } // namespace detail template constexpr DType type_v = detail::TypeToDtypeTraits::dtype; template bool dispatch_noexcept(DType t, F &&f) { static_assert(sizeof...(Ts) > 0, "you need to provide at least one type"); bool notFound = true; mp_for_each>([=, ¬Found, &f](auto I) { if (type_v == t && notFound) { std::forward(f)(decltype(I)()); notFound = false; } }); return !notFound; } template void dispatch(DType t, F &&f) { if (!dispatch_noexcept(t, std::forward(f))) { std::stringstream ss; mp_for_each>([=, &ss](auto I) { ss << detail::TypeToString::value << " "; }); TV_THROW_RT_ERR("unknown type", detail::typeString(t), ", available:", ss.str()); } } template void dispatch_scalar(T idx, F &&f) { static_assert(sizeof...(Is) > 0, "you need to provide at least one candidate"); bool notFound = true; mp_for_each>([=, ¬Found, &f](auto I) { if (T(I) == idx && notFound) { std::forward(f)(I); notFound = false; } }); if (notFound) { std::stringstream ss; mp_for_each>([=, &ss](auto I) { ss << T(I) << " "; }); TV_THROW_RT_ERR("unknown value", idx, ", available:", ss.str()); } } template bool dispatch_int_noexcept(int idx, F &&f) { static_assert(sizeof...(Is) > 0, "you need to provide at least one candidate"); bool notFound = true; mp_for_each>([=, ¬Found, &f](auto I) { if (decltype(I)::value == idx && notFound) { std::forward(f)(I); notFound = false; } }); return !notFound; } template bool dispatch_int_noexcept(int idx, BinaryPredicate p, F &&f) { static_assert(sizeof...(Is) > 0, "you need to provide at least one candidate"); bool notFound = true; mp_for_each>([=, ¬Found, &f](auto I) { if (p(idx, decltype(I)::value) && notFound) { std::forward(f)(I); notFound = false; } }); return !notFound; } template void dispatch_int(int idx, F &&f) { if (!dispatch_int_noexcept(idx, std::forward(f))) { std::stringstream ss; mp_for_each>( [=, &ss](auto I) { ss << decltype(I)::value << " "; }); TV_THROW_RT_ERR("unknown value", idx, ", available:", ss.str()); } } template void dispatch_int(int idx, BinaryPredicate p, F &&f) { // BinaryPredicate: BinaryPredicate(idx, candidate) if (!dispatch_int_noexcept(idx, p, std::forward(f))) { std::stringstream ss; mp_for_each>( [=, &ss](auto I) { ss << decltype(I)::value << " "; }); TV_THROW_RT_ERR("unknown value", idx, ", available:", ss.str()); } } /* template void dispatch_int(int idx, F &&f) { return dispatch_scalar(idx, f); } */ template struct Dispatch; template