Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

core/src: Add half math functions to private header #6124

Merged
merged 42 commits into from
Sep 7, 2023

Conversation

e10harvey
Copy link
Contributor

@e10harvey e10harvey commented May 10, 2023

Summary of changes

  • core/src: Add half math functions to private header

This PR introduces initial support for half math functions which rely on the floating_point_wrapper explicit conversion constructors to cast half_t and bhalf_t arguments to float in order to fall back on the float implementation of math functions. If this fall back approach is found to be a bottleneck by users, we could add a follow on PR to see if invoking backend-specific implementations (where available) of half_t and bhalf_t math functions helps.

Required for kokkos/kokkos-kernels#1774.

@e10harvey e10harvey marked this pull request as draft May 10, 2023 19:18
@crtrott
Copy link
Member

crtrott commented May 15, 2023

Looks like this needs a rebase? After that I assume its ready for review? We probably should decide which order the two half moving things should come in.

@e10harvey e10harvey marked this pull request as ready for review May 17, 2023 14:12
Copy link
Member

@dalg24 dalg24 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide a proper description.

Make sure you discuss why we cast to float and do not bother using native half precision intrinsics when available.

@e10harvey e10harvey requested a review from dalg24 May 17, 2023 16:57
@e10harvey
Copy link
Contributor Author

The CI failures appear to be unrelated to these changes.

@e10harvey
Copy link
Contributor Author

@dalg24, @crtrott, @lucbv: Ready to merge?

@dalg24
Copy link
Member

dalg24 commented May 22, 2023

@dalg24, @crtrott, @lucbv: Ready to merge?

Not reviewed so no not ready to merge. You can try to poll for reviews on nucleus.

@crtrott
Copy link
Member

crtrott commented May 22, 2023

Do we have no tests for this?

Copy link
Member

@crtrott crtrott left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need some nesting. Can we reuse the existing one?

