// SPDX-FileCopyrightText: Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. // SPDX-License-Identifier: BSD-3 #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if TEST_HALF_T() // Half support is provided by SM53+. We currently test against a few older architectures. // The specializations below can be removed once we drop these architectures. template <> _CCCL_HOST_DEVICE_API inline __half cuda::minimum::operator()<__half, __half>(const __half& a, const __half& b) const { # if defined(__CUDA_NO_HALF_OPERATORS__) return ::cuda::std::min(__half2float(a), __half2float(b)); # else // ^^^ __CUDA_NO_HALF_OPERATORS__ ^^^ / vvv !__CUDA_NO_HALF_OPERATORS__ vvv NV_IF_ELSE_TARGET( NV_PROVIDES_SM_53, (return ::cuda::std::min(a, b);), (return ::cuda::std::min(__half2float(a), __half2float(b));)); # endif // !__CUDA_NO_HALF_OPERATORS__ } template <> _CCCL_HOST_DEVICE_API inline __half cuda::maximum::operator()<__half, __half>(const __half& a, const __half& b) const { # if defined(__CUDA_NO_HALF_OPERATORS__) return ::cuda::std::max(__half2float(a), __half2float(b)); # else // ^^^ __CUDA_NO_HALF_OPERATORS__ ^^^ / vvv !__CUDA_NO_HALF_OPERATORS__ vvv NV_IF_ELSE_TARGET( NV_PROVIDES_SM_53, (return ::cuda::std::max(a, b);), (return ::cuda::std::max(__half2float(a), __half2float(b));)); # endif // !__CUDA_NO_HALF_OPERATORS__ } CUB_NAMESPACE_BEGIN template <> __host__ __device__ __forceinline__ // KeyValuePair ArgMin::operator()(const KeyValuePair& a, const KeyValuePair& b) const { const float av = __half2float(a.value); const float bv = __half2float(b.value); if ((bv < av) || ((av == bv) && (b.key < a.key))) { return b; } return a; } template <> __host__ __device__ __forceinline__ // KeyValuePair ArgMax::operator()(const KeyValuePair& a, const KeyValuePair& b) const { const float av = __half2float(a.value); const float bv = __half2float(b.value); if ((bv > av) || ((av == bv) && (b.key < a.key))) { return b; } return a; } CUB_NAMESPACE_END #endif // TEST_HALF_T() // Comparing results computed on CPU and GPU for extended floating point types is impossible. // For instance, when used with a constant iterator of two, the accumulator in sequential reference // computation (CPU) bumps into the 4096 limits, which will never change (`4096 + 2 = 4096`). // Meanwhile, per-thread aggregates (`2 * 16 = 32`) are accumulated within and among thread blocks, // yielding `inf` as a result. No reasonable epsilon can be selected to compare `inf` with `4096`. // To make `__half` and `__nv_bfloat16` arithmetic associative, the function object below raises // extended floating points to the area of unsigned short integers. This allows us to test large // inputs with few code-path differences in device algorithms. struct ExtendedFloatSum { template __host__ __device__ T operator()(T a, T b) const { T result{}; result.__x = a.raw() + b.raw(); return result; } #if TEST_HALF_T() __host__ __device__ __half operator()(__half a, __half b) const { uint16_t result = this->operator()(half_t{a}, half_t(b)).raw(); return reinterpret_cast<__half&>(result); } #endif // TEST_HALF_T() #if TEST_BF_T() __device__ __nv_bfloat16 operator()(__nv_bfloat16 a, __nv_bfloat16 b) const { uint16_t result = this->operator()(bfloat16_t{a}, bfloat16_t(b)).raw(); return reinterpret_cast<__nv_bfloat16&>(result); } #endif // TEST_BF_T() }; template inline It unwrap_it(It it) { return it; } #if TEST_HALF_T() inline __half* unwrap_it(half_t* it) { return reinterpret_cast<__half*>(it); } template inline cuda::constant_iterator<__half, OffsetT> unwrap_it(cuda::constant_iterator it) { half_t wrapped_val = *it; __half val = wrapped_val.operator __half(); return cuda::constant_iterator<__half, OffsetT>(val); } #endif // TEST_HALF_T() #if TEST_BF_T() inline __nv_bfloat16* unwrap_it(bfloat16_t* it) { return reinterpret_cast<__nv_bfloat16*>(it); } template cuda::constant_iterator<__nv_bfloat16, OffsetT> inline unwrap_it(cuda::constant_iterator it) { bfloat16_t wrapped_val = *it; __nv_bfloat16 val = wrapped_val.operator __nv_bfloat16(); return cuda::constant_iterator<__nv_bfloat16, OffsetT>(val); } #endif // TEST_BF_T() template using unwrap_value_t = std::remove_reference_t()))>; template ()))> std::integral_constant> // inline reference_extended_fp(WrappedItT) { return {}; } inline constexpr ExtendedFloatSum unwrap_op(std::true_type /* extended float */, ::cuda::std::plus<>) // { return {}; } template inline constexpr OpT unwrap_op(std::integral_constant /* base case */, OpT op) { return op; } /** * @brief Initializes the given item type with a constant non-zero value. */ template inline void init_default_constant(T& val, int element_val = 2) { val = T{static_cast(element_val)}; } template