// Copyright (C) 2018-2020 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // /** * @brief A header file that provides class for describing precision of data * * @file ie_precision.hpp */ #pragma once #include #include #include #include "details/ie_exception.hpp" namespace InferenceEngine { /** * @brief This class holds precision value and provides precision related operations */ class Precision { public: /** Enum to specify of different */ enum ePrecision : uint8_t { UNSPECIFIED = 255, /**< Unspecified value. Used by default */ MIXED = 0, /**< Mixed value. Can be received from network. No applicable for tensors */ FP32 = 10, /**< 32bit floating point value */ FP16 = 11, /**< 16bit floating point value, 5 bit for exponent, 10 bit for mantisa */ BF16 = 12, /**< 16bit floating point value, 8 bit for exponent, 7 bit for mantisa*/ FP64 = 13, /**< 64bit floating point value */ Q78 = 20, /**< 16bit specific signed fixed point precision */ I16 = 30, /**< 16bit signed integer value */ U8 = 40, /**< 8bit unsigned integer value */ I8 = 50, /**< 8bit signed integer value */ U16 = 60, /**< 16bit unsigned integer value */ I32 = 70, /**< 32bit signed integer value */ U32 = 74, /**< 32bit unsigned integer value */ I64 = 72, /**< 64bit signed integer value */ U64 = 73, /**< 64bit unsigned integer value */ BIN = 71, /**< 1bit integer value */ BOOL = 41, /**< 8bit bool type */ CUSTOM = 80 /**< custom precision has it's own name and size of elements */ }; private: struct PrecisionInfo { /** @brief Size of underlined element */ size_t bitsSize = 0; /** @brief Null terminated string with precision name */ const char* name = "UNSPECIFIED"; bool isFloat = false; ePrecision value = Precision::UNSPECIFIED; }; PrecisionInfo precisionInfo; public: /** @brief Default constructor */ Precision() = default; /** * @brief Constructor with specified precision * @param value A value of ePrecision to create an object from */ Precision(const Precision::ePrecision value) { // NOLINT precisionInfo = getPrecisionInfo(value); } /** * @brief Custom precision constructor * * @param bitsSize size of elements * @param name optional: name string, used in serialisation */ explicit Precision(size_t bitsSize, const char* name = nullptr) { if (bitsSize == 0) { THROW_IE_EXCEPTION << "Precision with 0 elements size not supported"; } precisionInfo.bitsSize = bitsSize; if (name == nullptr) { precisionInfo.name = "CUSTOM"; } else { precisionInfo.name = name; } precisionInfo.value = CUSTOM; } /** @brief Creates custom precision with specific underlined type */ template static Precision fromType(const char* typeName = nullptr) { return Precision(8 * sizeof(T), typeName == nullptr ? typeid(T).name() : typeName); } /** @brief checks whether given storage class T can be used to store objects of current precision */ template bool hasStorageType(const char* typeName = nullptr) const noexcept { try { if (precisionInfo.value != BIN) { if (sizeof(T) != size()) { return false; } } #define CASE(x, y) \ case x: \ return std::is_same() #define CASE2(x, y1, y2) \ case x: \ return std::is_same() || std::is_same() switch (precisionInfo.value) { CASE(FP32, float); CASE(FP64, double); CASE2(FP16, int16_t, uint16_t); CASE2(BF16, int16_t, uint16_t); CASE(I8, int8_t); CASE(I16, int16_t); CASE(I32, int32_t); CASE(I64, int64_t); CASE(U8, uint8_t); CASE(U16, uint16_t); CASE(U32, uint32_t); CASE(U64, uint64_t); CASE(BOOL, uint8_t); CASE2(Q78, int16_t, uint16_t); CASE2(BIN, int8_t, uint8_t); default: return areSameStrings(name(), typeName == nullptr ? typeid(T).name() : typeName); #undef CASE #undef CASE2 } } catch (...) { return false; } } /** * @brief Equality operator with Precision object * @param p A value of Precision to compare with * @return `true` if values represent the same precisions, `false` otherwise */ bool operator==(const Precision& p) const noexcept { return precisionInfo.value == p && precisionInfo.bitsSize == p.precisionInfo.bitsSize && areSameStrings(precisionInfo.name, p.precisionInfo.name); } /** * @brief Equality operator with ePrecision enum value * @param p A value of ePrecision to compare with * @return `true` if values represent the same precisions, `false` otherwise */ bool operator==(const ePrecision p) const noexcept { return precisionInfo.value == p; } /** * @brief Inequality operator with ePrecision enum value * @param p A value of ePrecision to compare with * @return `true` if values represent different precisions, `false` otherwise */ bool operator!=(const ePrecision p) const noexcept { return precisionInfo.value != p; } /** * @brief Assignment operator with ePrecision enum value * @param p A value of ePrecision enumeration * @return A Precision instance */ Precision& operator=(const ePrecision p) noexcept { precisionInfo = getPrecisionInfo(p); return *this; } /** * @brief Cast operator to a bool * @return `true` if precision is specified, `false` otherwise */ explicit operator bool() const noexcept { return precisionInfo.value != UNSPECIFIED; } /** * @brief Logical negation operator * @return `true` if precision is NOT specified, `false` otherwise */ bool operator!() const noexcept { return precisionInfo.value == UNSPECIFIED; } /** * @brief Cast operator to a ePrecision * @return A casted value of Precision::ePrecision enumeration */ operator Precision::ePrecision() const noexcept { return precisionInfo.value; } /** * @brief Gets the precision value of type ePrecision. * @return The preccision value. */ constexpr uint8_t getPrecVal() const noexcept { return precisionInfo.value; } /** * @brief Getter of precision name * @return A string representing precision name */ const char* name() const noexcept { return precisionInfo.name; } /** * @brief Creates Precision from string with precision name * @param str A string representing precision * @return Precision created from string representation */ static Precision FromStr(const std::string& str) { static const std::unordered_map names = { #define PRECISION_NAME(s) {#s, s} PRECISION_NAME(Q78), PRECISION_NAME(BOOL), PRECISION_NAME(BF16), PRECISION_NAME(I8), PRECISION_NAME(I16), PRECISION_NAME(I32), PRECISION_NAME(I64), PRECISION_NAME(U8), PRECISION_NAME(U16), PRECISION_NAME(U32), PRECISION_NAME(U64), PRECISION_NAME(FP32), PRECISION_NAME(FP64), PRECISION_NAME(FP16), PRECISION_NAME(MIXED), PRECISION_NAME(BIN), #undef PRECISION_NAME }; auto i = names.find(str); return i == names.end() ? Precision() : Precision(i->second); } /** * @brief Returns size of single element of that precision in bits * @returns Number of bytes per element */ size_t size() const { if (precisionInfo.bitsSize == 0) { THROW_IE_EXCEPTION << " cannot estimate element if precision is " << precisionInfo.name; } return precisionInfo.bitsSize >> 3; } /** * @brief Checks if it is a floating point value * @return True if precision is float point, `false` otherwise */ bool is_float() const noexcept { return precisionInfo.isFloat; } /** * @brief Checks if it is a signed value * @return True if precision is signed, `false` otherwise */ bool isSigned() const noexcept { return (precisionInfo.value == Precision::UNSPECIFIED) || (precisionInfo.value == Precision::MIXED) || (precisionInfo.value == Precision::FP32) || (precisionInfo.value == Precision::FP64) || (precisionInfo.value == Precision::FP16) || (precisionInfo.value == Precision::Q78) || (precisionInfo.value == Precision::I16) || (precisionInfo.value == Precision::I8) || (precisionInfo.value == Precision::I32) || (precisionInfo.value == Precision::I64) || (precisionInfo.value == Precision::BIN) || (precisionInfo.value == Precision::BF16) || (precisionInfo.value == Precision::CUSTOM); } protected: /** * @brief Creates PrecisionInfo by @p precision with a specified name * @tparam precision A precision to create PrecisionInfo for * @param name Name of precision * @return A PrecisionInfo object */ template static PrecisionInfo makePrecisionInfo(const char* name); /** * @brief Compare two c-strings * * @param l Const pointer to first string * @param r Const pointer to another string * @returns True if strings are the same */ static bool areSameStrings(const char* l, const char* r) noexcept { if (l == r) return true; if (l == nullptr || r == nullptr) return false; for (; *l && *r; l++, r++) { if (*l != *r) return false; } return *l == *r; } /** * @brief Creates PrecisionInfo based on ePrecision * @param v A value of ePrecision emuneration * @return Precision info object */ static PrecisionInfo getPrecisionInfo(ePrecision v) { #define CASE(x) \ case x: \ return makePrecisionInfo(#x); switch (v) { CASE(FP32); CASE(FP64); CASE(FP16); CASE(BF16); CASE(I8); CASE(I16); CASE(I32); CASE(I64); CASE(U8); CASE(U16); CASE(U32); CASE(U64); CASE(Q78); CASE(MIXED); CASE(BIN); CASE(BOOL); default: return makePrecisionInfo("UNSPECIFIED"); #undef CASE } } }; /** * @brief Particular precision traits */ template struct PrecisionTrait {}; /** @cond INTERNAL */ template <> struct PrecisionTrait { using value_type = float; }; template <> struct PrecisionTrait { using value_type = double; }; template <> struct PrecisionTrait { using value_type = int16_t; }; template <> struct PrecisionTrait { using value_type = int16_t; }; template<> struct PrecisionTrait { using value_type = uint16_t; }; template <> struct PrecisionTrait { using value_type = int16_t; }; template <> struct PrecisionTrait { using value_type = uint16_t; }; template <> struct PrecisionTrait { using value_type = uint8_t; }; template <> struct PrecisionTrait { using value_type = int8_t; }; template <> struct PrecisionTrait { using value_type = uint8_t; }; template <> struct PrecisionTrait { using value_type = int32_t; }; template <> struct PrecisionTrait { using value_type = uint32_t; }; template <> struct PrecisionTrait { using value_type = int64_t; }; template <> struct PrecisionTrait { using value_type = uint64_t; }; template <> struct PrecisionTrait { using value_type = int8_t; }; template inline uint8_t type_size_or_zero() { return sizeof(T); } template <> struct PrecisionTrait { using value_type = void; }; template <> struct PrecisionTrait : PrecisionTrait {}; template <> inline uint8_t type_size_or_zero() { return 0; } template inline typename std::enable_if, std::integral_constant>::value, bool>::type is_floating() { return true; } template inline typename std::enable_if, std::integral_constant>::value, bool>::type is_floating() { return std::is_floating_point::value_type>::value; } template inline Precision::PrecisionInfo Precision::makePrecisionInfo(const char* name) { Precision::PrecisionInfo info; info.name = name; size_t nBits = precision == BIN ? 1 : 8; info.bitsSize = nBits * type_size_or_zero::value_type>(); info.isFloat = is_floating(); info.value = precision; return info; } inline std::ostream& operator<<(std::ostream& out, const InferenceEngine::Precision& p) { return out << p.name(); } inline std::ostream& operator<<(std::ostream& out, const InferenceEngine::Precision::ePrecision& p) { return out << Precision(p).name(); } inline std::ostream& operator<<(std::ostream& os, const std::vector& values) { os << "{ "; for (size_t i = 0; i < values.size(); ++i) { os << values[i]; if (i != (values.size() - 1ul)) { os << ", "; } } os << " }"; return os; } inline constexpr uint32_t getPrecisionMask( InferenceEngine::Precision::ePrecision precision1, InferenceEngine::Precision::ePrecision precision2, InferenceEngine::Precision::ePrecision precision3 = InferenceEngine::Precision::MIXED, InferenceEngine::Precision::ePrecision precision4 = InferenceEngine::Precision::MIXED) { return (precision1) | (precision2 << 8) | (precision3 << 16) | (precision4 << 24); } /** @endcond */ } // namespace InferenceEngine