main
cuh 344 lines 13.2 KB
Raw
1 #pragma once
2
3 #include "../../../../attention/attention_dtypes.h"
4 #include <torch/headeronly/core/ScalarType.h>
5 #include <assert.h>
6 #include <float.h>
7 #include <stdint.h>
8 #include <type_traits>
9
10 namespace vllm {
11 #ifndef USE_ROCM
12
13 namespace fp8 {
14 #ifdef ENABLE_FP8
15
16 // Unspecialized conversions are a compile error: the old passthrough
17 // (`return x;`) silently skipped fp8 encoding for any (Tout, Tin) pair
18 // without a specialization below (e.g. the torch stable-ABI scalar types),
19 // corrupting quantized data with no runtime signal.
20 template <typename>
21 inline constexpr bool _no_conversion_specialization = false;
22
23 template <typename Tout, typename Tin>
24 __inline__ __device__ Tout vec_conversion(
25 const Tin& x, const __nv_fp8_interpretation_t fp8_type = __NV_E4M3) {
26 static_assert(_no_conversion_specialization<Tin>,
27 "no vec_conversion specialization for this (Tout, Tin) pair");
28 }
29
30 // float -> c10::Float8_e4m3fn
31 template <>
32 __inline__ __device__ c10::Float8_e4m3fn
33 vec_conversion<c10::Float8_e4m3fn, float>(
34 const float& a, const __nv_fp8_interpretation_t fp8_type) {
35 #if defined(__CUDA_ARCH__) && __CUDA_ARCH__ < 800
36 return static_cast<c10::Float8_e4m3fn>(a);
37 #else
38 return c10::Float8_e4m3fn(__nv_cvt_float_to_fp8(a, __NV_SATFINITE, fp8_type),
39 c10::Float8_e4m3fn::from_bits());
40 #endif
41 }
42
43 /* Scaled and vectorized conversions, for data exchange between high and low
44 precision domains Convention of the scale in API, e.g: FP8_data =
45 Quantization( High_Precision_data / scale ) s.t. Quantize(HP / scale) => FP8
46 Dequant(FP8) * scale => HP
47 */
48
49 template <typename Tout, typename Tin>
50 __inline__ __device__ Tout scaled_vec_conversion(
51 const Tin& x, const float scale, const __nv_fp8_interpretation_t fp8_type) {
52 static_assert(
53 _no_conversion_specialization<Tin>,
54 "no scaled_vec_conversion specialization for this (Tout, Tin) pair");
55 }
56
57 // fp8 -> half
58 template <>
59 __inline__ __device__ uint16_t scaled_vec_conversion<uint16_t, uint8_t>(
60 const uint8_t& a, const float scale,
61 const __nv_fp8_interpretation_t fp8_type) {
62 __half_raw tmp = __nv_cvt_fp8_to_halfraw(a, fp8_type);
63 return float_to_half(half_to_float(tmp.x) * scale);
64 }
65
66 // fp8x2 -> half2
67 template <>
68 __inline__ __device__ uint32_t scaled_vec_conversion<uint32_t, uint16_t>(
69 const uint16_t& a, const float scale,
70 const __nv_fp8_interpretation_t fp8_type) {
71 union {
72 uint16_t u16[2];
73 uint32_t u32;
74 } tmp;
75 __half2_raw res = __nv_cvt_fp8x2_to_halfraw2(a, fp8_type);
76 tmp.u16[0] = float_to_half(half_to_float(res.x) * scale);
77 tmp.u16[1] = float_to_half(half_to_float(res.y) * scale);
78 return tmp.u32;
79 }
80
81 // fp8x4 -> half2x2
82 template <>
83 __inline__ __device__ uint2 scaled_vec_conversion<uint2, uint32_t>(
84 const uint32_t& a, const float scale,
85 const __nv_fp8_interpretation_t fp8_type) {
86 union {
87 uint2 u32x2;
88 uint32_t u32[2];
89 } tmp;
90 tmp.u32[0] =
91 scaled_vec_conversion<uint32_t, uint16_t>((uint16_t)a, scale, fp8_type);
92 tmp.u32[1] = scaled_vec_conversion<uint32_t, uint16_t>((uint16_t)(a >> 16U),
93 scale, fp8_type);
94 return tmp.u32x2;
95 }
96
97 // fp8x8 -> half2x4
98 template <>
99 __inline__ __device__ uint4
100 scaled_vec_conversion<uint4, uint2>(const uint2& a, const float scale,
101 const __nv_fp8_interpretation_t fp8_type) {
102 union {
103 uint4 u64x2;
104 uint2 u64[2];
105 } tmp;
106 tmp.u64[0] = scaled_vec_conversion<uint2, uint32_t>(a.x, scale, fp8_type);
107 tmp.u64[1] = scaled_vec_conversion<uint2, uint32_t>(a.y, scale, fp8_type);
108 return tmp.u64x2;
109 }
110
111 // fp8 -> __nv_bfloat16
112 template <>
113 __inline__ __device__ __nv_bfloat16
114 scaled_vec_conversion<__nv_bfloat16, uint8_t>(
115 const uint8_t& a, const float scale,
116 const __nv_fp8_interpretation_t fp8_type) {
117 // Note there is no direct convert function from fp8 to bf16.
118 // fp8 -> half
119 __half_raw res = __nv_cvt_fp8_to_halfraw(a, fp8_type);
120 // half -> float -> bf16
121 float tmp = half_to_float(res.x);
122 return __float2bfloat16(tmp * scale);
123 }
124
125 // fp8x2 -> __nv_bfloat162
126 template <>
127 __inline__ __device__ __nv_bfloat162
128 scaled_vec_conversion<__nv_bfloat162, uint16_t>(
129 const uint16_t& a, const float scale,
130 const __nv_fp8_interpretation_t fp8_type) {
131 __nv_bfloat162 res;
132 res.x = scaled_vec_conversion<__nv_bfloat16, uint8_t>((uint8_t)a, scale,
133 fp8_type);
134 res.y = scaled_vec_conversion<__nv_bfloat16, uint8_t>((uint8_t)(a >> 8U),
135 scale, fp8_type);
136 return res;
137 }
138
139 // fp8x4 -> bf16_4_t
140 template <>
141 __inline__ __device__ bf16_4_t scaled_vec_conversion<bf16_4_t, uint32_t>(
142 const uint32_t& a, const float scale,
143 const __nv_fp8_interpretation_t fp8_type) {
144 bf16_4_t res;
145 res.x = scaled_vec_conversion<__nv_bfloat162, uint16_t>((uint16_t)a, scale,
146 fp8_type);
147 res.y = scaled_vec_conversion<__nv_bfloat162, uint16_t>((uint16_t)(a >> 16U),
148 scale, fp8_type);
149 return res;
150 }
151
152 // fp8x8 -> bf16_8_t
153 template <>
154 __inline__ __device__ bf16_8_t scaled_vec_conversion<bf16_8_t, uint2>(
155 const uint2& a, const float scale,
156 const __nv_fp8_interpretation_t fp8_type) {
157 bf16_4_t tmp1, tmp2;
158 tmp1 = scaled_vec_conversion<bf16_4_t, uint32_t>(a.x, scale, fp8_type);
159 tmp2 = scaled_vec_conversion<bf16_4_t, uint32_t>(a.y, scale, fp8_type);
160 bf16_8_t res;
161 res.x = tmp1.x;
162 res.y = tmp1.y;
163 res.z = tmp2.x;
164 res.w = tmp2.y;
165 return res;
166 }
167
168 // fp8 -> float
169 template <>
170 __inline__ __device__ float scaled_vec_conversion<float, uint8_t>(
171 const uint8_t& a, const float scale,
172 const __nv_fp8_interpretation_t fp8_type) {
173 // fp8 -> half
174 __half_raw res = __nv_cvt_fp8_to_halfraw(a, fp8_type);
175 uint16_t tmp = res.x;
176
177 // half -> float
178 return half_to_float(tmp) * scale;
179 }
180
181 // fp8x2 -> float2
182 template <>
183 __inline__ __device__ float2 scaled_vec_conversion<float2, uint16_t>(
184 const uint16_t& a, const float scale,
185 const __nv_fp8_interpretation_t fp8_type) {
186 // fp8x2 -> half2
187 uint32_t tmp = scaled_vec_conversion<uint32_t, uint16_t>(a, scale, fp8_type);
188 // half2 -> float2
189 return half2_to_float2(tmp);
190 }
191
192 // fp8x4 -> float4
193 template <>
194 __inline__ __device__ Float4_ scaled_vec_conversion<Float4_, uint32_t>(
195 const uint32_t& a, const float scale,
196 const __nv_fp8_interpretation_t fp8_type) {
197 Float4_ res;
198 res.x = scaled_vec_conversion<float2, uint16_t>((uint16_t)a, scale, fp8_type);
199 res.y = scaled_vec_conversion<float2, uint16_t>((uint16_t)(a >> 16U), scale,
200 fp8_type);
201 return res;
202 }
203
204 // fp8x8 -> float8
205 template <>
206 __inline__ __device__ Float8_ scaled_vec_conversion<Float8_, uint2>(
207 const uint2& a, const float scale,
208 const __nv_fp8_interpretation_t fp8_type) {
209 Float4_ tmp1, tmp2;
210 tmp1 = scaled_vec_conversion<Float4_, uint32_t>(a.x, scale, fp8_type);
211 tmp2 = scaled_vec_conversion<Float4_, uint32_t>(a.y, scale, fp8_type);
212 Float8_ res;
213 res.x = tmp1.x;
214 res.y = tmp1.y;
215 res.z = tmp2.x;
216 res.w = tmp2.y;
217 return res;
218 }
219
220 // half -> fp8
221 template <>
222 __inline__ __device__ uint8_t scaled_vec_conversion<uint8_t, uint16_t>(
223 const uint16_t& a, const float scale,
224 const __nv_fp8_interpretation_t fp8_type) {
225 __nv_fp8_storage_t res =
226 __nv_cvt_float_to_fp8(half_to_float(a) / scale, __NV_SATFINITE, fp8_type);
227 return (uint8_t)res;
228 }
229
230 // bf16 -> fp8
231 template <>
232 __inline__ __device__ uint8_t scaled_vec_conversion<uint8_t, __nv_bfloat16>(
233 const __nv_bfloat16& a, const float scale,
234 const __nv_fp8_interpretation_t fp8_type) {
235 #if defined(__CUDA_ARCH__) && __CUDA_ARCH__ < 800
236 assert(false);
237 #else
238 __nv_fp8_storage_t res = __nv_cvt_float_to_fp8(__bfloat162float(a) / scale,
239 __NV_SATFINITE, fp8_type);
240 return (uint8_t)res;
241 #endif
242 __builtin_unreachable(); // Suppress missing return statement warning
243 }
244
245 // torch stable-ABI (headeronly) scalar types delegate to the CUDA-native
246 // conversions, so libtorch_stable kernels dispatched on c10::BFloat16 /
247 // c10::Half quantize correctly without manual casts.
248 template <>
249 __inline__ __device__ uint8_t scaled_vec_conversion<uint8_t, c10::BFloat16>(
250 const c10::BFloat16& a, const float scale,
251 const __nv_fp8_interpretation_t fp8_type) {
252 return scaled_vec_conversion<uint8_t, __nv_bfloat16>(
253 reinterpret_cast<const __nv_bfloat16&>(a), scale, fp8_type);
254 }
255
256 template <>
257 __inline__ __device__ uint8_t scaled_vec_conversion<uint8_t, c10::Half>(
258 const c10::Half& a, const float scale,
259 const __nv_fp8_interpretation_t fp8_type) {
260 return scaled_vec_conversion<uint8_t, uint16_t>(
261 reinterpret_cast<const uint16_t&>(a), scale, fp8_type);
262 }
263
264 // float -> fp8
265 template <>
266 __inline__ __device__ uint8_t scaled_vec_conversion<uint8_t, float>(
267 const float& a, const float scale,
268 const __nv_fp8_interpretation_t fp8_type) {
269 __nv_fp8_storage_t res =
270 __nv_cvt_float_to_fp8(a / scale, __NV_SATFINITE, fp8_type);
271 return (uint8_t)res;
272 }
273
274 // fp8x4 -> float4
275 template <>
276 __inline__ __device__ float4 scaled_vec_conversion<float4, uint32_t>(
277 const uint32_t& a, const float scale,
278 const __nv_fp8_interpretation_t fp8_type) {
279 Float4_ tmp = scaled_vec_conversion<Float4_, uint32_t>(a, scale, fp8_type);
280 float4 res = make_float4(tmp.x.x, tmp.x.y, tmp.y.x, tmp.y.y);
281 return res;
282 }
283 #endif // ENABLE_FP8
284
285 template <typename Tout, typename Tin, Fp8KVCacheDataType kv_dt>
286 __inline__ __device__ Tout scaled_convert(const Tin& x, const float scale) {
287 #ifdef ENABLE_FP8
288 if constexpr (kv_dt == Fp8KVCacheDataType::kFp8E4M3) {
289 return scaled_vec_conversion<Tout, Tin>(x, scale, __NV_E4M3);
290 } else if constexpr (kv_dt == Fp8KVCacheDataType::kFp8E5M2) {
291 return scaled_vec_conversion<Tout, Tin>(x, scale, __NV_E5M2);
292 }
293 #endif
294 assert(false);
295 __builtin_unreachable(); // Suppress missing return statement warning
296 }
297
298 // The following macro is used to dispatch the conversion function based on
299 // the data type of the key and value cache. The FN is a macro that calls a
300 // function with template<typename scalar_t, typename cache_t,
301 // Fp8KVCacheDataType kv_dt>.
302 #define DISPATCH_BY_KV_CACHE_DTYPE(SRC_DTYPE, KV_DTYPE, FN) \
303 vllm::Fp8KVCacheDataType KV_CACHE_DTYPE = \
304 vllm::get_fp8_kv_cache_data_type(KV_DTYPE); \
305 if (KV_CACHE_DTYPE == vllm::Fp8KVCacheDataType::kAuto) { \
306 if (SRC_DTYPE == torch::headeronly::ScalarType::Float) { \
307 FN(float, float, vllm::Fp8KVCacheDataType::kAuto); \
308 } else if (SRC_DTYPE == torch::headeronly::ScalarType::Half) { \
309 FN(uint16_t, uint16_t, vllm::Fp8KVCacheDataType::kAuto); \
310 } else if (SRC_DTYPE == torch::headeronly::ScalarType::BFloat16) { \
311 FN(__nv_bfloat16, __nv_bfloat16, vllm::Fp8KVCacheDataType::kAuto); \
312 } else { \
313 STD_TORCH_CHECK(false, \
314 "Unsupported input type of kv cache: ", SRC_DTYPE); \
315 } \
316 } else if (KV_CACHE_DTYPE == vllm::Fp8KVCacheDataType::kFp8E4M3) { \
317 if (SRC_DTYPE == torch::headeronly::ScalarType::Float) { \
318 FN(float, uint8_t, vllm::Fp8KVCacheDataType::kFp8E4M3); \
319 } else if (SRC_DTYPE == torch::headeronly::ScalarType::Half) { \
320 FN(uint16_t, uint8_t, vllm::Fp8KVCacheDataType::kFp8E4M3); \
321 } else if (SRC_DTYPE == torch::headeronly::ScalarType::BFloat16) { \
322 FN(__nv_bfloat16, uint8_t, vllm::Fp8KVCacheDataType::kFp8E4M3); \
323 } else { \
324 STD_TORCH_CHECK(false, \
325 "Unsupported input type of kv cache: ", SRC_DTYPE); \
326 } \
327 } else if (KV_CACHE_DTYPE == vllm::Fp8KVCacheDataType::kFp8E5M2) { \
328 if (SRC_DTYPE == torch::headeronly::ScalarType::Float) { \
329 FN(float, uint8_t, vllm::Fp8KVCacheDataType::kFp8E5M2); \
330 } else if (SRC_DTYPE == torch::headeronly::ScalarType::Half) { \
331 FN(uint16_t, uint8_t, vllm::Fp8KVCacheDataType::kFp8E5M2); \
332 } else if (SRC_DTYPE == torch::headeronly::ScalarType::BFloat16) { \
333 FN(__nv_bfloat16, uint8_t, vllm::Fp8KVCacheDataType::kFp8E5M2); \
334 } else { \
335 STD_TORCH_CHECK(false, \
336 "Unsupported input type of kv cache: ", SRC_DTYPE); \
337 } \
338 } else { \
339 STD_TORCH_CHECK(false, "Unsupported data type of kv cache: ", KV_DTYPE); \
340 }
341
342 } // namespace fp8
343 #endif // not USE_ROCM
344 } // namespace vllm