/* * SPDX-FileCopyrightText: Copyright (c) 1993-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ #ifndef COMMON_CUH #define COMMON_CUH // TODO: Remove WAR once issue resolved in CUB (CUDA 12.6+?) #ifndef CUDA_VERSION #include #endif // CUDA_VERSION #include "common/cublasWrapper.h" #include "common/cubCcclCompat.h" #include #define HDI inline __host__ __device__ using kv_float = cub::KeyValuePair; using kv_half = cub::KeyValuePair; using kv_half2 = cub::KeyValuePair; template __device__ inline T tanh(const T& x); template __device__ inline T rsqrt(const T& x); template __device__ inline T exp(const T& x); // Float32 Operations template <> __device__ inline float tanh(const float& x) { return tanhf(x); } template <> __device__ inline float rsqrt(const float& x) { return rsqrtf(x); } template <> __device__ inline float exp(const float& x) { return expf(x); } __device__ inline kv_float operator+(const kv_float& a, const kv_float& b) { return kv_float(a.key + b.key, a.value + b.value); } // Half Operations __device__ inline half2 __hadd2_with_fallback(const half2 a, const half2 b) { #if __CUDA_ARCH__ >= 530 return __hadd2(a, b); #else float2 out{}; out.x = __half2float(a.x) + __half2float(b.x); out.y = __half2float(a.y) + __half2float(b.y); return __float22half2_rn(out); #endif } #if __CUDA_ARCH__ < 530 template __device__ inline T operator+(const T& a, const T& b); template __device__ inline T operator*(const T& a, const T& b); template <> __device__ inline half2 operator+(const half2& a, const half2& b) { return __hadd2_with_fallback(a, b); } template <> __device__ inline half2 operator*(const half2& a, const half2& b) { float2 out{}; out.x = __half2float(a.x) * __half2float(b.x); out.y = __half2float(a.y) * __half2float(b.y); return __float22half2_rn(out); } template __device__ inline T operator+(const T& a, const T& b); template __device__ inline T operator/(const T& a, const T& b); template __device__ inline T& operator+=(T& a, const T& b); template __device__ inline T operator-(const T& a, const T& b); template __device__ inline T operator*(const T& a, const T& b); template __device__ inline bool operator>(const T& a, const T& b); template __device__ inline bool operator>=(const T& a, const T& b); template __device__ inline bool operator<(const T& a, const T& b); template __device__ inline bool operator<=(const T& a, const T& b); template <> __device__ inline half operator+(const half& a, const half& b) { return __float2half(__half2float(a) + __half2float(b)); } template <> __device__ inline half& operator+=(half& a, const half& b) { a = __float2half(__half2float(a) + __half2float(b)); return a; } template <> __device__ inline half operator-(const half& a, const half& b) { return __float2half(__half2float(a) - __half2float(b)); } template <> __device__ inline half operator*(const half& a, const half& b) { return __float2half(__half2float(a) * __half2float(b)); } template <> __device__ inline half operator/(const half& a, const half& b) { return __float2half(__half2float(a) / __half2float(b)); } template <> __device__ inline bool operator>(const half& a, const half& b) { return __half2float(a) > __half2float(b); } template <> __device__ inline bool operator>=(const half& a, const half& b) { return __half2float(a) >= __half2float(b); } template <> __device__ inline bool operator<(const half& a, const half& b) { return __half2float(a) < __half2float(b); } template <> __device__ inline bool operator<=(const half& a, const half& b) { return __half2float(a) <= __half2float(b); } #endif template <> __device__ inline half tanh(const half& x) { const float tmp = tanhf(__half2float(x)); return __float2half(tmp); } template <> __device__ inline half2 tanh(const half2& x) { // at the moment, there is no half2 tanh builtin float2 tmp = (__half22float2(x)); tmp.x = tanhf(tmp.x); tmp.y = tanhf(tmp.y); return __float22half2_rn(tmp); } template <> __device__ inline half rsqrt(const half& x) { #if __CUDA_ARCH__ >= 530 return hrsqrt(x); #else return __float2half(rsqrt(__half2float(x))); #endif } template <> __device__ inline half exp(const half& x) { #if __CUDA_ARCH__ >= 530 return hexp(x); #else return __float2half(exp(__half2float(x))); #endif } __device__ inline kv_half operator+(const kv_half& a, const kv_half& b) { const half2 a2 = __halves2half2(a.key, a.value); const half2 b2 = __halves2half2(b.key, b.value); const half2 res = __hadd2_with_fallback(a2, b2); return kv_half(res.x, res.y); } __device__ inline kv_half2 operator+(const kv_half2& a, const kv_half2& b) { return kv_half2(__hadd2_with_fallback(a.key, b.key), __hadd2_with_fallback(a.value, b.value)); } // Helper Functions template using kvp = cub::KeyValuePair; template __device__ inline void layerNorm( const kvp& threadData, const int32_t ld, const int32_t offset, const P* beta, const P* gamma, T* output) { // Assuming threadData is already divided by ld using BlockReduce = cub::BlockReduce, TPB>; __shared__ typename BlockReduce::TempStorage temp_storage; __shared__ R mu; // mean __shared__ R rsigma; // 1 / std.dev. const auto sumKV = BlockReduce(temp_storage).Reduce(threadData, [](auto const& lhs, auto const& rhs){return lhs + rhs;}); if (threadIdx.x == 0) { mu = sumKV.key; rsigma = rsqrt(sumKV.value - mu * mu); } __syncthreads(); for (int32_t i = threadIdx.x; i < ld; i += TPB) { const int32_t idx = offset + i; const R val = output[idx]; const R g(gamma[i]); const R b(beta[i]); output[idx] = g * (val - mu) * rsigma + b; } } template __device__ inline void layerNormSmall( const T val, const kvp& threadData, const int32_t ld, const int32_t idx, const P* beta, const P* gamma, T* output) { // Assuming threadData is already divided by ld // Small settings: the block covers the leading dimension TPB >= ld. The input // value is available in a register using BlockReduce = cub::BlockReduce, TPB>; __shared__ typename BlockReduce::TempStorage temp_storage; __shared__ T mu; // mean __shared__ T rsigma; // 1 / std.dev. const auto sumKV = BlockReduce(temp_storage).Reduce(threadData, [](auto const& lhs, auto const& rhs){return lhs + rhs;}); if (threadIdx.x == 0) { mu = sumKV.key; rsigma = rsqrt(sumKV.value - mu * mu); } __syncthreads(); if (threadIdx.x < ld) { const T g(gamma[threadIdx.x]); const T b(beta[threadIdx.x]); output[idx] = g * (val - mu) * rsigma + b; } } template __device__ inline void scaledSoftmaxSmall( const int32_t ld, const int32_t lastValid, const float rsqrtHeadSize, const T* input, T* output) { using BlockReduce = cub::BlockReduce; __shared__ typename BlockReduce::TempStorage tmpStorage; __shared__ float rZ; __shared__ float fMax; const int32_t offset = (blockIdx.y * gridDim.x + blockIdx.x) * ld; const float w(rsqrtHeadSize); float threadData(-FLT_MAX); const int32_t idx = offset + threadIdx.x; if (threadIdx.x < lastValid) { threadData = input[idx]; } const float maxElem = BlockReduce(tmpStorage).Reduce(threadData, compat::getCudaMaxOp()); if (threadIdx.x == 0) { fMax = maxElem; } __syncthreads(); if (threadIdx.x < lastValid) { threadData = exp((threadData - fMax) * w); } else { threadData = 0; } const auto Z = BlockReduce(tmpStorage).Reduce(threadData, [](auto const& lhs, auto const& rhs){return lhs + rhs;}); if (threadIdx.x == 0) { rZ = (1.f) / Z; } __syncthreads(); if (threadIdx.x < ld) { float const val = (threadIdx.x < lastValid) ? threadData * rZ : 0.F; output[idx] = static_cast(val); } } template __device__ inline void scaledSoftmax( const int32_t ld, const int32_t lastValid, const float rsqrtHeadSize, const T* input, T* output) { using BlockReduce = cub::BlockReduce; __shared__ typename BlockReduce::TempStorage tmpStorage; __shared__ float rZ; __shared__ float fMax; const int32_t offset = (blockIdx.y * gridDim.x + blockIdx.x) * ld; const float w(rsqrtHeadSize); float threadData(-FLT_MAX); if (lastValid >= blockDim.x) { threadData = 0; } for (int32_t i = threadIdx.x; i < lastValid; i += TPB) { const int32_t idx = offset + i; threadData = max(static_cast(input[idx]), threadData); } const float maxElem = BlockReduce(tmpStorage).Reduce(threadData, compat::getCudaMaxOp()); if (threadIdx.x == 0) { fMax = maxElem; } __syncthreads(); threadData = 0; for (int32_t i = threadIdx.x; i < lastValid; i += TPB) { const int32_t idx = offset + i; threadData += exp((static_cast(input[idx]) - fMax) * w); } const auto Z = BlockReduce(tmpStorage).Reduce(threadData, [](auto const& lhs, auto const& rhs){return lhs + rhs;}); if (threadIdx.x == 0) { rZ = 1.f / Z; } __syncthreads(); for (int32_t i = threadIdx.x; i < ld; i += TPB) { const int32_t idx = offset + i; const float val = (i < lastValid) ? exp((static_cast(input[idx]) - fMax) * w) * rZ : 0.f; output[idx] = T(val); } } template constexpr HDI IntType ceildiv(IntType a, IntType b) { return (a + b - 1) / b; } template constexpr HDI IntType alignTo(IntType a, IntType b) { return ceildiv(a, b) * b; } template struct BytesToType; template <> struct BytesToType<2> { using type = uint16_t; }; template <> struct BytesToType<4> { using type = uint32_t; }; template <> struct BytesToType<8> { using type = uint64_t; }; template <> struct BytesToType<16> { using type = float4; }; template __device__ inline void copy(const void* local, void* data) { using T = typename BytesToType::type; const T* in = static_cast(local); T* out = static_cast(data); *out = *in; } template __device__ inline T myExp(const T x); template <> __device__ inline half myExp(const half x) { return exp(x); } template <> __device__ inline float myExp(const float x) { return __expf(x); } static inline __device__ uint32_t float4_to_char4(float x, float y, float z, float w) { uint32_t dst; #if defined(__CUDA_ARCH__) && __CUDA_ARCH__ >= 720 uint32_t a; asm volatile("cvt.rni.sat.s32.f32 %0, %1;\n" : "=r"(a) : "f"(x)); uint32_t b; asm volatile("cvt.rni.sat.s32.f32 %0, %1;\n" : "=r"(b) : "f"(y)); uint32_t c; asm volatile("cvt.rni.sat.s32.f32 %0, %1;\n" : "=r"(c) : "f"(z)); uint32_t d; asm volatile("cvt.rni.sat.s32.f32 %0, %1;\n" : "=r"(d) : "f"(w)); asm volatile("cvt.pack.sat.s8.s32.b32 %0, %1, %2, 0;\n" : "=r"(dst) : "r"(d), "r"(c)); asm volatile("cvt.pack.sat.s8.s32.b32 %0, %1, %2, %0;\n" : "+r"(dst) : "r"(b), "r"(a)); #else char4 tmp; tmp.x = x; tmp.y = y; tmp.z = z; tmp.w = w; dst = reinterpret_cast(tmp); #endif return dst; } inline __device__ char quantize(const float x, const float qScale) { int32_t tmpq = __float2int_rn(qScale * x); // scale and round char tmpq8 = min(127, max(-127, tmpq)); // clip and cast return tmpq8; } inline __device__ void ldg(const int8_t* input, uint4& data) { data = *reinterpret_cast(input); } inline __device__ void stg(int8_t* output, uint4& data) { *reinterpret_cast(output) = data; } inline __device__ uint32_t pack4(const float (&hdata)[4], const float qScale) { return float4_to_char4(hdata[0] * qScale, hdata[1] * qScale , hdata[2] * qScale, hdata[3] * qScale); } #endif // #ifndef COMMON_CUH