1616#ifndef VEC_DTYPES_CUH_
1717#define VEC_DTYPES_CUH_
1818
19- #ifdef FLASHINFER_ENABLE_BF16
2019#include < cuda_bf16.h>
21- #endif
2220#include < cuda_fp16.h>
23- #ifdef FLASHINFER_ENABLE_FP8
2421#include < cuda_fp8.h>
25- #endif
2622#include < cuda_runtime.h>
2723
2824#include < type_traits>
2925
3026namespace flashinfer {
3127
28+ #if (!defined(__CUDA_ARCH__) || (__CUDA_ARCH__ >= 900))
29+ #define FLASHINFER_HARDWARE_FP8_CONVERSION_ENABLED
30+ #endif
31+
3232#define FLASHINFER_INLINE inline __attribute__ ((always_inline)) __device__
3333
3434/* ****************** vec_t type cast *******************/
@@ -74,11 +74,130 @@ struct vec_cast<half, float> {
7474 }
7575};
7676
77- #if (!defined(__CUDA_ARCH__) || (__CUDA_ARCH__ >= 900))
77+ template <typename T>
78+ constexpr FLASHINFER_INLINE int get_exponent_bits () {
79+ if constexpr (std::is_same<T, __nv_fp8_e4m3>::value) {
80+ return 4 ;
81+ } else if constexpr (std::is_same<T, __nv_fp8_e5m2>::value) {
82+ return 5 ;
83+ } else if constexpr (std::is_same<T, half>::value) {
84+ return 5 ;
85+ } else if constexpr (std::is_same<T, nv_bfloat16>::value) {
86+ return 8 ;
87+ }
88+ }
89+
90+ template <typename T>
91+ constexpr FLASHINFER_INLINE int get_mantissa_bits () {
92+ if constexpr (std::is_same<T, __nv_fp8_e4m3>::value) {
93+ return 3 ;
94+ } else if constexpr (std::is_same<T, __nv_fp8_e5m2>::value) {
95+ return 2 ;
96+ } else if constexpr (std::is_same<T, half>::value) {
97+ return 11 ;
98+ } else if constexpr (std::is_same<T, nv_bfloat16>::value) {
99+ return 7 ;
100+ }
101+ }
102+
103+ /* !
104+ * \brief Fallback to software fast dequant implementation if hardware dequantization is not
105+ * available.
106+ * \note Inspired by Marlin's fast dequantization, but here we don't have to permute
107+ * weights order.
108+ * \ref
109+ * https://github.com/vllm-project/vllm/blob/6dffa4b0a6120159ef2fe44d695a46817aff65bc/csrc/quantization/fp8/fp8_marlin.cu#L120
110+ */
111+ template <typename fp8_dtype, typename fp16_dtype>
112+ __device__ void fast_dequant_f8f16x4 (uint32_t * input, uint2 * output) {
113+ uint32_t q = *input;
114+ if constexpr (std::is_same<fp8_dtype, __nv_fp8_e5m2>::value &&
115+ std::is_same<fp16_dtype, half>::value) {
116+ output->x = __byte_perm (0U , q, 0x5140 );
117+ output->y = __byte_perm (0U , q, 0x7362 );
118+ } else {
119+ constexpr int FP8_EXPONENT = get_exponent_bits<fp8_dtype>();
120+ constexpr int FP8_MANTISSA = get_mantissa_bits<fp8_dtype>();
121+ constexpr int FP16_EXPONENT = get_exponent_bits<fp16_dtype>();
122+
123+ constexpr int RIGHT_SHIFT = FP16_EXPONENT - FP8_EXPONENT ;
124+ // Calculate MASK for extracting mantissa and exponent
125+ constexpr int MASK1 = 0x80000000 ;
126+ constexpr int MASK2 = MASK1 >> (FP8_EXPONENT + FP8_MANTISSA );
127+ constexpr int MASK3 = MASK2 & 0x7fffffff ;
128+ constexpr int MASK = MASK3 | (MASK3 >> 16 );
129+ // Final MASK value: 0x7F007F00
130+ q = __byte_perm (q, q, 0x1302 );
131+
132+ // Extract and shift FP8 values to FP16 format
133+ uint32_t Out1 = (q & 0x80008000 ) | ((q & MASK ) >> RIGHT_SHIFT );
134+ uint32_t Out2 = ((q << 8 ) & 0x80008000 ) | (((q << 8 ) & MASK ) >> RIGHT_SHIFT );
135+
136+ constexpr int BIAS_OFFSET = (1 << (FP16_EXPONENT - 1 )) - (1 << (FP8_EXPONENT - 1 ));
137+ // Construct and apply exponent bias
138+ if (std::is_same<fp16_dtype, half>::value) {
139+ const half2 bias_reg = __float2half2_rn (float (1 << BIAS_OFFSET ));
140+
141+ // Convert to half2 and apply bias
142+ *(half2*)&(output->x ) = __hmul2 (*reinterpret_cast <const half2*>(&Out1), bias_reg);
143+ *(half2*)&(output->y ) = __hmul2 (*reinterpret_cast <const half2*>(&Out2), bias_reg);
144+ } else {
145+ constexpr uint32_t BIAS = (BIAS_OFFSET + 127 ) << 23 ;
146+ const nv_bfloat162 bias_reg = __float2bfloat162_rn (*reinterpret_cast <const float *>(&BIAS ));
147+ // Convert to bfloat162 and apply bias
148+ *(nv_bfloat162*)&(output->x ) =
149+ __hmul2 (*reinterpret_cast <const nv_bfloat162*>(&Out1), bias_reg);
150+ *(nv_bfloat162*)&(output->y ) =
151+ __hmul2 (*reinterpret_cast <const nv_bfloat162*>(&Out2), bias_reg);
152+ }
153+ }
154+ }
155+
156+ template <>
157+ struct vec_cast <nv_bfloat16, __nv_fp8_e4m3> {
158+ template <size_t vec_size>
159+ FLASHINFER_INLINE static void cast (nv_bfloat16* dst, const __nv_fp8_e4m3* src) {
160+ if constexpr (vec_size == 1 ) {
161+ dst[0 ] = nv_bfloat16 (src[0 ]);
162+ } else if constexpr (vec_size == 2 ) {
163+ dst[0 ] = nv_bfloat16 (src[0 ]);
164+ dst[1 ] = nv_bfloat16 (src[1 ]);
165+ } else {
166+ static_assert (vec_size % 4 == 0 , " vec_size must be a multiple of 4" );
167+ #pragma unroll
168+ for (uint32_t i = 0 ; i < vec_size / 4 ; ++i) {
169+ fast_dequant_f8f16x4<__nv_fp8_e4m3, nv_bfloat16>((uint32_t *)&src[i * 4 ],
170+ (uint2 *)&dst[i * 4 ]);
171+ }
172+ }
173+ }
174+ };
175+
176+ template <>
177+ struct vec_cast <nv_bfloat16, __nv_fp8_e5m2> {
178+ template <size_t vec_size>
179+ FLASHINFER_INLINE static void cast (nv_bfloat16* dst, const __nv_fp8_e5m2* src) {
180+ if constexpr (vec_size == 1 ) {
181+ dst[0 ] = nv_bfloat16 (src[0 ]);
182+ } else if constexpr (vec_size == 2 ) {
183+ dst[0 ] = nv_bfloat16 (src[0 ]);
184+ dst[1 ] = nv_bfloat16 (src[1 ]);
185+ } else {
186+ static_assert (vec_size % 4 == 0 , " vec_size must be a multiple of 4" );
187+ #pragma unroll
188+ for (uint32_t i = 0 ; i < vec_size / 4 ; ++i) {
189+ fast_dequant_f8f16x4<__nv_fp8_e5m2, nv_bfloat16>((uint32_t *)&src[i * 4 ],
190+ (uint2 *)&dst[i * 4 ]);
191+ }
192+ }
193+ }
194+ };
195+
78196template <>
79197struct vec_cast <__nv_fp8_e4m3, half> {
80198 template <size_t vec_size>
81199 FLASHINFER_INLINE static void cast (__nv_fp8_e4m3* dst, const half* src) {
200+ #ifdef FLASHINFER_HARDWARE_FP8_CONVERSION_ENABLED
82201 if constexpr (vec_size == 1 ) {
83202 dst[0 ] = __nv_fp8_e4m3 (src[0 ]);
84203 } else {
@@ -90,13 +209,20 @@ struct vec_cast<__nv_fp8_e4m3, half> {
90209 *(uint16_t *)&dst[i * 2 ] = y;
91210 }
92211 }
212+ #else
213+ #pragma unroll
214+ for (size_t i = 0 ; i < vec_size; ++i) {
215+ dst[i] = __nv_fp8_e4m3 (src[i]);
216+ }
217+ #endif // FLASHINFER_HARDWARE_FP8_CONVERSION_ENABLED
93218 }
94219};
95220
96221template <>
97222struct vec_cast <__nv_fp8_e5m2, half> {
98223 template <size_t vec_size>
99224 FLASHINFER_INLINE static void cast (__nv_fp8_e5m2* dst, const half* src) {
225+ #ifdef FLASHINFER_HARDWARE_FP8_CONVERSION_ENABLED
100226 if constexpr (vec_size == 1 ) {
101227 dst[0 ] = __nv_fp8_e5m2 (src[0 ]);
102228 } else {
@@ -108,13 +234,20 @@ struct vec_cast<__nv_fp8_e5m2, half> {
108234 *(uint16_t *)&dst[i * 2 ] = y;
109235 }
110236 }
237+ #else
238+ #pragma unroll
239+ for (size_t i = 0 ; i < vec_size; ++i) {
240+ dst[i] = __nv_fp8_e5m2 (src[i]);
241+ }
242+ #endif // FLASHINFER_HARDWARE_FP8_CONVERSION_ENABLED
111243 }
112244};
113245
114246template <>
115247struct vec_cast <half, __nv_fp8_e4m3> {
116248 template <size_t vec_size>
117249 FLASHINFER_INLINE static void cast (half* dst, const __nv_fp8_e4m3* src) {
250+ #ifdef FLASHINFER_HARDWARE_FP8_CONVERSION_ENABLED
118251 if constexpr (vec_size == 1 ) {
119252 dst[0 ] = half (src[0 ]);
120253 } else {
@@ -126,13 +259,28 @@ struct vec_cast<half, __nv_fp8_e4m3> {
126259 *(uint32_t *)&dst[i * 2 ] = y;
127260 }
128261 }
262+ #else
263+ if constexpr (vec_size == 1 ) {
264+ dst[0 ] = half (src[0 ]);
265+ } else if constexpr (vec_size == 2 ) {
266+ dst[0 ] = half (src[0 ]);
267+ dst[1 ] = half (src[1 ]);
268+ } else {
269+ static_assert (vec_size % 4 == 0 , " vec_size must be a multiple of 4" );
270+ #pragma unroll
271+ for (uint32_t i = 0 ; i < vec_size / 4 ; ++i) {
272+ fast_dequant_f8f16x4<__nv_fp8_e4m3, half>((uint32_t *)&src[i * 4 ], (uint2 *)&dst[i * 4 ]);
273+ }
274+ }
275+ #endif // FLASHINFER_HARDWARE_FP8_CONVERSION_ENABLED
129276 }
130277};
131278
132279template <>
133280struct vec_cast <half, __nv_fp8_e5m2> {
134281 template <size_t vec_size>
135282 FLASHINFER_INLINE static void cast (half* dst, const __nv_fp8_e5m2* src) {
283+ #ifdef FLASHINFER_HARDWARE_FP8_CONVERSION_ENABLED
136284 if constexpr (vec_size == 1 ) {
137285 dst[0 ] = half (src[0 ]);
138286 } else {
@@ -144,13 +292,23 @@ struct vec_cast<half, __nv_fp8_e5m2> {
144292 *(uint32_t *)&dst[i * 2 ] = y;
145293 }
146294 }
295+ #else
296+ if constexpr (vec_size == 1 ) {
297+ dst[0 ] = half (src[0 ]);
298+ } else if constexpr (vec_size == 2 ) {
299+ dst[0 ] = half (src[0 ]);
300+ dst[1 ] = half (src[1 ]);
301+ } else {
302+ static_assert (vec_size % 4 == 0 , " vec_size must be a multiple of 4" );
303+ #pragma unroll
304+ for (uint32_t i = 0 ; i < vec_size / 4 ; ++i) {
305+ fast_dequant_f8f16x4<__nv_fp8_e5m2, half>((uint32_t *)&src[i * 4 ], (uint2 *)&dst[i * 4 ]);
306+ }
307+ }
308+ #endif // FLASHINFER_HARDWARE_FP8_CONVERSION_ENABLED
147309 }
148310};
149311
150- #endif // !defined(__CUDA_ARCH__) || (__CUDA_ARCH__ >= 900)
151-
152- #ifdef FLASHINFER_ENABLE_BF16
153-
154312template <>
155313struct vec_cast <float , nv_bfloat16> {
156314 template <size_t vec_size>
@@ -180,7 +338,6 @@ struct vec_cast<nv_bfloat16, float> {
180338 }
181339 }
182340};
183- #endif // FLASHINFER_ENABLE_BF16
184341
185342template <typename float_t , size_t vec_size>
186343struct vec_t {
@@ -230,7 +387,6 @@ FLASHINFER_INLINE void cast_store_impl(tgt_float_t* dst_ptr,
230387 }
231388}
232389
233- #ifdef FLASHINFER_ENABLE_FP8
234390/* ****************** vec_t<__nv_fp8_e4m3> *******************/
235391
236392// __nv_fp8_e4m3 x 1
@@ -724,7 +880,6 @@ struct vec_t<__nv_fp8_e5m2, vec_size> {
724880 }
725881 }
726882};
727- #endif
728883
729884/* ****************** vec_t<half> *******************/
730885
@@ -889,7 +1044,6 @@ struct vec_t<half, vec_size> {
8891044 }
8901045};
8911046
892- #ifdef FLASHINFER_ENABLE_BF16
8931047/* ****************** vec_t<nv_bfloat16> *******************/
8941048
8951049// nv_bfloat16 x 1
@@ -1071,8 +1225,6 @@ struct vec_t<nv_bfloat16, vec_size> {
10711225 }
10721226};
10731227
1074- #endif
1075-
10761228/* ****************** vec_t<float> *******************/
10771229
10781230// float x 1
0 commit comments