Allow avoiding Constant casting / printing VisualizeTree (#19845)

* Avoid Constant casting / printing when OV_VISUALIZE_TREE_CONST_MAX_ELEMENTS==0
Cast only requested amount of elements in Constant::cast_vector<>

* Refactor

* Revert style back

* Fix signed/unsigned comparison

* test

* Style

* Style
This commit is contained in:
Evgenya Nugmanova
2023-09-19 22:13:19 +04:00
committed by GitHub
parent 215a2f435b
commit 00bc4436a9
3 changed files with 108 additions and 79 deletions

View File

@@ -295,10 +295,11 @@ public:
/// \brief Return the Constant's value as a vector cast to type T
///
/// \tparam T Type to which data vector's entries will be cast.
/// \tparam T Type to which data vector's entries will be cast.
/// \param num_elements (Optional) Number of elements to cast. In default case returns all elements
/// \return Constant's data vector.
template <typename T>
std::vector<T> cast_vector() const {
std::vector<T> cast_vector(int64_t num_elements = -1) const {
auto source_type = get_element_type();
std::vector<T> rc;
using Type_t = element::Type_t;
@@ -306,54 +307,58 @@ public:
# pragma warning(push)
# pragma warning(disable : 4244)
#endif
size_t num_elements_in_constant = shape_size(m_shape);
size_t num_elements_to_cast =
(num_elements < 0 ? num_elements_in_constant
: std::min(static_cast<size_t>(num_elements), num_elements_in_constant));
switch (source_type) {
case Type_t::boolean:
cast_vector<Type_t::boolean>(rc);
cast_vector<Type_t::boolean>(rc, num_elements_to_cast);
break;
case Type_t::bf16:
cast_vector<Type_t::bf16>(rc);
cast_vector<Type_t::bf16>(rc, num_elements_to_cast);
break;
case Type_t::f16:
cast_vector<Type_t::f16>(rc);
cast_vector<Type_t::f16>(rc, num_elements_to_cast);
break;
case Type_t::f32:
cast_vector<Type_t::f32>(rc);
cast_vector<Type_t::f32>(rc, num_elements_to_cast);
break;
case Type_t::f64:
cast_vector<Type_t::f64>(rc);
cast_vector<Type_t::f64>(rc, num_elements_to_cast);
break;
case Type_t::i4:
cast_vector<Type_t::i4>(rc);
cast_vector<Type_t::i4>(rc, num_elements_to_cast);
break;
case Type_t::i8:
cast_vector<Type_t::i8>(rc);
cast_vector<Type_t::i8>(rc, num_elements_to_cast);
break;
case Type_t::i16:
cast_vector<Type_t::i16>(rc);
cast_vector<Type_t::i16>(rc, num_elements_to_cast);
break;
case Type_t::i32:
cast_vector<Type_t::i32>(rc);
cast_vector<Type_t::i32>(rc, num_elements_to_cast);
break;
case Type_t::i64:
cast_vector<Type_t::i64>(rc);
cast_vector<Type_t::i64>(rc, num_elements_to_cast);
break;
case Type_t::u1:
cast_vector<Type_t::u1>(rc);
cast_vector<Type_t::u1>(rc, num_elements_to_cast);
break;
case Type_t::u4:
cast_vector<Type_t::u4>(rc);
cast_vector<Type_t::u4>(rc, num_elements_to_cast);
break;
case Type_t::u8:
cast_vector<Type_t::u8>(rc);
cast_vector<Type_t::u8>(rc, num_elements_to_cast);
break;
case Type_t::u16:
cast_vector<Type_t::u16>(rc);
cast_vector<Type_t::u16>(rc, num_elements_to_cast);
break;
case Type_t::u32:
cast_vector<Type_t::u32>(rc);
cast_vector<Type_t::u32>(rc, num_elements_to_cast);
break;
case Type_t::u64:
cast_vector<Type_t::u64>(rc);
cast_vector<Type_t::u64>(rc, num_elements_to_cast);
break;
default:
OPENVINO_THROW("unsupported type");
@@ -438,15 +443,19 @@ private:
typename std::enable_if<Type != element::Type_t::u1 && Type != element::Type_t::u4 &&
Type != element::Type_t::i4,
bool>::type = true>
void cast_vector(std::vector<OUT_T>& output_vector) const {
void cast_vector(std::vector<OUT_T>& output_vector, size_t num_elements) const {
// this function is workaround for waring during windows building
// build complains for vector creation based on iterators
// which point on different type than destination vector::value_type
using IN_T = fundamental_type_for<Type>;
auto source_vector = get_vector<IN_T>();
output_vector.reserve(source_vector.size());
auto output_size = std::min(num_elements, source_vector.size());
output_vector.reserve(output_size);
std::transform(source_vector.begin(), source_vector.end(), std::back_inserter(output_vector), [](IN_T c) {
std::transform(source_vector.begin(),
source_vector.begin() + output_size,
std::back_inserter(output_vector),
[](IN_T c) {
#ifdef __clang__
# pragma clang diagnostic push
# ifdef __has_warning
@@ -465,12 +474,13 @@ private:
# pragma warning(disable : 4018)
# pragma warning(disable : 4804)
#endif
if (!std::is_same<OUT_T, IN_T>::value) {
OPENVINO_ASSERT(!std::numeric_limits<IN_T>::is_signed || std::numeric_limits<OUT_T>::lowest() <= c,
"Cannot cast vector from constant. Some values are outside the range.");
OPENVINO_ASSERT(std::numeric_limits<OUT_T>::max() >= c,
"Cannot cast vector from constant. Some values are outside the range.");
}
if (!std::is_same<OUT_T, IN_T>::value) {
OPENVINO_ASSERT(
!std::numeric_limits<IN_T>::is_signed || std::numeric_limits<OUT_T>::lowest() <= c,
"Cannot cast vector from constant. Some values are outside the range.");
OPENVINO_ASSERT(std::numeric_limits<OUT_T>::max() >= c,
"Cannot cast vector from constant. Some values are outside the range.");
}
#if defined(__clang__)
# pragma clang diagnostic pop
#elif defined(__GNUC__)
@@ -478,16 +488,16 @@ private:
#elif defined(_MSC_VER)
# pragma warning(pop)
#endif
return static_cast<OUT_T>(c);
});
return static_cast<OUT_T>(c);
});
}
template <element::Type_t Type,
typename OUT_T,
typename std::enable_if<Type == element::Type_t::u1, bool>::type = true>
void cast_vector(std::vector<OUT_T>& output) const {
void cast_vector(std::vector<OUT_T>& output, size_t num_elements) const {
using IN_T = fundamental_type_for<Type>;
const auto element_number = shape_size(m_shape);
const auto element_number = std::min(num_elements, shape_size(m_shape));
const auto source_begin = get_data_ptr<uint8_t>();
const auto source_end = std::next(source_begin, (element_number + 7) / 8);
const auto round_element_no = element_number % 8 ? element_number - element_number % 8 + 8 : element_number;
@@ -504,9 +514,9 @@ private:
template <element::Type_t Type,
typename OUT_T,
typename std::enable_if<Type == element::Type_t::u4, bool>::type = true>
void cast_vector(std::vector<OUT_T>& output) const {
void cast_vector(std::vector<OUT_T>& output, size_t num_elements) const {
using IN_T = fundamental_type_for<Type>;
const auto element_number = shape_size(m_shape);
const auto element_number = std::min(num_elements, shape_size(m_shape));
const auto source_begin = get_data_ptr<uint8_t>();
const auto source_end = std::next(source_begin, (element_number + 1) / 2);
const auto round_element_no = element_number % 2 ? element_number + 1 : element_number;
@@ -522,9 +532,9 @@ private:
template <element::Type_t Type,
typename OUT_T,
typename std::enable_if<Type == element::Type_t::i4, bool>::type = true>
void cast_vector(std::vector<OUT_T>& output) const {
void cast_vector(std::vector<OUT_T>& output, size_t num_elements) const {
using IN_T = fundamental_type_for<Type>;
const auto element_number = shape_size(m_shape);
const auto element_number = std::min(num_elements, shape_size(m_shape));
const auto source_begin = get_data_ptr<uint8_t>();
const auto source_end = std::next(source_begin, (element_number + 1) / 2);
const auto round_element_no = element_number % 2 ? element_number + 1 : element_number;

View File

@@ -213,8 +213,6 @@ ov::pass::VisualizeTree::VisualizeTree(const std::string& file_name, node_modifi
void ov::pass::VisualizeTree::add_node_arguments(std::shared_ptr<Node> node,
std::unordered_map<Node*, HeightMap>& height_maps,
size_t& fake_node_ctr) {
static const int const_max_elements = ov::util::getenv_int("OV_VISUALIZE_TREE_CONST_MAX_ELEMENTS", 7);
size_t arg_index = 0;
for (auto input_value : node->input_values()) {
auto arg = input_value.get_node_shared_ptr();
@@ -227,7 +225,7 @@ void ov::pass::VisualizeTree::add_node_arguments(std::shared_ptr<Node> node,
"style=\"dashed\"",
color,
std::string("label=\"") + get_node_name(arg) + std::string("\n") +
get_constant_value(arg, const_max_elements) + std::string("\"")};
get_constant_value(arg) + std::string("\"")};
if (m_node_modifiers && !arg->output(0).get_rt_info().empty()) {
m_node_modifiers(*arg, attributes);
@@ -280,13 +278,7 @@ std::string ov::pass::VisualizeTree::add_attributes(std::shared_ptr<Node> node)
static std::string pretty_partial_shape(const ov::PartialShape& shape) {
std::stringstream ss;
if (shape.rank().is_dynamic()) {
ss << "?";
} else {
ss << shape;
}
ss << shape;
return ss.str();
}
@@ -327,21 +319,11 @@ static std::string pretty_min_max_denormal_value(const std::vector<T>& values) {
}
template <typename T>
static std::string pretty_value(const std::vector<T>& values, size_t max_elements, bool allow_obfuscate = false) {
static std::string pretty_value(const std::vector<T>& values, bool allow_obfuscate = false) {
std::stringstream ss;
for (size_t i = 0; i < values.size(); ++i) {
if (i < max_elements) {
if (i != 0 && i % 8 == 0) {
ss << std::endl;
}
} else {
bool all_same = std::all_of(values.begin(), values.end(), [&](const T& el) {
return el == values[0];
});
ss << "..." << (all_same ? " same" : "");
break;
}
if (i != 0 && i % 8 == 0)
ss << std::endl;
const auto& value = values[i];
if (i > 0)
ss << ", ";
@@ -361,46 +343,46 @@ static std::string pretty_value(const std::vector<T>& values, size_t max_element
return ss.str();
}
static std::string get_value(const std::shared_ptr<ov::op::v0::Constant>& constant,
size_t max_elements,
bool allow_obfuscate = false) {
static std::string get_value(const std::shared_ptr<ov::op::v0::Constant>& constant, bool allow_obfuscate = false) {
static const int max_elements = ov::util::getenv_int("OV_VISUALIZE_TREE_CONST_MAX_ELEMENTS", 7);
std::stringstream ss;
ss << "[ ";
switch (constant->get_output_element_type(0)) {
case ov::element::Type_t::undefined:
ss << "[ undefined value ]";
break;
case ov::element::Type_t::dynamic:
ss << "[ dynamic value ]";
break;
case ov::element::Type_t::u1:
ss << "[ u1 value ]";
break;
case ov::element::Type_t::u4:
ss << "[ u4 value ]";
break;
case ov::element::Type_t::i4:
ss << "[ i4 value ]";
ss << constant->get_output_element_type(0).get_type_name() << " value";
break;
case ov::element::Type_t::bf16:
case ov::element::Type_t::f16:
case ov::element::Type_t::f32:
case ov::element::Type_t::f64:
ss << "[" << pretty_value(constant->cast_vector<double>(), max_elements, allow_obfuscate) << "]";
ss << pretty_value(constant->cast_vector<double>(max_elements), allow_obfuscate);
break;
case ov::element::Type_t::i8:
case ov::element::Type_t::i16:
case ov::element::Type_t::i32:
case ov::element::Type_t::i64:
ss << "[" << pretty_value(constant->cast_vector<int64_t>(), max_elements, allow_obfuscate) << "]";
ss << pretty_value(constant->cast_vector<int64_t>(max_elements), allow_obfuscate);
break;
case ov::element::Type_t::boolean:
case ov::element::Type_t::u8:
case ov::element::Type_t::u16:
case ov::element::Type_t::u32:
case ov::element::Type_t::u64:
ss << "[" << pretty_value(constant->cast_vector<uint64_t>(), max_elements, allow_obfuscate) << "]";
ss << pretty_value(constant->cast_vector<uint64_t>(max_elements), allow_obfuscate);
break;
}
const auto num_elements_in_constant = static_cast<int>(shape_size(constant->get_shape()));
if (num_elements_in_constant == 0)
ss << "empty";
else if (max_elements == 0)
ss << "suppressed";
else if (num_elements_in_constant > max_elements)
ss << ", ...";
ss << " ]";
return ss.str();
}
@@ -418,12 +400,9 @@ static std::string get_bounds_and_label_info(const ov::Output<ov::Node> output)
if (size == 0) {
label << "empty";
} else {
static const int const_max_elements = ov::util::getenv_int("OV_VISUALIZE_TREE_CONST_MAX_ELEMENTS", 7);
label << " lower: "
<< (lower ? get_value(std::make_shared<ov::op::v0::Constant>(lower), const_max_elements, true) : "NONE");
label << " upper: "
<< (upper ? get_value(std::make_shared<ov::op::v0::Constant>(upper), const_max_elements, true) : "NONE");
label << " label: " << (value_label.empty() ? "NONE" : pretty_value(value_label, const_max_elements));
label << " lower: " << (lower ? get_value(std::make_shared<ov::op::v0::Constant>(lower), true) : "NONE");
label << " upper: " << (upper ? get_value(std::make_shared<ov::op::v0::Constant>(upper), true) : "NONE");
label << " label: " << (value_label.empty() ? "NONE" : pretty_value(value_label));
}
return label.str();
}

View File

@@ -1779,3 +1779,43 @@ TEST(constant, lazy_bitwise_identical) {
// '10' times is guaranteed to be faster here (typical value is ~200'000)
EXPECT_GT(bitwise_check_count_only, bitwise_check_count * 10);
}
TEST(constant, cast_vector) {
std::vector<element::Type_t> types = {element::boolean,
element::bf16,
element::f16,
element::f32,
element::f64,
element::i4,
element::i8,
element::i16,
element::i32,
element::i64,
element::u1,
element::u4,
element::u8,
element::u16,
element::u32,
element::u64};
std::vector<int64_t> data = {0, 1, 0, 0, 1, 1, 0, 1};
std::vector<int64_t> expected_partial_data = {0, 1, 0, 0, 1, 1};
for (const auto& type : types) {
const auto& constant = op::v0::Constant::create(type, Shape{data.size()}, data);
const auto& default_casted = constant->cast_vector<int64_t>();
EXPECT_EQ(default_casted, data) << "Constant::cast_vector failed default casting for type " << type;
int64_t num_elements_for_partial_casting = static_cast<int64_t>(expected_partial_data.size());
const auto& partially_casted = constant->cast_vector<int64_t>(num_elements_for_partial_casting);
EXPECT_EQ(partially_casted, expected_partial_data)
<< "Constant::cast_vector failed partial casting for type " << type;
int64_t num_elements_for_over_casting = static_cast<int64_t>(data.size()) + 10;
const auto& over_casted = constant->cast_vector<int64_t>(num_elements_for_over_casting);
EXPECT_EQ(over_casted, data) << "Constant::cast_vector failed for partial casting for type " << type;
EXPECT_TRUE(constant->cast_vector<int64_t>(0).empty())
<< "Constant::cast_vector failed empty casting for type " << type;
}
}