Skip to content

Commit

Permalink
[core] Use custom functions in reference binary ops impl (#24444)
Browse files Browse the repository at this point in the history
### Details:
- Custom function used in binary element wise operators to reduce bin
size (around 130k).

### Tickets:
 - [CVS-138266](https://jira.devtools.intel.com/browse/CVS-138266)
  • Loading branch information
praasz committed May 16, 2024
1 parent 8d6bb92 commit 74829b1
Show file tree
Hide file tree
Showing 11 changed files with 131 additions and 176 deletions.
11 changes: 9 additions & 2 deletions src/core/reference/include/openvino/reference/add.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,17 @@

namespace ov {
namespace reference {
namespace func {
// Usage of custom function instead of std::plus gives smaller binary size.
template <class T>
constexpr T add(const T a, const T b) {
return a + b;
}
} // namespace func

template <class T>
void add(const T* arg0, const T* arg1, T* out, const size_t count) {
std::transform(arg0, std::next(arg0, count), arg1, out, std::plus<T>());
std::transform(arg0, std::next(arg0, count), arg1, out, func::add<T>);
}

/**
Expand All @@ -34,7 +41,7 @@ void add(const T* arg0,
const Shape& arg0_shape,
const Shape& arg1_shape,
const op::AutoBroadcastSpec& broadcast_spec) {
autobroadcast_binop(arg0, arg1, out, arg0_shape, arg1_shape, broadcast_spec, std::plus<T>());
autobroadcast_binop(arg0, arg1, out, arg0_shape, arg1_shape, broadcast_spec, func::add<T>);
}
} // namespace reference
} // namespace ov
12 changes: 10 additions & 2 deletions src/core/reference/include/openvino/reference/and.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,17 @@

namespace ov {
namespace reference {
namespace func {
// Usage of custom function instead of std::logical_and gives smaller binary size.
template <class T>
constexpr T logical_and(const T a, const T b) {
return static_cast<bool>(a) && static_cast<bool>(b);
}
} // namespace func

template <class T>
void logical_and(const T* arg0, const T* arg1, T* out, size_t count) {
std::transform(arg0, std::next(arg0, count), arg1, out, std::logical_and<T>());
std::transform(arg0, std::next(arg0, count), arg1, out, func::logical_and<T>);
}

/**
Expand All @@ -34,7 +42,7 @@ void logical_and(const T* arg0,
const Shape& arg0_shape,
const Shape& arg1_shape,
const op::AutoBroadcastSpec& broadcast_spec) {
autobroadcast_binop(arg0, arg1, out, arg0_shape, arg1_shape, broadcast_spec, std::logical_and<T>());
autobroadcast_binop(arg0, arg1, out, arg0_shape, arg1_shape, broadcast_spec, func::logical_and<T>);
}
} // namespace reference
} // namespace ov
35 changes: 15 additions & 20 deletions src/core/reference/include/openvino/reference/bitwise_and.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,24 @@
#include <algorithm>
#include <cstddef>

#include "openvino/reference/and.hpp"
#include "openvino/reference/autobroadcast_binop.hpp"

namespace ov {
namespace reference {
/**
* @brief Reference implementation of binary elementwise bitwise AND operator.
*
* @param arg0 Pointer to input 0 data.
* @param arg1 Pointer to input 1 data.
* @param out Pointer to output data.
* @param arg_shape0 Input 0 shape.
* @param arg_shape1 Input 1 shape.
* @param broadcast_spec Broadcast specification mode.
*/
template <class T, typename std::enable_if<std::is_same<typename std::decay<T>::type, char>::value>::type* = nullptr>
namespace func {
// Check for char datatype used by ov::element::boolean
void bitwise_and(const T* arg0,
const T* arg1,
T* out,
const Shape& arg0_shape,
const Shape& arg1_shape,
const op::AutoBroadcastSpec& broadcast_spec) {
autobroadcast_binop(arg0, arg1, out, arg0_shape, arg1_shape, broadcast_spec, std::bit_and<bool>());
template <class T, typename std::enable_if<std::is_same<typename std::decay<T>::type, char>::value>::type* = nullptr>
constexpr T bit_and(const T a, const T b) {
return logical_and(a, b);
}

template <class T, typename std::enable_if<!std::is_same<typename std::decay<T>::type, char>::value>::type* = nullptr>
constexpr T bit_and(const T a, const T b) {
return a & b;
}
} // namespace func

/**
* @brief Reference implementation of binary elementwise bitwise AND operator.
*
Expand All @@ -41,14 +35,15 @@ void bitwise_and(const T* arg0,
* @param arg_shape1 Input 1 shape.
* @param broadcast_spec Broadcast specification mode.
*/
template <class T, typename std::enable_if<!std::is_same<typename std::decay<T>::type, char>::value>::type* = nullptr>
template <class T>
// Check for char datatype used by ov::element::boolean
void bitwise_and(const T* arg0,
const T* arg1,
T* out,
const Shape& arg0_shape,
const Shape& arg1_shape,
const op::AutoBroadcastSpec& broadcast_spec) {
autobroadcast_binop(arg0, arg1, out, arg0_shape, arg1_shape, broadcast_spec, std::bit_and<T>());
autobroadcast_binop(arg0, arg1, out, arg0_shape, arg1_shape, broadcast_spec, func::bit_and<T>);
}
} // namespace reference
} // namespace ov
34 changes: 14 additions & 20 deletions src/core/reference/include/openvino/reference/bitwise_or.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,23 @@
#include <cstddef>

#include "openvino/reference/autobroadcast_binop.hpp"
#include "openvino/reference/or.hpp"

namespace ov {
namespace reference {
/**
* @brief Reference implementation of binary elementwise bitwise OR operator.
*
* @param arg0 Pointer to input 0 data.
* @param arg1 Pointer to input 1 data.
* @param out Pointer to output data.
* @param arg_shape0 Input 0 shape.
* @param arg_shape1 Input 1 shape.
* @param broadcast_spec Broadcast specification mode.
*/
template <class T, typename std::enable_if<std::is_same<typename std::decay<T>::type, char>::value>::type* = nullptr>
namespace func {
// Check for char datatype used by ov::element::boolean
void bitwise_or(const T* arg0,
const T* arg1,
T* out,
const Shape& arg0_shape,
const Shape& arg1_shape,
const op::AutoBroadcastSpec& broadcast_spec) {
autobroadcast_binop(arg0, arg1, out, arg0_shape, arg1_shape, broadcast_spec, std::bit_or<bool>());
template <class T, typename std::enable_if<std::is_same<typename std::decay<T>::type, char>::value>::type* = nullptr>
constexpr T bit_or(const T a, const T b) {
return logical_or(a, b);
}

template <class T, typename std::enable_if<!std::is_same<typename std::decay<T>::type, char>::value>::type* = nullptr>
constexpr T bit_or(const T a, const T b) {
return a | b;
}
} // namespace func

/**
* @brief Reference implementation of binary elementwise bitwise OR operator.
*
Expand All @@ -41,14 +35,14 @@ void bitwise_or(const T* arg0,
* @param arg_shape1 Input 1 shape.
* @param broadcast_spec Broadcast specification mode.
*/
template <class T, typename std::enable_if<!std::is_same<typename std::decay<T>::type, char>::value>::type* = nullptr>
template <class T>
void bitwise_or(const T* arg0,
const T* arg1,
T* out,
const Shape& arg0_shape,
const Shape& arg1_shape,
const op::AutoBroadcastSpec& broadcast_spec) {
autobroadcast_binop(arg0, arg1, out, arg0_shape, arg1_shape, broadcast_spec, std::bit_or<T>());
autobroadcast_binop(arg0, arg1, out, arg0_shape, arg1_shape, broadcast_spec, func::bit_or<T>);
}
} // namespace reference
} // namespace ov
35 changes: 15 additions & 20 deletions src/core/reference/include/openvino/reference/bitwise_xor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,23 @@
#include <cstddef>

#include "openvino/reference/autobroadcast_binop.hpp"
#include "openvino/reference/xor.hpp"

namespace ov {
namespace reference {
/**
* @brief Reference implementation of binary elementwise bitwise XOR operator.
*
* @param arg0 Pointer to input 0 data.
* @param arg1 Pointer to input 1 data.
* @param out Pointer to output data.
* @param arg_shape0 Input 0 shape.
* @param arg_shape1 Input 1 shape.
* @param broadcast_spec Broadcast specification mode.
*/
template <class T, typename std::enable_if<std::is_same<typename std::decay<T>::type, char>::value>::type* = nullptr>
namespace func {
// Check for char datatype used by ov::element::boolean
void bitwise_xor(const T* arg0,
const T* arg1,
T* out,
const Shape& arg0_shape,
const Shape& arg1_shape,
const op::AutoBroadcastSpec& broadcast_spec) {
autobroadcast_binop(arg0, arg1, out, arg0_shape, arg1_shape, broadcast_spec, std::bit_xor<bool>());
template <class T, typename std::enable_if<std::is_same<typename std::decay<T>::type, char>::value>::type* = nullptr>
constexpr T bit_xor(const T a, const T b) {
return logical_xor(a, b);
}

template <class T, typename std::enable_if<!std::is_same<typename std::decay<T>::type, char>::value>::type* = nullptr>
constexpr T bit_xor(const T a, const T b) {
return a ^ b;
}
} // namespace func

/**
* @brief Reference implementation of binary elementwise bitwise XOR operator.
*
Expand All @@ -41,14 +35,15 @@ void bitwise_xor(const T* arg0,
* @param arg_shape1 Input 1 shape.
* @param broadcast_spec Broadcast specification mode.
*/
template <class T, typename std::enable_if<!std::is_same<typename std::decay<T>::type, char>::value>::type* = nullptr>
template <class T>
// Check for char datatype used by ov::element::boolean
void bitwise_xor(const T* arg0,
const T* arg1,
T* out,
const Shape& arg0_shape,
const Shape& arg1_shape,
const op::AutoBroadcastSpec& broadcast_spec) {
autobroadcast_binop(arg0, arg1, out, arg0_shape, arg1_shape, broadcast_spec, std::bit_xor<T>());
autobroadcast_binop(arg0, arg1, out, arg0_shape, arg1_shape, broadcast_spec, func::bit_xor<T>);
}
} // namespace reference
} // namespace ov
89 changes: 30 additions & 59 deletions src/core/reference/include/openvino/reference/divide.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,38 +16,33 @@

namespace ov {
namespace reference {
namespace func {

template <class T>
constexpr T div(const T x, const T y) {
return x / y;
}

// NOTE: Execution throws `std::domain_error` if either a non-integral value or an
// out-of-bounds value is detected in the input tensor.
template <class T>
T try_div(const T x, const T y) {
if (y == 0) {
throw std::domain_error("integer division by zero");
}
return div(x, y);
}

// In English: return type is void and T must be an integral type.
template <typename T>
typename std::enable_if<std::is_integral<T>::value>::type divide(const T* arg0,
const T* arg1,
T* out,
size_t count,
bool pythondiv) {
if (pythondiv) {
for (size_t i = 0; i < count; i++) {
if (arg1[i] == 0) {
throw std::domain_error("integer division by zero");
}
T quot = arg0[i] / arg1[i];
T rem = arg0[i] % arg1[i];
if ((rem != 0) && ((arg0[i] < 0) != (arg1[i] < 0))) {
out[i] = quot - 1;
} else {
out[i] = quot;
}
}
} else {
for (size_t i = 0; i < count; i++) {
if (arg1[i] == 0) {
throw std::domain_error("integer division by zero");
}
out[i] = arg0[i] / arg1[i];
}
template <class T>
T try_python_div(const T x, const T y) {
if (y == 0) {
throw std::domain_error("integer division by zero");
}

T quot = div(x, y);
return (((x < 0) != (y < 0)) && (x % y != 0)) ? quot - T{1} : quot;
}
} // namespace func

template <typename T>
typename std::enable_if<std::is_integral<T>::value>::type divide(const T* arg0,
Expand All @@ -57,40 +52,19 @@ typename std::enable_if<std::is_integral<T>::value>::type divide(const T* arg0,
const Shape& arg1_shape,
const op::AutoBroadcastSpec& broadcast_spec,
bool pythondiv) {
auto functor = [pythondiv](T x, T y) -> T {
if (pythondiv) {
if (y == 0) {
throw std::domain_error("integer division by zero");
}
T quot = x / y;
T rem = x % y;
if ((rem != 0) && ((x < 0) != (y < 0))) {
return quot - 1;
} else {
return quot;
}
} else {
if (y == 0) {
throw std::domain_error("integer division by zero");
}
return x / y;
}
};
autobroadcast_binop(arg0, arg1, out, arg0_shape, arg1_shape, broadcast_spec, functor);
auto div = pythondiv ? func::try_python_div<T> : func::try_div<T>;
autobroadcast_binop(arg0, arg1, out, arg0_shape, arg1_shape, broadcast_spec, div);
}

// In English: return type is void and T must be a standard floating point type, or
// bfloat16, or float16.
template <typename T>
typename std::enable_if<std::is_floating_point<T>::value || std::is_same<T, bfloat16>::value ||
std::is_same<T, float16>::value>::type
divide(const T* arg0, const T* arg1, T* out, size_t count, bool pythondiv) {
(void)pythondiv;
for (size_t i = 0; i < count; i++) {
// TODO: Here we do not check for div by zero, so we'll get +-inf here
// if arg1[i] == 0. Is that the right thing to do? Jury's still out.
out[i] = arg0[i] / arg1[i];
}
divide(const T* arg0, const T* arg1, T* out, size_t count, bool) {
// TODO: Here we do not check for div by zero, so we'll get +-inf here
// if arg1[i] == 0. Is that the right thing to do? Jury's still out.
std::transform(arg0, arg0 + count, arg1, out, func::div<T>);
}

template <typename T>
Expand All @@ -102,11 +76,8 @@ divide(const T* arg0,
const Shape& arg0_shape,
const Shape& arg1_shape,
const op::AutoBroadcastSpec& broadcast_spec,
bool pythondiv) {
(void)pythondiv;
autobroadcast_binop(arg0, arg1, out, arg0_shape, arg1_shape, broadcast_spec, [](T x, T y) -> T {
return x / y;
});
bool) {
autobroadcast_binop(arg0, arg1, out, arg0_shape, arg1_shape, broadcast_spec, func::div<T>);
}
} // namespace reference
} // namespace ov
11 changes: 9 additions & 2 deletions src/core/reference/include/openvino/reference/or.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,17 @@

namespace ov {
namespace reference {
namespace func {
// Usage of custom function instead of std::logical_or gives smaller binary size.
template <class T>
constexpr T logical_or(const T a, const T b) {
return static_cast<bool>(a) || static_cast<bool>(b);
}
} // namespace func

template <class T>
void logical_or(const T* arg0, const T* arg1, T* out, const size_t count) {
std::transform(arg0, std::next(arg0, count), arg1, out, std::logical_or<T>());
std::transform(arg0, std::next(arg0, count), arg1, out, func::logical_or<T>);
}

/**
Expand All @@ -35,7 +42,7 @@ void logical_or(const T* arg0,
const Shape& arg0_shape,
const Shape& arg1_shape,
const op::AutoBroadcastSpec& broadcast_spec) {
autobroadcast_binop(arg0, arg1, out, arg0_shape, arg1_shape, broadcast_spec, std::logical_or<T>());
autobroadcast_binop(arg0, arg1, out, arg0_shape, arg1_shape, broadcast_spec, func::logical_or<T>);
}
} // namespace reference
} // namespace ov

0 comments on commit 74829b1

Please sign in to comment.