Round ref implementation select optimal solution (#19021)
* Round ref implementation select optimal solution * Add rounding guard - minor corrections after review.
This commit is contained in:
parent
5e1bcbdb93
commit
5b273aaba6
@ -34,9 +34,7 @@ public:
|
||||
|
||||
std::shared_ptr<Node> clone_with_new_inputs(const OutputVector& new_args) const override;
|
||||
|
||||
OPENVINO_SUPPRESS_DEPRECATED_START
|
||||
bool evaluate(const HostTensorVector& outputs, const HostTensorVector& inputs) const override;
|
||||
OPENVINO_SUPPRESS_DEPRECATED_END
|
||||
bool evaluate(TensorVector& outputs, const TensorVector& inputs) const override;
|
||||
bool has_evaluate() const override;
|
||||
|
||||
RoundMode get_mode() const {
|
||||
|
@ -1,35 +0,0 @@
|
||||
// Copyright (C) 2018-2023 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace ngraph {
|
||||
namespace runtime {
|
||||
namespace reference {
|
||||
template <typename T>
|
||||
T round_to_nearest_even(const T arg) {
|
||||
const auto floor_arg = std::floor(arg);
|
||||
const auto diff = arg - floor_arg;
|
||||
if (diff < 0.5f || (diff == 0.5f && static_cast<int>(floor_arg) % 2 == 0)) {
|
||||
return floor_arg;
|
||||
} else {
|
||||
return floor_arg + 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void round(const T* arg, T* out, size_t count, const op::v5::Round::RoundMode mode) {
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
if (mode == op::v5::Round::RoundMode::HALF_TO_EVEN) {
|
||||
out[i] = round_to_nearest_even(arg[i]);
|
||||
} else {
|
||||
out[i] = std::round(arg[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace reference
|
||||
} // namespace runtime
|
||||
} // namespace ngraph
|
80
src/core/reference/include/openvino/reference/round.hpp
Normal file
80
src/core/reference/include/openvino/reference/round.hpp
Normal file
@ -0,0 +1,80 @@
|
||||
// Copyright (C) 2018-2023 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cfenv>
|
||||
#include <cmath>
|
||||
#include <type_traits>
|
||||
|
||||
#include "openvino/op/round.hpp"
|
||||
#include "openvino/reference/round_guard.hpp"
|
||||
|
||||
namespace ov {
|
||||
template <class T>
|
||||
constexpr bool is_floating_point() {
|
||||
using U = typename std::decay<T>::type;
|
||||
return std::is_floating_point<U>::value || std::is_same<float16, U>::value || std::is_same<bfloat16, U>::value;
|
||||
}
|
||||
|
||||
namespace reference {
|
||||
/**
|
||||
* @brief Rounding algorithm for ov::op::v5::Round::RoundMode::HALF_TO_EVEN.
|
||||
*
|
||||
* @tparam T Value type.
|
||||
* @param value Value for rounding.
|
||||
* @return Rounded value.
|
||||
*/
|
||||
template <typename T>
|
||||
T round_to_nearest_even(T value) {
|
||||
return std::nearbyint(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Rounding algorithm for ov::op::v5::Round::RoundMode::HALF_AWAY_FROM_ZERO.
|
||||
*
|
||||
* @tparam T Value type.
|
||||
* @param value Value for rounding.
|
||||
* @return Rounded value.
|
||||
*/
|
||||
template <typename T>
|
||||
T round_half_away_zero(T value) {
|
||||
return std::round(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reference implementation of Round operator.
|
||||
*
|
||||
* Used when T is OpenVINO floating type.
|
||||
*
|
||||
* @param arg Input buffer pointer with data to round.
|
||||
* @param out Output buffer pointer with rounded results.
|
||||
* @param count Number of elements in input tensor.
|
||||
* @param mode Rounding mode.
|
||||
*/
|
||||
template <typename T, typename std::enable_if<ov::is_floating_point<T>()>::type* = nullptr>
|
||||
void round(const T* arg, T* out, const size_t count, const op::v5::Round::RoundMode mode) {
|
||||
const ov::RoundGuard round_g{FE_TONEAREST};
|
||||
const auto round_algo =
|
||||
(mode == op::v5::Round::RoundMode::HALF_TO_EVEN) ? round_to_nearest_even<T> : round_half_away_zero<T>;
|
||||
|
||||
std::transform(arg, arg + count, out, round_algo);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reference implementation of Round operator.
|
||||
*
|
||||
* Used when T is OpenVINO integral type.
|
||||
*
|
||||
* @param arg Input buffer pointer with data to round.
|
||||
* @param out Output buffer pointer with rounded results.
|
||||
* @param count Number of elements in input tensor.
|
||||
* @param mode Rounding mode.
|
||||
*/
|
||||
template <typename T, typename std::enable_if<!ov::is_floating_point<T>()>::type* = nullptr>
|
||||
void round(const T* arg, T* out, const size_t count, const op::v5::Round::RoundMode) {
|
||||
std::copy_n(arg, count, out);
|
||||
}
|
||||
} // namespace reference
|
||||
} // namespace ov
|
@ -0,0 +1,29 @@
|
||||
// Copyright (C) 2018-2023 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cfenv>
|
||||
|
||||
namespace ov {
|
||||
|
||||
/**
|
||||
* @brief Set current round direction for scoped block.
|
||||
*
|
||||
* Round direction can be one of:
|
||||
* - FE_DOWNWARD
|
||||
* - FE_TONEAREST
|
||||
* - FE_TOWARDZERO
|
||||
* - FE_UPWARD
|
||||
* see std <cfenv> header for details.
|
||||
*/
|
||||
class RoundGuard {
|
||||
public:
|
||||
RoundGuard(int mode);
|
||||
~RoundGuard();
|
||||
|
||||
private:
|
||||
int m_prev_round_mode;
|
||||
};
|
||||
} // namespace ov
|
@ -0,0 +1,11 @@
|
||||
#include "openvino/reference/round_guard.hpp"
|
||||
|
||||
namespace ov {
|
||||
RoundGuard::RoundGuard(int mode) : m_prev_round_mode{std::fegetround()} {
|
||||
std::fesetround(mode);
|
||||
}
|
||||
|
||||
RoundGuard::~RoundGuard() {
|
||||
std::fesetround(m_prev_round_mode);
|
||||
}
|
||||
} // namespace ov
|
@ -2,127 +2,90 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include "ngraph/op/round.hpp"
|
||||
#include "openvino/op/round.hpp"
|
||||
|
||||
#include "element_visitor.hpp"
|
||||
#include "itt.hpp"
|
||||
#include "ngraph/attribute_visitor.hpp"
|
||||
#include "ngraph/op/util/eval_copy.hpp"
|
||||
#include "ngraph/runtime/host_tensor.hpp"
|
||||
#include "ngraph/runtime/reference/copy.hpp"
|
||||
#include "ngraph/runtime/reference/round.hpp"
|
||||
#include "openvino/core/attribute_visitor.hpp"
|
||||
#include "openvino/core/type/element_type_traits.hpp"
|
||||
#include "openvino/reference/round.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace ngraph;
|
||||
|
||||
OPENVINO_SUPPRESS_DEPRECATED_START
|
||||
namespace roundop {
|
||||
namespace ov {
|
||||
namespace op {
|
||||
namespace round {
|
||||
|
||||
class Evaluate : public ov::element::NoAction<bool> {
|
||||
template <element::Type_t ET>
|
||||
static constexpr bool is_floating() {
|
||||
return (ET == element::f16) || (ET == element::f32) || (ET == element::bf16);
|
||||
}
|
||||
|
||||
public:
|
||||
using ov::element::NoAction<bool>::visit;
|
||||
template <element::Type_t ET, typename std::enable_if<!is_floating<ET>()>::type* = nullptr>
|
||||
static result_type visit(const HostTensorPtr& arg0,
|
||||
const HostTensorPtr& out,
|
||||
const size_t count,
|
||||
const op::v5::Round::RoundMode) {
|
||||
memcpy(out->get_data_ptr(), arg0->get_data_ptr(), out->get_size_in_bytes());
|
||||
return true;
|
||||
}
|
||||
|
||||
template <element::Type_t ET, typename std::enable_if<is_floating<ET>()>::type* = nullptr>
|
||||
static result_type visit(const HostTensorPtr& arg0,
|
||||
const HostTensorPtr& out,
|
||||
const size_t count,
|
||||
const op::v5::Round::RoundMode mode) {
|
||||
ngraph::runtime::reference::round(arg0->get_data_ptr<ET>(), out->get_data_ptr<ET>(), count, mode);
|
||||
template <element::Type_t ET, class TMode>
|
||||
static result_type visit(const Tensor& arg0, Tensor& out, const size_t count, TMode&& mode) {
|
||||
using T = typename element_type_traits<ET>::value_type;
|
||||
reference::round(arg0.data<T>(), out.data<T>(), count, std::forward<TMode>(mode));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
} // namespace round
|
||||
|
||||
namespace {
|
||||
bool evaluate_round(const HostTensorPtr& arg0,
|
||||
const HostTensorPtr& out,
|
||||
const size_t count,
|
||||
const op::v5::Round::RoundMode mode) {
|
||||
out->set_unary(arg0);
|
||||
|
||||
using namespace ov::element;
|
||||
return IfTypeOf<boolean, i8, i16, i32, i64, u8, u16, u32, u64, f16, f32, bf16>::apply<Evaluate>(
|
||||
arg0->get_element_type(),
|
||||
arg0,
|
||||
out,
|
||||
count,
|
||||
mode);
|
||||
}
|
||||
} // namespace
|
||||
} // namespace roundop
|
||||
|
||||
op::v5::Round::Round(const Output<Node>& arg, RoundMode mode) : util::UnaryElementwiseArithmetic(arg), m_mode(mode) {
|
||||
namespace v5 {
|
||||
Round::Round(const Output<Node>& arg, RoundMode mode) : util::UnaryElementwiseArithmetic(arg), m_mode(mode) {
|
||||
constructor_validate_and_infer_types();
|
||||
}
|
||||
|
||||
bool ngraph::op::v5::Round::visit_attributes(AttributeVisitor& visitor) {
|
||||
bool Round::visit_attributes(AttributeVisitor& visitor) {
|
||||
OV_OP_SCOPE(v5_Round_visit_attributes);
|
||||
visitor.on_attribute("mode", m_mode);
|
||||
return true;
|
||||
}
|
||||
|
||||
void op::v5::Round::validate_and_infer_types() {
|
||||
void Round::validate_and_infer_types() {
|
||||
OV_OP_SCOPE(v5_Round_validate_and_infer_types);
|
||||
NODE_VALIDATION_CHECK(this, get_input_size() == 1, "Only accepts one argument. Got: ", get_input_size());
|
||||
set_output_size(1);
|
||||
set_output_type(0, get_input_element_type(0), get_input_partial_shape(0));
|
||||
}
|
||||
|
||||
shared_ptr<Node> op::v5::Round::clone_with_new_inputs(const OutputVector& new_args) const {
|
||||
shared_ptr<Node> Round::clone_with_new_inputs(const OutputVector& new_args) const {
|
||||
OV_OP_SCOPE(v5_Round_clone_with_new_inputs);
|
||||
check_new_args_count(this, new_args);
|
||||
return make_shared<v5::Round>(new_args.at(0), m_mode);
|
||||
return make_shared<Round>(new_args.at(0), m_mode);
|
||||
}
|
||||
|
||||
bool op::v5::Round::evaluate(const HostTensorVector& outputs, const HostTensorVector& inputs) const {
|
||||
bool Round::evaluate(TensorVector& outputs, const TensorVector& inputs) const {
|
||||
OV_OP_SCOPE(v5_Round_evaluate);
|
||||
return roundop::evaluate_round(inputs[0], outputs[0], shape_size(inputs[0]->get_shape()), get_mode());
|
||||
auto& arg0 = inputs.front();
|
||||
auto& out = outputs.front();
|
||||
|
||||
using namespace ov::element;
|
||||
return IfTypeOf<boolean, i8, i16, i32, i64, u8, u16, u32, u64, bf16, f16, f32>::apply<round::Evaluate>(
|
||||
arg0.get_element_type(),
|
||||
arg0,
|
||||
out,
|
||||
shape_size(arg0.get_shape()),
|
||||
get_mode());
|
||||
}
|
||||
|
||||
bool op::v5::Round::has_evaluate() const {
|
||||
bool Round::has_evaluate() const {
|
||||
OV_OP_SCOPE(v5_Round_has_evaluate);
|
||||
switch (get_input_element_type(0)) {
|
||||
case ngraph::element::boolean:
|
||||
case ngraph::element::i8:
|
||||
case ngraph::element::i16:
|
||||
case ngraph::element::i32:
|
||||
case ngraph::element::i64:
|
||||
case ngraph::element::u8:
|
||||
case ngraph::element::u16:
|
||||
case ngraph::element::u32:
|
||||
case ngraph::element::u64:
|
||||
case ngraph::element::f16:
|
||||
case ngraph::element::f32:
|
||||
case ngraph::element::bf16:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const auto& et = get_input_element_type(0);
|
||||
|
||||
std::ostream& ov::operator<<(std::ostream& s, const op::v5::Round::RoundMode& type) {
|
||||
return et.is_static() && (et != element::f64) && (et.is_real() || et.is_integral());
|
||||
}
|
||||
} // namespace v5
|
||||
} // namespace op
|
||||
|
||||
std::ostream& operator<<(std::ostream& s, const op::v5::Round::RoundMode& type) {
|
||||
return s << as_string(type);
|
||||
}
|
||||
|
||||
namespace ov {
|
||||
template <>
|
||||
NGRAPH_API EnumNames<ngraph::op::v5::Round::RoundMode>& EnumNames<ngraph::op::v5::Round::RoundMode>::get() {
|
||||
static auto enum_names = EnumNames<ngraph::op::v5::Round::RoundMode>(
|
||||
"op::v5::Round::RoundMode",
|
||||
{{"half_to_even", ngraph::op::v5::Round::RoundMode::HALF_TO_EVEN},
|
||||
{"half_away_from_zero", ngraph::op::v5::Round::RoundMode::HALF_AWAY_FROM_ZERO}});
|
||||
OPENVINO_API EnumNames<op::v5::Round::RoundMode>& EnumNames<op::v5::Round::RoundMode>::get() {
|
||||
static auto enum_names =
|
||||
EnumNames<op::v5::Round::RoundMode>("op::v5::Round::RoundMode",
|
||||
{{"half_to_even", op::v5::Round::RoundMode::HALF_TO_EVEN},
|
||||
{"half_away_from_zero", op::v5::Round::RoundMode::HALF_AWAY_FROM_ZERO}});
|
||||
return enum_names;
|
||||
}
|
||||
} // namespace ov
|
||||
|
Loading…
Reference in New Issue
Block a user