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:
Pawel Raasz 2023-08-16 09:42:55 +02:00 committed by GitHub
parent 5e1bcbdb93
commit 5b273aaba6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 163 additions and 117 deletions

View File

@ -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 {

View File

@ -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

View 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

View File

@ -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

View File

@ -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

View File

@ -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