Files
2026-07-13 12:47:05 +08:00

2725 lines
80 KiB
C++

/*
* ******************************************************************************
* *
* *
* * This program and the accompanying materials are made available under the
* * terms of the Apache License, Version 2.0 which is available at
* * https://www.apache.org/licenses/LICENSE-2.0.
* *
* * See the NOTICE file distributed with this work for additional
* * information regarding copyright ownership.
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* * License for the specific language governing permissions and limitations
* * under the License.
* *
* * SPDX-License-Identifier: Apache-2.0
* *****************************************************************************
*/
/*
* templatemath.h
*
* Created on: Jan 1, 2016
* Author: agibsonccc
*/
#ifndef TEMPLATEMATH_H_
#define TEMPLATEMATH_H_
#include <array/DataTypeUtils.h>
#include <math/platformmath.h>
#include <system/common.h>
#include "platformmath.h"
#define BFLOAT16_MAX_VALUE 32737.
#define HALF_MAX_VALUE 65504.
#define FLOAT_MAX_VALUE 3.4028235E38
#define DOUBLE_MAX_VALUE 1.7976931348623157E308
#define SD_FLOAT_MIN_NORMAL 1.17549435e-38
#ifndef M_E
#define M_E 2.718281828459
#endif
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
namespace sd {
namespace math {
#include <type_traits>
#include <types/float16.h>
#include <types/bfloat16.h>
#include <types/type_promote.h>
/*
*
*
* SD_PROMOTE_FUNC(FUNC_NAME, BODY): This macro takes two parameters: FUNC_NAME (the name of the function to be defined) and BODY (the body of the function).
Template Function:
The macro defines a template function with three template parameters: T, U (defaulting to T), and Z (defaulting to T).
The function returns a value of type Z and takes two parameters: val1 of type T and val2 of type U.
Type Promotion:
Inside the function, a type alias calc_type is defined using promote_type3<T, U, Z>::type, which determines the promoted type among T, U, and Z.
The input values val1 and val2 are cast to calc_type.
Function Body:
The BODY parameter is evaluated to compute the result, which is then cast to type Z before being returned.
* */
// Macro to define functions with advanced type promotion and debugging
// Updated SD_PROMOTE_FUNC macro
#define SD_PROMOTE_FUNC(FUNC_NAME, BODY) \
template<typename T, typename U = T, typename Z = T, \
typename std::enable_if< \
std::is_arithmetic<T>::value && \
std::is_arithmetic<U>::value && \
std::is_arithmetic<Z>::value, int>::type = 0> \
SD_HOST_DEVICE SD_INLINE Z FUNC_NAME(T val1, U val2) { \
using calc_type = typename promote_type3<T, U, Z>::type; \
calc_type promoted_val1 = static_cast<calc_type>(val1); \
calc_type promoted_val2 = static_cast<calc_type>(val2); \
calc_type result = BODY; \
SD_PRINT_MATH_FUNC2(#FUNC_NAME, promoted_val1, promoted_val2, result,Z); \
return static_cast<Z>(result); \
}
#define SD_PROMOTE_FUNC3(FUNC_NAME, BODY) \
template<typename T, typename U = T, typename V = T, typename Z = T, \
typename std::enable_if< \
std::is_arithmetic<T>::value && \
std::is_arithmetic<U>::value && \
std::is_arithmetic<V>::value && \
std::is_arithmetic<Z>::value, int>::type = 0> \
SD_HOST_DEVICE SD_INLINE Z FUNC_NAME(T val1, U val2, V eps) { \
using calc_type = typename promote_type3<T, U, Z>::type; \
calc_type promoted_val1 = static_cast<calc_type>(val1); \
calc_type promoted_val2 = static_cast<calc_type>(val2); \
calc_type promoted_eps = static_cast<calc_type>(eps); \
calc_type result; \
if constexpr (std::is_same_v<calc_type COMMA_MATH bool> || std::is_same_v<calc_type COMMA_MATH bfloat16> || std::is_same_v<calc_type COMMA_MATH float16>) { \
bool bool_result = BODY; \
result = static_cast<calc_type>(bool_result ? 1 : 0); \
} else { \
result = BODY; \
} \
SD_PRINT_MATH_FUNC2(#FUNC_NAME, promoted_val1, promoted_val2, result,Z); \
return static_cast<Z>(result); \
}
template <typename T, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_abs(T value);
SD_PROMOTE_FUNC3(sd_eq, (sd_abs<calc_type, calc_type>(promoted_val1 - promoted_val2) <= promoted_eps))
template <typename T>
SD_HOST_DEVICE SD_INLINE void sd_swap(T& val1, T& val2);
SD_PROMOTE_FUNC(sd_max, (promoted_val1 > promoted_val2 ? promoted_val1 : promoted_val2))
SD_PROMOTE_FUNC(sd_min, (promoted_val1 < promoted_val2 ? promoted_val1 : promoted_val2))
SD_PROMOTE_FUNC(sd_add, (promoted_val1 + promoted_val2))
SD_PROMOTE_FUNC(sd_subtract, (promoted_val1 - promoted_val2))
SD_PROMOTE_FUNC(sd_multiply, (promoted_val1 * promoted_val2))
SD_PROMOTE_FUNC(sd_divide, (promoted_val1 / promoted_val2))
template <typename T, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_re(T val1, T val2);
template <typename T, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_rint(T val1);
template <typename T, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_copysign(T val1, T val2);
template <typename T, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_softplus(T val);
template <typename T>
SD_HOST_DEVICE SD_INLINE T sd_rotl(T val, T shift);
template <typename T>
SD_HOST_DEVICE SD_INLINE T sd_rotr(T val, T shift);
template <typename X, typename Y, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_dot(X* x, Y* y, int length);
template <typename T, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_ceil(T val1);
template <typename T>
SD_HOST_DEVICE SD_INLINE bool sd_isnan(T val1);
template <typename T>
SD_HOST_DEVICE SD_INLINE bool sd_isinf(T val1);
template <typename T>
SD_HOST_DEVICE SD_INLINE bool sd_isfin(T val1);
template <typename T, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_cos(T val);
template <typename T, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_cosh(T val);
template <typename X, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_exp(X val);
template <typename T, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_floor(T val);
template <typename X, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_log(X val);
template <typename X, typename Y, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_pow(X val, Y val2);
template <typename X, typename Y, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_floordiv(X val, Y val2);
template <typename T, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_round(T val);
template <typename X, typename Y, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_remainder(X num, Y denom);
template <typename X, typename Y, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_fmod(X num, Y denom);
template <typename T, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_erf(T num);
template <typename T, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_erfc(T num);
SD_HOST_DEVICE SD_INLINE int32_t floatToRawIntBits(float d) {
union {
float f;
int32_t i;
} tmp;
tmp.f = d;
return tmp.i;
}
SD_HOST_DEVICE SD_INLINE float intBitsToFloat(int32_t i) {
union {
float f;
int32_t i;
} tmp;
tmp.i = i;
return tmp.f;
}
SD_HOST_DEVICE SD_INLINE float mulsignf(float x, float y) {
return intBitsToFloat(floatToRawIntBits(x) ^ (floatToRawIntBits(y) & (1 << 31)));
}
SD_HOST_DEVICE SD_INLINE float copysignfk(float x, float y) {
return intBitsToFloat((floatToRawIntBits(x) & ~(1 << 31)) ^ (floatToRawIntBits(y) & (1 << 31)));
}
template <typename T, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_sigmoid(T val) {
Z result = (Z)1.0f / ((Z)1.0f + sd_exp<T, Z>(-val));
SD_PRINT_MATH_FUNC("sd_sigmoid", val, result,Z);
return result;
}
template <typename T, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_elu(T val, T alpha) {
Z result;
if (val >= (T)0.f)
result = static_cast<Z>(val);
else
result = static_cast<Z>(alpha) * (sd_exp<T, Z>(val) - static_cast<Z>(1.0f));
SD_PRINT_MATH_FUNC2("sd_elu", val, alpha, result,Z);
return result;
}
template <typename T, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_leakyrelu(T val, T alpha) {
Z result;
if (val < (T)0.0f)
result = static_cast<Z>(alpha * val);
else
result = static_cast<Z>(val);
SD_PRINT_MATH_FUNC2("sd_leakyrelu", val, alpha, result,Z);
return result;
}
template <typename T, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_eluderivative(T val, T alpha) {
Z result;
if (val >= static_cast<T>(0.0f))
result = static_cast<Z>(1.0f);
else
result = static_cast<Z>(alpha) * sd_exp<T, Z>(val);
SD_PRINT_MATH_FUNC2("sd_eluderivative", val, alpha, result,Z);
return result;
}
template <typename T, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_sin(T val);
template <typename T, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_sinh(T val);
template <typename T, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_softplus(T val) {
Z result = sd_log<T, Z>((Z)1.0f + sd_exp<T, Z>(val));
SD_PRINT_MATH_FUNC("sd_softplus", val, result,Z);
return result;
}
template <typename X, typename Z>
SD_HOST_DEVICE inline Z sd_floor(X val) {
Z result = static_cast<Z>(p_floor<X>(val));
SD_PRINT_MATH_FUNC("sd_floor", val, result,Z);
return result;
}
template <typename X, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_log(X val) {
Z result = static_cast<Z>(p_log<X>(val));
SD_PRINT_MATH_FUNC("sd_log", static_cast<Z>(val), static_cast<Z>(result),Z);
return result;
}
template <typename X, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_log2(X val) {
Z result = static_cast<Z>(p_log2<X>(val));
SD_PRINT_MATH_FUNC("sd_log2", static_cast<Z>(val), static_cast<Z>(result),Z);
return result;
}
template <typename T, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_softsign(T val) {
Z result = static_cast<Z>(val) / (static_cast<Z>(1.0f) + sd::math::sd_abs<T, Z>(val));
SD_PRINT_MATH_FUNC("sd_softsign", val, result,Z);
return result;
}
template <typename X, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_sqrt(X val);
template <typename X, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_tanh(X val);
template <typename T, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_tan(T val);
template <typename X, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_atan2(X val1, X val2);
template <typename X, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_atan2(X val1, X val2) {
Z result = p_atan2<Z>(static_cast<Z>(val1), static_cast<Z>(val2));
SD_PRINT_MATH_FUNC2("sd_atan2", val1, val2, result,Z);
return result;
}
template <typename T, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_tan(T value) {
Z result = p_tan<Z>(static_cast<Z>(value));
SD_PRINT_MATH_FUNC("sd_tan", value, result,Z);
return result;
}
template <typename T, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_tanhderivative(T val) {
Z tanh_val = sd_tanh<T, Z>(val);
Z result = (Z)1.0f - tanh_val * tanh_val;
SD_PRINT_MATH_FUNC("sd_tanhderivative", val, result,Z);
return result;
}
template <typename T, typename Z>
SD_HOST_DEVICE SD_INLINE T sd_sigmoidderivative(T val) {
Z sigmoid = sd_sigmoid<T, Z>(val);
T result = static_cast<T>(sigmoid * (static_cast<Z>(1.0f) - sigmoid));
SD_PRINT_MATH_FUNC("sd_sigmoidderivative", val, result,Z);
return result;
}
template <typename T, typename Z>
SD_HOST_DEVICE SD_INLINE T sd_softsignderivative(T val) {
T y = static_cast<T>(1.0f) + sd_abs<T, T>(val);
T result = static_cast<T>(static_cast<Z>(1.0f) / (y * y));
SD_PRINT_MATH_FUNC("sd_softsignderivative", val, result,Z);
return result;
}
template <typename T, typename Z>
SD_HOST_DEVICE SD_INLINE T sd_sgn(T val) {
T result = val < static_cast<T>(0.0f) ? static_cast<T>(-1.0f) : val > static_cast<T>(0.0f) ? static_cast<T>(1.0f) : static_cast<T>(0.0f);
SD_PRINT_MATH_FUNC("sd_sgn", val, result,Z);
return result;
}
template <typename T, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_sign(T val) {
Z result = sd_sgn<T, Z>(val);
SD_PRINT_MATH_FUNC("sd_sign", val, result,Z);
return result;
}
template <typename T, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_signum(T val) {
Z result = sd_sgn<T, Z>(val);
SD_PRINT_MATH_FUNC("sd_signum", val, result,Z);
return result;
}
template <typename X, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_gamma(X a);
template <typename X, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_lgamma(X x);
template <typename X, typename Y, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_dot(X* x, Y* y, int length) {
Z dot = (Z)0.0f;
for (int e = 0; e < length; e++) {
dot += static_cast<Z>(x[e]) * static_cast<Z>(y[e]);
}
SD_PRINT_MATH_FUNC("sd_dot", length, dot,Z);
return dot;
}
template <typename T, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_acos(T val);
template <typename T, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_sech(T val);
template <typename T, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_acosh(T val);
template <typename T, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_asin(T val);
template <typename T, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_asinh(T val);
template <typename T, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_asinh(T val) {
Z result = sd_log<Z, Z>(sd_sqrt<Z, Z>(sd_pow<T, T, Z>(val, (T)2) + (Z)1.f) + (Z)val);
SD_PRINT_MATH_FUNC("sd_asinh", val, result,Z);
return result;
}
template <typename T, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_atan(T val);
template <typename T, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_atanh(T val);
// ============================================================================
// CONDITIONAL SPECIALIZATIONS BASED ON HAS_* MACROS
// ============================================================================
#ifdef HAS_FLOAT16
template <>
SD_HOST_DEVICE SD_INLINE float16 sd_abs<float16, float16>(float16 value) {
#ifdef SD_NATIVE_HALFS
float16 result;
if (value < (float16)0.f) {
result = float16(__hneg(value.data));
} else {
result = value;
}
#else
float16 result = (float16)fabsf((float)value);
#endif
SD_PRINT_MATH_FUNC("sd_abs<float16>", value, result,float16);
return result;
}
#endif // HAS_FLOAT16
#ifdef HAS_BFLOAT16
template <>
SD_HOST_DEVICE SD_INLINE bfloat16 sd_abs<bfloat16, bfloat16>(bfloat16 value) {
bfloat16 result = (bfloat16)fabsf((float)value);
SD_PRINT_MATH_FUNC("sd_abs<bfloat16>", value, result,bfloat16);
return result;
}
#endif // HAS_BFLOAT16
#ifdef HAS_FLOAT32
template <>
SD_HOST_DEVICE SD_INLINE float sd_abs<float, float>(float value) {
float result = fabsf(value);
SD_PRINT_MATH_FUNC("sd_abs<float>", value, result,float);
return result;
}
#endif // HAS_FLOAT32
#ifdef HAS_DOUBLE
template <>
SD_HOST_DEVICE SD_INLINE double sd_abs<double, double>(double value) {
double result = fabs(value);
SD_PRINT_MATH_FUNC("sd_abs<double>", value, result,double);
return result;
}
#endif // HAS_DOUBLE
#ifdef HAS_INT32
template <>
SD_HOST_DEVICE SD_INLINE int sd_abs<int, int>(int value) {
int result = abs(value);
SD_PRINT_MATH_FUNC("sd_abs<int>", value, result,int);
return result;
}
#endif // HAS_INT32
#ifdef HAS_LONG
template <>
SD_HOST_DEVICE SD_INLINE sd::LongType sd_abs<sd::LongType, sd::LongType>(sd::LongType value) {
sd::LongType result = llabs(value);
SD_PRINT_MATH_FUNC("sd_abs<sd::LongType>", value, result,sd::LongType);
return result;
}
template <>
SD_HOST_DEVICE SD_INLINE long sd_abs<long, long>(long value) {
long result = labs(value);
SD_PRINT_MATH_FUNC("sd_abs<long>", value, result,long);
return result;
}
#endif // HAS_LONG
#ifdef HAS_BOOL
template <>
SD_HOST_DEVICE SD_INLINE bool sd_abs<bool>(bool value) {
SD_PRINT_MATH_FUNC("sd_abs<bool>", value, value,bool);
return value;
}
#endif // HAS_BOOL
#ifdef HAS_UINT8
template <>
SD_HOST_DEVICE SD_INLINE uint8_t sd_abs<uint8_t>(uint8_t value) {
SD_PRINT_MATH_FUNC("sd_abs<uint8_t>", value, value,uint8_t);
return value;
}
#endif // HAS_UINT8
#ifdef HAS_UINT16
template <>
SD_HOST_DEVICE SD_INLINE uint16_t sd_abs<uint16_t>(uint16_t value) {
SD_PRINT_MATH_FUNC("sd_abs<uint16_t>", value, value,uint16_t);
return value;
}
template <>
SD_HOST_DEVICE SD_INLINE unsigned short sd_abs<unsigned short, unsigned short>(unsigned short value) {
SD_PRINT_MATH_FUNC("sd_abs<unsigned short>", value, value,unsigned short);
return value;
}
#endif // HAS_UINT16
#ifdef HAS_UINT32
template <>
SD_HOST_DEVICE SD_INLINE uint32_t sd_abs<uint32_t>(uint32_t value) {
SD_PRINT_MATH_FUNC("sd_abs<uint32_t>", value, value,uint32_t);
return value;
}
template <>
SD_HOST_DEVICE SD_INLINE unsigned int sd_abs<unsigned int, unsigned int>(unsigned int value) {
SD_PRINT_MATH_FUNC("sd_abs<unsigned int>", value, value,unsigned int);
return value;
}
#endif // HAS_UINT32
#ifdef HAS_UINT64
template <>
SD_HOST_DEVICE SD_INLINE sd::UnsignedLong sd_abs<sd::UnsignedLong>(sd::UnsignedLong value) {
SD_PRINT_MATH_FUNC("sd_abs<sd::UnsignedLong>", value, value,sd::UnsignedLong);
return value;
}
#endif
#ifdef HAS_INT8
template <>
SD_HOST_DEVICE SD_INLINE int8_t sd_abs<int8_t>(int8_t value) {
int8_t result = value < 0 ? -value : value;
SD_PRINT_MATH_FUNC("sd_abs<int8_t>", value, result,int8_t);
return result;
}
template <>
SD_HOST_DEVICE SD_INLINE char sd_abs<char>(char value) {
char result = (value < 0) ? static_cast<char>(-value) : value;
SD_PRINT_MATH_FUNC("sd_abs<char>", value, result, char);
return result;
}
#endif // HAS_INT8
#ifdef HAS_INT16
template <>
SD_HOST_DEVICE SD_INLINE int16_t sd_abs<int16_t>(int16_t value) {
int16_t result = value < 0 ? -value : value;
SD_PRINT_MATH_FUNC("sd_abs<int16_t>", value, result,int16_t);
return result;
}
template <>
SD_HOST_DEVICE SD_INLINE short sd_abs<short, short>(short value) {
short result = value < 0 ? -value : value;
SD_PRINT_MATH_FUNC("sd_abs<short>", value, result,short);
return result;
}
#endif // HAS_INT16
#ifdef HAS_FLOAT16
template <>
SD_HOST_DEVICE SD_INLINE bool sd_isnan<float16>(float16 value) {
bool result = *(value.data.getXP()) == 0x7fffU;
SD_PRINT_MATH_FUNC("sd_isnan<float16>", value, result,bool);
return result;
}
#endif // HAS_FLOAT16
#ifdef HAS_BFLOAT16
template <>
SD_HOST_DEVICE SD_INLINE bool sd_isnan<bfloat16>(bfloat16 value) {
bool result = value == bfloat16::nan(); // 0x7fffU;
SD_PRINT_MATH_FUNC("sd_isnan<bfloat16>", value, result,bool);
return result;
}
#endif // HAS_BFLOAT16
#ifdef HAS_FLOAT32
template <>
SD_HOST_DEVICE SD_INLINE bool sd_isnan<float>(float value) {
bool result = value != value;
SD_PRINT_MATH_FUNC("sd_isnan<float>", value, result,bool);
return result;
}
#endif // HAS_FLOAT32
#ifdef HAS_DOUBLE
template <>
SD_HOST_DEVICE SD_INLINE bool sd_isnan<double>(double value) {
bool result = value != value;
SD_PRINT_MATH_FUNC("sd_isnan<double>", value, result,double);
return result;
}
#endif // HAS_DOUBLE
#ifdef HAS_INT32
template <>
SD_HOST_DEVICE SD_INLINE bool sd_isnan<int>(int value) {
bool result = false;
SD_PRINT_MATH_FUNC("sd_isnan<int>", value, result,int);
return result;
}
#endif // HAS_INT32
#ifdef HAS_UINT32
template <>
SD_HOST_DEVICE SD_INLINE bool sd_isnan<uint32_t>(uint32_t value) {
bool result = false;
SD_PRINT_MATH_FUNC("sd_isnan<uint32_t>", value, result,uint32_t);
return result;
}
template <>
SD_HOST_DEVICE SD_INLINE bool sd_isnan<unsigned int>(unsigned int value) {
bool result = false;
SD_PRINT_MATH_FUNC("sd_isnan<unsigned int>", value, result,unsigned int);
return result;
}
#endif // HAS_UINT32
#ifdef HAS_UINT16
template <>
SD_HOST_DEVICE SD_INLINE bool sd_isnan<uint16_t>(uint16_t value) {
bool result = false;
SD_PRINT_MATH_FUNC("sd_isnan<uint16_t>", value, result,uint16_t);
return result;
}
template <>
SD_HOST_DEVICE SD_INLINE bool sd_isnan<unsigned short>(unsigned short value) {
bool result = false;
SD_PRINT_MATH_FUNC("sd_isnan<unsigned short>", value, result,unsigned short);
return result;
}
#endif // HAS_UINT16
#ifdef HAS_UINT8
template <>
SD_HOST_DEVICE SD_INLINE bool sd_isnan<uint8_t>(uint8_t value) {
bool result = false;
SD_PRINT_MATH_FUNC("sd_isnan<uint8_t>", value, result,uint8_t);
return result;
}
#endif // HAS_UINT8
#ifdef HAS_INT16
template <>
SD_HOST_DEVICE SD_INLINE bool sd_isnan<int16_t>(int16_t value) {
bool result = false;
SD_PRINT_MATH_FUNC("sd_isnan<int16_t>", value, result,int16_t);
return result;
}
template <>
SD_HOST_DEVICE SD_INLINE bool sd_isnan<short>(short value) {
bool result = false;
SD_PRINT_MATH_FUNC("sd_isnan<short>", value, result,short);
return result;
}
#endif // HAS_INT16
#ifdef HAS_INT8
template <>
SD_HOST_DEVICE SD_INLINE bool sd_isnan<int8_t>(int8_t value) {
bool result = false;
SD_PRINT_MATH_FUNC("sd_isnan<int8_t>", value, result,int8_t);
return result;
}
template <>
SD_HOST_DEVICE SD_INLINE bool sd_isnan<char>(char value) {
bool result = false;
SD_PRINT_MATH_FUNC("sd_isnan<char>", value, result, char);
return result;
}
#endif // HAS_INT8
#ifdef HAS_BOOL
template <>
SD_HOST_DEVICE SD_INLINE bool sd_isnan<bool>(bool value) {
bool result = false;
SD_PRINT_MATH_FUNC("sd_isnan<bool>", value, result,bool);
return result;
}
#endif // HAS_BOOL
#ifdef HAS_LONG
template <>
SD_HOST_DEVICE SD_INLINE bool sd_isnan<sd::LongType>(sd::LongType value) {
bool result = false;
SD_PRINT_MATH_FUNC("sd_isnan<sd::LongType>", value, result,sd::LongType);
return result;
}
template <>
SD_HOST_DEVICE SD_INLINE bool sd_isnan<long>(long value) {
bool result = false;
SD_PRINT_MATH_FUNC("sd_isnan<long>", value, result,long);
return result;
}
#endif // HAS_LONG
#ifdef HAS_UINT64
template <>
SD_HOST_DEVICE SD_INLINE bool sd_isnan<sd::UnsignedLong>(sd::UnsignedLong value) {
bool result = false;
SD_PRINT_MATH_FUNC("sd_isnan<sd::UnsignedLong>", value, result,sd::UnsignedLong);
return result;
}
#endif // HAS_UNSIGNEDLONG
// sd_isinf specializations with HAS_* guards
#ifdef HAS_FLOAT16
template <>
SD_HOST_DEVICE SD_INLINE bool sd_isinf<float16>(float16 value) {
bool result = value < (float16)-HALF_MAX_VALUE || value > (float16)HALF_MAX_VALUE;
SD_PRINT_MATH_FUNC("sd_isinf<float16>", value, result, bool);
return result;
}
#endif // HAS_FLOAT16
#ifdef HAS_BFLOAT16
template <>
SD_HOST_DEVICE SD_INLINE bool sd_isinf<bfloat16>(bfloat16 value) {
bool result = value < (bfloat16)-BFLOAT16_MAX_VALUE || value > (bfloat16)BFLOAT16_MAX_VALUE;
SD_PRINT_MATH_FUNC("sd_isinf<bfloat16>", value, result, bool);
return result;
}
#endif // HAS_BFLOAT16
#ifdef HAS_FLOAT32
template <>
SD_HOST_DEVICE SD_INLINE bool sd_isinf<float>(float value) {
#ifdef __CUDACC__
bool result = isinf(value);
#else
bool result = std::isinf(value);
#endif
SD_PRINT_MATH_FUNC("sd_isinf<float>", value, result,float);
return result;
}
#endif // HAS_FLOAT32
#ifdef HAS_DOUBLE
template <>
SD_HOST_DEVICE SD_INLINE bool sd_isinf<double>(double value) {
#ifdef __CUDACC__
bool result = isinf(value);
#else
bool result = std::isinf(value);
#endif
SD_PRINT_MATH_FUNC("sd_isinf<double>", value, result,double);
return result;
}
#endif // HAS_DOUBLE
#ifdef HAS_INT32
template <>
SD_HOST_DEVICE SD_INLINE bool sd_isinf<int>(int value) {
bool result = false;
SD_PRINT_MATH_FUNC("sd_isinf<int>", value, result,int);
return result;
}
#endif // HAS_INT32
#ifdef HAS_UINT32
template <>
SD_HOST_DEVICE SD_INLINE bool sd_isinf<uint32_t>(uint32_t value) {
bool result = false;
SD_PRINT_MATH_FUNC("sd_isinf<uint32_t>", value, result,uint32_t);
return result;
}
template <>
SD_HOST_DEVICE SD_INLINE bool sd_isinf<unsigned int>(unsigned int value) {
bool result = false;
SD_PRINT_MATH_FUNC("sd_isinf<unsigned int>", value, result,unsigned int);
return result;
}
#endif // HAS_UINT32
#ifdef HAS_UINT16
template <>
SD_HOST_DEVICE SD_INLINE bool sd_isinf<uint16_t>(uint16_t value) {
bool result = false;
SD_PRINT_MATH_FUNC("sd_isinf<uint16_t>", value, result,uint16_t);
return result;
}
template <>
SD_HOST_DEVICE SD_INLINE bool sd_isinf<unsigned short>(unsigned short value) {
bool result = false;
SD_PRINT_MATH_FUNC("sd_isinf<unsigned short>", value, result,unsigned short);
return result;
}
#endif // HAS_UINT16
#ifdef HAS_UINT8
template <>
SD_HOST_DEVICE SD_INLINE bool sd_isinf<uint8_t>(uint8_t value) {
bool result = false;
SD_PRINT_MATH_FUNC("sd_isinf<uint8_t>", value, result,uint8_t);
return result;
}
template <>
SD_HOST_DEVICE SD_INLINE bool sd_isinf<unsigned short>(unsigned short value) {
bool result = false;
SD_PRINT_MATH_FUNC("sd_isinf<unsigned short>", value, result,unsigned short);
return result;
}
#endif // HAS_UINT8
#ifdef HAS_INT16
template <>
SD_HOST_DEVICE SD_INLINE bool sd_isinf<int16_t>(int16_t value) {
bool result = false;
SD_PRINT_MATH_FUNC("sd_isinf<int16_t>", value, result,int16_t);
return result;
}
template <>
SD_HOST_DEVICE SD_INLINE bool sd_isinf<short>(short value) {
bool result = false;
SD_PRINT_MATH_FUNC("sd_isinf<short>", value, result,short);
return result;
}
#endif // HAS_INT16
#ifdef HAS_INT8
template <>
SD_HOST_DEVICE SD_INLINE bool sd_isinf<int8_t>(int8_t value) {
bool result = false;
SD_PRINT_MATH_FUNC("sd_isinf<int8_t>", value, result,int8_t);
return result;
}
template <>
SD_HOST_DEVICE SD_INLINE bool sd_isinf<char>(char value) {
bool result = false;
SD_PRINT_MATH_FUNC("sd_isinf<char>", value, result, char);
return result;
}
#endif // HAS_INT8
#ifdef HAS_BOOL
template <>
SD_HOST_DEVICE SD_INLINE bool sd_isinf<bool>(bool value) {
bool result = false;
SD_PRINT_MATH_FUNC("sd_isinf<bool>", value, result,bool);
return result;
}
#endif // HAS_BOOL
#ifdef HAS_LONG
template <>
SD_HOST_DEVICE SD_INLINE bool sd_isinf<sd::LongType>(sd::LongType value) {
bool result = false;
SD_PRINT_MATH_FUNC("sd_isinf<sd::LongType>", value, result,sd::LongType);
return result;
}
template <>
SD_HOST_DEVICE SD_INLINE bool sd_isinf<long>(long value) {
bool result = false;
SD_PRINT_MATH_FUNC("sd_isinf<long>", value, result,long);
return result;
}
#endif // HAS_LONG
#ifdef HAS_UINT64
template <>
SD_HOST_DEVICE SD_INLINE bool sd_isinf<sd::UnsignedLong>(sd::UnsignedLong value) {
bool result = false;
SD_PRINT_MATH_FUNC("sd_isinf<sd::UnsignedLong>", value, result,sd::UnsignedLong);
return result;
}
#endif // HAS_UINT64
template <typename T>
SD_HOST_DEVICE SD_INLINE bool sd_isfin(T value) {
bool result = !sd_isnan<T>(value) && !sd_isinf<T>(value);
SD_PRINT_MATH_FUNC("sd_isfin", value, result, bool);
return result;
}
// sd_copysign specializations with HAS_* guards
#ifdef HAS_FLOAT16
template <>
SD_HOST_DEVICE SD_INLINE float16 sd_copysign<float16>(float16 val1, float16 val2) {
float16 result = (float16)copysignf((float)val1, (float)val2);
SD_PRINT_MATH_FUNC2("sd_copysign<float16>", val1, val2, result,float16);
return result;
}
#endif // HAS_FLOAT16
#ifdef HAS_FLOAT32
template <>
SD_HOST_DEVICE SD_INLINE float sd_copysign<float>(float val1, float val2) {
float result = copysignf(val1, val2);
SD_PRINT_MATH_FUNC2("sd_copysign<float>", val1, val2, result,float);
return result;
}
#endif // HAS_FLOAT32
#ifdef HAS_DOUBLE
template <>
SD_HOST_DEVICE SD_INLINE double sd_copysign<double>(double val1, double val2) {
double result = copysign(val1, val2);
SD_PRINT_MATH_FUNC2("sd_copysign<double>", val1, val2, result,double);
return result;
}
#endif // HAS_DOUBLE
#ifdef HAS_INT32
template <>
SD_HOST_DEVICE SD_INLINE int sd_copysign<int>(int val1, int val2) {
int result = (val2 < 0) ? -(sd_abs<int,int>(val1)) : sd_abs<int,int>(val1);
SD_PRINT_MATH_FUNC2("sd_copysign<int>", val1, val2, result,int);
return result;
}
#endif // HAS_INT32
#ifdef HAS_LONG
template <>
SD_HOST_DEVICE SD_INLINE sd::LongType sd_copysign<sd::LongType>(sd::LongType val1, sd::LongType val2) {
sd::LongType result = (val2 < 0) ? -(sd_abs<sd::LongType,sd::LongType>(val1)) : sd_abs<sd::LongType,sd::LongType>(val1);
SD_PRINT_MATH_FUNC2("sd_copysign<sd::LongType>", val1, val2, result,sd::LongType);
return result;
}
#endif // HAS_LONG
template <typename X, typename Y, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_igamma(X a, Y x) {
Z result;
if (a <= X(0.000001)) {
result = Z(0);
} else {
// Convert x to type X to avoid type mismatch errors when Y is bool or other incompatible type
X x_converted = static_cast<X>(x);
// Convert a to type Y for sd_gamma function call
Y a_converted = static_cast<Y>(a);
Z aim = sd_pow<X, X, Z>(x_converted, a) / (sd_exp<X, Z>(x_converted) * sd_gamma<Y, Z>(a_converted));
Z sum = Z(0.);
Z denom = Z(1.);
for (int i = 0; Z(1. / denom) > Z(1.0e-12); i++) {
denom *= static_cast<Z>(a + i);
sum += sd_pow<X, int, Z>(x_converted, i) / denom;
}
result = aim * sum;
}
SD_PRINT_MATH_FUNC2("sd_igamma", a, x, result, Z);
return result;
}
template <typename X, typename Y, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_igammac(X a, Y x) {
Z result = Z(1.) - sd_igamma<X, Y, Z>(a, x);
SD_PRINT_MATH_FUNC2("sd_igammac", a, x, result,Z);
return result;
}
/**
* This func is special case - it must return floating point value, and optionally Y arg can be floating point argument
* @tparam X
* @tparam Y
* @tparam Z
* @param val
* @param val2
* @return
*/
#ifdef HAS_FLOAT32
template <>
SD_HOST_DEVICE SD_INLINE float sd_pow(float val, float val2) {
float result = p_pow<float>(val, val2);
SD_PRINT_MATH_FUNC2("sd_pow float", val, val2, result,float);
return result;
}
#endif // HAS_FLOAT32
#ifdef HAS_BFLOAT16
template <>
SD_HOST_DEVICE SD_INLINE bfloat16 sd_pow<bfloat16, bfloat16, bfloat16>(bfloat16 val, bfloat16 val2) {
bfloat16 result = (bfloat16)p_pow<float>((float)val, (float)val2);
SD_PRINT_MATH_FUNC2("sd_pow<bfloat16>", val, val2, result, bfloat16);
return result;
}
#endif // HAS_BFLOAT16
template <typename X, typename Y, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_pow(X val, Y val2) {
Z result = p_pow<Z>(static_cast<Z>(val), static_cast<Z>(val2));
SD_PRINT_MATH_FUNC2("sd_pow", static_cast<Z>(val), static_cast<Z>(val2), result,Z);
return result;
}
template <typename X, typename Y, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_floordiv(X val, Y val2) {
Z result = static_cast<Z>(std::floor(static_cast<double>(val) / static_cast<double>(val2)));
SD_PRINT_MATH_FUNC2("sd_floordiv", val, val2, result,Z);
return result;
}
template <typename X, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_ceil(X val) {
return static_cast<Z>(p_ceil<X>(val));
}
template <typename X, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_round(X val) {
return static_cast<Z>(p_round<X>(val));
}
template <typename X, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_asin(X val) {
return p_asin<Z>(static_cast<Z>(val));
}
template <typename X, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_atan(X val) {
return p_atan<Z>(static_cast<Z>(val));
}
template <typename X, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_atanh(X val) {
return p_atanh<Z>(static_cast<Z>(val));
}
template <typename X, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_cosh(X val) {
return p_cosh<Z>(static_cast<Z>(val));
}
template <typename X, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_rint(X val) {
return p_rint<X>(val);
}
template <typename X, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_sinh(X val) {
return p_sinh<Z>(static_cast<Z>(val));
}
template <typename X, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_acos(X val) {
return p_acos<Z>(static_cast<Z>(val));
}
template <typename X, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_sech(X val) {
return static_cast<Z>(1) / sd_cosh<X, Z>(val);
}
template <typename X, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_acosh(X val) {
return p_acosh<Z>(static_cast<Z>(val));
}
template <typename X, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_cos(X val) {
return p_cos<Z>(static_cast<Z>(val));
}
template <typename X, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_exp(X val) {
return static_cast<Z>(p_exp<X>(val));
}
#ifdef HAS_BFLOAT16
template <>
SD_HOST_DEVICE SD_INLINE bfloat16 sd_exp<bfloat16, bfloat16>(bfloat16 val) {
bfloat16 result = (bfloat16)p_exp<float>((float)val);
SD_PRINT_MATH_FUNC("sd_exp<bfloat16>", val, result, bfloat16);
return result;
}
#endif // HAS_BFLOAT16
#ifdef HAS_FLOAT16
template <>
SD_HOST_DEVICE SD_INLINE float16 sd_exp<float16, float16>(float16 val) {
float16 result = (float16)p_exp<float>((float)val);
SD_PRINT_MATH_FUNC("sd_exp<bfloat16>", val, result, float16);
return result;
}
#endif // HAS_FLOAT16
// Implement sd_lgamma with print statements
template <typename X, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_lgamma(X x) {
Z result;
if (x < X(12.0)) {
result = sd_log<Z, Z>(sd_gamma<X, Z>(x));
} else {
static const double c[8] = {1.0 / 12.0, -1.0 / 360.0, 1.0 / 1260.0, -1.0 / 1680.0,
1.0 / 1188.0, -691.0 / 360360.0, 1.0 / 156.0, -3617.0 / 122400.0};
double z = Z(1.0 / Z(x * x));
double sum = c[7];
for (int i = 6; i >= 0; i--) {
sum *= z;
sum += c[i];
}
double series = sum / Z(x);
static const double halfLogTwoPi = 0.91893853320467274178032973640562;
result = Z((double(x) - 0.5) * sd_log<X, double>(x) - double(x) + halfLogTwoPi + series);
}
SD_PRINT_MATH_FUNC("sd_lgamma", x, result,Z);
return result;
}
template <typename T>
SD_HOST_DEVICE SD_INLINE T sd_re(T val1, T val2) {
T result;
if (val1 == (T)0.0f && val2 == (T)0.0f)
result = (T)0.0f;
else
result = sd_abs<T,T>(val1 - val2) / (sd_abs<T,T>(val1) + sd_abs<T,T>(val2));
SD_PRINT_MATH_FUNC2("sd_re", val1, val2, result,T);
return result;
}
template <typename X, typename Y, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_remainder(X val, Y val2) {
Z result = p_remainder<Z>(static_cast<Z>(val), static_cast<Z>(val2));
SD_PRINT_MATH_FUNC2("sd_remainder", val, val2, result,Z);
return result;
}
template <typename X, typename Y, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_fmod(X val, Y val2) {
Z result = p_fmod<Z>(static_cast<Z>(val), static_cast<Z>(val2));
SD_PRINT_MATH_FUNC2("sd_fmod", val, val2, result,Z);
return result;
}
template <typename X, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_sin(X val) {
Z result = p_sin<Z>(static_cast<Z>(val));
SD_PRINT_MATH_FUNC("sd_sin", val, result,Z);
return result;
}
template <typename X, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_sqrt(X val) {
Z result = p_sqrt<Z>(static_cast<Z>(val));
SD_PRINT_MATH_FUNC("sd_sqrt", static_cast<Z>(val), result,Z);
return result;
}
template <typename X>
SD_HOST_DEVICE SD_INLINE X neg_tanh(X val) {
X o = static_cast<X>(1.0f);
X t = static_cast<X>(2.0f);
X e = static_cast<X>(M_E);
auto p = sd::math::sd_pow<X, X, X>(e, val * t);
X result = (p - o) / (p + o);
SD_PRINT_MATH_FUNC("neg_tanh", val, result,X);
return result;
}
template <typename X>
SD_HOST_DEVICE SD_INLINE X pos_tanh(X val) {
X o = static_cast<X>(1.0f);
X t = static_cast<X>(-2.0f);
X e = static_cast<X>(M_E);
auto p = sd::math::sd_pow<X, X, X>(e, val * t);
X result = (o - p) / (o + p);
SD_PRINT_MATH_FUNC("pos_tanh", val, result,X);
return result;
}
SD_HOST_DEVICE SD_INLINE float neu_tanh(float val, float sign) {
float e(M_E);
float av = sign * val;
auto p = sd::math::sd_pow<float, float, float>(e, -av * 2.f);
float result = (1 - p) / (1 + p);
SD_PRINT_MATH_FUNC2("neu_tanh", val, sign, result,float);
return result;
}
#ifdef HAS_FLOAT32
template <>
SD_HOST_DEVICE SD_INLINE float sd_tanh(float val) {
float sign = copysignfk(1.0f, val);
float result = sign * neu_tanh(val, sign);
SD_PRINT_MATH_FUNC("sd_tanh<float>", val, result,float);
return result;
}
#endif // HAS_FLOAT32
template <typename X, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_tanh(X val) {
Z result = val <= 0 ? neg_tanh(val) : pos_tanh(val);
SD_PRINT_MATH_FUNC("sd_tanh", val, result,Z);
return result;
}
template <typename T>
SD_HOST_DEVICE SD_INLINE T sd_rotl(T val, T shift) {
T result = p_rotl<T>(val, shift);
SD_PRINT_MATH_FUNC2("sd_rotl", val, shift, result,T);
return result;
}
template <typename T>
SD_HOST_DEVICE SD_INLINE T sd_rotr(T val, T shift) {
T result = p_rotr<T>(val, shift);
SD_PRINT_MATH_FUNC2("sd_rotr", val, shift, result,T);
return result;
}
template <typename X, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_erf(X val) {
Z result = p_erf<Z>(static_cast<Z>(val));
SD_PRINT_MATH_FUNC("sd_erf", val, result,Z);
return result;
}
template <typename X, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_erfc(X val) {
Z result = p_erfc<Z>(static_cast<Z>(val));
SD_PRINT_MATH_FUNC("sd_erfc", val, result,Z);
return result;
}
template <typename T>
SD_HOST_DEVICE SD_INLINE void sd_swap(T& val1, T& val2) {
T temp = val1;
val1 = val2;
val2 = temp;
};
// Implement sd_gamma with print statements
template <typename X, typename Z>
SD_HOST_DEVICE SD_INLINE Z sd_gamma(X a) {
Z result;
if (a < X(0.001)) {
const double eulerGamma = 0.577215664901532860606512090;
result = Z(1.0 / ((double)a * (1.0 + eulerGamma * (double)a)));
} else if (a < X(12.0)) {
double y = (double)a;
int n = 0;
bool argWasLessThanOne = y < 1.0;
if (argWasLessThanOne) {
y += 1.0;
} else {
n = static_cast<int>(floor(y)) - 1;
y -= n;
}
static const double p[] = {-1.71618513886549492533811E+0, 2.47656508055759199108314E+1,
-3.79804256470945635097577E+2, 6.29331155312818442661052E+2,
8.66966202790413211295064E+2, -3.14512729688483675254357E+4,
-3.61444134186911729807069E+4, 6.64561438202405440627855E+4};
static const double q[] = {-3.08402300119738975254353E+1, 3.15350626979604161529144E+2,
-1.01515636749021914166146E+3, -3.10777167157231109440444E+3,
2.25381184209801510330112E+4, 4.75584627752788110767815E+3,
-1.34659959864969306392456E+5, -1.15132259675553483497211E+5};
double num = 0.0;
double den = 1.0;
double z = y - 1;
for (auto i = 0; i < 8; i++) {
num = (num + p[i]) * z;
den = den * z + q[i];
}
double result_temp = num / den + 1.0;
if (argWasLessThanOne) {
result_temp /= (y - 1.0);
} else {
for (auto i = 0; i < n; i++) result_temp *= y++;
}
result = Z(result_temp);
} else {
if (a > 171.624) {
result = Z(DOUBLE_MAX_VALUE);
} else {
result = sd::math::sd_exp<Z, Z>(sd::math::sd_lgamma<X, Z>(a));
}
}
SD_PRINT_MATH_FUNC("sd_gamma", a, result,Z);
return result;
}
// CUDA-specific atomics section (continued in next part due to length)
#if defined(__CUDACC__)
namespace atomics {
SD_DEVICE SD_INLINE int atomicCAS(int* address, int compare, int val);
SD_DEVICE SD_INLINE unsigned int atomicCAS(unsigned int* address, unsigned int compare, unsigned int val);
// Type conversion functions
SD_DEVICE SD_INLINE int __float_as_int(float val) {
return *reinterpret_cast<int*>(&val);
}
SD_DEVICE SD_INLINE float __int_as_float(int val) {
return *reinterpret_cast<float*>(&val);
}
SD_DEVICE SD_INLINE long long int __double_as_longlong(double val) {
return *reinterpret_cast<long long int*>(&val);
}
SD_DEVICE SD_INLINE double __longlong_as_double(long long int val) {
return *reinterpret_cast<double*>(&val);
}
SD_DEVICE SD_INLINE unsigned short atomicCAS(unsigned short* address, unsigned short compare, unsigned short val) {
unsigned int* base_address = (unsigned int*)((size_t)address & ~2);
unsigned int long_compare = compare;
unsigned int long_val = val;
unsigned int shift = ((size_t)address & 2) * 8;
unsigned int mask = 0xffff << shift;
long_compare = long_compare << shift;
long_val = long_val << shift;
unsigned int old = *base_address, assumed;
do {
assumed = old;
old = atomicCAS(base_address, assumed,
(assumed & ~mask) | (long_val & mask));
} while (assumed != old);
return (unsigned short)((old & mask) >> shift);
}
template <typename T>
SD_DEVICE SD_INLINE T __sync_val_compare_and_swap_custom(T* address, T compare, T val) {
T old;
bool success;
do {
old = *address;
if (old != compare) {
return old;
}
__threadfence();
success = (compare == __ldcg(address)); // Volatile load
if (success) {
*address = val;
}
__threadfence();
} while (!success);
return old;
}
// Specializations for common types
SD_DEVICE SD_INLINE int __sync_val_compare_and_swap_custom(int* address, int compare, int val) {
return __sync_val_compare_and_swap_custom<int>(address, compare, val);
}
SD_DEVICE SD_INLINE unsigned int __sync_val_compare_and_swap_custom(unsigned int* address, unsigned int compare, unsigned int val) {
return __sync_val_compare_and_swap_custom<unsigned int>(address, compare, val);
}
SD_DEVICE SD_INLINE unsigned long long __sync_val_compare_and_swap_custom(unsigned long long* address, unsigned long long compare, unsigned long long val) {
return __sync_val_compare_and_swap_custom<unsigned long long>(address, compare, val);
}
SD_DEVICE SD_INLINE float __sync_val_compare_and_swap_custom(float* address, float compare, float val) {
return __sync_val_compare_and_swap_custom<float>(address, compare, val);
}
SD_DEVICE SD_INLINE double __sync_val_compare_and_swap_custom(double* address, double compare, double val) {
return __sync_val_compare_and_swap_custom<double>(address, compare, val);
}
// SD_INLINE atomicCAS implementations for integer types
SD_DEVICE SD_INLINE int atomicCAS(int* address, int compare, int val) {
return (int) __sync_val_compare_and_swap_custom((unsigned int*)address, (unsigned int)compare, (unsigned int)val);
}
SD_DEVICE SD_INLINE unsigned int atomicCAS(unsigned int* address, unsigned int compare, unsigned int val) {
return __sync_val_compare_and_swap_custom(address, compare, val);
}
SD_DEVICE SD_INLINE unsigned long long int atomicCAS(unsigned long long int* address,
unsigned long long int compare,
unsigned long long int val) {
return __sync_val_compare_and_swap_custom(address, compare, val);
}
SD_DEVICE SD_INLINE unsigned long atomicCAS(unsigned long * address,
unsigned long compare,
unsigned long val) {
return __sync_val_compare_and_swap_custom(address, compare, val);
}
template <typename T>
SD_INLINE SD_DEVICE T sd_atomicAdd(T* address, T val);
template <typename T>
SD_INLINE SD_DEVICE T sd_atomicSub(T* address, T val);
template <typename T>
SD_INLINE SD_DEVICE T sd_atomicMul(T* address, T val);
template <typename T>
SD_INLINE SD_DEVICE T sd_atomicDiv(T* address, T val);
template <typename T>
SD_INLINE SD_DEVICE T sd_atomicMin(T* address, T val);
template <typename T>
SD_INLINE SD_DEVICE T sd_atomicMax(T* address, T val);
template <typename T>
SD_INLINE SD_DEVICE T sd_atomicCAS(T* address, T compare, T val);
#ifdef HAS_INT32
template <>
SD_INLINE SD_DEVICE int32_t sd_atomicCAS<int32_t>(int32_t* address,int32_t compare, int32_t val) {
return atomicCAS((int *) address, (int )compare,(int) val);
}
#endif // HAS_INT32
#ifdef HAS_UINT32
template <>
SD_INLINE SD_DEVICE uint32_t sd_atomicCAS<uint32_t>(uint32_t* address, uint32_t compare,uint32_t val) {
return atomicCAS((int *)address, (int) compare,(int) val);
}
#endif // HAS_UINT32
SD_DEVICE SD_INLINE int atomicMin(int* address, int val) {
int old = *address, assumed;
do {
assumed = old;
old = atomicCAS(address, assumed, (val < assumed) ? val : assumed);
} while (assumed != old);
return old;
}
SD_DEVICE SD_INLINE unsigned int atomicMin(unsigned int* address, unsigned int val) {
unsigned int old = *address, assumed;
do {
assumed = old;
old = atomicCAS(address, assumed, (val < assumed) ? val : assumed);
} while (assumed != old);
return old;
}
SD_DEVICE SD_INLINE unsigned long long int atomicMin(unsigned long long int* address, unsigned long long int val) {
unsigned long long int old = *address, assumed;
do {
assumed = old;
old = atomicCAS(address, assumed, (val < assumed) ? val : assumed);
} while (assumed != old);
return old;
}
#ifdef HAS_INT32
template <>
inline SD_DEVICE int32_t sd_atomicMin<int32_t>(int32_t* address, int32_t val) {
return atomicMin(address, val);
}
#endif // HAS_INT32
#ifdef HAS_UINT32
template <>
inline SD_DEVICE uint32_t sd_atomicMin<uint32_t>(uint32_t* address, uint32_t val) {
return atomicMin(address, val);
}
#endif // HAS_UINT32
// Generic wrapper for atomicCAS
template <typename T>
inline SD_DEVICE T sd_atomicCAS(T* address, T compare, T val) {
// Default implementation using atomicCAS directly
return atomicCAS(address, compare, val);
}
#ifdef HAS_UINT8
template <>
inline SD_DEVICE uint8_t sd_atomicCAS<uint8_t>(uint8_t* address, uint8_t compare, uint8_t val) {
unsigned int* address_as_uint = reinterpret_cast<unsigned int*>(reinterpret_cast<char*>(address) - (reinterpret_cast<size_t>(address) & 3));
unsigned int old, assumed, fresh;
int shift = (reinterpret_cast<size_t>(address) & 3) * 8;
old = *address_as_uint;
do {
fresh = old;
if ((static_cast<unsigned int>(compare) == ((old >> shift) & 0xFF))) {
fresh = (old & ~(0xFF << shift)) | (static_cast<unsigned int>(val) << shift);
}
assumed = old;
old = atomicCAS(address_as_uint, assumed, fresh);
} while (assumed != old);
return (old >> shift) & 0xFF;
}
#endif // HAS_UINT8
// Specialization for float
#ifdef HAS_FLOAT32
template <>
inline SD_DEVICE float sd_atomicCAS<float>(float* address, float compare, float val) {
int* address_as_int = reinterpret_cast<int*>(address);
int old = *address_as_int, assumed;
int compare_as_int = __float_as_int(compare);
int val_as_int = __float_as_int(val);
do {
assumed = old;
old = atomicCAS(address_as_int, assumed, (assumed == compare_as_int) ? val_as_int : assumed);
} while (assumed != old);
return __int_as_float(old);
}
#endif // HAS_FLOAT32
#ifdef HAS_UINT64
template <>
inline SD_DEVICE uint64_t sd_atomicCAS<uint64_t>(uint64_t* address, uint64_t compare, uint64_t val) {
unsigned long long int* address_as_ull = reinterpret_cast<unsigned long long int*>(address);
return atomicCAS(address_as_ull, static_cast<unsigned long long int>(compare), static_cast<unsigned long long int>(val));
}
#endif // HAS_UINT64
#ifdef HAS_UINT16
template <>
inline SD_DEVICE uint16_t sd_atomicCAS<uint16_t>(uint16_t* address, uint16_t compare, uint16_t val) {
unsigned int* address_as_uint = reinterpret_cast<unsigned int*>(reinterpret_cast<char*>(address) - (reinterpret_cast<size_t>(address) & 2));
unsigned int old, assumed, fresh;
old = *address_as_uint;
do {
if (reinterpret_cast<size_t>(address) & 2) {
fresh = (old & 0xFFFF) | ((static_cast<unsigned int>(compare) == (old >> 16)) ? (static_cast<unsigned int>(val) << 16) : (old & 0xFFFF0000));
} else {
fresh = (old & 0xFFFF0000) | ((static_cast<unsigned int>(compare) == (old & 0xFFFF)) ? static_cast<unsigned int>(val) : (old & 0xFFFF));
}
assumed = old;
old = atomicCAS(address_as_uint, assumed, fresh);
} while (assumed != old);
return (reinterpret_cast<size_t>(address) & 2) ? (old >> 16) : (old & 0xFFFF);
}
#endif // HAS_UINT16
#ifdef HAS_INT16
template <>
inline SD_DEVICE int16_t sd_atomicCAS<int16_t>(int16_t* address, int16_t compare, int16_t val) {
int* address_as_uint = reinterpret_cast<int*>(reinterpret_cast<char*>(address) - (reinterpret_cast<size_t>(address) & 2));
int old, assumed, fresh;
old = *address_as_uint;
do {
if (reinterpret_cast<size_t>(address) & 2) {
fresh = (old & 0xFFFF) | ((static_cast<unsigned int>(compare) == (old >> 16)) ? (static_cast<unsigned int>(val) << 16) : (old & 0xFFFF0000));
} else {
fresh = (old & 0xFFFF0000) | ((static_cast<unsigned int>(compare) == (old & 0xFFFF)) ? static_cast<unsigned int>(val) : (old & 0xFFFF));
}
assumed = old;
old = atomicCAS(address_as_uint, assumed, fresh);
} while (assumed != old);
return (reinterpret_cast<size_t>(address) & 2) ? (old >> 16) : (old & 0xFFFF);
}
#endif // HAS_INT16
#ifdef HAS_LONG
template <>
inline SD_DEVICE sd::LongType sd_atomicCAS<sd::LongType>(sd::LongType* address, sd::LongType compare, sd::LongType val) {
unsigned long long int* address_as_ull = reinterpret_cast<unsigned long long int*>(address);
unsigned long long int compare_as_ull = static_cast<unsigned long long int>(compare);
unsigned long long int val_as_ull = static_cast<unsigned long long int>(val);
unsigned long long int old_as_ull = atomicCAS(address_as_ull, compare_as_ull, val_as_ull);
return static_cast<sd::LongType>(old_as_ull);
}
#endif // HAS_LONG
#ifdef HAS_DOUBLE
template <>
inline SD_DEVICE double sd_atomicCAS<double>(double* address, double compare, double val) {
unsigned long long int* address_as_ull = (unsigned long long int*)address;
unsigned long long int compare_as_ull = __double_as_longlong(compare);
unsigned long long int val_as_ull = __double_as_longlong(val);
unsigned long long int old_as_ull = atomicCAS(address_as_ull, compare_as_ull, val_as_ull);
return __longlong_as_double(old_as_ull);
}
#endif // HAS_DOUBLE
#ifdef HAS_INT8
template <>
inline SD_DEVICE int8_t sd_atomicCAS<int8_t>(int8_t* address, int8_t compare, int8_t val) {
int* address_as_int = reinterpret_cast<int*>(reinterpret_cast<char*>(address) - (reinterpret_cast<size_t>(address) & 3));
int old, assumed, fresh;
int shift = (reinterpret_cast<size_t>(address) & 3) * 8;
old = *address_as_int;
do {
fresh = old;
if ((static_cast<int>(compare) == ((old >> shift) & 0xFF))) {
fresh = (old & ~(0xFF << shift)) | (static_cast<int>(val) << shift);
}
assumed = old;
old = atomicCAS(address_as_int, assumed, fresh);
} while (assumed != old);
return (old >> shift) & 0xFF;
}
#endif // HAS_INT8
#ifdef HAS_FLOAT16
template <>
inline __device__ float16 sd_atomicCAS<float16>(float16* address, float16 compare, float16 val) {
auto address_as_ushort = reinterpret_cast<unsigned short*>(address);
auto addr = reinterpret_cast<size_t>(address);
bool misaligned = addr & 0x1;
if (misaligned) address_as_ushort = reinterpret_cast<unsigned short*>(address - 1);
unsigned short old = *address_as_ushort;
unsigned short assumed;
do {
assumed = old;
unsigned short compare_as_ushort = misaligned ?
(old & 0xFF00) | (compare & 0xFF) :
(old & 0x00FF) | (compare & 0xFF00);
unsigned short val_as_ushort = misaligned ?
(old & 0xFF00) | (val & 0xFF) :
(old & 0x00FF) | (val & 0xFF00);
old = atomicCAS(address_as_ushort, compare_as_ushort, val_as_ushort);
} while (assumed != old);
float16 result;
result = misaligned ? (old & 0xFF) : (old & 0xFF00);
return result;
}
#endif // HAS_FLOAT16
// Updated BPAIR structure for bfloat16 operations
union BPAIR {
SD_HOST_DEVICE BPAIR() {}
struct {
unsigned short L;
unsigned short H;
} B;
int W;
};
#ifdef HAS_BFLOAT16
// Specialization for bfloat16
template <>
inline SD_DEVICE bfloat16 sd_atomicCAS<bfloat16>(bfloat16* address, bfloat16 compare, bfloat16 val) {
auto address_as_int = reinterpret_cast<int*>(address);
auto addr = reinterpret_cast<size_t>(address);
bool misaligned = addr & 0x2;
if (misaligned) address_as_int = reinterpret_cast<int*>(reinterpret_cast<char*>(address) - 2);
BPAIR old, assumed, fresh;
old.W = *address_as_int;
do {
if (!misaligned) {
fresh.B.H = (bfloat16(old.B.H) == bfloat16(compare)) ? bfloat16(val) : bfloat16(old.B.H);
fresh.B.L = bfloat16(old.B.L);
} else {
fresh.B.L = (bfloat16(old.B.L) == bfloat16(compare)) ? bfloat16(val) : bfloat16(old.B.L);
fresh.B.H = bfloat16(old.B.H);
}
assumed.W = old.W;
old.W = atomicCAS(address_as_int, assumed.W, fresh.W);
} while (assumed.W != old.W);
if (!misaligned)
return bfloat16(old.B.H);
else
return bfloat16(old.B.L);
}
#endif // HAS_BFLOAT16
// Fallback implementation for __half_as_ushort
#ifdef HAS_FLOAT16
SD_DEVICE SD_INLINE unsigned short __half_as_ushort(float16 h) {
return *reinterpret_cast<unsigned short*>(&h);
}
// Fallback implementation for __ushort_as_half
SD_DEVICE SD_INLINE float16 __ushort_as_half(unsigned short u) {
return *reinterpret_cast<float16*>(&u);
}
#endif // HAS_FLOAT16
#ifdef HAS_FLOAT32
template <>
inline SD_DEVICE float sd_atomicMin<float>(float* address, float val) {
int* address_as_ull = (int*)address;
int old = __float_as_int(val), assumed;
do {
assumed = old;
old = atomicCAS(address_as_ull, assumed, __float_as_int(math::sd_min(val, __int_as_float(assumed))));
} while (assumed != old);
return __int_as_float(old);
}
#endif // HAS_FLOAT32
#ifdef HAS_DOUBLE
template <>
inline SD_DEVICE double sd_atomicMin<double>(double* address, double val) {
unsigned long long int* address_as_ull = (unsigned long long int*)address;
unsigned long long int old = __double_as_longlong(val), assumed;
do {
assumed = old;
old = atomicCAS(address_as_ull, assumed,
__double_as_longlong(math::sd_min(val, __longlong_as_double(assumed))));
} while (assumed != old);
return __longlong_as_double(old);
}
#endif // HAS_DOUBLE
#ifdef HAS_UINT64
template <>
inline SD_DEVICE uint64_t sd_atomicMin<uint64_t>(uint64_t* address, uint64_t val) {
#if __CUDA_ARCH__ >= 350
return atomicMin((unsigned long long*)address, (unsigned long long)val);
#else
unsigned long long int* address_as_ull = (unsigned long long int*)address;
unsigned long long int old = __double_as_longlong(val), assumed;
do {
assumed = old;
old = atomicCAS(address_as_ull, assumed, math::sd_min((unsigned long long)val, assumed));
} while (assumed != old);
return old;
#endif
}
#endif // HAS_UINT64
#ifdef HAS_LONG
template <>
inline SD_DEVICE sd::LongType sd_atomicMin<sd::LongType>(sd::LongType* address, sd::LongType val) {
#if __CUDA_ARCH__ >= 350
return atomicMin((unsigned long long*)address, (unsigned long long)val);
#else
unsigned long long int* address_as_ull = (unsigned long long int*)address;
unsigned long long int old = (unsigned long long)val, assumed;
do {
assumed = old;
old = atomicCAS(address_as_ull, assumed, math::sd_min(val, (sd::LongType)assumed));
} while (assumed != old);
return old;
#endif
}
#endif // HAS_LONG
#ifdef HAS_INT16
template <>
inline SD_DEVICE int16_t sd_atomicMin<int16_t>(int16_t* address, int16_t val) {
int32_t temp = *address;
*address = atomicMin(&temp, (int)val);
return *address;
}
#endif // HAS_INT16
#ifdef HAS_FLOAT16
template <>
inline SD_DEVICE float16 sd_atomicMin<float16>(float16* address, float16 val) {
return float16(sd_atomicMin<int16_t>(reinterpret_cast<int16_t*>(&address->data), (int16_t)val.data));
}
#endif // HAS_FLOAT16
// Custom max functions
SD_DEVICE SD_INLINE int32_t sd_max(int32_t a, int32_t b) {
return a > b ? a : b;
}
SD_DEVICE SD_INLINE uint32_t sd_max(uint32_t a, uint32_t b) {
return a > b ? a : b;
}
#ifdef HAS_INT32
template <>
inline SD_DEVICE int32_t sd_atomicMax<int32_t>(int32_t* address, int32_t val) {
int32_t old = *address, assumed;
do {
assumed = old;
old = atomicCAS(address, assumed, sd_max(val, assumed));
} while (assumed != old);
return old;
}
#endif // HAS_INT32
#ifdef HAS_UINT32
template <>
SD_DEVICE SD_INLINE uint32_t sd_atomicMax<uint32_t>(uint32_t* address, uint32_t val) {
uint32_t old = *address, assumed;
do {
assumed = old;
old = atomicCAS(address, assumed, sd_max(val, assumed));
} while (assumed != old);
return old;
}
#endif // HAS_UINT32
#ifdef HAS_UINT64
template <>
SD_DEVICE SD_INLINE unsigned long sd_atomicMax<unsigned long>(unsigned long* address, unsigned long val) {
uint32_t old = *address, assumed;
do {
assumed = old;
old = atomicCAS(address, assumed, sd_max(val, assumed));
} while (assumed != old);
return old;
}
#endif // HAS_UINT64
#ifdef HAS_DOUBLE
template <>
SD_DEVICE SD_INLINE double sd_atomicMax<double>(double* address, double val) {
unsigned long long int* address_as_ull = (unsigned long long int*)address;
unsigned long long int old = __double_as_longlong(val), assumed;
do {
assumed = old;
old = atomicCAS(address_as_ull, assumed,
__double_as_longlong(math::sd_max(val, __longlong_as_double(assumed))));
} while (assumed != old);
return __longlong_as_double(old);
}
#endif // HAS_DOUBLE
#ifdef HAS_FLOAT32
template <>
SD_DEVICE SD_INLINE float sd_atomicMax<float>(float* address, float val) {
int* address_as_ull = (int*)address;
int old = __float_as_int(val), assumed;
do {
assumed = old;
old = atomicCAS(address_as_ull, assumed, __float_as_int(math::sd_max(val, __int_as_float(assumed))));
} while (assumed != old);
return __int_as_float(old);
}
#endif // HAS_FLOAT32
#ifdef HAS_UINT8
template <>
SD_DEVICE SD_INLINE uint8_t sd_atomicMin<uint8_t>(uint8_t* address, uint8_t val) {
uint32_t temp = *address;
*address = atomicMin(&temp, (uint32_t)val);
return *address;
}
#endif // HAS_UINT8
#ifdef HAS_INT8
template <>
SD_DEVICE SD_INLINE int8_t sd_atomicMin<int8_t>(int8_t* address, int8_t val) {
int32_t temp = *address;
*address = atomicMin(&temp, (int)val);
return *address;
}
#endif // HAS_INT8
#ifdef HAS_UINT16
template <>
SD_DEVICE SD_INLINE uint16_t sd_atomicMin<uint16_t>(uint16_t* address, uint16_t val) {
uint32_t temp = *address;
*address = atomicMin(&temp, (uint32_t)val);
return *address;
}
#endif // HAS_UINT16
#ifdef HAS_BFLOAT16
template <>
inline SD_DEVICE bfloat16 sd_atomicMin<bfloat16>(bfloat16* address, bfloat16 val) {
return bfloat16(sd_atomicMin<uint16_t>(&address->_data, val._data));
}
#endif // HAS_BFLOAT16
// Custom max functions
SD_DEVICE SD_INLINE uint8_t sd_max(uint8_t a, uint8_t b) {
return a > b ? a : b;
}
SD_DEVICE SD_INLINE int8_t sd_max(int8_t a, int8_t b) {
return a > b ? a : b;
}
// Simplified __byte_perm for uint8_t operations
SD_DEVICE SD_INLINE unsigned int __byte_perm_uint8(unsigned int a, unsigned int b, unsigned int selector) {
unsigned int result;
unsigned int byte_index = selector & 0x3;
if (selector & 0x4) {
// Extract byte from b
result = (b >> (byte_index * 8)) & 0xFF;
} else {
// Extract byte from a
result = (a >> (byte_index * 8)) & 0xFF;
}
return result;
}
#ifdef HAS_UINT8
template <>
inline SD_DEVICE uint8_t sd_atomicMax<uint8_t>(uint8_t* address, uint8_t val) {
unsigned int* base_address = (unsigned int*)((size_t)address & ~3);
unsigned int selectors[] = {0x3214, 0x3240, 0x3410, 0x4210};
unsigned int sel = selectors[(size_t)address & 3];
unsigned int old, assumed, max_, new_;
old = *base_address;
do {
assumed = old;
max_ = sd_max((uint8_t)(__byte_perm_uint8(old, 0, ((size_t)address & 3) | 0x4440)), val);
new_ = __byte_perm_uint8(old, max_, sel);
old = atomicCAS(base_address, assumed, new_);
} while (assumed != old);
return (uint8_t)(__byte_perm_uint8(old, 0, ((size_t)address & 3) | 0x4440));
}
#endif // HAS_UINT8
// Custom implementation of __byte_perm
SD_DEVICE SD_INLINE unsigned int __byte_perm(unsigned int a, unsigned int b, unsigned int selector) {
unsigned int result = 0;
for (int i = 0; i < 4; ++i) {
unsigned int byteSel = (selector >> (i * 4)) & 0xF;
unsigned int byte;
if (byteSel < 4)
byte = (a >> (byteSel * 8)) & 0xFF;
else if (byteSel < 8)
byte = (b >> ((byteSel - 4) * 8)) & 0xFF;
else
byte = 0;
result |= byte << (i * 8);
}
return result;
}
#ifdef HAS_INT8
template <>
inline SD_DEVICE int8_t sd_atomicMax<int8_t>(int8_t* address, int8_t val) {
unsigned int* base_address = (unsigned int*)((size_t)address & ~3);
unsigned int selectors[] = {0x3214, 0x3240, 0x3410, 0x4210};
unsigned int sel = selectors[(size_t)address & 3];
unsigned int old, assumed, max_, new_;
old = *base_address;
do {
assumed = old;
max_ = sd_max((int8_t)(__byte_perm(old, 0, ((size_t)address & 3) | 0x4440)), val);
new_ = __byte_perm(old, max_, sel);
old = atomicCAS(base_address, assumed, new_);
} while (assumed != old);
return (int8_t)(__byte_perm(old, 0, ((size_t)address & 3) | 0x4440));
}
#endif // HAS_INT8
// AtomicMax signatures
SD_DEVICE SD_INLINE int atomicMax(int* address, int val);
SD_DEVICE SD_INLINE unsigned int atomicMax(unsigned int* address, unsigned int val);
SD_DEVICE SD_INLINE unsigned long long int atomicMax(unsigned long long int* address, unsigned long long int val);
// Custom atomicMax for 16-bit types
SD_DEVICE SD_INLINE uint16_t atomicMax(uint16_t* address, uint16_t val) {
unsigned int* base_address = (unsigned int*)((size_t)address & ~2);
unsigned int offset = ((size_t)address & 2) << 3;
unsigned int mask = 0xFFFF << offset;
unsigned int old = *base_address, assumed;
do {
assumed = old;
uint16_t current = (old & mask) >> offset;
uint16_t maximum = current > val ? current : val;
unsigned int new_val = (old & ~mask) | (maximum << offset);
old = atomicCAS(base_address, assumed, new_val);
} while (assumed != old);
return (old & mask) >> offset;
}
SD_DEVICE SD_INLINE int16_t atomicMax(int16_t* address, int16_t val) {
unsigned int* base_address = (unsigned int*)((size_t)address & ~2);
unsigned int offset = ((size_t)address & 2) << 3;
unsigned int mask = 0xFFFF << offset;
unsigned int old = *base_address, assumed;
do {
assumed = old;
int16_t current = (old & mask) >> offset;
int16_t maximum = current > val ? current : val;
unsigned int new_val = (old & ~mask) | ((unsigned short)maximum << offset);
old = atomicCAS(base_address, assumed, new_val);
} while (assumed != old);
return (int16_t)((old & mask) >> offset);
}
#ifdef HAS_UINT16
// Updated sd_atomicMax implementations
template <>
inline SD_DEVICE uint16_t sd_atomicMax<uint16_t>(uint16_t* address, uint16_t val) {
return atomicMax(address, val);
}
#endif // HAS_UINT16
// Proper PAIR struct for float16 operations
struct PAIR {
SD_HOST_DEVICE PAIR() {}
union {
struct {
#ifdef HAS_FLOAT16
float16 L;
float16 H;
#else
unsigned short L;
unsigned short H;
#endif
} B;
int W;
};
};
#ifdef HAS_INT16
template <>
inline SD_DEVICE int16_t sd_atomicMax<int16_t>(int16_t* address, int16_t val) {
return atomicMax(address, val);
}
#endif // HAS_INT16
#ifdef HAS_FLOAT16
template <>
SD_INLINE SD_DEVICE float16 sd_atomicMax<float16>(float16* address, float16 val) {
unsigned int* address_as_uint = reinterpret_cast<unsigned int*>((reinterpret_cast<char*>(address) - (reinterpret_cast<uintptr_t>(address) & 2)));
unsigned int old, assumed, fresh;
float16 old_val, max_val;
old = *address_as_uint;
do {
assumed = old;
if (reinterpret_cast<uintptr_t>(address) & 2) {
old_val = float16(static_cast<unsigned short>(old >> 16));
max_val = sd::math::sd_max<float16>(old_val, val);
fresh = (old & 0xFFFF) | (reinterpret_cast<unsigned short&>(max_val) << 16);
} else {
old_val = float16(static_cast<unsigned short>(old & 0xFFFF));
max_val = sd::math::sd_max<float16>(old_val, val);
fresh = (old & 0xFFFF0000) | reinterpret_cast<unsigned short&>(max_val);
}
old = atomicCAS(address_as_uint, assumed, fresh);
} while (assumed != old);
return (reinterpret_cast<uintptr_t>(address) & 2) ? float16(static_cast<unsigned short>(old >> 16))
: float16(static_cast<unsigned short>(old & 0xFFFF));
}
#endif // HAS_FLOAT16
#ifdef HAS_BFLOAT16
template <>
SD_INLINE SD_DEVICE bfloat16 sd_atomicMax<bfloat16>(bfloat16* address, bfloat16 val) {
unsigned int* address_as_uint = reinterpret_cast<unsigned int*>((reinterpret_cast<char*>(address) - (reinterpret_cast<uintptr_t>(address) & 2)));
unsigned int old, assumed, fresh;
bfloat16 old_val, max_val;
old = *address_as_uint;
do {
assumed = old;
if (reinterpret_cast<uintptr_t>(address) & 2) {
old_val = bfloat16(static_cast<unsigned short>(old >> 16));
max_val = sd::math::sd_max<bfloat16>(old_val, val);
fresh = (old & 0xFFFF) | (reinterpret_cast<unsigned short&>(max_val) << 16);
} else {
old_val = bfloat16(static_cast<unsigned short>(old & 0xFFFF));
max_val = sd::math::sd_max<bfloat16>(old_val, val);
fresh = (old & 0xFFFF0000) | reinterpret_cast<unsigned short&>(max_val);
}
old = atomicCAS(address_as_uint, assumed, fresh);
} while (assumed != old);
return (reinterpret_cast<uintptr_t>(address) & 2) ? bfloat16(static_cast<unsigned short>(old >> 16))
: bfloat16(static_cast<unsigned short>(old & 0xFFFF));
}
#endif // HAS_BFLOAT16
#ifdef HAS_LONG
template <>
inline SD_DEVICE sd::LongType sd_atomicMax<sd::LongType>(sd::LongType* address, sd::LongType val) {
unsigned long long int* address_as_ull = (unsigned long long int*)address;
unsigned long long int old = *address_as_ull, assumed;
do {
assumed = old;
old = atomicCAS(address_as_ull, assumed, (unsigned long long) sd::math::sd_max<LongType>(val, (sd::LongType)assumed));
} while (assumed != old);
return old;
}
#endif // HAS_LONG
#ifdef HAS_DOUBLE
template <>
inline SD_DEVICE double sd_atomicAdd<double>(double* address, double val) {
unsigned long long int* address_as_ull = (unsigned long long int*)address;
unsigned long long int old = *address_as_ull, assumed;
do {
assumed = old;
old = atomicCAS(address_as_ull, assumed, __double_as_longlong(val + __longlong_as_double(assumed)));
} while (assumed != old);
return __longlong_as_double(old);
}
#endif // HAS_DOUBLE
#ifdef HAS_LONG
template <>
inline SD_DEVICE sd::LongType sd_atomicAdd<sd::LongType>(sd::LongType* address, sd::LongType val) {
unsigned long long int* address_as_ull = (unsigned long long int*)address;
unsigned long long int old = *address_as_ull, assumed;
do {
assumed = old;
old = atomicCAS(address_as_ull, assumed, val + assumed);
} while (assumed != old);
return old;
}
template <>
inline SD_DEVICE long sd_atomicAdd<long>(long* address, long val) {
unsigned long long* address_as_ull = (unsigned long long int*)address;
// return atomicAdd(address, val);
unsigned long int old = *address_as_ull, assumed;
do {
assumed = old;
old = atomicCAS(address_as_ull, assumed, val + assumed);
} while (assumed != old);
return old;
}
#endif // HAS_LONG
// Custom atomicAdd for uint32_t
SD_DEVICE SD_INLINE uint32_t atomicAdd(uint32_t* address, uint32_t val) {
uint32_t old = *address, assumed;
do {
assumed = old;
old = atomicCAS(address, assumed, assumed + val);
} while (assumed != old);
return old;
}
// Custom atomicAdd for uint64_t
SD_DEVICE SD_INLINE uint64_t atomicAdd(uint64_t* address, uint64_t val) {
unsigned long long int* address_as_ull = (unsigned long long int*)address;
unsigned long long int old = *address_as_ull, assumed;
do {
assumed = old;
old = atomicCAS(address_as_ull, assumed, assumed + val);
} while (assumed != old);
return old;
}
#ifdef HAS_UINT32
// Updated sd_atomicAdd implementation for uint32_t
template <>
inline SD_DEVICE uint32_t sd_atomicAdd<uint32_t>(uint32_t* address, uint32_t val) {
return atomicAdd(address, val);
}
#endif // HAS_UINT32
#ifdef HAS_UINT64
// Updated sd_atomicAdd implementation for uint64_t
template <>
inline SD_DEVICE uint64_t sd_atomicAdd<uint64_t>(uint64_t* address, uint64_t val) {
return atomicAdd(address, val);
}
#endif // HAS_UINT64
#ifdef HAS_FLOAT16
template <>
inline SD_DEVICE float16 sd_atomicAdd<float16>(float16* address, float16 val) {
#if __CUDA_ARCH__ >= 700 && CUDA_VERSION_MAJOR >= 10
atomicAdd(reinterpret_cast<__half*>(address), val.data);
#else
auto address_as_ull = (int*)address;
long addr = (long)address;
bool misaligned = addr & 0x3;
if (misaligned) address_as_ull = (int*)(address - 1);
PAIR old, assumed, fresh;
old.W = *address_as_ull;
do {
if (!misaligned) {
float16 res = ((float16)old.B.H) + val;
fresh.B.H = res.data;
fresh.B.L = old.B.L;
} else {
float16 res = ((float16)old.B.L) + val;
fresh.B.L = res.data;
fresh.B.H = old.B.H;
}
assumed.W = old.W;
old.W = atomicCAS(address_as_ull, assumed.W, fresh.W);
} while (assumed.W != old.W);
if (!misaligned)
return old.B.H;
else
return old.B.L;
#endif
}
#endif // HAS_FLOAT16
#ifdef HAS_BFLOAT16
template <>
inline SD_DEVICE bfloat16 sd_atomicAdd<bfloat16>(bfloat16* address, bfloat16 val) {
auto address_as_ull = (int*)address;
auto addr = (long)(address);
bool misaligned = addr & 0x3;
if (misaligned) address_as_ull = (int*)(address - 1);
BPAIR old, assumed, fresh;
old.W = *address_as_ull;
do {
if (!misaligned) {
bfloat16 res = old.B.H + val;
fresh.B.H = res;
fresh.B.L = old.B.L;
} else {
bfloat16 res = old.B.L + val;
fresh.B.L = res;
fresh.B.H = old.B.H;
}
assumed.W = old.W;
old.W = atomicCAS(address_as_ull, assumed.W, fresh.W);
} while (assumed.W != old.W);
if (!misaligned)
return old.B.H;
else
return old.B.L;
}
#endif // HAS_BFLOAT16
template <typename T>
static SD_INLINE SD_DEVICE T internal_16bit_atomicAdd(T* address, T val) {
size_t shift = ((size_t)address & 2);
int* base_address = (int*)((char*)address - shift);
union I16PAIR {
struct {
T H;
T L;
} B;
int W;
SD_HOST_DEVICE
I16PAIR(){};
SD_HOST_DEVICE
~I16PAIR(){};
};
I16PAIR pairNew, pairOld, pairAssumed;
if (reinterpret_cast<int*>(address) == base_address) {
pairOld.B.L = val;
do {
pairNew.B.L = pairOld.B.L;
pairNew.B.H = pairOld.B.H + val;
pairAssumed.W = pairOld.W;
pairOld.W = atomicCAS(base_address, pairAssumed.W, pairNew.W);
} while (pairAssumed.W != pairOld.W);
return (T)pairOld.B.H;
} else {
pairOld.B.H = val;
do {
pairNew.B.H = pairOld.B.H;
pairNew.B.L = pairOld.B.L + val;
pairAssumed.W = pairOld.W;
pairOld.W = atomicCAS(base_address, pairAssumed.W, pairNew.W);
} while (pairAssumed.W != pairOld.W);
return (T)pairOld.B.L;
}
}
#ifdef HAS_INT16
template <>
inline SD_DEVICE int16_t sd_atomicAdd<int16_t>(int16_t* address, int16_t val) {
return internal_16bit_atomicAdd<int16_t>(address, val);
}
#endif // HAS_INT16
#ifdef HAS_UINT16
template <>
inline SD_DEVICE uint16_t sd_atomicAdd<uint16_t>(uint16_t* address, uint16_t val) {
return internal_16bit_atomicAdd<uint16_t>(address, val);
}
#endif // HAS_UINT16
// Custom atomicAdd for int8_t
SD_DEVICE SD_INLINE int8_t atomicAdd(int8_t* address, int8_t val) {
unsigned int* base_address = (unsigned int*)((size_t)address & ~3);
unsigned int shift = ((size_t)address & 3) * 8;
unsigned int mask = 0xFF << shift;
unsigned int assumed, old, sum;
old = *base_address;
do {
assumed = old;
sum = (assumed & mask) + (val << shift);
sum = (sum & mask) | (assumed & ~mask);
old = atomicCAS(base_address, assumed, sum);
} while (assumed != old);
return (int8_t)((old & mask) >> shift);
}
// Custom atomicAdd for uint8_t
SD_DEVICE SD_INLINE uint8_t atomicAdd(uint8_t* address, uint8_t val) {
unsigned int* base_address = (unsigned int*)((size_t)address & ~3);
unsigned int shift = ((size_t)address & 3) * 8;
unsigned int mask = 0xFF << shift;
unsigned int assumed, old, sum;
old = *base_address;
do {
assumed = old;
sum = (assumed & mask) + (val << shift);
sum = (sum & mask) | (assumed & ~mask);
old = atomicCAS(base_address, assumed, sum);
} while (assumed != old);
return (uint8_t)((old & mask) >> shift);
}
#ifdef HAS_INT8
// Updated sd_atomicAdd implementation for int8_t
template <>
inline SD_DEVICE int8_t sd_atomicAdd<int8_t>(int8_t* address, int8_t val) {
return atomicAdd(address, val);
}
#endif // HAS_INT8
#ifdef HAS_UINT8
// Updated sd_atomicAdd implementation for uint8_t
template <>
inline SD_DEVICE uint8_t sd_atomicAdd<uint8_t>(uint8_t* address, uint8_t val) {
return atomicAdd(address, val);
}
#endif // HAS_UINT8
#ifdef HAS_BOOL
template <>
inline SD_DEVICE bool sd_atomicAdd<bool>(bool* address, bool val) {
*address += (val);
return *address;
}
#endif // HAS_BOOL
#ifdef HAS_DOUBLE
template <>
inline SD_DEVICE double sd_atomicSub<double>(double* address, double val) {
return sd_atomicAdd<double>(address, -val);
}
template <>
inline SD_DEVICE double sd_atomicMul<double>(double* address, double val) {
unsigned long long int* address_as_ull = (unsigned long long int*)address;
unsigned long long int old = *address_as_ull, assumed;
do {
assumed = old;
old = atomicCAS(address_as_ull, assumed, __double_as_longlong(val * __longlong_as_double(assumed)));
} while (assumed != old);
return __longlong_as_double(old);
}
template <>
inline SD_DEVICE double sd_atomicDiv<double>(double* address, double val) {
return sd_atomicMul<double>(address, 1. / val);
}
#endif // HAS_DOUBLE
// Helper functions for float-int conversions
SD_DEVICE SD_INLINE unsigned int __float_as_uint(float f) {
return *reinterpret_cast<unsigned int*>(&f);
}
SD_DEVICE SD_INLINE float __uint_as_float(unsigned int u) {
return *reinterpret_cast<float*>(&u);
}
// Custom atomicAdd for float
SD_DEVICE SD_INLINE float atomicAdd(float* address, float val) {
unsigned int* address_as_uint = (unsigned int*)address;
unsigned int old = *address_as_uint, assumed;
do {
assumed = old;
old = atomicCAS(address_as_uint, assumed,
__float_as_uint(val + __uint_as_float(assumed)));
} while (assumed != old);
return __uint_as_float(old);
}
// Custom atomicAdd for int32_t
SD_DEVICE SD_INLINE int32_t atomicAdd(int32_t* address, int32_t val) {
unsigned int* address_as_uint = (unsigned int*)address;
unsigned int old = *address_as_uint, assumed;
do {
assumed = old;
old = atomicCAS(address_as_uint, assumed,
(unsigned int)((int)assumed + val));
} while (assumed != old);
return (int32_t)old;
}
#ifdef HAS_FLOAT32
// Updated sd_atomicAdd implementation for float
template <>
inline SD_DEVICE float sd_atomicAdd<float>(float* address, float val) {
return atomicAdd(address, val);
}
template <>
inline SD_DEVICE float sd_atomicSub<float>(float* address, float val) {
return sd_atomicAdd<float>(address, -val);
}
template <>
inline SD_DEVICE float sd_atomicMul<float>(float* address, float val) {
int* address_as_ull = (int*)address;
int old = *address_as_ull, assumed;
do {
assumed = old;
old = atomicCAS(address_as_ull, assumed, __float_as_int(val * __int_as_float(assumed)));
} while (assumed != old);
return __int_as_float(old);
}
template <>
inline SD_DEVICE float sd_atomicDiv<float>(float* address, float val) {
return sd_atomicMul<float>(address, 1.f / val);
}
#endif // HAS_FLOAT32
#ifdef HAS_INT32
// Updated sd_atomicAdd implementation for int32_t
template <>
inline SD_DEVICE int32_t sd_atomicAdd<int32_t>(int32_t* address, int32_t val) {
return atomicAdd(address, val);
}
#endif // HAS_INT32
#ifdef HAS_FLOAT16
template <>
inline SD_DEVICE float16 sd_atomicSub<float16>(float16* address, float16 val) {
return sd_atomicAdd<float16>(address, -val);
}
template <>
inline SD_DEVICE float16 sd_atomicDiv<float16>(float16* address, float16 val) {
return internal_16bit_atomicMul<float16>(address, (float16)1.f / val);
}
#endif // HAS_FLOAT16
#ifdef HAS_BFLOAT16
template <>
inline SD_DEVICE bfloat16 sd_atomicSub<bfloat16>(bfloat16* address, bfloat16 val) {
return sd_atomicAdd<bfloat16>(address, -val);
}
template <>
inline SD_DEVICE bfloat16 sd_atomicMul<bfloat16>(bfloat16* address, bfloat16 val) {
return internal_16bit_atomicMul<bfloat16>(address, val);
}
template <>
inline SD_DEVICE bfloat16 sd_atomicDiv<bfloat16>(bfloat16* address, bfloat16 val) {
return internal_16bit_atomicMul<bfloat16>(address, (bfloat16)1 / val);
}
#endif // HAS_BFLOAT16
#ifdef HAS_INT8
template <>
inline SD_DEVICE int8_t sd_atomicMul<int8_t>(int8_t* address, int8_t val) {
unsigned int* base_address = (unsigned int*)((size_t)address & ~3);
unsigned int selectors[] = {0x3214, 0x3240, 0x3410, 0x4210};
unsigned int sel = selectors[(size_t)address & 3];
unsigned int old, assumed, mul, new_;
old = *base_address;
do {
assumed = old;
mul = val * (int8_t)__byte_perm(old, 0, ((size_t)address & 3) | 0x4440);
new_ = __byte_perm(old, mul, sel);
if (new_ == old) break;
old = atomicCAS(base_address, assumed, new_);
} while (assumed != old);
return (int8_t)old;
}
#endif // HAS_INT8
#ifdef HAS_UINT8
template <>
inline SD_DEVICE unsigned char sd_atomicMul<unsigned char>(unsigned char* address, unsigned char val) {
unsigned int* base_address = (unsigned int*)((size_t)address & ~3);
unsigned int selectors[] = {0x3214, 0x3240, 0x3410, 0x4210};
unsigned int sel = selectors[(size_t)address & 3];
unsigned int old, assumed, mul, new_;
old = *base_address;
do {
assumed = old;
mul = val * (uint8_t)__byte_perm(old, 0, ((size_t)address & 3) | 0x4440);
new_ = __byte_perm(old, mul, sel);
if (new_ == old) break;
old = atomicCAS(base_address, assumed, new_);
} while (assumed != old);
return (uint8_t)old;
}
#endif // HAS_UINT8
template <typename T>
static SD_INLINE SD_DEVICE T internal_16bit_atomicMul(T* address, T val) {
size_t shift = ((size_t)address & 2);
int* base_address = (int*)((char*)address - shift);
union I16PAIR {
struct {
T H;
T L;
} B;
int W;
SD_HOST_DEVICE
I16PAIR(){};
SD_HOST_DEVICE
~I16PAIR(){};
};
I16PAIR pairNew, pairOld, pairAssumed;
if (reinterpret_cast<int*>(address) == base_address) {
pairOld.B.L = val;
do {
pairNew.B.L = pairOld.B.L;
pairNew.B.H = pairOld.B.H * val;
pairAssumed.W = pairOld.W;
pairOld.W = atomicCAS(base_address, pairAssumed.W, pairNew.W);
} while (pairAssumed.W != pairOld.W);
return (T)pairOld.B.H;
} else {
pairOld.B.H = val;
do {
pairNew.B.H = pairOld.B.H;
pairNew.B.L = pairOld.B.L * val;
pairAssumed.W = pairOld.W;
pairOld.W = atomicCAS(base_address, pairAssumed.W, pairNew.W);
} while (pairAssumed.W != pairOld.W);
return (T)pairOld.B.L;
}
}
#ifdef HAS_INT16
template <>
inline SD_DEVICE int16_t sd_atomicMul<int16_t>(int16_t* address, int16_t val) {
return internal_16bit_atomicMul<int16_t>(address, val);
}
#endif // HAS_INT16
#ifdef HAS_UINT16
template <>
inline SD_DEVICE uint16_t sd_atomicMul<uint16_t>(uint16_t* address, uint16_t val) {
return internal_16bit_atomicMul<uint16_t>(address, val);
}
#endif // HAS_UINT16
#ifdef HAS_INT32
template <>
inline SD_DEVICE int sd_atomicMul<int>(int* address, int val) {
int* res_address = address;
int old = *res_address, assumed;
do {
assumed = old;
old = atomicCAS(res_address, assumed, val * assumed);
} while (assumed != old);
return old;
}
#endif // HAS_INT32
#ifdef HAS_UINT32
template <>
inline SD_DEVICE unsigned int sd_atomicMul<unsigned int>(unsigned int* address, unsigned int val) {
unsigned int* res_address = address;
unsigned int old = *res_address, assumed;
do {
assumed = old;
old = atomicCAS(res_address, assumed, val * assumed);
} while (assumed != old);
return old;
}
#endif // HAS_UINT32
#ifdef HAS_LONG
template <>
inline SD_DEVICE int64_t sd_atomicMul<int64_t>(int64_t* address, int64_t val) {
unsigned long long int* res_address = (unsigned long long int*)address;
unsigned long long int old = *res_address, assumed;
do {
assumed = old;
old = atomicCAS(res_address, assumed, val * assumed);
} while (assumed != old);
return (int64_t)old;
}
#endif // HAS_LONG
#ifdef HAS_UINT64
template <>
inline SD_DEVICE uint64_t sd_atomicMul<uint64_t>(uint64_t* address, uint64_t val) {
unsigned long long int* res_address = (unsigned long long int*)address;
unsigned long long int old = *res_address, assumed;
do {
assumed = old;
old = atomicCAS(res_address, assumed, val * assumed);
} while (assumed != old);
return (uint64_t)old;
}
#endif // HAS_UINT64
#if !defined(_WIN32) && !defined(_WIN64) && defined(HAS_LONG)
template <>
inline SD_DEVICE sd::LongType sd_atomicMul<sd::LongType>(sd::LongType* address, sd::LongType val) {
unsigned long long int* res_address = (unsigned long long*)address;
unsigned long long int old = *res_address, assumed;
do {
assumed = old;
old = atomicCAS(res_address, assumed, val * assumed);
} while (assumed != old);
return (sd::LongType)old;
}
#endif
#ifdef HAS_FLOAT16
template <>
inline SD_DEVICE float16 sd_atomicMul<float16>(float16* address, float16 val) {
return internal_16bit_atomicMul<float16>(address, val);
}
#endif // HAS_FLOAT16
} // namespace atomics
#endif // __CUDACC__
} // namespace math
} // namespace sd
#endif /* TEMPLATEMATH_H_ */