#include <Kokkos_MathematicalFunctions.hpp> // For the float overloads
namespace Kokkos {
#if defined(KOKKOS_HALF_T_IS_FLOAT) && !KOKKOS_HALF_T_IS_FLOAT
#if defined(KOKKOS_BHALF_T_IS_FLOAT) && !KOKKOS_BHALF_T_IS_FLOAT
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since we anyway have clang-format off here, can we indent the preprocess if clauses so its easier to see the if/else/endif structure of this nested clause?

@e10harvey
Copy link
Contributor Author

It is tested already via the float math functions. Shall I add tests for these wrappers?

@e10harvey
Copy link
Contributor Author

e10harvey commented May 22, 2023

Please provide a proper description.

Make sure you discuss why we cast to float and do not bother using native half precision intrinsics when available.

@dalg24: I made these changes in the description above; does it address your feedback?

@e10harvey
Copy link
Contributor Author

I think we need some testing. Can we reuse the existing one?

@dalg24: Is it best to add half_t and bhalf_t to the type lists for the existing math function tests?

Comment on lines 20 to 22
// clang-format off
#include <Kokkos_MathematicalFunctions.hpp> // For the float overloads
namespace Kokkos {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where are you reenabling/intending to reenable clang-format?
I would appreciate at least some separation after the header include like

Suggested change
// clang-format off
#include <Kokkos_MathematicalFunctions.hpp> // For the float overloads
namespace Kokkos {
#include <Kokkos_MathematicalFunctions.hpp> // For the float overloads
// clang-format off
namespace Kokkos {

@e10harvey
Copy link
Contributor Author

I think we need some testing. Can we reuse the existing one?

@dalg24: Is it best to add half_t and bhalf_t to the type lists for the existing math function tests?

I do not see a way to reuse the existing one as expected values are computed from std::FUNC.

@dalg24
Copy link
Member

dalg24 commented May 25, 2023

A bare minimum test would be calling a few selected math functions on the device and make sure it compiles and executes.
A more rigorous one would probably compare the result to higher precision reference converted to half types.

e10harvey and others added 2 commits July 13, 2023 13:02
Co-authored-by: Damien L-G <dalg24+github@gmail.com>
Co-authored-by: Damien L-G <dalg24+github@gmail.com>
@masterleinad
Copy link
Contributor

You will need to fix the indentation.

Copy link
Contributor

@masterleinad masterleinad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assuming this is passing testing.

Copy link
Member

@dalg24 dalg24 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I realize there is virtually no coverage for this with the fundamental types either but where do we define the promotion rules when mixing half types with other fundamental types in binary functions?

@@ -53,6 +61,14 @@ template <class T>
using math_unary_function_return_type_t = typename math_unary_function_return_type<T>::type;
template <class, class>
struct math_binary_function_return_type;
#if defined(KOKKOS_HALF_T_IS_FLOAT) && !KOKKOS_HALF_T_IS_FLOAT
template <> struct math_binary_function_return_type<KE::half_t, KE::half_t> { using type = KE::half_t; };
template <> struct math_binary_function_return_type<int, KE::half_t> { using type = KE::half_t; };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This look fishy

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You would need to provide much more than that.
Where is it being used?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The math binary function test calls. Please add what's missing.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we mix int and half_t with the first argument as int and not the second?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I also do not follow this question.

Thank you for your detailed reviews, @dalg24 and @masterleinad.

static auto eval_std(T x) { \
if constexpr (std::is_same<T, KE::half_t>::value || \
std::is_same<T, KE::bhalf_t>::value) { \
return std::FUNC(static_cast<float>(x)); \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't quite understand why comparing them as float is the right thing to do.

@e10harvey
Copy link
Contributor Author

e10harvey commented Jul 13, 2023

I realize there is virtually no coverage for this with the fundamental types either but where do we define the promotion rules when mixing half types with other fundamental types in binary functions?

@dalg24: I don't understand your question. We always upcast for mixed precision, though. Please feel free to make any further changes you see fit to this PR.

@dalg24
Copy link
Member

dalg24 commented Jul 13, 2023

I realize there is virtually no coverage for this with the fundamental types either but where do we define the promotion rules when mixing half types with other fundamental types in binary functions?

@dalg24: I don't understand your question. We always upcast for mixed precision, though. Please feel free to make any further changes you see fit to this PR.

Is Kokkos::hypot(int, half_t) -> half_t the right return type?
Because we do hypot(int, float) -> double (that is what the standard mandates).
Also Kokkos::hypot(double, half_t) -> ???

@e10harvey
Copy link
Contributor Author

I realize there is virtually no coverage for this with the fundamental types either but where do we define the promotion rules when mixing half types with other fundamental types in binary functions?

@dalg24: I don't understand your question. We always upcast for mixed precision, though. Please feel free to make any further changes you see fit to this PR.

Is Kokkos::hypot(int, half_t) -> half_t the right return type? Because we do hypot(int, float) -> double (that is what the standard mandates). Also Kokkos::hypot(double, half_t) -> ???

I see. I would expect integral types to be cast to the floating point type, always. I would also expect to always upcast to higher-precision floating point type. In the case of Kokkos::hypot(int, half_t) -> half_t, I think it does make the most sense to cast both arguments to float and return a float.

@e10harvey
Copy link
Contributor Author

I realize there is virtually no coverage for this with the fundamental types either but where do we define the promotion rules when mixing half types with other fundamental types in binary functions?

@dalg24: I think what you're looking for here are the mixed precision operator overloads defined in Kokkos_Half_FloatingPointWrapper.hpp. IIRC, we do not support mixed precision for operators that result in a binary type.

@dalg24
Copy link
Member

dalg24 commented Jul 19, 2023

I realize there is virtually no coverage for this with the fundamental types either but where do we define the promotion rules when mixing half types with other fundamental types in binary functions?

@dalg24: I don't understand your question. We always upcast for mixed precision, though. Please feel free to make any further changes you see fit to this PR.

Is Kokkos::hypot(int, half_t) -> half_t the right return type? Because we do hypot(int, float) -> double (that is what the standard mandates). Also Kokkos::hypot(double, half_t) -> ???

I see. I would expect integral types to be cast to the floating point type, always. I would also expect to always upcast to higher-precision floating point type. In the case of Kokkos::hypot(int, half_t) -> half_t, I think it does make the most sense to cast both arguments to float and return a float.

No. We want to be aligned with standard C++ which states that integral types will be promoted to double
https://eel.is/c++draft/c.math#cmath.syn-3

@e10harvey
Copy link
Contributor Author

I don't quite understand why comparing them as float is the right thing to do.

@dalg24: Can you offer a appropriate alternative? e.g. comparing as double.

KOKKOS_IMPL_MATH_BINARY_FUNCTION_HALF_MIXED(FUNC, HALF_TYPE, long) \
KOKKOS_IMPL_MATH_BINARY_FUNCTION_HALF_MIXED(FUNC, HALF_TYPE, unsigned long) \
KOKKOS_IMPL_MATH_BINARY_FUNCTION_HALF_MIXED(FUNC, HALF_TYPE, long long) \
KOKKOS_IMPL_MATH_BINARY_FUNCTION_HALF_MIXED(FUNC, HALF_TYPE, unsigned long long)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we be also adding

  long double FUNC(long double x, HALF_TYPE y) {  \
    return Kokkos::FUNC(x, static_cast<long double>(y)); \
  } \
  long double FUNC(HALF_TYPE x, long double y) {  \
    return Kokkos::FUNC(static_cast<long double>(x), y); \
  } \

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could do this. It would require adding a cast_from_half specialization that accepts long double as well as a explicit conversion constructor to support static_cast from half to long double. Would you like me to add those changes to this PR?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We discussed this in the developer meeting today and came to a consensus. Since long double is not supported in all backend and archuitectures, we will not be adding support for this at this time.



#define KOKKOS_IMPL_MATH_UNARY_FUNCTION_HALF_TYPE(FUNC, HALF_TYPE) \
KOKKOS_INLINE_FUNCTION HALF_TYPE FUNC(HALF_TYPE x) { \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a thought, should we be specializing the function templates rather than adding new overloads?
@nliber please advise

Copy link
Contributor

@nliber nliber Sep 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think overloads are the way to go. I keep going back to the advice in P0551 Thou Shalt Not Specialize std Function Templates! (and his various talks on the subject). Fewer surprises with overloads.

Function template specializations don't participate in overload resolution; basically, they are just an implementation detail once overload resolution picks the function to call.

@dalg24 dalg24 dismissed crtrott’s stale review September 7, 2023 14:25

Changes requested were implemented

@dalg24 dalg24 merged commit 1affb05 into kokkos:develop Sep 7, 2023
28 checks passed
@e10harvey e10harvey mentioned this pull request Sep 13, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants