From 608fa33343460f6daac865b9380f12dee837f16c Mon Sep 17 00:00:00 2001 From: "Raasz, Pawel" Date: Tue, 5 Mar 2024 07:26:49 +0000 Subject: [PATCH 01/12] Add u2, u3, u6 to ov::element Types --- .../include/openvino/core/type/element_type.hpp | 12 ++++++++++++ .../openvino/core/type/element_type_traits.hpp | 15 +++++++++++++++ src/core/include/openvino/op/constant.hpp | 6 ++++++ src/core/src/pass/visualize_tree.cpp | 3 +++ src/core/src/type/element_type.cpp | 12 ++++++++++++ .../intel_cpu/src/nodes/executors/type_mask.hpp | 3 ++- 6 files changed, 50 insertions(+), 1 deletion(-) diff --git a/src/core/include/openvino/core/type/element_type.hpp b/src/core/include/openvino/core/type/element_type.hpp index bea57a6ce98479..5538fa480f1c74 100644 --- a/src/core/include/openvino/core/type/element_type.hpp +++ b/src/core/include/openvino/core/type/element_type.hpp @@ -48,7 +48,10 @@ enum class Type_t { i32, //!< i32 element type i64, //!< i64 element type u1, //!< binary element type + u2, //!< u2 element type + u3, //!< u3 element type u4, //!< u4 element type + u6, //!< u6 element type u8, //!< u8 element type u16, //!< u16 element type u32, //!< u32 element type @@ -168,9 +171,18 @@ constexpr Type i64(Type_t::i64); /// \brief binary element type /// \ingroup ov_element_cpp_api constexpr Type u1(Type_t::u1); +/// \brief u2 element type +/// \ingroup ov_element_cpp_api +constexpr Type u2(Type_t::u2); +/// \brief u3 element type +/// \ingroup ov_element_cpp_api +constexpr Type u3(Type_t::u3); /// \brief u4 element type /// \ingroup ov_element_cpp_api constexpr Type u4(Type_t::u4); +/// \brief u4 element type +/// \ingroup ov_element_cpp_api +constexpr Type u6(Type_t::u6); /// \brief u8 element type /// \ingroup ov_element_cpp_api constexpr Type u8(Type_t::u8); diff --git a/src/core/include/openvino/core/type/element_type_traits.hpp b/src/core/include/openvino/core/type/element_type_traits.hpp index fefbac51866417..299ea38dae075d 100644 --- a/src/core/include/openvino/core/type/element_type_traits.hpp +++ b/src/core/include/openvino/core/type/element_type_traits.hpp @@ -68,11 +68,26 @@ struct element_type_traits { using value_type = int8_t; }; +template <> +struct element_type_traits { + using value_type = int8_t; +}; + +template <> +struct element_type_traits { + using value_type = int8_t; +}; + template <> struct element_type_traits { using value_type = int8_t; }; +template <> +struct element_type_traits { + using value_type = int8_t; +}; + template <> struct element_type_traits { using value_type = uint8_t; diff --git a/src/core/include/openvino/op/constant.hpp b/src/core/include/openvino/op/constant.hpp index ae539351058574..97d56098b89e4c 100644 --- a/src/core/include/openvino/op/constant.hpp +++ b/src/core/include/openvino/op/constant.hpp @@ -146,6 +146,9 @@ class OPENVINO_API Constant : public Op { case Type_t::string: fill_data(value); break; + case Type_t::u2: + case Type_t::u3: + case Type_t::u6: case Type_t::undefined: case Type_t::dynamic: OPENVINO_THROW("unsupported type"); @@ -872,6 +875,9 @@ class OPENVINO_API Constant : public Op { case Type_t::string: write_buffer(source); break; + case element::Type_t::u2: + case element::Type_t::u3: + case element::Type_t::u6: case element::Type_t::undefined: case element::Type_t::dynamic: OPENVINO_THROW("unsupported type"); diff --git a/src/core/src/pass/visualize_tree.cpp b/src/core/src/pass/visualize_tree.cpp index 68a21d53c3d4fa..e0560754df525d 100644 --- a/src/core/src/pass/visualize_tree.cpp +++ b/src/core/src/pass/visualize_tree.cpp @@ -373,7 +373,10 @@ static std::string get_value(const std::shared_ptr& consta case ov::element::Type_t::undefined: case ov::element::Type_t::dynamic: case ov::element::Type_t::u1: + case ov::element::Type_t::u2: + case ov::element::Type_t::u3: case ov::element::Type_t::u4: + case ov::element::Type_t::u6: case ov::element::Type_t::nf4: case ov::element::Type_t::i4: case ov::element::Type_t::f8e4m3: diff --git a/src/core/src/type/element_type.cpp b/src/core/src/type/element_type.cpp index a49312b6530378..397acb8b98f9b1 100644 --- a/src/core/src/type/element_type.cpp +++ b/src/core/src/type/element_type.cpp @@ -163,7 +163,10 @@ ov::element::Type::Type(size_t bitwidth, {ov::element::Type_t::i32, {32, false, true, true, "int32_t", "i32"}}, {ov::element::Type_t::i64, {64, false, true, false, "int64_t", "i64"}}, {ov::element::Type_t::u1, {1, false, false, false, "uint1_t", "u1"}}, + {ov::element::Type_t::u2, {2, false, false, false, "uint2_t", "u2"}}, + {ov::element::Type_t::u3, {3, false, false, false, "uint3_t", "u3"}}, {ov::element::Type_t::u4, {4, false, false, false, "uint4_t", "u4"}}, + {ov::element::Type_t::u6, {6, false, false, false, "uint6_t", "u6"}}, {ov::element::Type_t::u8, {8, false, false, true, "uint8_t", "u8"}}, {ov::element::Type_t::u16, {16, false, false, false, "uint16_t", "u16"}}, {ov::element::Type_t::u32, {32, false, false, false, "uint32_t", "u32"}}, @@ -304,8 +307,14 @@ Type fundamental_type_for(const Type& type) { return from::value_type>(); case Type_t::u1: return from::value_type>(); + case Type_t::u2: + return from::value_type>(); + case Type_t::u3: + return from::value_type>(); case Type_t::u4: return from::value_type>(); + case Type_t::u6: + return from::value_type>(); case Type_t::u8: return from::value_type>(); case Type_t::u16: @@ -415,7 +424,10 @@ inline size_t compiler_byte_size(ov::element::Type_t et) { ET_CASE(i32); ET_CASE(i64); ET_CASE(u1); + ET_CASE(u2); + ET_CASE(u3); ET_CASE(u4); + ET_CASE(u6); ET_CASE(u8); ET_CASE(u16); ET_CASE(u32); diff --git a/src/plugins/intel_cpu/src/nodes/executors/type_mask.hpp b/src/plugins/intel_cpu/src/nodes/executors/type_mask.hpp index 366026070e0cb9..199a44f9c28a71 100644 --- a/src/plugins/intel_cpu/src/nodes/executors/type_mask.hpp +++ b/src/plugins/intel_cpu/src/nodes/executors/type_mask.hpp @@ -81,9 +81,10 @@ struct TypeMask { CASE(f8e4m3) CASE(f8e5m2) CASE(string) + default: + return _undefined; } #undef CASE - return _undefined; } }; From cf08f2ef4c154776d4e23741abe9b9377bfa6dbd Mon Sep 17 00:00:00 2001 From: "Raasz, Pawel" Date: Tue, 5 Mar 2024 12:08:36 +0000 Subject: [PATCH 02/12] Add low precision element iterator --- .../openvino/core/type/element_iterator.hpp | 449 ++++++++++++++++++ src/core/tests/element_iterator_test.cpp | 420 ++++++++++++++++ 2 files changed, 869 insertions(+) create mode 100644 src/core/dev_api/openvino/core/type/element_iterator.hpp create mode 100644 src/core/tests/element_iterator_test.cpp diff --git a/src/core/dev_api/openvino/core/type/element_iterator.hpp b/src/core/dev_api/openvino/core/type/element_iterator.hpp new file mode 100644 index 00000000000000..54a5e51d106375 --- /dev/null +++ b/src/core/dev_api/openvino/core/type/element_iterator.hpp @@ -0,0 +1,449 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "openvino/core/type/element_type_traits.hpp" + +namespace ov { +namespace util { + +/** + * @brief Make bit mask by set N bits start from right. + * + * @tparam T Type of value. + * @param n Number of bits to set. + * @return Bit-mask value with N bits set. + */ +template +constexpr T make_n_bit_mask(const T n) { + return (1ULL << n) - 1ULL; +} +} // namespace util + +namespace element { + +/** + * @brief Checks if element type is N in-raw bits type. + * + * @param et Element type to check + * @return True if element type is bit type otherwise false. + */ +constexpr bool is_bit_type(Type_t et) { + return et == Type_t::u1 || et == Type_t::u2 || et == Type_t::u4 || et == Type_t::i4; +} + +/** + * @brief Checks if element type is split split bit type. + * + * The value is stored in byte(s) like [b0, b1, x, .., x, b2, b3]. + * + * @param et Element type to check + * @return True if element type is split bit type otherwise false. + */ +constexpr bool is_split_bit_type(Type_t et) { + return et == Type_t::u3 || et == Type_t::u6; +} + +/** + * @brief Check element type if is using only byte. + * + * @param et Element type to check. + * @return True if element type use bytes for its value otherwise false. + */ +constexpr bool is_byte_type(Type_t et) { + return !is_bit_type(et) && !is_split_bit_type(et) && et != Type_t::nf4; +} + +/** + * @brief Get bit width of ov::element::Type_t. + * + * @return Number of bits representing the Type_t. + */ +template +constexpr size_t bit_width(); + +template <> +constexpr size_t bit_width() { + return 1; +} + +template <> +constexpr size_t bit_width() { + return 2; +} + +template <> +constexpr size_t bit_width() { + return 3; +} + +template <> +constexpr size_t bit_width() { + return 4; +} + +template <> +constexpr size_t bit_width() { + return 4; +} + +template <> +constexpr size_t bit_width() { + return 6; +} + +/** + * @brief The BitProxy value class used by ov::element::Iterator to access values which has no standard byte(s) layout. + * + * It used by iterator to access values represented by precisions like u2, i4, u6 etc. in the way like stored + * on bytes. + * The R/W access is done via conversion and copy assignment operators. + * The public members are used to work on sub-byte value like on its fundamental type defined by T. + * + * @tparam T Fundamental type of sub-byte value which must be same as fundamental type of element::Type_t. + * @tparam N Number of bits for sub-byte value. + * @tparam S Flag to indicate sub-byte value i signed. + * @tparam Enable class for specific type, bit layouts. + */ +template +class BitProxy {}; + +/** + * @brief The BitProxy specialization for types which are represented by N in-raw bits in byte. + * + * @tparam T Fundamental type of sub-byte value which must be same as fundamental type of element::Type_t. + * @tparam N Number of bits for sub-byte value. + * @tparam S Flag to indicate sub-byte value i signed. + */ +template +class BitProxy::type> { +private: + template + friend class Iterator; //!< Iterator class is friend to access private members to manipulate pointer. + + static constexpr size_t m_bits = N; //!< Number of bit for single value. + static constexpr size_t m_num_values = 8 / N; //!< Number values in byte. + static constexpr size_t m_shift_init = 8 - N; //!< Initial value for bit shift. + + T* m_ptr; //!< Pointer to T value used to get sub-byte value. + size_t m_bit_shift; //!< Current bit shift to get sub-byte value. + + constexpr BitProxy(T* ptr) noexcept : m_ptr{ptr}, m_bit_shift{m_shift_init} {} + +public: + using value_type = typename std::decay::type; //!< Fundamental type of bound to BitProxy. + + /** + * @brief Compare proxy value with other provided value. + * @param rhs Value to compare. + * @return True if equal otherwise false. + */ + template + constexpr bool operator==(const U& rhs) const { + return static_cast(*this) == rhs; + } + + /** + * @brief Compare proxy value is less than rhs. + * + * @tparam U Type of value to compare. + * @param rhs Value to compare. + * @return True if less otherwise false. + */ + template + constexpr bool operator<(const U& rhs) const { + return static_cast(*this) < rhs; + } + + /** + * @brief Converts to fundamental type. + * + * @return Value of BitProxy. + */ + operator value_type() const { + constexpr uint8_t value_mask = util::make_n_bit_mask(m_bits); + uint8_t tmp = (*m_ptr >> m_bit_shift) & value_mask; + if (S && (tmp & (1U << (m_bits - 1U)))) { + // If N bit value MSB bit is set then value is negative. + // As tmp is byte then all bits above N must be set to be two's complement. + tmp |= ~value_mask; + } + return static_cast(tmp); + } + + /** + * @brief Sets current ProxyBit to value. + * @param v Value to be set. + */ + BitProxy& operator=(const value_type v) { + constexpr uint8_t value_mask = util::make_n_bit_mask(m_bits); + *m_ptr &= ~(value_mask << m_bit_shift); + *m_ptr |= (static_cast(v) & value_mask) << m_bit_shift; + return *this; + } +}; + +/** + * @brief The BitProxy specialization for u3, u6 precisions. + * + * @note The input pointer must point on buffer which has got 3 * n bytes. + * + * @tparam T Fundamental type of sub-byte value which must be same as fundamental type of element::Type_t. + * @tparam N Number of bits for sub-byte value. + */ +template +class BitProxy::type> { +private: + template + friend class Iterator; //!< Iterator class is friend to access private members to manipulate pointer. + + static constexpr size_t m_bits = N; //!< Number of bit for single value. + static constexpr size_t m_num_values = (3 * 8) / N; //!< Number values in byte. + static constexpr size_t m_shift_init = m_num_values - 1; //!< Initial value for bit shift. + + struct ByteValue { + uint8_t b0; + uint8_t b1; + uint8_t b2; + }; + + union { + T* m_ptr; //!< Pointer to T buffer. + ByteValue* m_bytes; //!< Pointer to buffer as 3 bytes representation. + }; + + size_t m_bit_shift; //!< Current bit shift to get BitProxy value. + + constexpr BitProxy(T* ptr) noexcept : m_ptr{ptr}, m_bit_shift{m_shift_init} {} + +public: + using value_type = typename std::decay::type; //!< Fundamental type of sub-byte. + + /** + * @brief Compare proxy value is less than rhs. + * + * @tparam U Type of value to compare. + * @param rhs Value to compare. + * @return True if less otherwise false. + */ + template + constexpr bool operator==(const U& rhs) const { + return static_cast(*this) == rhs; + } + + /** + * @brief Compare proxy value is less than rhs. + * + * @tparam U Type of value to compare. + * @param rhs Value to compare. + * @return True if less otherwise false. + */ + template + constexpr bool operator<(const U& rhs) const { + return static_cast(*this) < rhs; + } + + /** + * @brief Converts to fundamental type. + * + * @return Value of BitProxy. + */ + operator value_type() const { + constexpr uint16_t lower_mask_bits = 16 / m_num_values; + constexpr uint16_t upper_mask_bits = 8 / m_num_values; + constexpr uint16_t mask_lower = util::make_n_bit_mask(lower_mask_bits); + constexpr uint16_t mask_upper = util::make_n_bit_mask(upper_mask_bits) << lower_mask_bits; + + // get lower part of value + uint16_t v = ((m_bytes->b0 << 8U) | m_bytes->b1) >> (lower_mask_bits * m_bit_shift); + v &= mask_lower; + // get upper part of value + v |= ((m_bytes->b2 << lower_mask_bits) >> (upper_mask_bits * m_bit_shift)) & mask_upper; + return static_cast(v); + } + + /** + * @brief Sets current ProxyBit to value. + * @param v Value to be set. + */ + BitProxy& operator=(const value_type v) { + constexpr uint16_t lower_mask_bits = 16 / m_num_values; + constexpr uint16_t upper_mask_bits = 8 / m_num_values; + constexpr uint16_t mask_lower = util::make_n_bit_mask(lower_mask_bits); + constexpr uint16_t mask_upper = util::make_n_bit_mask(upper_mask_bits) << lower_mask_bits; + + uint16_t tmp = (m_bytes->b0 << 8U) | m_bytes->b1; + tmp &= ~(mask_lower << (lower_mask_bits * m_bit_shift)); + tmp |= (v & mask_lower) << (lower_mask_bits * m_bit_shift); + m_bytes->b0 = tmp >> 8U; + m_bytes->b1 = tmp & 0x00ff; + + tmp = m_bytes->b2 & ~((mask_upper >> lower_mask_bits) << (upper_mask_bits * m_bit_shift)); + tmp |= (((v & mask_upper) >> lower_mask_bits) << (upper_mask_bits * m_bit_shift)); + m_bytes->b2 = tmp; + return *this; + } +}; + +/** + * @brief Put BitProxy value to output stream. + * + * @param os Reference to output stream. + * @param value Value to print. + * @return return output stream. + */ +template +std::ostream& operator<<(std::ostream& os, const BitProxy& value) { + os << +value; + return os; +} + +/** + * @brief Bidirectional iterator of specified precision. + * + * The iterator supports low precisions using BitProxy to access values via conversion. + * + * @tparam ET Type of OpenVINO element type (ov::element::Type_t). + * @tparam T Must be fundamental type for specified ET. + */ +template +class Iterator { +public: + using iterator_category = std::bidirectional_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = BitProxy(), (ET == i4)>; + using reference = value_type&; + using pointer = T*; + + static_assert(std::is_same::type, ov::fundamental_type_for>::value, + "Iterator value_type must be same as fundamental type of ET"); + + constexpr Iterator(pointer ptr) noexcept : m_et_ptr{ptr} {} + + // Iteration operators + template + typename std::enable_if>::type& operator++() { + m_et_ptr.m_bit_shift -= m_et_ptr.m_bits; + m_et_ptr.m_bit_shift = m_et_ptr.m_bit_shift % ((m_et_ptr.m_num_values * m_et_ptr.m_bits)); + m_et_ptr.m_ptr += static_cast(m_et_ptr.m_bit_shift == m_et_ptr.m_shift_init); + return *this; + } + + template + typename std::enable_if>::type& operator++() { + --m_et_ptr.m_bit_shift; + m_et_ptr.m_bit_shift = m_et_ptr.m_bit_shift % m_et_ptr.m_num_values; + m_et_ptr.m_ptr += (m_et_ptr.m_bit_shift == m_et_ptr.m_shift_init) ? 3 : 0; + return *this; + } + + Iterator operator++(int) { + auto old = *this; + ++(*this); + return old; + } + + template + typename std::enable_if>::type& operator+=(const difference_type& n) { + const auto advance = n + (m_et_ptr.m_shift_init - m_et_ptr.m_bit_shift) / m_et_ptr.m_bits; + m_et_ptr.m_bit_shift = m_et_ptr.m_shift_init - (advance % m_et_ptr.m_num_values) * m_et_ptr.m_bits; + m_et_ptr.m_ptr += advance / m_et_ptr.m_num_values; + return *this; + } + + template + typename std::enable_if>::type& operator+=(const difference_type& n) { + const auto advance = n + m_et_ptr.m_shift_init - m_et_ptr.m_bit_shift; + m_et_ptr.m_bit_shift = m_et_ptr.m_shift_init - (advance % m_et_ptr.m_num_values); + m_et_ptr.m_ptr += 3 * (advance / m_et_ptr.m_num_values); + return *this; + } + + Iterator operator+(const difference_type& n) { + auto tmp(*this); + tmp += n; + return tmp; + } + + template + typename std::enable_if>::type& operator--() { + m_et_ptr.m_bit_shift += m_et_ptr.m_bits; + m_et_ptr.m_bit_shift = m_et_ptr.m_bit_shift % (m_et_ptr.m_num_values * m_et_ptr.m_bits); + m_et_ptr.m_ptr -= static_cast(m_et_ptr.m_bit_shift == 0); + return *this; + } + + template + typename std::enable_if>::type& operator--() { + ++m_et_ptr.m_bit_shift; + m_et_ptr.m_bit_shift = m_et_ptr.m_bit_shift % m_et_ptr.m_num_values; + m_et_ptr.m_ptr -= m_et_ptr.m_bit_shift == 0 ? 3 : 0; + return *this; + } + + Iterator operator--(int) { + auto old = *this; + --(*this); + return old; + } + + template + typename std::enable_if>::type& operator-=(const difference_type& n) { + const auto advance = m_et_ptr.m_bit_shift / m_et_ptr.m_bits + n; + m_et_ptr.m_bit_shift = (advance % m_et_ptr.m_num_values) * m_et_ptr.m_bits; + m_et_ptr.m_ptr -= advance / m_et_ptr.m_num_values; + return *this; + } + + template + typename std::enable_if>::type& operator-=(const difference_type& n) { + const auto advance = m_et_ptr.m_bit_shift + n; + m_et_ptr.m_bit_shift = advance % m_et_ptr.m_num_values; + m_et_ptr.m_ptr -= 3 * (advance / m_et_ptr.m_num_values); + return *this; + } + + Iterator operator-(const difference_type& n) { + auto tmp(*this); + tmp -= n; + return tmp; + } + + // compare operators + template ::type* = nullptr> + constexpr bool operator!=(const Iterator& rhs) const { + return (m_et_ptr.m_ptr != rhs.m_et_ptr.m_ptr) || (m_et_ptr.m_bit_shift != rhs.m_et_ptr.m_bit_shift); + } + + // dereference operators + template ::type* = nullptr> + reference operator*() const { + return m_et_ptr; + } + + template ::type* = nullptr> + reference operator*() { + return m_et_ptr; + } + +private: + value_type m_et_ptr; +}; + +/** + * @brief Make element iterator from pointer. + * + * @tparam ET Type of ov::element::Type_t. + * @tparam T Type of pointer data. Must be fundamental type of ET. + + * @param ptr Pointer to data. + * @return Element iterator for type ET. + */ +template +constexpr Iterator iterator(T* ptr) { + return {ptr}; +} +} // namespace element +} // namespace ov diff --git a/src/core/tests/element_iterator_test.cpp b/src/core/tests/element_iterator_test.cpp new file mode 100644 index 00000000000000..adf5c81a5998cc --- /dev/null +++ b/src/core/tests/element_iterator_test.cpp @@ -0,0 +1,420 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "openvino/core/type/element_iterator.hpp" + +#include + +#include + +namespace ov { +namespace test { + +using testing::ElementsAre; +using testing::ElementsAreArray; + +namespace { +constexpr size_t get_buffer_size(const size_t bit_width, const size_t num_of_elements) { + return (num_of_elements * bit_width + 7) / 8; +} +} // namespace + +// bits number in comments are counted [b7, b6, ..., b0] +// ---- u1 +TEST(ElementIteratorTest, write_u1_data) { + constexpr auto elements_count = 16; + auto input = std::array{0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1}; + auto output = std::array{}; + auto iter = element::iterator(output.data()); + + std::copy(input.begin(), input.end(), iter); + EXPECT_THAT(output, ElementsAre(0x16, 0xB3)); +} + +TEST(ElementIteratorTest, read_const_u1_data) { + constexpr auto elements_count = 16; + constexpr auto input = std::array{0x21, static_cast(0xa3)}; + auto iter = element::iterator(input.data()); + + EXPECT_THAT(std::vector(iter, iter + elements_count), + ElementsAre(0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1)); +} + +TEST(ElementIteratorTest, read_non_const_u1_data) { + constexpr auto elements_count = 16; + auto input = std::array{0x21, static_cast(0xa3)}; + auto iter = element::iterator(input.data()); + + EXPECT_THAT(std::vector(iter, iter + elements_count), + ElementsAre(0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1)); +} + +TEST(ElementIteratorTest, read_u1_data_increment_decrement_iterator) { + auto input = std::array{0x32, static_cast(0xa3), 0x55}; + auto iter = element::iterator(input.data() + 1); + + EXPECT_EQ(*iter--, 1); // 2nd byte bit7 + EXPECT_EQ(*iter++, 0); // 1st byte bit0 + EXPECT_EQ(*++iter, 0); // 2nd byte bit6 + EXPECT_EQ(*iter--, 0); // 2nd byte bit6 + EXPECT_EQ(*iter, 1); // 2nd byte bit7 +} + +TEST(ElementIteratorTest, read_u1_data_iterator_with_offset) { + auto input = std::array{0x32, static_cast(0xa3), 0x41}; + auto iter = element::iterator(input.data() + 1); + + EXPECT_EQ(*iter, 1); // 2nd byte bit7 + EXPECT_EQ(*(iter - 2), 1); // 1st byte bit1 + EXPECT_EQ(*(iter - 5), 1); // 1st byte bit4 + EXPECT_EQ(*(iter + 1), 0); // 2nd byte bit6 + EXPECT_EQ(*(iter + 8), 0); // 3rd byte bit7 + EXPECT_EQ(*(iter + 9), 1); // 3rd byte bit6 + EXPECT_EQ(*std::prev(iter, 1), 0); // 1st byte bit0 + EXPECT_EQ(*std::next(iter, 2), 1); // 2nd byte bit5 +} + +TEST(ElementIteratorTest, u1_value_to_output_stream) { + constexpr int8_t value = 0x80; + auto iter = element::iterator(&value); + + std::stringstream s; + s << *iter; + + EXPECT_EQ(s.str(), "1"); +} + +// ---- u2 +TEST(ElementIteratorTest, write_u2_data) { + constexpr auto elements_count = 16; + auto input = std::array{2, 0, 1, 3, 0, 0, 3, 3, 1, 2, 1, 2, 3, 2, 1, 0}; + auto output = std::array{}; + auto iter = element::iterator(output.data()); + + std::copy(input.begin(), input.end(), iter); + + EXPECT_THAT(output, ElementsAre(0x87, 0x0f, 0x66, 0xe4)); +} + +TEST(ElementIteratorTest, read_const_u2_data) { + constexpr auto elements_count = 16; + constexpr auto input = std::array{static_cast(0x87), + 0x0f, + 0x66, + static_cast(0xe4)}; + auto iter = element::iterator(input.data()); + + EXPECT_THAT(std::vector(iter, iter + elements_count), + ElementsAre(2, 0, 1, 3, 0, 0, 3, 3, 1, 2, 1, 2, 3, 2, 1, 0)); +} + +TEST(ElementIteratorTest, read_non_const_u2_data) { + constexpr auto elements_count = 16; + auto input = std::array{static_cast(0x87), + 0x0f, + 0x66, + static_cast(0xe4)}; + auto iter = element::iterator(input.data()); + + EXPECT_THAT(std::vector(iter, iter + elements_count), + ElementsAre(2, 0, 1, 3, 0, 0, 3, 3, 1, 2, 1, 2, 3, 2, 1, 0)); +} + +TEST(ElementIteratorTest, read_u2_data_increment_decrement_iterator) { + auto input = std::array{0x33, static_cast(0x93)}; + auto iter = element::iterator(input.data() + 1); + + EXPECT_EQ(*iter--, 2); // 2nd byte 1st half-nibble + EXPECT_EQ(*iter++, 3); // 1st byte 4th half-nibble + EXPECT_EQ(*++iter, 1); // 2nd byte 2nd half-nibble + EXPECT_EQ(*iter--, 1); // 2nd byte 2nd half-nibble + EXPECT_EQ(*--iter, 3); // 1st byte 4th half-nibble +} + +TEST(ElementIteratorTest, read_u2_data_iterator_with_offset) { + auto input = std::array{0x43, static_cast(0x93), 0x41}; + auto iter = element::iterator(input.data() + 1); + + EXPECT_EQ(*iter, 2); // 2nd byte 1st half-nibble + EXPECT_EQ(*(iter - 3), 0); // 1st byte 2nd half-nibble + EXPECT_EQ(*(iter - 4), 1); // 1st byte 1st half-nibble + EXPECT_EQ(*(iter + 1), 1); // 2nd byte 2nd half-nibble + EXPECT_EQ(*(iter + 7), 1); // 3rd byte 4th half-nibble + EXPECT_EQ(*std::prev(iter, 1), 3); // 1st byte 4th half-nibble + EXPECT_EQ(*std::next(iter, 2), 0); // 2nd byte 3rd half-nibble +} + +TEST(ElementIteratorTest, u2_value_to_output_stream) { + constexpr int8_t value = 0x80; + auto iter = element::iterator(&value); + + std::stringstream s; + s << *iter; + + EXPECT_EQ(s.str(), "2"); +} + +// --- u3 +TEST(ElementIteratorTest, write_u3_data) { + constexpr auto elements_count = 8; + auto input = std::array{2, 3, 0, 1, 4, 5, 6, 7}; + auto output = std::array{}; + auto iter = element::iterator(output.data()); + + std::copy(input.begin(), input.end(), iter); + + EXPECT_THAT(output, ElementsAre(0b10110001, 0b00011011, 0b00001111)); +} + +TEST(ElementIteratorTest, read_non_const_u3_data) { + constexpr auto elements_count = 16; + auto input = std::array{0x7a, 0x6f, 0x55, static_cast(0xb1), 0x1b, 0x0f}; + auto iter = element::iterator(input.data()); + + EXPECT_THAT(std::vector(iter, iter + elements_count), + ElementsAre(1, 7, 2, 6, 1, 6, 3, 7, 2, 3, 0, 1, 4, 5, 6, 7)); +} + +TEST(ElementIteratorTest, read_const_u3_data) { + constexpr auto elements_count = 8; + constexpr auto input = std::array{static_cast(0b10110001), 0b00011011, 0b00001111}; + auto iter = element::iterator(input.data()); + + EXPECT_THAT(std::vector(iter, iter + elements_count), ElementsAre(2, 3, 0, 1, 4, 5, 6, 7)); +} + +TEST(ElementIteratorTest, read_u3_data_iterator_with_offset) { + // Has values {1, 7, 2, 6, 1, 6, 3, 7, [2], 3, 0, 1, 4, 5, 6, 7} + auto input = std::array{0x7a, 0x6f, 0x55, static_cast(0xb1), 0x1b, 0x0f}; + auto iter = element::iterator(input.data() + 3); + + EXPECT_EQ(*iter, 2); + EXPECT_EQ(*(iter - 3), 6); + EXPECT_EQ(*(iter - 4), 1); + EXPECT_EQ(*(iter - 5), 6); + EXPECT_EQ(*(iter + 1), 3); + EXPECT_EQ(*(iter + 5), 5); + EXPECT_EQ(*(iter + 7), 7); + EXPECT_EQ(*std::prev(iter, 1), 7); + EXPECT_EQ(*std::next(iter, 2), 0); +} + +// --- u4 +TEST(ElementIteratorTest, write_u4_data) { + constexpr auto elements_count = 16; + auto input = std::array{1, 2, 3, 10, 12, 15, 14, 4, 7, 9, 11, 13, 8, 0, 5, 6}; + auto output = std::array{}; + auto iter = element::iterator(output.data()); + + std::copy(input.begin(), input.end(), iter); + + EXPECT_THAT(output, ElementsAre(0x12, 0x3a, 0xcf, 0xe4, 0x79, 0xbd, 0x80, 0x56)); +} + +TEST(ElementIteratorTest, read_const_u4_data) { + constexpr auto elements_count = 16; + constexpr auto byte_size = get_buffer_size(4, elements_count); + constexpr auto input = std::array{0x12, + 0x3a, + static_cast(0xcf), + static_cast(0xe4), + 0x79, + static_cast(0xbd), + 0x08, + 0x56}; + auto iter = element::iterator(input.data()); + + EXPECT_THAT(std::vector(iter, iter + elements_count), + ElementsAre(1, 2, 3, 10, 12, 15, 14, 4, 7, 9, 11, 13, 0, 8, 5, 6)); +} + +TEST(ElementIteratorTest, read_non_const_u4_data) { + constexpr auto elements_count = 16; + constexpr auto byte_size = get_buffer_size(4, elements_count); + auto input = std::array{0x12, + 0x3a, + static_cast(0xcf), + static_cast(0xe4), + 0x79, + static_cast(0xbd), + 0x08, + 0x56}; + auto iter = element::iterator(input.data()); + + EXPECT_THAT(std::vector(iter, iter + elements_count), + ElementsAre(1, 2, 3, 10, 12, 15, 14, 4, 7, 9, 11, 13, 0, 8, 5, 6)); +} + +TEST(ElementIteratorTest, read_u4_data_increment_decrement_iterator) { + auto input = std::array{0x12, 0x3a}; + auto iter = element::iterator(input.data() + 1); + + EXPECT_EQ(*iter--, 3); // 2nd byte 1st nibble + EXPECT_EQ(*iter++, 2); // 1st byte 2nd nibble + EXPECT_EQ(*++iter, 10); // 2nd byte 2nd nibble + EXPECT_EQ(*iter--, 10); // 2nd byte 2nd nibble + EXPECT_EQ(*--iter, 2); // 1st byte 2nd nibble +} + +TEST(ElementIteratorTest, read_u4_data_iterator_with_offset) { + auto input = std::array{0x42, 0x3a, 0x61, 0x79, 0x5b}; + auto iter = element::iterator(input.data() + 1); + + EXPECT_EQ(*iter, 3); // 2nd byte 1st nibble + EXPECT_EQ(*(iter - 2), 4); // 1st byte 1st nibble + EXPECT_EQ(*(iter + 7), 11); // 5th byte 2nd nibble + EXPECT_EQ(*(iter + 6), 5); // 2nd byte 1st nibble + EXPECT_EQ(*(iter - 1), 2); // 1st byte 2nd nibble + EXPECT_EQ(*std::prev(iter, 1), 2); // 1st byte 2nd nibble + EXPECT_EQ(*std::next(iter, 2), 6); // 3rd byte 1st nibble +} + +// --- i4 +TEST(ElementIteratorTest, write_i4_data) { + constexpr auto elements_count = 16; + auto input = std::array{1, 2, 3, -6, -4, -1, -2, 4, 7, -7, -5, -3, -8, 0, 5, 6}; + auto output = std::array{}; + auto iter = element::iterator(output.data()); + + std::copy(input.begin(), input.end(), iter); + + EXPECT_THAT(output, ElementsAre(0x12, 0x3a, 0xcf, 0xe4, 0x79, 0xbd, 0x80, 0x56)); +} + +TEST(ElementIteratorTest, read_const_i4_data) { + constexpr auto elements_count = 16; + constexpr auto byte_size = get_buffer_size(4, elements_count); + constexpr auto input = std::array{0x12, + 0x3a, + static_cast(0xcf), + static_cast(0xe4), + 0x79, + static_cast(0xbd), + 0x08, + 0x56}; + auto iter = element::iterator(input.data()); + + EXPECT_THAT(std::vector(iter, iter + elements_count), + ElementsAre(1, 2, 3, -6, -4, -1, -2, 4, 7, -7, -5, -3, 0, -8, 5, 6)); +} + +TEST(ElementIteratorTest, read_non_const_i4_data) { + constexpr auto elements_count = 16; + constexpr auto byte_size = get_buffer_size(4, elements_count); + auto input = std::array{0x12, + 0x3a, + static_cast(0xcf), + static_cast(0xe4), + 0x79, + static_cast(0xbd), + 0x08, + 0x56}; + auto iter = element::iterator(input.data()); + + EXPECT_THAT(std::vector(iter, iter + elements_count), + ElementsAre(1, 2, 3, -6, -4, -1, -2, 4, 7, -7, -5, -3, 0, -8, 5, 6)); +} + +TEST(ElementIteratorTest, read_i4_data_increment_decrement_iterator) { + auto input = std::array{0x12, 0x3a}; + auto iter = element::iterator(input.data() + 1); + + EXPECT_EQ(*iter--, 3); // 2nd byte 1st nibble + EXPECT_EQ(*iter++, 2); // 1st byte 2nd nibble + EXPECT_EQ(*++iter, -6); // 2nd byte 2nd nibble + EXPECT_EQ(*iter--, -6); // 2nd byte 2nd nibble + EXPECT_EQ(*--iter, 2); // 1st byte 2nd nibble +} + +TEST(ElementIteratorTest, read_i4_data_iterator_with_offset) { + auto input = std::array{0x42, 0x3a, 0x61, 0x79, 0x5b}; + auto iter = element::iterator(input.data() + 1); + + EXPECT_EQ(*iter, 3); // 2nd byte 1st nibble + EXPECT_EQ(*(iter - 2), 4); // 1st byte 1st nibble + EXPECT_EQ(*(iter + 7), -5); // 5th byte 2nd nibble + EXPECT_EQ(*(iter + 6), 5); // 2nd byte 1st nibble + EXPECT_EQ(*(iter - 1), 2); // 1st byte 2nd nibble + EXPECT_EQ(*std::prev(iter, 1), 2); // 1st byte 2nd nibble + EXPECT_EQ(*std::next(iter, 2), 6); // 3rd byte 1st nibble +} + +TEST(ElementIteratorTest, i4_value_to_output_stream) { + constexpr int8_t value = 0x91; + auto iter = element::iterator(&value); + + std::stringstream s; + s << *iter; + + EXPECT_EQ(s.str(), "-7"); +} + +// --- u6 +TEST(ElementIteratorTest, write_u6_data) { + constexpr auto elements_count = 8; + auto input = std::array{2, 1, 0, 3, 18, 49, 35, 16}; + auto output = std::array{}; + auto iter = element::iterator(output.data()); + + std::copy(input.begin(), input.end(), iter); + + EXPECT_THAT(output, ElementsAre(0x21, 0x03, 0x00, 0x21, 0x30, 0x79)); +} + +TEST(ElementIteratorTest, read_non_const_u6_data) { + constexpr auto elements_count = 8; + auto input = std::array{0x21, 0x03, 0x00, 0x21, 0x30, 0x79}; + auto iter = element::iterator(input.data()); + + EXPECT_THAT(std::vector(iter, iter + elements_count), ElementsAre(2, 1, 0, 3, 18, 49, 35, 16)); +} + +TEST(ElementIteratorTest, read_const_u6_data) { + constexpr auto elements_count = 8; + constexpr auto input = std::array{0x21, 0x03, 0x00, 0x21, 0x30, 0x79}; + auto iter = element::iterator(input.data()); + + EXPECT_THAT(std::vector(iter, iter + elements_count), ElementsAre(2, 1, 0, 3, 18, 49, 35, 16)); +} + +TEST(ElementIteratorTest, read_u6_data_increment_decrement_iterator) { + // Has values {1, 2, 3, 10, [3], 8, 7, 2} + auto input = std::array{0x12, 0x3a, 0x00, 0x38, 0x72, 0x00}; + auto iter = element::iterator(input.data() + 3); + + EXPECT_EQ(*iter--, 3); + EXPECT_EQ(*iter++, 10); + EXPECT_EQ(*++iter, 8); + EXPECT_EQ(*iter--, 8); + EXPECT_EQ(*--iter, 10); +} + +TEST(ElementIteratorTest, read_u6_data_iterator_with_offset) { + // Has values {1, 2, 3, 10, [3], 8, 7, 2, 1, 42, 4, 20} + auto input = std::array{0x12, 0x3a, 0x00, 0x38, 0x72, 0x00, 0x1a, 0x44, 0x21}; + auto iter = element::iterator(input.data() + 3); + + EXPECT_EQ(*iter, 3); + EXPECT_EQ(*(iter - 3), 2); + EXPECT_EQ(*(iter - 4), 1); + EXPECT_EQ(*(iter - 2), 3); + EXPECT_EQ(*(iter + 1), 8); + EXPECT_EQ(*(iter + 5), 42); + EXPECT_EQ(*(iter + 7), 20); + EXPECT_EQ(*std::prev(iter, 1), 10); + EXPECT_EQ(*std::next(iter, 2), 7); +} + +TEST(ElementIteratorTest, u6_value_to_output_stream) { + auto input = std::array{0x12, 0x3a, 0x00}; + auto iter = element::iterator(input.data()); + + std::stringstream s; + s << *iter; + + EXPECT_EQ(s.str(), "1"); +} + +} // namespace test +} // namespace ov From 5cd0bc44f5188532cbdd9830f09ddb20010ddddd Mon Sep 17 00:00:00 2001 From: "Raasz, Pawel" Date: Tue, 5 Mar 2024 13:06:10 +0000 Subject: [PATCH 03/12] Test read low precision data from tensor with iter --- src/core/src/type/element_type.cpp | 25 ++++++++--- src/core/tests/element_iterator_test.cpp | 56 ++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 5 deletions(-) diff --git a/src/core/src/type/element_type.cpp b/src/core/src/type/element_type.cpp index 397acb8b98f9b1..8dec60db621ed4 100644 --- a/src/core/src/type/element_type.cpp +++ b/src/core/src/type/element_type.cpp @@ -59,8 +59,14 @@ inline TypeInfo get_type_info(ov::element::Type_t type) { return {64, false, true, false, "int64_t", "i64"}; case ov::element::Type_t::u1: return {1, false, false, false, "uint1_t", "u1"}; + case ov::element::Type_t::u2: + return {2, false, false, false, "uint2_t", "u2"}; + case ov::element::Type_t::u3: + return {3, false, false, false, "uint3_t", "u3"}; case ov::element::Type_t::u4: return {4, false, false, false, "uint4_t", "u4"}; + case ov::element::Type_t::u6: + return {6, false, false, false, "uint6_t", "u6"}; case ov::element::Type_t::u8: return {8, false, false, true, "uint8_t", "u8"}; case ov::element::Type_t::u16: @@ -103,8 +109,14 @@ ov::element::Type type_from_string(const std::string& type) { return ::ov::element::Type(::ov::element::Type_t::i64); } else if (type == "u1" || type == "U1" || type == "BIN" || type == "bin") { return ::ov::element::Type(::ov::element::Type_t::u1); + } else if (type == "u2" || type == "U2") { + return ::ov::element::Type(::ov::element::Type_t::u2); + } else if (type == "u3" || type == "U3") { + return ::ov::element::Type(::ov::element::Type_t::u3); } else if (type == "u4" || type == "U4") { return ::ov::element::Type(::ov::element::Type_t::u4); + } else if (type == "u6" || type == "U6") { + return ::ov::element::Type(::ov::element::Type_t::u6); } else if (type == "u8" || type == "U8") { return ::ov::element::Type(::ov::element::Type_t::u8); } else if (type == "u16" || type == "U16") { @@ -135,11 +147,11 @@ ov::element::Type type_from_string(const std::string& type) { std::vector ov::element::Type::get_known_types() { std::vector rc = { - &ov::element::dynamic, &ov::element::boolean, &ov::element::bf16, &ov::element::f16, &ov::element::f32, - &ov::element::f64, &ov::element::i4, &ov::element::i8, &ov::element::i16, &ov::element::i32, - &ov::element::i64, &ov::element::u1, &ov::element::u4, &ov::element::u8, &ov::element::u16, - &ov::element::u32, &ov::element::u64, &ov::element::nf4, &ov::element::f8e4m3, &ov::element::f8e5m2, - &ov::element::string}; + &ov::element::dynamic, &ov::element::boolean, &ov::element::bf16, &ov::element::f16, &ov::element::f32, + &ov::element::f64, &ov::element::i4, &ov::element::i8, &ov::element::i16, &ov::element::i32, + &ov::element::i64, &ov::element::u1, &ov::element::u2, &ov::element::u3, &ov::element::u4, + &ov::element::u6, &ov::element::u8, &ov::element::u16, &ov::element::u32, &ov::element::u64, + &ov::element::nf4, &ov::element::f8e4m3, &ov::element::f8e5m2, &ov::element::string}; return rc; } @@ -463,7 +475,10 @@ OPENVINO_API EnumNames& EnumNames::get() { {"i32", element::Type_t::i32}, {"i64", element::Type_t::i64}, {"u1", element::Type_t::u1}, + {"u2", element::Type_t::u2}, + {"u3", element::Type_t::u3}, {"u4", element::Type_t::u4}, + {"u6", element::Type_t::u6}, {"u8", element::Type_t::u8}, {"u16", element::Type_t::u16}, {"u32", element::Type_t::u32}, diff --git a/src/core/tests/element_iterator_test.cpp b/src/core/tests/element_iterator_test.cpp index adf5c81a5998cc..87ba93624a778f 100644 --- a/src/core/tests/element_iterator_test.cpp +++ b/src/core/tests/element_iterator_test.cpp @@ -8,6 +8,8 @@ #include +#include "openvino/runtime/tensor.hpp" + namespace ov { namespace test { @@ -76,6 +78,16 @@ TEST(ElementIteratorTest, read_u1_data_iterator_with_offset) { } TEST(ElementIteratorTest, u1_value_to_output_stream) { + auto input = std::array{0x32, static_cast(0xa3), 0x41, 0x11}; + auto t = ov::Tensor(element::u1, Shape{2, 16}, input.data()); + auto iter = element::iterator(static_cast(t.data(element::u1))); + + EXPECT_THAT( + std::vector(iter, iter + t.get_size()), + ElementsAre(0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1)); +} + +TEST(ElementIteratorTest, read_u1_from_tensor) { constexpr int8_t value = 0x80; auto iter = element::iterator(&value); @@ -155,6 +167,15 @@ TEST(ElementIteratorTest, u2_value_to_output_stream) { EXPECT_EQ(s.str(), "2"); } +TEST(ElementIteratorTest, read_u2_from_tensor) { + auto input = std::array{0x32, static_cast(0xa3), 0x41, 0x11}; + auto t = ov::Tensor(element::u2, Shape{4, 4}, input.data()); + auto iter = element::iterator(static_cast(t.data(element::u2))); + + EXPECT_THAT(std::vector(iter, iter + t.get_size()), + ElementsAre(0, 3, 0, 2, 2, 2, 0, 3, 1, 0, 0, 1, 0, 1, 0, 1)); +} + // --- u3 TEST(ElementIteratorTest, write_u3_data) { constexpr auto elements_count = 8; @@ -200,6 +221,16 @@ TEST(ElementIteratorTest, read_u3_data_iterator_with_offset) { EXPECT_EQ(*std::next(iter, 2), 0); } +TEST(ElementIteratorTest, read_u3_from_tensor) { + // Has values {1, 7, 2, 6, 1, 6, 3, 7, [2], 3, 0, 1, 4, 5, 6, 7} + auto input = std::array{0x7a, 0x6f, 0x55, static_cast(0xb1), 0x1b, 0x0f}; + auto t = ov::Tensor(element::u3, Shape{4, 2, 2}, input.data()); + auto iter = element::iterator(static_cast(t.data(element::u3))); + + EXPECT_THAT(std::vector(iter, iter + t.get_size()), + ElementsAre(1, 7, 2, 6, 1, 6, 3, 7, 2, 3, 0, 1, 4, 5, 6, 7)); +} + // --- u4 TEST(ElementIteratorTest, write_u4_data) { constexpr auto elements_count = 16; @@ -270,6 +301,14 @@ TEST(ElementIteratorTest, read_u4_data_iterator_with_offset) { EXPECT_EQ(*std::next(iter, 2), 6); // 3rd byte 1st nibble } +TEST(ElementIteratorTest, read_u4_from_tensor) { + auto input = std::array{0x42, 0x3a, 0x61, 0x79, 0x5b}; + auto t = ov::Tensor(element::u4, Shape{5, 2}, input.data()); + auto iter = element::iterator(static_cast(t.data(element::u4))); + + EXPECT_THAT(std::vector(iter, iter + t.get_size()), ElementsAre(4, 2, 3, 10, 6, 1, 7, 9, 5, 11)); +} + // --- i4 TEST(ElementIteratorTest, write_i4_data) { constexpr auto elements_count = 16; @@ -350,6 +389,14 @@ TEST(ElementIteratorTest, i4_value_to_output_stream) { EXPECT_EQ(s.str(), "-7"); } +TEST(ElementIteratorTest, read_i4_from_tensor) { + auto input = std::array{0x42, 0x3a, 0x61, 0x79, 0x5b}; + auto t = ov::Tensor(element::i4, Shape{10, 1, 1}, input.data()); + auto iter = element::iterator(static_cast(t.data(element::i4))); + + EXPECT_THAT(std::vector(iter, iter + t.get_size()), ElementsAre(4, 2, 3, -6, 6, 1, 7, -7, 5, -5)); +} + // --- u6 TEST(ElementIteratorTest, write_u6_data) { constexpr auto elements_count = 8; @@ -416,5 +463,14 @@ TEST(ElementIteratorTest, u6_value_to_output_stream) { EXPECT_EQ(s.str(), "1"); } +TEST(ElementIteratorTest, read_u6_from_tensor) { + // Has values {1, 2, 3, 10, 3, 8, 7, 2, 1, 42, 4, 20} + auto input = std::array{0x12, 0x3a, 0x00, 0x38, 0x72, 0x00, 0x1a, 0x44, 0x21}; + auto t = ov::Tensor(element::u6, Shape{4, 1, 3}, input.data()); + auto iter = element::iterator(static_cast(t.data(element::u6))); + + EXPECT_THAT(std::vector(iter, iter + t.get_size()), ElementsAre(1, 2, 3, 10, 3, 8, 7, 2, 1, 42, 4, 20)); +} + } // namespace test } // namespace ov From 2d2ea55434db0373e43f96ee2aea077a99d532c2 Mon Sep 17 00:00:00 2001 From: "Raasz, Pawel" Date: Tue, 5 Mar 2024 14:14:54 +0000 Subject: [PATCH 04/12] Exclude precisions names from naming check --- cmake/developer_package/ncc_naming_style/openvino.style | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/developer_package/ncc_naming_style/openvino.style b/cmake/developer_package/ncc_naming_style/openvino.style index 6608795381e4a1..141f02b9ab5808 100644 --- a/cmake/developer_package/ncc_naming_style/openvino.style +++ b/cmake/developer_package/ncc_naming_style/openvino.style @@ -18,7 +18,7 @@ VariableReference: '^\w+$' EnumName: '^[A-Z][\w]+$' # excepts element_type -EnumConstantName: '^([A-Z\d_]+|undefined|dynamic|boolean|bf16|f16|f32|f64|i4|i8|i16|i32|i64|u1|u4|u8|u16|u32|u64|nf4|f8e4m3|f8e5m2|string|asymmetric|align_corners|round_prefer_floor|round_prefer_ceil|floor|ceil|simple|nearest|linear|linear_onnx|cubic|area|scales|sizes|half_pixel|tf_half_pixel_for_nn|pytorch_half_pixel|asymetric)$' +EnumConstantName: '^([A-Z\d_]+|undefined|dynamic|boolean|bf16|f16|f32|f64|i4|i8|i16|i32|i64|u1|u2|u3|u4|u6|u8|u16|u32|u64|nf4|f8e4m3|f8e5m2|string|asymmetric|align_corners|round_prefer_floor|round_prefer_ceil|floor|ceil|simple|nearest|linear|linear_onnx|cubic|area|scales|sizes|half_pixel|tf_half_pixel_for_nn|pytorch_half_pixel|asymetric)$' # TODO: align UsingDeclaration: '^.*$' TypedefName: '^.*$' From 317211426ad443cd8fe1d24411ef9348d64131fa Mon Sep 17 00:00:00 2001 From: "Raasz, Pawel" Date: Wed, 6 Mar 2024 12:44:57 +0000 Subject: [PATCH 05/12] Swap nibbles access for 4-bit precisions --- .../openvino/core/type/element_iterator.hpp | 48 ++++++++++-- src/core/tests/element_iterator_test.cpp | 74 ++++++++++--------- 2 files changed, 80 insertions(+), 42 deletions(-) diff --git a/src/core/dev_api/openvino/core/type/element_iterator.hpp b/src/core/dev_api/openvino/core/type/element_iterator.hpp index 54a5e51d106375..36ef61c1bac1c8 100644 --- a/src/core/dev_api/openvino/core/type/element_iterator.hpp +++ b/src/core/dev_api/openvino/core/type/element_iterator.hpp @@ -31,7 +31,17 @@ namespace element { * @return True if element type is bit type otherwise false. */ constexpr bool is_bit_type(Type_t et) { - return et == Type_t::u1 || et == Type_t::u2 || et == Type_t::u4 || et == Type_t::i4; + return et == Type_t::u1 || et == Type_t::u2; +} + +/** + * @brief Checks if element type is 4-bits type. + * + * @param et Element type to check + * @return True if element type is nibble type otherwise false. + */ +constexpr bool is_nibble_type(Type_t et) { + return et == Type_t::u4 || et == Type_t::i4 || et == nf4; } /** @@ -53,7 +63,7 @@ constexpr bool is_split_bit_type(Type_t et) { * @return True if element type use bytes for its value otherwise false. */ constexpr bool is_byte_type(Type_t et) { - return !is_bit_type(et) && !is_split_bit_type(et) && et != Type_t::nf4; + return !is_bit_type(et) && !is_split_bit_type(et) && !is_nibble_type(et); } /** @@ -163,7 +173,7 @@ class BitProxy::type> { * @return Value of BitProxy. */ operator value_type() const { - constexpr uint8_t value_mask = util::make_n_bit_mask(m_bits); + constexpr auto value_mask = util::make_n_bit_mask(m_bits); uint8_t tmp = (*m_ptr >> m_bit_shift) & value_mask; if (S && (tmp & (1U << (m_bits - 1U)))) { // If N bit value MSB bit is set then value is negative. @@ -178,7 +188,7 @@ class BitProxy::type> { * @param v Value to be set. */ BitProxy& operator=(const value_type v) { - constexpr uint8_t value_mask = util::make_n_bit_mask(m_bits); + constexpr auto value_mask = util::make_n_bit_mask(m_bits); *m_ptr &= ~(value_mask << m_bit_shift); *m_ptr |= (static_cast(v) & value_mask) << m_bit_shift; return *this; @@ -282,7 +292,7 @@ class BitProxy::type> { tmp = m_bytes->b2 & ~((mask_upper >> lower_mask_bits) << (upper_mask_bits * m_bit_shift)); tmp |= (((v & mask_upper) >> lower_mask_bits) << (upper_mask_bits * m_bit_shift)); - m_bytes->b2 = tmp; + m_bytes->b2 = tmp & 0x00ff; return *this; } }; @@ -326,7 +336,14 @@ class Iterator { template typename std::enable_if>::type& operator++() { m_et_ptr.m_bit_shift -= m_et_ptr.m_bits; - m_et_ptr.m_bit_shift = m_et_ptr.m_bit_shift % ((m_et_ptr.m_num_values * m_et_ptr.m_bits)); + m_et_ptr.m_bit_shift = m_et_ptr.m_bit_shift % (m_et_ptr.m_num_values * m_et_ptr.m_bits); + m_et_ptr.m_ptr += static_cast(m_et_ptr.m_bit_shift == m_et_ptr.m_shift_init); + return *this; + } + + template + typename std::enable_if>::type& operator++() { + m_et_ptr.m_bit_shift ^= m_et_ptr.m_bits; m_et_ptr.m_ptr += static_cast(m_et_ptr.m_bit_shift == m_et_ptr.m_shift_init); return *this; } @@ -353,6 +370,12 @@ class Iterator { return *this; } + template + typename std::enable_if>::type& operator+=(const difference_type& n) { + m_et_ptr.m_ptr += n / m_et_ptr.m_num_values; + return (n % m_et_ptr.m_num_values) ? ++*this : *this; + } + template typename std::enable_if>::type& operator+=(const difference_type& n) { const auto advance = n + m_et_ptr.m_shift_init - m_et_ptr.m_bit_shift; @@ -375,6 +398,13 @@ class Iterator { return *this; } + template + typename std::enable_if>::type& operator--() { + m_et_ptr.m_bit_shift ^= m_et_ptr.m_bits; + m_et_ptr.m_ptr -= static_cast(m_et_ptr.m_bit_shift == 4); + return *this; + } + template typename std::enable_if>::type& operator--() { ++m_et_ptr.m_bit_shift; @@ -397,6 +427,12 @@ class Iterator { return *this; } + template + typename std::enable_if>::type& operator-=(const difference_type& n) { + m_et_ptr.m_ptr -= n / m_et_ptr.m_num_values; + return (n % m_et_ptr.m_num_values) ? --*this : *this; + } + template typename std::enable_if>::type& operator-=(const difference_type& n) { const auto advance = m_et_ptr.m_bit_shift + n; diff --git a/src/core/tests/element_iterator_test.cpp b/src/core/tests/element_iterator_test.cpp index 87ba93624a778f..16cec8ce3eb565 100644 --- a/src/core/tests/element_iterator_test.cpp +++ b/src/core/tests/element_iterator_test.cpp @@ -88,7 +88,7 @@ TEST(ElementIteratorTest, u1_value_to_output_stream) { } TEST(ElementIteratorTest, read_u1_from_tensor) { - constexpr int8_t value = 0x80; + constexpr auto value = static_cast(0x80); auto iter = element::iterator(&value); std::stringstream s; @@ -158,7 +158,7 @@ TEST(ElementIteratorTest, read_u2_data_iterator_with_offset) { } TEST(ElementIteratorTest, u2_value_to_output_stream) { - constexpr int8_t value = 0x80; + constexpr auto value = static_cast(0x80); auto iter = element::iterator(&value); std::stringstream s; @@ -232,6 +232,7 @@ TEST(ElementIteratorTest, read_u3_from_tensor) { } // --- u4 +// nibbles are counted as [n1, n0] TEST(ElementIteratorTest, write_u4_data) { constexpr auto elements_count = 16; auto input = std::array{1, 2, 3, 10, 12, 15, 14, 4, 7, 9, 11, 13, 8, 0, 5, 6}; @@ -240,7 +241,7 @@ TEST(ElementIteratorTest, write_u4_data) { std::copy(input.begin(), input.end(), iter); - EXPECT_THAT(output, ElementsAre(0x12, 0x3a, 0xcf, 0xe4, 0x79, 0xbd, 0x80, 0x56)); + EXPECT_THAT(output, ElementsAre(0x21, 0xa3, 0xfc, 0x4e, 0x97, 0xdb, 0x08, 0x65)); } TEST(ElementIteratorTest, read_const_u4_data) { @@ -257,7 +258,7 @@ TEST(ElementIteratorTest, read_const_u4_data) { auto iter = element::iterator(input.data()); EXPECT_THAT(std::vector(iter, iter + elements_count), - ElementsAre(1, 2, 3, 10, 12, 15, 14, 4, 7, 9, 11, 13, 0, 8, 5, 6)); + ElementsAre(2, 1, 10, 3, 15, 12, 4, 14, 9, 7, 13, 11, 8, 0, 6, 5)); } TEST(ElementIteratorTest, read_non_const_u4_data) { @@ -274,31 +275,31 @@ TEST(ElementIteratorTest, read_non_const_u4_data) { auto iter = element::iterator(input.data()); EXPECT_THAT(std::vector(iter, iter + elements_count), - ElementsAre(1, 2, 3, 10, 12, 15, 14, 4, 7, 9, 11, 13, 0, 8, 5, 6)); + ElementsAre(2, 1, 10, 3, 15, 12, 4, 14, 9, 7, 13, 11, 8, 0, 6, 5)); } TEST(ElementIteratorTest, read_u4_data_increment_decrement_iterator) { - auto input = std::array{0x12, 0x3a}; + auto input = std::array{0x12, 0x3a}; auto iter = element::iterator(input.data() + 1); - EXPECT_EQ(*iter--, 3); // 2nd byte 1st nibble - EXPECT_EQ(*iter++, 2); // 1st byte 2nd nibble - EXPECT_EQ(*++iter, 10); // 2nd byte 2nd nibble - EXPECT_EQ(*iter--, 10); // 2nd byte 2nd nibble - EXPECT_EQ(*--iter, 2); // 1st byte 2nd nibble + EXPECT_EQ(*iter--, 10); // 2nd byte 1st nibble + EXPECT_EQ(*iter++, 1); // 1st byte 2nd nibble + EXPECT_EQ(*++iter, 3); // 2nd byte 2nd nibble + EXPECT_EQ(*iter--, 3); // 2nd byte 2nd nibble + EXPECT_EQ(*--iter, 1); // 1st byte 2nd nibble } TEST(ElementIteratorTest, read_u4_data_iterator_with_offset) { auto input = std::array{0x42, 0x3a, 0x61, 0x79, 0x5b}; auto iter = element::iterator(input.data() + 1); - EXPECT_EQ(*iter, 3); // 2nd byte 1st nibble - EXPECT_EQ(*(iter - 2), 4); // 1st byte 1st nibble - EXPECT_EQ(*(iter + 7), 11); // 5th byte 2nd nibble - EXPECT_EQ(*(iter + 6), 5); // 2nd byte 1st nibble - EXPECT_EQ(*(iter - 1), 2); // 1st byte 2nd nibble - EXPECT_EQ(*std::prev(iter, 1), 2); // 1st byte 2nd nibble - EXPECT_EQ(*std::next(iter, 2), 6); // 3rd byte 1st nibble + EXPECT_EQ(*iter, 10); // 2nd byte 1st nibble + EXPECT_EQ(*(iter - 2), 2); // 1st byte 1st nibble + EXPECT_EQ(*(iter + 7), 5); // 5th byte 2nd nibble + EXPECT_EQ(*(iter + 6), 11); // 2nd byte 1st nibble + EXPECT_EQ(*(iter - 1), 4); // 1st byte 2nd nibble + EXPECT_EQ(*std::prev(iter, 1), 4); // 1st byte 2nd nibble + EXPECT_EQ(*std::next(iter, 2), 1); // 3rd byte 1st nibble } TEST(ElementIteratorTest, read_u4_from_tensor) { @@ -306,10 +307,11 @@ TEST(ElementIteratorTest, read_u4_from_tensor) { auto t = ov::Tensor(element::u4, Shape{5, 2}, input.data()); auto iter = element::iterator(static_cast(t.data(element::u4))); - EXPECT_THAT(std::vector(iter, iter + t.get_size()), ElementsAre(4, 2, 3, 10, 6, 1, 7, 9, 5, 11)); + EXPECT_THAT(std::vector(iter, iter + t.get_size()), ElementsAre(2, 4, 10, 3, 1, 6, 9, 7, 11, 5)); } // --- i4 +// nibbles are counted as [n1, n0] TEST(ElementIteratorTest, write_i4_data) { constexpr auto elements_count = 16; auto input = std::array{1, 2, 3, -6, -4, -1, -2, 4, 7, -7, -5, -3, -8, 0, 5, 6}; @@ -318,7 +320,7 @@ TEST(ElementIteratorTest, write_i4_data) { std::copy(input.begin(), input.end(), iter); - EXPECT_THAT(output, ElementsAre(0x12, 0x3a, 0xcf, 0xe4, 0x79, 0xbd, 0x80, 0x56)); + EXPECT_THAT(output, ElementsAre(0x21, 0xa3, 0xfc, 0x4e, 0x97, 0xdb, 0x08, 0x65)); } TEST(ElementIteratorTest, read_const_i4_data) { @@ -335,7 +337,7 @@ TEST(ElementIteratorTest, read_const_i4_data) { auto iter = element::iterator(input.data()); EXPECT_THAT(std::vector(iter, iter + elements_count), - ElementsAre(1, 2, 3, -6, -4, -1, -2, 4, 7, -7, -5, -3, 0, -8, 5, 6)); + ElementsAre(2, 1, -6, 3, -1, -4, 4, -2, -7, 7, -3, -5, -8, 0, 6, 5)); } TEST(ElementIteratorTest, read_non_const_i4_data) { @@ -352,35 +354,35 @@ TEST(ElementIteratorTest, read_non_const_i4_data) { auto iter = element::iterator(input.data()); EXPECT_THAT(std::vector(iter, iter + elements_count), - ElementsAre(1, 2, 3, -6, -4, -1, -2, 4, 7, -7, -5, -3, 0, -8, 5, 6)); + ElementsAre(2, 1, -6, 3, -1, -4, 4, -2, -7, 7, -3, -5, -8, 0, 6, 5)); } TEST(ElementIteratorTest, read_i4_data_increment_decrement_iterator) { auto input = std::array{0x12, 0x3a}; auto iter = element::iterator(input.data() + 1); - EXPECT_EQ(*iter--, 3); // 2nd byte 1st nibble - EXPECT_EQ(*iter++, 2); // 1st byte 2nd nibble - EXPECT_EQ(*++iter, -6); // 2nd byte 2nd nibble - EXPECT_EQ(*iter--, -6); // 2nd byte 2nd nibble - EXPECT_EQ(*--iter, 2); // 1st byte 2nd nibble + EXPECT_EQ(*iter--, -6); // 2nd byte 1st nibble + EXPECT_EQ(*iter++, 1); // 1st byte 2nd nibble + EXPECT_EQ(*++iter, 3); // 2nd byte 2nd nibble + EXPECT_EQ(*iter--, 3); // 2nd byte 2nd nibble + EXPECT_EQ(*--iter, 1); // 1st byte 2nd nibble } TEST(ElementIteratorTest, read_i4_data_iterator_with_offset) { auto input = std::array{0x42, 0x3a, 0x61, 0x79, 0x5b}; auto iter = element::iterator(input.data() + 1); - EXPECT_EQ(*iter, 3); // 2nd byte 1st nibble - EXPECT_EQ(*(iter - 2), 4); // 1st byte 1st nibble - EXPECT_EQ(*(iter + 7), -5); // 5th byte 2nd nibble - EXPECT_EQ(*(iter + 6), 5); // 2nd byte 1st nibble - EXPECT_EQ(*(iter - 1), 2); // 1st byte 2nd nibble - EXPECT_EQ(*std::prev(iter, 1), 2); // 1st byte 2nd nibble - EXPECT_EQ(*std::next(iter, 2), 6); // 3rd byte 1st nibble + EXPECT_EQ(*iter, -6); // 2nd byte 1st nibble + EXPECT_EQ(*(iter - 2), 2); // 1st byte 1st nibble + EXPECT_EQ(*(iter + 7), 5); // 5th byte 2nd nibble + EXPECT_EQ(*(iter + 6), -5); // 2nd byte 1st nibble + EXPECT_EQ(*(iter - 1), 4); // 1st byte 2nd nibble + EXPECT_EQ(*std::prev(iter, 1), 4); // 1st byte 2nd nibble + EXPECT_EQ(*std::next(iter, 2), 1); // 3rd byte 1st nibble } TEST(ElementIteratorTest, i4_value_to_output_stream) { - constexpr int8_t value = 0x91; + constexpr auto value = static_cast(0x19); auto iter = element::iterator(&value); std::stringstream s; @@ -394,7 +396,7 @@ TEST(ElementIteratorTest, read_i4_from_tensor) { auto t = ov::Tensor(element::i4, Shape{10, 1, 1}, input.data()); auto iter = element::iterator(static_cast(t.data(element::i4))); - EXPECT_THAT(std::vector(iter, iter + t.get_size()), ElementsAre(4, 2, 3, -6, 6, 1, 7, -7, 5, -5)); + EXPECT_THAT(std::vector(iter, iter + t.get_size()), ElementsAre(2, 4, -6, 3, 1, 6, -7, 7, -5, 5)); } // --- u6 From 274cacb3f51cb07808899589f4bf16711bd95090 Mon Sep 17 00:00:00 2001 From: "Raasz, Pawel" Date: Wed, 6 Mar 2024 15:39:36 +0000 Subject: [PATCH 06/12] Fix BitProxy member values --- src/core/dev_api/openvino/core/type/element_iterator.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/dev_api/openvino/core/type/element_iterator.hpp b/src/core/dev_api/openvino/core/type/element_iterator.hpp index 36ef61c1bac1c8..bd4e930c9272d8 100644 --- a/src/core/dev_api/openvino/core/type/element_iterator.hpp +++ b/src/core/dev_api/openvino/core/type/element_iterator.hpp @@ -133,9 +133,9 @@ class BitProxy::type> { template friend class Iterator; //!< Iterator class is friend to access private members to manipulate pointer. - static constexpr size_t m_bits = N; //!< Number of bit for single value. - static constexpr size_t m_num_values = 8 / N; //!< Number values in byte. - static constexpr size_t m_shift_init = 8 - N; //!< Initial value for bit shift. + static constexpr size_t m_bits = N; //!< Number of bit for single value. + static constexpr size_t m_num_values = 8 / N; //!< Number values in byte. + static constexpr size_t m_shift_init = N == 4 ? 0 : 8 - N; //!< Initial value for bit shift. T* m_ptr; //!< Pointer to T value used to get sub-byte value. size_t m_bit_shift; //!< Current bit shift to get sub-byte value. From f22b1e192699bd2bfb544ed814aaa5f1719f5a93 Mon Sep 17 00:00:00 2001 From: "Raasz, Pawel" Date: Wed, 6 Mar 2024 15:43:23 +0000 Subject: [PATCH 07/12] Correct Iterator using --- .../dev_api/openvino/core/type/element_iterator.hpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/core/dev_api/openvino/core/type/element_iterator.hpp b/src/core/dev_api/openvino/core/type/element_iterator.hpp index bd4e930c9272d8..618de3d5c04f9d 100644 --- a/src/core/dev_api/openvino/core/type/element_iterator.hpp +++ b/src/core/dev_api/openvino/core/type/element_iterator.hpp @@ -320,17 +320,19 @@ std::ostream& operator<<(std::ostream& os, const BitProxy& value) { */ template class Iterator { + using proxy_type = BitProxy(), (ET == i4)>; + public: using iterator_category = std::bidirectional_iterator_tag; using difference_type = std::ptrdiff_t; - using value_type = BitProxy(), (ET == i4)>; - using reference = value_type&; - using pointer = T*; + using value_type = T; + using reference = proxy_type&; + using pointer = proxy_type*; static_assert(std::is_same::type, ov::fundamental_type_for>::value, "Iterator value_type must be same as fundamental type of ET"); - constexpr Iterator(pointer ptr) noexcept : m_et_ptr{ptr} {} + constexpr Iterator(T* ptr) noexcept : m_et_ptr{ptr} {} // Iteration operators template @@ -465,7 +467,7 @@ class Iterator { } private: - value_type m_et_ptr; + proxy_type m_et_ptr; }; /** From 6b9ca5b5235d8ccd783fb42e73000d23784e0db0 Mon Sep 17 00:00:00 2001 From: "Raasz, Pawel" Date: Fri, 8 Mar 2024 14:19:58 +0100 Subject: [PATCH 08/12] Fixes in Iterator operators --- .../openvino/core/type/element_iterator.hpp | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/src/core/dev_api/openvino/core/type/element_iterator.hpp b/src/core/dev_api/openvino/core/type/element_iterator.hpp index 618de3d5c04f9d..47ee8f7aacb18c 100644 --- a/src/core/dev_api/openvino/core/type/element_iterator.hpp +++ b/src/core/dev_api/openvino/core/type/element_iterator.hpp @@ -45,7 +45,7 @@ constexpr bool is_nibble_type(Type_t et) { } /** - * @brief Checks if element type is split split bit type. + * @brief Checks if element type is split bit type. * * The value is stored in byte(s) like [b0, b1, x, .., x, b2, b3]. * @@ -57,10 +57,10 @@ constexpr bool is_split_bit_type(Type_t et) { } /** - * @brief Check element type if is using only byte. + * @brief Check element type is using only byte(s). * * @param et Element type to check. - * @return True if element type use bytes for its value otherwise false. + * @return True if element type use byte(s) for its value otherwise false. */ constexpr bool is_byte_type(Type_t et) { return !is_bit_type(et) && !is_split_bit_type(et) && !is_nibble_type(et); @@ -114,7 +114,7 @@ constexpr size_t bit_width() { * * @tparam T Fundamental type of sub-byte value which must be same as fundamental type of element::Type_t. * @tparam N Number of bits for sub-byte value. - * @tparam S Flag to indicate sub-byte value i signed. + * @tparam S Flag to indicate sub-byte value is signed. * @tparam Enable class for specific type, bit layouts. */ template @@ -125,7 +125,7 @@ class BitProxy {}; * * @tparam T Fundamental type of sub-byte value which must be same as fundamental type of element::Type_t. * @tparam N Number of bits for sub-byte value. - * @tparam S Flag to indicate sub-byte value i signed. + * @tparam S Flag to indicate sub-byte value is signed. */ template class BitProxy::type> { @@ -137,8 +137,8 @@ class BitProxy::type> { static constexpr size_t m_num_values = 8 / N; //!< Number values in byte. static constexpr size_t m_shift_init = N == 4 ? 0 : 8 - N; //!< Initial value for bit shift. - T* m_ptr; //!< Pointer to T value used to get sub-byte value. - size_t m_bit_shift; //!< Current bit shift to get sub-byte value. + T* m_ptr; //!< Pointer to T used to get value. + size_t m_bit_shift; //!< Current bit shift to get value. constexpr BitProxy(T* ptr) noexcept : m_ptr{ptr}, m_bit_shift{m_shift_init} {} @@ -224,7 +224,7 @@ class BitProxy::type> { ByteValue* m_bytes; //!< Pointer to buffer as 3 bytes representation. }; - size_t m_bit_shift; //!< Current bit shift to get BitProxy value. + size_t m_bit_shift; //!< Current bit shift to get value. constexpr BitProxy(T* ptr) noexcept : m_ptr{ptr}, m_bit_shift{m_shift_init} {} @@ -326,8 +326,8 @@ class Iterator { using iterator_category = std::bidirectional_iterator_tag; using difference_type = std::ptrdiff_t; using value_type = T; - using reference = proxy_type&; - using pointer = proxy_type*; + using reference = typename std::conditional::value, const proxy_type&, proxy_type&>::type; + using pointer = typename std::conditional::value, const proxy_type*, proxy_type*>::type; static_assert(std::is_same::type, ov::fundamental_type_for>::value, "Iterator value_type must be same as fundamental type of ET"); @@ -450,18 +450,15 @@ class Iterator { } // compare operators - template ::type* = nullptr> constexpr bool operator!=(const Iterator& rhs) const { return (m_et_ptr.m_ptr != rhs.m_et_ptr.m_ptr) || (m_et_ptr.m_bit_shift != rhs.m_et_ptr.m_bit_shift); } // dereference operators - template ::type* = nullptr> - reference operator*() const { + constexpr const proxy_type& operator*() const { return m_et_ptr; } - template ::type* = nullptr> reference operator*() { return m_et_ptr; } From 75531f693685b5bc53c44320b11f4e8e374d5aa4 Mon Sep 17 00:00:00 2001 From: "Raasz, Pawel" Date: Mon, 11 Mar 2024 11:36:06 +0000 Subject: [PATCH 09/12] Change BitProxy to Type_t instead bits, sign - apply review comments --- .../openvino/core/type/element_iterator.hpp | 94 +++++++++++-------- 1 file changed, 56 insertions(+), 38 deletions(-) diff --git a/src/core/dev_api/openvino/core/type/element_iterator.hpp b/src/core/dev_api/openvino/core/type/element_iterator.hpp index 47ee8f7aacb18c..822254afecd4aa 100644 --- a/src/core/dev_api/openvino/core/type/element_iterator.hpp +++ b/src/core/dev_api/openvino/core/type/element_iterator.hpp @@ -10,7 +10,7 @@ namespace ov { namespace util { /** - * @brief Make bit mask by set N bits start from right. + * @brief Make bit mask by setting N less significant bits. * * @tparam T Type of value. * @param n Number of bits to set. @@ -31,7 +31,7 @@ namespace element { * @return True if element type is bit type otherwise false. */ constexpr bool is_bit_type(Type_t et) { - return et == Type_t::u1 || et == Type_t::u2; + return et == u1 || et == u2; } /** @@ -41,7 +41,7 @@ constexpr bool is_bit_type(Type_t et) { * @return True if element type is nibble type otherwise false. */ constexpr bool is_nibble_type(Type_t et) { - return et == Type_t::u4 || et == Type_t::i4 || et == nf4; + return et == u4 || et == i4 || et == nf4; } /** @@ -53,26 +53,28 @@ constexpr bool is_nibble_type(Type_t et) { * @return True if element type is split bit type otherwise false. */ constexpr bool is_split_bit_type(Type_t et) { - return et == Type_t::u3 || et == Type_t::u6; + return et == u3 || et == u6; } /** - * @brief Check element type is using only byte(s). + * @brief Checks element type is using only N bytes as value. * * @param et Element type to check. - * @return True if element type use byte(s) for its value otherwise false. + * @return True if element type use byte(s) for its value, false otherwise. */ constexpr bool is_byte_type(Type_t et) { - return !is_bit_type(et) && !is_split_bit_type(et) && !is_nibble_type(et); + return !is_bit_type(et) && !is_split_bit_type(et) && !is_nibble_type(et) && et != string; } /** - * @brief Get bit width of ov::element::Type_t. + * @brief Gets bit width of ov::element::Type_t. * * @return Number of bits representing the Type_t. */ template -constexpr size_t bit_width(); +constexpr size_t bit_width() { + return sizeof(typename ov::fundamental_type_for()); +} template <> constexpr size_t bit_width() { @@ -112,36 +114,39 @@ constexpr size_t bit_width() { * The R/W access is done via conversion and copy assignment operators. * The public members are used to work on sub-byte value like on its fundamental type defined by T. * - * @tparam T Fundamental type of sub-byte value which must be same as fundamental type of element::Type_t. - * @tparam N Number of bits for sub-byte value. - * @tparam S Flag to indicate sub-byte value is signed. - * @tparam Enable class for specific type, bit layouts. + * @tparam T Fundamental type of sub-byte value which must be same as fundamental type of element::Type_t. + * @tparam ET OpenVINO element type. + * @tparam Enable Type to enable/disable this class. */ -template +template class BitProxy {}; /** * @brief The BitProxy specialization for types which are represented by N in-raw bits in byte. * * @tparam T Fundamental type of sub-byte value which must be same as fundamental type of element::Type_t. - * @tparam N Number of bits for sub-byte value. - * @tparam S Flag to indicate sub-byte value is signed. + * @tparam ET OpenVINO element type. */ -template -class BitProxy::type> { +template +class BitProxy::type> { private: template friend class Iterator; //!< Iterator class is friend to access private members to manipulate pointer. - static constexpr size_t m_bits = N; //!< Number of bit for single value. - static constexpr size_t m_num_values = 8 / N; //!< Number values in byte. - static constexpr size_t m_shift_init = N == 4 ? 0 : 8 - N; //!< Initial value for bit shift. + static constexpr size_t m_bits = bit_width(); //!< Number of bit for single value. + static constexpr size_t m_num_values = 8 / m_bits; //!< Number values in byte. + static constexpr size_t m_shift_init = is_nibble_type(ET) ? 0 : 8 - m_bits; //!< Initial value for bit shift. T* m_ptr; //!< Pointer to T used to get value. size_t m_bit_shift; //!< Current bit shift to get value. constexpr BitProxy(T* ptr) noexcept : m_ptr{ptr}, m_bit_shift{m_shift_init} {} + uint8_t get_bit_value() const { + constexpr auto value_mask = util::make_n_bit_mask(m_bits); + return (*m_ptr >> m_bit_shift) & value_mask; + } + public: using value_type = typename std::decay::type; //!< Fundamental type of bound to BitProxy. @@ -172,22 +177,35 @@ class BitProxy::type> { * * @return Value of BitProxy. */ + template ::type* = nullptr> + operator value_type() const { + return static_cast(get_bit_value()); + } + + /** + * @brief Converts to fundamental type. + * + * @return Value of BitProxy. + */ + template ::type* = nullptr> operator value_type() const { constexpr auto value_mask = util::make_n_bit_mask(m_bits); - uint8_t tmp = (*m_ptr >> m_bit_shift) & value_mask; - if (S && (tmp & (1U << (m_bits - 1U)))) { + constexpr auto value_msb_mask = (1U << (m_bits - 1U)); + + auto v = get_bit_value(); + if (v & value_msb_mask) { // If N bit value MSB bit is set then value is negative. - // As tmp is byte then all bits above N must be set to be two's complement. - tmp |= ~value_mask; + // As v is byte then all bits above N must be set to be two's complement. + v |= ~value_mask; } - return static_cast(tmp); + return static_cast(v); } /** * @brief Sets current ProxyBit to value. * @param v Value to be set. */ - BitProxy& operator=(const value_type v) { + BitProxy& operator=(const value_type v) { constexpr auto value_mask = util::make_n_bit_mask(m_bits); *m_ptr &= ~(value_mask << m_bit_shift); *m_ptr |= (static_cast(v) & value_mask) << m_bit_shift; @@ -201,16 +219,16 @@ class BitProxy::type> { * @note The input pointer must point on buffer which has got 3 * n bytes. * * @tparam T Fundamental type of sub-byte value which must be same as fundamental type of element::Type_t. - * @tparam N Number of bits for sub-byte value. + * @tparam ET OpenVINO element type. */ -template -class BitProxy::type> { +template +class BitProxy::type> { private: template friend class Iterator; //!< Iterator class is friend to access private members to manipulate pointer. - static constexpr size_t m_bits = N; //!< Number of bit for single value. - static constexpr size_t m_num_values = (3 * 8) / N; //!< Number values in byte. + static constexpr size_t m_bits = bit_width(); //!< Number of bit for single value. + static constexpr size_t m_num_values = (3 * 8) / m_bits; //!< Number values in byte. static constexpr size_t m_shift_init = m_num_values - 1; //!< Initial value for bit shift. struct ByteValue { @@ -278,7 +296,7 @@ class BitProxy::type> { * @brief Sets current ProxyBit to value. * @param v Value to be set. */ - BitProxy& operator=(const value_type v) { + BitProxy& operator=(const value_type v) { constexpr uint16_t lower_mask_bits = 16 / m_num_values; constexpr uint16_t upper_mask_bits = 8 / m_num_values; constexpr uint16_t mask_lower = util::make_n_bit_mask(lower_mask_bits); @@ -304,9 +322,9 @@ class BitProxy::type> { * @param value Value to print. * @return return output stream. */ -template -std::ostream& operator<<(std::ostream& os, const BitProxy& value) { - os << +value; +template +std::ostream& operator<<(std::ostream& os, const BitProxy& value) { + os << +static_cast(value); return os; } @@ -320,7 +338,7 @@ std::ostream& operator<<(std::ostream& os, const BitProxy& value) { */ template class Iterator { - using proxy_type = BitProxy(), (ET == i4)>; + using proxy_type = BitProxy; public: using iterator_category = std::bidirectional_iterator_tag; @@ -476,7 +494,7 @@ class Iterator { * @param ptr Pointer to data. * @return Element iterator for type ET. */ -template +template ::type* = nullptr> constexpr Iterator iterator(T* ptr) { return {ptr}; } From dad46fd1934bbc4502da5731fbcf68efa08c91bf Mon Sep 17 00:00:00 2001 From: "Raasz, Pawel" Date: Wed, 13 Mar 2024 10:41:17 +0000 Subject: [PATCH 10/12] Correct doxygen comments --- src/core/dev_api/openvino/core/type/element_iterator.hpp | 4 ++-- src/core/include/openvino/core/type/element_type.hpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/dev_api/openvino/core/type/element_iterator.hpp b/src/core/dev_api/openvino/core/type/element_iterator.hpp index 822254afecd4aa..331bd3684e576e 100644 --- a/src/core/dev_api/openvino/core/type/element_iterator.hpp +++ b/src/core/dev_api/openvino/core/type/element_iterator.hpp @@ -250,11 +250,11 @@ class BitProxy::type> { using value_type = typename std::decay::type; //!< Fundamental type of sub-byte. /** - * @brief Compare proxy value is less than rhs. + * @brief Compare proxy value is equal than rhs. * * @tparam U Type of value to compare. * @param rhs Value to compare. - * @return True if less otherwise false. + * @return True if equal, false otherwise. */ template constexpr bool operator==(const U& rhs) const { diff --git a/src/core/include/openvino/core/type/element_type.hpp b/src/core/include/openvino/core/type/element_type.hpp index 5538fa480f1c74..76ad336414a468 100644 --- a/src/core/include/openvino/core/type/element_type.hpp +++ b/src/core/include/openvino/core/type/element_type.hpp @@ -180,7 +180,7 @@ constexpr Type u3(Type_t::u3); /// \brief u4 element type /// \ingroup ov_element_cpp_api constexpr Type u4(Type_t::u4); -/// \brief u4 element type +/// \brief u6 element type /// \ingroup ov_element_cpp_api constexpr Type u6(Type_t::u6); /// \brief u8 element type From 2f99ea526dc86ef97f83742e4a304b8029af2293 Mon Sep 17 00:00:00 2001 From: "Raasz, Pawel" Date: Wed, 13 Mar 2024 12:10:13 +0000 Subject: [PATCH 11/12] Correct test names --- src/core/tests/element_iterator_test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/tests/element_iterator_test.cpp b/src/core/tests/element_iterator_test.cpp index 16cec8ce3eb565..cfe4b164c7c0c8 100644 --- a/src/core/tests/element_iterator_test.cpp +++ b/src/core/tests/element_iterator_test.cpp @@ -77,7 +77,7 @@ TEST(ElementIteratorTest, read_u1_data_iterator_with_offset) { EXPECT_EQ(*std::next(iter, 2), 1); // 2nd byte bit5 } -TEST(ElementIteratorTest, u1_value_to_output_stream) { +TEST(ElementIteratorTest, read_u1_from_tensor) { auto input = std::array{0x32, static_cast(0xa3), 0x41, 0x11}; auto t = ov::Tensor(element::u1, Shape{2, 16}, input.data()); auto iter = element::iterator(static_cast(t.data(element::u1))); @@ -87,7 +87,7 @@ TEST(ElementIteratorTest, u1_value_to_output_stream) { ElementsAre(0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1)); } -TEST(ElementIteratorTest, read_u1_from_tensor) { +TEST(ElementIteratorTest, u1_value_to_output_stream) { constexpr auto value = static_cast(0x80); auto iter = element::iterator(&value); From 4bf6f1becad7b9439ccf81de1a0bc0333eed38ac Mon Sep 17 00:00:00 2001 From: Pawel Raasz Date: Sun, 17 Mar 2024 06:21:46 +0000 Subject: [PATCH 12/12] Update hash value for params in test due to insert new element types --- .../intel_gpu/tests/unit/test_cases/hash_key_gpu_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/intel_gpu/tests/unit/test_cases/hash_key_gpu_test.cpp b/src/plugins/intel_gpu/tests/unit/test_cases/hash_key_gpu_test.cpp index 48d596ab3ba0e9..a4837187b29a6a 100644 --- a/src/plugins/intel_gpu/tests/unit/test_cases/hash_key_gpu_test.cpp +++ b/src/plugins/intel_gpu/tests/unit/test_cases/hash_key_gpu_test.cpp @@ -255,7 +255,7 @@ class check_hash_value: public ::testing::Test { const auto primitive_hash = primitve->hash(); const auto params_hash = prim_inst->get_impl_params()->hash(); ASSERT_EQ(primitive_hash, 4135863035456568493UL); - ASSERT_EQ(params_hash, 5990757629995899044UL); + ASSERT_EQ(params_hash, 11563701278302723583UL); } };