danieldk HF Staff commited on
Commit
bcb60f9
·
1 Parent(s): 89f3e6f

Remove spurious files

Browse files
Files changed (41) hide show
  1. build.toml +0 -110
  2. cuda-utils/cuda_utils.h +0 -41
  3. cuda-utils/cuda_utils_kernels.cu +0 -35
  4. flake.lock +0 -168
  5. flake.nix +0 -17
  6. paged-attention-metal/attention/paged_attention.metal +0 -1401
  7. paged-attention-metal/cache.mm +0 -562
  8. paged-attention-metal/cache/copy_blocks.metal +0 -51
  9. paged-attention-metal/cache/reshape_and_cache.metal +0 -193
  10. paged-attention-metal/convert_fp8.metal +0 -77
  11. paged-attention-metal/convert_fp8.mm +0 -138
  12. paged-attention-metal/device.mm +0 -17
  13. paged-attention-metal/float8.metal +0 -122
  14. paged-attention-metal/paged_attention.mm +0 -693
  15. paged-attention-metal/utils.metal +0 -246
  16. paged-attention/attention/attention_dtypes.h +0 -7
  17. paged-attention/attention/attention_generic.cuh +0 -65
  18. paged-attention/attention/attention_kernels.cuh +0 -670
  19. paged-attention/attention/attention_utils.cuh +0 -57
  20. paged-attention/attention/dtype_bfloat16.cuh +0 -463
  21. paged-attention/attention/dtype_float16.cuh +0 -504
  22. paged-attention/attention/dtype_float32.cuh +0 -251
  23. paged-attention/attention/dtype_fp8.cuh +0 -41
  24. paged-attention/attention/paged_attention_v1.cu +0 -187
  25. paged-attention/attention/paged_attention_v2.cu +0 -197
  26. paged-attention/cache_kernels.cu +0 -734
  27. paged-attention/cuda_compat.h +0 -49
  28. paged-attention/dispatch_utils.h +0 -83
  29. paged-attention/quantization/fp8/amd/quant_utils.cuh +0 -671
  30. paged-attention/quantization/fp8/nvidia/quant_utils.cuh +0 -573
  31. tests/kernels/__init__.py +0 -0
  32. tests/kernels/allclose_default.py +0 -14
  33. tests/kernels/conftest.py +0 -157
  34. tests/kernels/test_attention.py +0 -439
  35. tests/kernels/test_cache.py +0 -500
  36. tests/kernels/utils.py +0 -99
  37. torch-ext/paged_attention/__init__.py +0 -21
  38. torch-ext/paged_attention/_custom_ops.py +0 -173
  39. torch-ext/paged_attention/platforms.py +0 -92
  40. torch-ext/torch_binding.cpp +0 -122
  41. torch-ext/torch_binding.h +0 -56
build.toml DELETED
@@ -1,110 +0,0 @@
1
- [general]
2
- name = "paged_attention"
3
- universal = false
4
-
5
- [torch]
6
- src = [
7
- "torch-ext/torch_binding.cpp",
8
- "torch-ext/torch_binding.h"
9
- ]
10
-
11
- [kernel.cuda_utils]
12
- backend = "cuda"
13
- src = [
14
- "cuda-utils/cuda_utils.h",
15
- "cuda-utils/cuda_utils_kernels.cu",
16
- ]
17
- depends = []
18
-
19
- [kernel.cuda_utils_rocm]
20
- backend = "rocm"
21
- rocm-archs = [
22
- "gfx906",
23
- "gfx908",
24
- "gfx90a",
25
- "gfx940",
26
- "gfx941",
27
- "gfx942",
28
- "gfx1030",
29
- "gfx1100",
30
- "gfx1101",
31
- ]
32
- src = [
33
- "cuda-utils/cuda_utils.h",
34
- "cuda-utils/cuda_utils_kernels.cu",
35
- ]
36
- depends = ["torch"]
37
-
38
- [kernel.paged_attention]
39
- backend = "cuda"
40
- src = [
41
- "cuda-utils/cuda_utils.h",
42
- "paged-attention/attention/attention_dtypes.h",
43
- "paged-attention/attention/attention_generic.cuh",
44
- "paged-attention/attention/attention_kernels.cuh",
45
- "paged-attention/attention/attention_utils.cuh",
46
- "paged-attention/attention/dtype_bfloat16.cuh",
47
- "paged-attention/attention/dtype_float16.cuh",
48
- "paged-attention/attention/dtype_float32.cuh",
49
- "paged-attention/attention/dtype_fp8.cuh",
50
- "paged-attention/attention/paged_attention_v1.cu",
51
- "paged-attention/attention/paged_attention_v2.cu",
52
- "paged-attention/cache_kernels.cu",
53
- "paged-attention/cuda_compat.h",
54
- "paged-attention/dispatch_utils.h",
55
- "paged-attention/quantization/fp8/amd/quant_utils.cuh",
56
- "paged-attention/quantization/fp8/nvidia/quant_utils.cuh",
57
- ]
58
- include = [ "cuda-utils", "paged-attention" ]
59
- depends = [ "torch" ]
60
-
61
- [kernel.paged_attention_rocm]
62
- backend = "rocm"
63
- rocm-archs = [
64
- "gfx906",
65
- "gfx908",
66
- "gfx90a",
67
- "gfx940",
68
- "gfx941",
69
- "gfx942",
70
- "gfx1030",
71
- "gfx1100",
72
- "gfx1101",
73
- ]
74
- src = [
75
- "cuda-utils/cuda_utils.h",
76
- "paged-attention/attention/attention_dtypes.h",
77
- "paged-attention/attention/attention_generic.cuh",
78
- "paged-attention/attention/attention_kernels.cuh",
79
- "paged-attention/attention/attention_utils.cuh",
80
- "paged-attention/attention/dtype_bfloat16.cuh",
81
- "paged-attention/attention/dtype_float16.cuh",
82
- "paged-attention/attention/dtype_float32.cuh",
83
- "paged-attention/attention/dtype_fp8.cuh",
84
- "paged-attention/attention/paged_attention_v1.cu",
85
- "paged-attention/attention/paged_attention_v2.cu",
86
- "paged-attention/cache_kernels.cu",
87
- "paged-attention/cuda_compat.h",
88
- "paged-attention/dispatch_utils.h",
89
- "paged-attention/quantization/fp8/amd/quant_utils.cuh",
90
- "paged-attention/quantization/fp8/nvidia/quant_utils.cuh",
91
- ]
92
- include = [ "cuda-utils", "paged-attention" ]
93
- depends = [ "torch" ]
94
-
95
- [kernel.paged_attention_metal]
96
- backend = "metal"
97
- src = [
98
- "paged-attention-metal/attention/paged_attention.metal",
99
- "paged-attention-metal/cache/copy_blocks.metal",
100
- "paged-attention-metal/cache/reshape_and_cache.metal",
101
- "paged-attention-metal/convert_fp8.metal",
102
- "paged-attention-metal/float8.metal",
103
- "paged-attention-metal/utils.metal",
104
- "paged-attention-metal/paged_attention.mm",
105
- "paged-attention-metal/cache.mm",
106
- "paged-attention-metal/convert_fp8.mm",
107
- "paged-attention-metal/device.mm",
108
- ]
109
- include = [ "." ]
110
- depends = [ "torch" ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cuda-utils/cuda_utils.h DELETED
@@ -1,41 +0,0 @@
1
- #pragma once
2
-
3
- #include <stdio.h>
4
-
5
- #if defined(__HIPCC__)
6
- #define HOST_DEVICE_INLINE __host__ __device__
7
- #define DEVICE_INLINE __device__
8
- #define HOST_INLINE __host__
9
- #elif defined(__CUDACC__) || defined(_NVHPC_CUDA)
10
- #define HOST_DEVICE_INLINE __host__ __device__ __forceinline__
11
- #define DEVICE_INLINE __device__ __forceinline__
12
- #define HOST_INLINE __host__ __forceinline__
13
- #else
14
- #define HOST_DEVICE_INLINE inline
15
- #define DEVICE_INLINE inline
16
- #define HOST_INLINE inline
17
- #endif
18
-
19
- #define CUDA_CHECK(cmd) \
20
- do { \
21
- cudaError_t e = cmd; \
22
- if (e != cudaSuccess) { \
23
- printf("Failed: Cuda error %s:%d '%s'\n", __FILE__, __LINE__, \
24
- cudaGetErrorString(e)); \
25
- exit(EXIT_FAILURE); \
26
- } \
27
- } while (0)
28
-
29
- int64_t get_device_attribute(int64_t attribute, int64_t device_id);
30
-
31
- int64_t get_max_shared_memory_per_block_device_attribute(int64_t device_id);
32
-
33
- namespace cuda_utils {
34
-
35
- template <typename T>
36
- HOST_DEVICE_INLINE constexpr std::enable_if_t<std::is_integral_v<T>, T>
37
- ceil_div(T a, T b) {
38
- return (a + b - 1) / b;
39
- }
40
-
41
- }; // namespace cuda_utils
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cuda-utils/cuda_utils_kernels.cu DELETED
@@ -1,35 +0,0 @@
1
- #include "cuda_utils.h"
2
- #ifdef USE_ROCM
3
- #include <hip/hip_runtime.h>
4
- #include <hip/hip_runtime_api.h>
5
- #endif
6
-
7
- int64_t get_device_attribute(int64_t attribute, int64_t device_id) {
8
- // Return the cached value on subsequent calls
9
- static int value = [=]() {
10
- int device = static_cast<int>(device_id);
11
- if (device < 0) {
12
- CUDA_CHECK(cudaGetDevice(&device));
13
- }
14
- int value;
15
- CUDA_CHECK(cudaDeviceGetAttribute(
16
- &value, static_cast<cudaDeviceAttr>(attribute), device));
17
- return static_cast<int>(value);
18
- }();
19
-
20
- return value;
21
- }
22
-
23
- int64_t get_max_shared_memory_per_block_device_attribute(int64_t device_id) {
24
- int64_t attribute;
25
- // https://docs.nvidia.com/cuda/cuda-runtime-api/group__CUDART__TYPES.html
26
- // cudaDevAttrMaxSharedMemoryPerBlockOptin = 97 if not is_hip() else 74
27
-
28
- #ifdef USE_ROCM
29
- attribute = hipDeviceAttributeMaxSharedMemoryPerBlock;
30
- #else
31
- attribute = cudaDevAttrMaxSharedMemoryPerBlockOptin;
32
- #endif
33
-
34
- return get_device_attribute(attribute, device_id);
35
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
flake.lock DELETED
@@ -1,168 +0,0 @@
1
- {
2
- "nodes": {
3
- "flake-compat": {
4
- "locked": {
5
- "lastModified": 1747046372,
6
- "narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=",
7
- "owner": "edolstra",
8
- "repo": "flake-compat",
9
- "rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885",
10
- "type": "github"
11
- },
12
- "original": {
13
- "owner": "edolstra",
14
- "repo": "flake-compat",
15
- "type": "github"
16
- }
17
- },
18
- "flake-compat_2": {
19
- "locked": {
20
- "lastModified": 1747046372,
21
- "narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=",
22
- "owner": "edolstra",
23
- "repo": "flake-compat",
24
- "rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885",
25
- "type": "github"
26
- },
27
- "original": {
28
- "owner": "edolstra",
29
- "repo": "flake-compat",
30
- "type": "github"
31
- }
32
- },
33
- "flake-utils": {
34
- "inputs": {
35
- "systems": "systems"
36
- },
37
- "locked": {
38
- "lastModified": 1731533236,
39
- "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
40
- "owner": "numtide",
41
- "repo": "flake-utils",
42
- "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
43
- "type": "github"
44
- },
45
- "original": {
46
- "owner": "numtide",
47
- "repo": "flake-utils",
48
- "type": "github"
49
- }
50
- },
51
- "flake-utils_2": {
52
- "inputs": {
53
- "systems": "systems_2"
54
- },
55
- "locked": {
56
- "lastModified": 1731533236,
57
- "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
58
- "owner": "numtide",
59
- "repo": "flake-utils",
60
- "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
61
- "type": "github"
62
- },
63
- "original": {
64
- "owner": "numtide",
65
- "repo": "flake-utils",
66
- "type": "github"
67
- }
68
- },
69
- "hf-nix": {
70
- "inputs": {
71
- "flake-compat": "flake-compat_2",
72
- "flake-utils": "flake-utils_2",
73
- "nixpkgs": "nixpkgs"
74
- },
75
- "locked": {
76
- "lastModified": 1759493343,
77
- "narHash": "sha256-8fhl0gwMAnOkQbogPIVq+Fha+Yeq52FaRXfwF+F9Q+k=",
78
- "owner": "huggingface",
79
- "repo": "hf-nix",
80
- "rev": "b1fc3a18b52447a0f24bc6884418edc5e66082b9",
81
- "type": "github"
82
- },
83
- "original": {
84
- "owner": "huggingface",
85
- "repo": "hf-nix",
86
- "type": "github"
87
- }
88
- },
89
- "kernel-builder": {
90
- "inputs": {
91
- "flake-compat": "flake-compat",
92
- "flake-utils": "flake-utils",
93
- "hf-nix": "hf-nix",
94
- "nixpkgs": [
95
- "kernel-builder",
96
- "hf-nix",
97
- "nixpkgs"
98
- ]
99
- },
100
- "locked": {
101
- "lastModified": 1759823856,
102
- "narHash": "sha256-wxnoxY8Whem8NmE5UDOkv74puV68CoAMIRpVMCuKjJ8=",
103
- "owner": "huggingface",
104
- "repo": "kernel-builder",
105
- "rev": "62a9652083262f49c11a1eb1aa2cbbbcf2170c93",
106
- "type": "github"
107
- },
108
- "original": {
109
- "owner": "huggingface",
110
- "repo": "kernel-builder",
111
- "type": "github"
112
- }
113
- },
114
- "nixpkgs": {
115
- "locked": {
116
- "lastModified": 1755963616,
117
- "narHash": "sha256-6yD0ww/S8n+U2uPYcJZ3DRURP8Kx036GRpR2uPNZroE=",
118
- "owner": "nixos",
119
- "repo": "nixpkgs",
120
- "rev": "73e96df7cff5783f45e21342a75a1540c4eddce4",
121
- "type": "github"
122
- },
123
- "original": {
124
- "owner": "nixos",
125
- "ref": "nixos-unstable-small",
126
- "repo": "nixpkgs",
127
- "type": "github"
128
- }
129
- },
130
- "root": {
131
- "inputs": {
132
- "kernel-builder": "kernel-builder"
133
- }
134
- },
135
- "systems": {
136
- "locked": {
137
- "lastModified": 1681028828,
138
- "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
139
- "owner": "nix-systems",
140
- "repo": "default",
141
- "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
142
- "type": "github"
143
- },
144
- "original": {
145
- "owner": "nix-systems",
146
- "repo": "default",
147
- "type": "github"
148
- }
149
- },
150
- "systems_2": {
151
- "locked": {
152
- "lastModified": 1681028828,
153
- "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
154
- "owner": "nix-systems",
155
- "repo": "default",
156
- "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
157
- "type": "github"
158
- },
159
- "original": {
160
- "owner": "nix-systems",
161
- "repo": "default",
162
- "type": "github"
163
- }
164
- }
165
- },
166
- "root": "root",
167
- "version": 7
168
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
flake.nix DELETED
@@ -1,17 +0,0 @@
1
- {
2
- description = "Flake for attention kernels";
3
-
4
- inputs = {
5
- kernel-builder.url = "github:huggingface/kernel-builder";
6
- };
7
-
8
- outputs =
9
- {
10
- self,
11
- kernel-builder,
12
- }:
13
- kernel-builder.lib.genFlakeOutputs {
14
- inherit self;
15
- path = ./.;
16
- };
17
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
paged-attention-metal/attention/paged_attention.metal DELETED
@@ -1,1401 +0,0 @@
1
- // Updated from MLX commit has f70764a
2
-
3
- #include "../utils.metal"
4
- #include "../float8.metal"
5
- #include <metal_simdgroup>
6
- #include <metal_stdlib>
7
-
8
- using namespace metal;
9
-
10
- // ========================================== Generic vector types
11
-
12
- // A vector type to store Q, K, V elements.
13
- template <typename T, int VEC_SIZE> struct Vec {};
14
-
15
- // A vector type to store FP32 accumulators.
16
- template <typename T> struct FloatVec {};
17
-
18
- // Template vector operations.
19
- template <typename Acc, typename A, typename B> inline Acc mul(A a, B b);
20
-
21
- template <typename T> inline float sum(T v);
22
-
23
- template <typename T> inline float dot(T a, T b) {
24
- return sum(mul<T, T, T>(a, b));
25
- }
26
-
27
- template <typename A, typename T> inline float dot(T a, T b) {
28
- return sum(mul<A, T, T>(a, b));
29
- }
30
-
31
- // FP32 vector data types.
32
- struct Float8_ {
33
- float4 x;
34
- float4 y;
35
- };
36
-
37
- template <> struct Vec<float, 1> {
38
- using Type = float;
39
- };
40
- template <> struct Vec<float, 2> {
41
- using Type = float2;
42
- };
43
- template <> struct Vec<float, 4> {
44
- using Type = float4;
45
- };
46
- template <> struct Vec<float, 8> {
47
- using Type = Float8_;
48
- };
49
-
50
- template <> struct FloatVec<float> {
51
- using Type = float;
52
- };
53
- template <> struct FloatVec<float2> {
54
- using Type = float2;
55
- };
56
- template <> struct FloatVec<float4> {
57
- using Type = float4;
58
- };
59
- template <> struct FloatVec<Float8_> {
60
- using Type = Float8_;
61
- };
62
-
63
- template <> inline float mul(float a, float b) { return a * b; }
64
-
65
- template <> inline float2 mul(float2 a, float2 b) { return a * b; }
66
-
67
- template <> inline float4 mul(float4 a, float4 b) { return a * b; }
68
-
69
- template <> inline Float8_ mul(Float8_ a, Float8_ b) {
70
- Float8_ c;
71
- c.x = a.x * b.x;
72
- c.y = a.y * b.y;
73
- return c;
74
- }
75
-
76
- template <> inline float sum(float a) { return a; }
77
-
78
- template <> inline float sum(float2 a) { return a.x + a.y; }
79
-
80
- template <> inline float sum(float4 a) { return a.x + a.y + a.z + a.w; }
81
-
82
- template <> inline float sum(Float8_ a) { return sum(a.x) + sum(a.y); }
83
-
84
- inline Float8_ fma(Float8_ a, Float8_ b, Float8_ c) {
85
- Float8_ res;
86
- res.x = fma(a.x, b.x, c.x);
87
- res.y = fma(a.y, b.y, c.y);
88
- return res;
89
- }
90
-
91
- inline void from_float(thread float &dst, float src) { dst = src; }
92
- inline void from_float(thread float2 &dst, float2 src) { dst = src; }
93
- inline void from_float(thread float4 &dst, float4 src) { dst = src; }
94
- inline void from_float(thread Float8_ &dst, Float8_ src) { dst = src; }
95
-
96
- // BF16 vector data types.
97
- // #if defined(__HAVE_BFLOAT__)
98
-
99
- // struct Bfloat8_ {
100
- // bfloat4 x;
101
- // bfloat4 y;
102
- // };
103
-
104
- // template<>
105
- // struct Vec<bfloat, 1> {
106
- // using Type = bfloat;
107
- // };
108
- // template<>
109
- // struct Vec<bfloat, 2> {
110
- // using Type = bfloat2;
111
- // };
112
- // template<>
113
- // struct Vec<bfloat, 4> {
114
- // using Type = bfloat4;
115
- // };
116
- // template<>
117
- // struct Vec<bfloat, 8> {
118
- // using Type = Bfloat8_;
119
- // };
120
-
121
- // template<>
122
- // struct FloatVec<bfloat> {
123
- // using Type = float;
124
- // };
125
- // template<>
126
- // struct FloatVec<bfloat2> {
127
- // using Type = float2;
128
- // };
129
- // template<>
130
- // struct FloatVec<bfloat4> {
131
- // using Type = float4;
132
- // };
133
- // template<>
134
- // struct FloatVec<Bfloat8_> {
135
- // using Type = Float8_;
136
- // };
137
-
138
- // template<>
139
- // inline float mul(bfloat a, bfloat b) {
140
- // return (float)a * (float)b;
141
- // }
142
- // template<>
143
- // inline bfloat mul(bfloat a, bfloat b) {
144
- // return a*b;
145
- // }
146
-
147
- // template<>
148
- // inline float2 mul(bfloat2 a, bfloat2 b) {
149
- // return (float2)a * (float2)b;
150
- // }
151
- // template<>
152
- // inline bfloat2 mul(bfloat2 a, bfloat2 b) {
153
- // return a * b;
154
- // }
155
-
156
- // template<>
157
- // inline float4 mul(bfloat4 a, bfloat4 b) {
158
- // return (float4)a * (float4)b;
159
- // }
160
- // template<>
161
- // inline bfloat4 mul(bfloat4 a, bfloat4 b) {
162
- // return a * b;
163
- // }
164
-
165
- // template<>
166
- // inline Float8_ mul(Bfloat8_ a, Bfloat8_ b) {
167
- // Float8_ c;
168
- // c.x = mul<float4, bfloat4, bfloat4>(a.x, b.x);
169
- // c.y = mul<float4, bfloat4, bfloat4>(a.y, b.y);
170
- // return c;
171
- // }
172
- // template<>
173
- // inline Bfloat8_ mul(Bfloat8_ a, Bfloat8_ b) {
174
- // Bfloat8_ c;
175
- // c.x = mul<bfloat4, bfloat4, bfloat4>(a.x, b.x);
176
- // c.y = mul<bfloat4, bfloat4, bfloat4>(a.y, b.y);
177
- // return c;
178
- // }
179
-
180
- // template<>
181
- // inline float sum(bfloat a) {
182
- // return (float)a;
183
- // }
184
-
185
- // template<>
186
- // inline float sum(bfloat2 a) {
187
- // return (float)a.x + (float)a.y;
188
- // }
189
-
190
- // template<>
191
- // inline float sum(bfloat4 a) {
192
- // return sum(a.x) + sum(a.y);
193
- // }
194
-
195
- // template<>
196
- // inline float sum(Bfloat8_ a) {
197
- // return sum(a.x) + sum(a.y);
198
- // }
199
-
200
- // inline float fma(bfloat a, bfloat b, float c) {
201
- // return (float)a * (float)b + c;
202
- // }
203
-
204
- // inline float2 fma(bfloat2 a, bfloat2 b, float2 c) {
205
- // return (float2)a * (float2)b + c;
206
- // }
207
-
208
- // inline float4 fma(bfloat4 a, bfloat4 b, float4 c) {
209
- // return (float4)a * (float4)b + c;
210
- // }
211
-
212
- // inline Float8_ fma(Bfloat8_ a, Bfloat8_ b, Float8_ c) {
213
- // Float8_ res;
214
- // res.x = fma((float4)a.x, (float4)b.x, (float4)c.x);
215
- // res.y = fma((float4)a.y, (float4)b.y, (float4)c.y);
216
- // return res;
217
- // }
218
- // inline Bfloat8_ fma(Bfloat8_ a, Bfloat8_ b, Bfloat8_ c) {
219
- // Bfloat8_ res;
220
- // res.x = (bfloat4)fma((float4)a.x, (float4)b.x, (float4)c.x);
221
- // res.y = (bfloat4)fma((float4)a.y, (float4)b.x, (float4)c.y);
222
- // return c;
223
- // }
224
-
225
- // inline void from_float(thread bfloat& dst, float src) {
226
- // dst = static_cast<bfloat>(src);
227
- // }
228
- // inline void from_float(thread bfloat2& dst, float2 src) {
229
- // dst.x = static_cast<bfloat>(src.x);
230
- // dst.y = static_cast<bfloat>(src.y);
231
- // }
232
- // inline void from_float(thread bfloat4& dst, float4 src) {
233
- // dst.x = static_cast<bfloat>(src.x);
234
- // dst.y = static_cast<bfloat>(src.y);
235
- // dst.z = static_cast<bfloat>(src.z);
236
- // dst.w = static_cast<bfloat>(src.w);
237
- // }
238
- // inline void from_float(thread Bfloat8_& dst, Float8_ src) {
239
- // bfloat4 x;
240
- // bfloat4 y;
241
- // from_float(x, src.x);
242
- // from_float(y, src.y);
243
- // dst.x = x;
244
- // dst.y = y;
245
- // }
246
-
247
- // #else
248
-
249
- struct Bfloat2_ {
250
- bfloat16_t x;
251
- bfloat16_t y;
252
- };
253
-
254
- struct Bfloat4_ {
255
- Bfloat2_ x;
256
- Bfloat2_ y;
257
- };
258
-
259
- struct Bfloat8_ {
260
- Bfloat4_ x;
261
- Bfloat4_ y;
262
- };
263
-
264
- template <> struct Vec<bfloat16_t, 1> {
265
- using Type = bfloat16_t;
266
- };
267
- template <> struct Vec<bfloat16_t, 2> {
268
- using Type = Bfloat2_;
269
- };
270
- template <> struct Vec<bfloat16_t, 4> {
271
- using Type = Bfloat4_;
272
- };
273
- template <> struct Vec<bfloat16_t, 8> {
274
- using Type = Bfloat8_;
275
- };
276
-
277
- template <> struct FloatVec<bfloat16_t> {
278
- using Type = float;
279
- };
280
- template <> struct FloatVec<Bfloat2_> {
281
- using Type = float2;
282
- };
283
- template <> struct FloatVec<Bfloat4_> {
284
- using Type = float4;
285
- };
286
- template <> struct FloatVec<Bfloat8_> {
287
- using Type = Float8_;
288
- };
289
-
290
- template <> inline float mul(bfloat16_t a, bfloat16_t b) {
291
- return (float)a * (float)b;
292
- }
293
- template <> inline bfloat16_t mul(bfloat16_t a, bfloat16_t b) { return a * b; }
294
-
295
- template <> inline float2 mul(Bfloat2_ a, Bfloat2_ b) {
296
- float2 a_f((float)a.x, (float)a.y);
297
- float2 b_f((float)b.x, (float)b.y);
298
- return a_f * b_f;
299
- }
300
- template <> inline Bfloat2_ mul(Bfloat2_ a, Bfloat2_ b) {
301
- Bfloat2_ c;
302
- c.x = a.x * b.x;
303
- c.y = a.y * b.y;
304
- return c;
305
- }
306
-
307
- template <> inline float4 mul(Bfloat4_ a, Bfloat4_ b) {
308
- float2 x = mul<float2, Bfloat2_, Bfloat2_>(a.x, b.x);
309
- float2 y = mul<float2, Bfloat2_, Bfloat2_>(a.y, b.y);
310
- float4 c;
311
- c.x = x.x;
312
- c.y = x.y;
313
- c.z = y.x;
314
- c.w = y.y;
315
- return c;
316
- }
317
- template <> inline Bfloat4_ mul(Bfloat4_ a, Bfloat4_ b) {
318
- Bfloat4_ c;
319
- c.x = mul<Bfloat2_, Bfloat2_, Bfloat2_>(a.x, b.x);
320
- c.y = mul<Bfloat2_, Bfloat2_, Bfloat2_>(a.y, b.y);
321
- return c;
322
- }
323
-
324
- template <> inline Float8_ mul(Bfloat8_ a, Bfloat8_ b) {
325
- Float8_ c;
326
- c.x = mul<float4, Bfloat4_, Bfloat4_>(a.x, b.x);
327
- c.y = mul<float4, Bfloat4_, Bfloat4_>(a.y, b.y);
328
- return c;
329
- }
330
- template <> inline Bfloat8_ mul(Bfloat8_ a, Bfloat8_ b) {
331
- Bfloat8_ c;
332
- c.x = mul<Bfloat4_, Bfloat4_, Bfloat4_>(a.x, b.x);
333
- c.y = mul<Bfloat4_, Bfloat4_, Bfloat4_>(a.y, b.y);
334
- return c;
335
- }
336
-
337
- template <> inline float sum(bfloat16_t a) { return (float)a; }
338
-
339
- template <> inline float sum(Bfloat2_ a) { return (float)a.x + (float)a.y; }
340
-
341
- template <> inline float sum(Bfloat4_ a) { return sum(a.x) + sum(a.y); }
342
-
343
- template <> inline float sum(Bfloat8_ a) { return sum(a.x) + sum(a.y); }
344
-
345
- inline float fma(bfloat16_t a, bfloat16_t b, float c) {
346
- return (float)a * (float)b + c;
347
- }
348
- inline bfloat16_t fma(bfloat16_t a, bfloat16_t b, bfloat16_t c) {
349
- return a * b + c;
350
- }
351
-
352
- inline float2 fma(Bfloat2_ a, Bfloat2_ b, float2 c) {
353
- float2 a_f((float)a.x, (float)a.y);
354
- float2 b_f((float)b.x, (float)b.y);
355
- return a_f * b_f + c;
356
- }
357
- inline Bfloat2_ fma(Bfloat2_ a, Bfloat2_ b, Bfloat2_ c) {
358
- Bfloat2_ res;
359
- res.x = a.x * b.x + c.x;
360
- res.y = a.y * b.y + c.y;
361
- return res;
362
- }
363
-
364
- inline float4 fma(Bfloat4_ a, Bfloat4_ b, float4 c) {
365
- float4 res;
366
- res.x = fma(a.x.x, b.x.x, c.x);
367
- res.y = fma(a.x.y, b.x.y, c.y);
368
- res.z = fma(a.y.x, b.y.x, c.z);
369
- res.w = fma(a.y.y, b.y.y, c.w);
370
- return res;
371
- }
372
- inline Bfloat4_ fma(Bfloat4_ a, Bfloat4_ b, Bfloat4_ c) {
373
- Bfloat4_ res;
374
- res.x = fma(a.x, b.x, c.x);
375
- res.y = fma(a.y, b.y, c.y);
376
- return res;
377
- }
378
-
379
- inline Float8_ fma(Bfloat8_ a, Bfloat8_ b, Float8_ c) {
380
- float4 x = fma(a.x, b.x, c.x);
381
- float4 y = fma(a.y, b.y, c.y);
382
- Float8_ res;
383
- res.x = x;
384
- res.y = y;
385
- return res;
386
- }
387
- inline Bfloat8_ fma(Bfloat8_ a, Bfloat8_ b, Bfloat8_ c) {
388
- Bfloat8_ res;
389
- res.x = fma(a.x, b.x, c.x);
390
- res.y = fma(a.y, b.y, c.y);
391
- return res;
392
- }
393
-
394
- inline void from_float(thread bfloat16_t &dst, float src) {
395
- dst = static_cast<bfloat16_t>(src);
396
- }
397
- inline void from_float(thread Bfloat2_ &dst, float2 src) {
398
- dst.x = static_cast<bfloat16_t>(src.x);
399
- dst.y = static_cast<bfloat16_t>(src.y);
400
- }
401
- inline void from_float(thread Bfloat4_ &dst, float4 src) {
402
- dst.x.x = static_cast<bfloat16_t>(src.x);
403
- dst.x.y = static_cast<bfloat16_t>(src.y);
404
- dst.y.x = static_cast<bfloat16_t>(src.z);
405
- dst.y.y = static_cast<bfloat16_t>(src.w);
406
- }
407
- inline void from_float(thread Bfloat8_ &dst, Float8_ src) {
408
- Bfloat4_ x;
409
- Bfloat4_ y;
410
- from_float(x, src.x);
411
- from_float(y, src.y);
412
- dst.x = x;
413
- dst.y = y;
414
- }
415
-
416
- // #endif
417
-
418
- // FP16 vector data types.
419
- struct Half8_ {
420
- half4 x;
421
- half4 y;
422
- };
423
-
424
- template <> struct Vec<half, 1> {
425
- using Type = half;
426
- };
427
- template <> struct Vec<half, 2> {
428
- using Type = half2;
429
- };
430
- template <> struct Vec<half, 4> {
431
- using Type = half4;
432
- };
433
- template <> struct Vec<half, 8> {
434
- using Type = Half8_;
435
- };
436
-
437
- template <> struct FloatVec<half> {
438
- using Type = float;
439
- };
440
- template <> struct FloatVec<half2> {
441
- using Type = float2;
442
- };
443
- template <> struct FloatVec<half4> {
444
- using Type = float4;
445
- };
446
- template <> struct FloatVec<Half8_> {
447
- using Type = Float8_;
448
- };
449
-
450
- template <> inline float mul(half a, half b) { return (float)a * (float)b; }
451
- template <> inline half mul(half a, half b) { return a * b; }
452
-
453
- template <> inline float2 mul(half2 a, half2 b) {
454
- return (float2)a * (float2)b;
455
- }
456
- template <> inline half2 mul(half2 a, half2 b) { return a * b; }
457
-
458
- template <> inline float4 mul(half4 a, half4 b) {
459
- return (float4)a * (float4)b;
460
- }
461
- template <> inline half4 mul(half4 a, half4 b) { return a * b; }
462
-
463
- template <> inline Float8_ mul(Half8_ a, Half8_ b) {
464
- float4 x = mul<float4, half4, half4>(a.x, b.x);
465
- float4 y = mul<float4, half4, half4>(a.y, b.y);
466
- Float8_ c;
467
- c.x = x;
468
- c.y = y;
469
- return c;
470
- }
471
- template <> inline Half8_ mul(Half8_ a, Half8_ b) {
472
- Half8_ c;
473
- c.x = mul<half4, half4, half4>(a.x, b.x);
474
- c.y = mul<half4, half4, half4>(a.y, b.y);
475
- return c;
476
- }
477
-
478
- template <> inline float sum(half a) { return (float)a; }
479
-
480
- template <> inline float sum(half2 a) { return (float)a.x + (float)a.y; }
481
-
482
- template <> inline float sum(half4 a) { return a.x + a.y + a.z + a.w; }
483
-
484
- template <> inline float sum(Half8_ a) { return sum(a.x) + sum(a.y); }
485
-
486
- inline float fma(half a, half b, float c) { return (float)a * (float)b + c; }
487
-
488
- inline float2 fma(half2 a, half2 b, float2 c) {
489
- return (float2)a * (float2)b + c;
490
- }
491
-
492
- inline float4 fma(half4 a, half4 b, float4 c) {
493
- return (float4)a * (float4)b + c;
494
- }
495
-
496
- inline Float8_ fma(Half8_ a, Half8_ b, Float8_ c) {
497
- float4 x = fma(a.x, b.x, c.x);
498
- float4 y = fma(a.y, b.y, c.y);
499
- Float8_ res;
500
- res.x = x;
501
- res.y = y;
502
- return res;
503
- }
504
- inline Half8_ fma(Half8_ a, Half8_ b, Half8_ c) {
505
- Half8_ res;
506
- res.x = fma(a.x, b.x, c.x);
507
- res.y = fma(a.y, b.y, c.y);
508
- return res;
509
- }
510
-
511
- inline void from_float(thread half &dst, float src) {
512
- dst = static_cast<half>(src);
513
- }
514
- inline void from_float(thread half2 &dst, float2 src) {
515
- dst.x = static_cast<half>(src.x);
516
- dst.y = static_cast<half>(src.y);
517
- }
518
- inline void from_float(thread half4 &dst, float4 src) {
519
- dst.x = static_cast<half>(src.x);
520
- dst.y = static_cast<half>(src.y);
521
- dst.z = static_cast<half>(src.z);
522
- dst.w = static_cast<half>(src.w);
523
- }
524
- inline void from_float(thread Half8_ &dst, Float8_ src) {
525
- half4 x;
526
- half4 y;
527
- from_float(x, src.x);
528
- from_float(y, src.y);
529
- dst.x = x;
530
- dst.y = y;
531
- }
532
-
533
- // ========================================== FP8 (uchar) vector data types.
534
-
535
- // 8‑lane uchar vector – Metal only provides up to uchar4, so build our own.
536
- struct Uchar8_ {
537
- uchar4 x;
538
- uchar4 y;
539
- };
540
-
541
- // Vec specialisations so Vec<uchar, N>::Type resolves correctly.
542
- template <> struct Vec<uchar, 1> {
543
- using Type = uchar;
544
- };
545
- template <> struct Vec<uchar, 2> {
546
- using Type = uchar2;
547
- };
548
- template <> struct Vec<uchar, 4> {
549
- using Type = uchar4;
550
- };
551
- template <> struct Vec<uchar, 8> {
552
- using Type = Uchar8_;
553
- };
554
-
555
- // General case: not uchar
556
- template <typename T> inline constexpr bool is_uchar() { return false; }
557
-
558
- // Specialization: T is uchar
559
- template <> inline constexpr bool is_uchar<uchar>() { return true; }
560
-
561
- // Generic fallback – will fail to compile if a required specialisation is
562
- // missing.
563
- template <typename Vec, typename Quant_vec>
564
- inline Vec fp8_convert(const thread Quant_vec &, float scale) {
565
- static_assert(sizeof(Vec) == 0, "Missing fp8_convert specialisation");
566
- }
567
-
568
- // ========================================== FP8 → float/half/bfloat
569
- inline float __dequant_single(uchar v, float scale) {
570
- return fp8_e4m3_to_float(v) * scale;
571
- }
572
-
573
- // ---- 1‑lane ----
574
- template <>
575
- inline float fp8_convert<float, uchar>(const thread uchar &in, float scale) {
576
- return __dequant_single(in, scale);
577
- }
578
- template <>
579
- inline half fp8_convert<half, uchar>(const thread uchar &in, float scale) {
580
- return half(__dequant_single(in, scale));
581
- }
582
- template <>
583
- inline bfloat16_t fp8_convert<bfloat16_t, uchar>(const thread uchar &in,
584
- float scale) {
585
- return bfloat16_t(__dequant_single(in, scale));
586
- }
587
-
588
- // ---- 2‑lane ----
589
- template <>
590
- inline float2 fp8_convert<float2, uchar2>(const thread uchar2 &in,
591
- float scale) {
592
- return float2(__dequant_single(in.x, scale), __dequant_single(in.y, scale));
593
- }
594
- template <>
595
- inline half2 fp8_convert<half2, uchar2>(const thread uchar2 &in, float scale) {
596
- half2 out;
597
- out.x = half(__dequant_single(in.x, scale));
598
- out.y = half(__dequant_single(in.y, scale));
599
- return out;
600
- }
601
- template <>
602
- inline Bfloat2_ fp8_convert<Bfloat2_, uchar2>(const thread uchar2 &in,
603
- float scale) {
604
- Bfloat2_ out;
605
- out.x = bfloat16_t(__dequant_single(in.x, scale));
606
- out.y = bfloat16_t(__dequant_single(in.y, scale));
607
- return out;
608
- }
609
-
610
- // ---- 4‑lane ----
611
- template <>
612
- inline float4 fp8_convert<float4, uchar4>(const thread uchar4 &in,
613
- float scale) {
614
- return float4(__dequant_single(in.x, scale), __dequant_single(in.y, scale),
615
- __dequant_single(in.z, scale), __dequant_single(in.w, scale));
616
- }
617
- template <>
618
- inline half4 fp8_convert<half4, uchar4>(const thread uchar4 &in, float scale) {
619
- half4 out;
620
- out.x = half(__dequant_single(in.x, scale));
621
- out.y = half(__dequant_single(in.y, scale));
622
- out.z = half(__dequant_single(in.z, scale));
623
- out.w = half(__dequant_single(in.w, scale));
624
- return out;
625
- }
626
- template <>
627
- inline Bfloat4_ fp8_convert<Bfloat4_, uchar4>(const thread uchar4 &in,
628
- float scale) {
629
- Bfloat4_ out;
630
- out.x.x = bfloat16_t(__dequant_single(in.x, scale));
631
- out.x.y = bfloat16_t(__dequant_single(in.y, scale));
632
- out.y.x = bfloat16_t(__dequant_single(in.z, scale));
633
- out.y.y = bfloat16_t(__dequant_single(in.w, scale));
634
- return out;
635
- }
636
-
637
- // ---- 8‑lane ----
638
- template <>
639
- inline Float8_ fp8_convert<Float8_, Uchar8_>(const thread Uchar8_ &in,
640
- float scale) {
641
- Float8_ out;
642
- out.x =
643
- float4(__dequant_single(in.x.x, scale), __dequant_single(in.x.y, scale),
644
- __dequant_single(in.x.z, scale), __dequant_single(in.x.w, scale));
645
- out.y =
646
- float4(__dequant_single(in.y.x, scale), __dequant_single(in.y.y, scale),
647
- __dequant_single(in.y.z, scale), __dequant_single(in.y.w, scale));
648
- return out;
649
- }
650
- template <>
651
- inline Half8_ fp8_convert<Half8_, Uchar8_>(const thread Uchar8_ &in,
652
- float scale) {
653
- Half8_ out;
654
- out.x = half4(half(__dequant_single(in.x.x, scale)),
655
- half(__dequant_single(in.x.y, scale)),
656
- half(__dequant_single(in.x.z, scale)),
657
- half(__dequant_single(in.x.w, scale)));
658
- out.y = half4(half(__dequant_single(in.y.x, scale)),
659
- half(__dequant_single(in.y.y, scale)),
660
- half(__dequant_single(in.y.z, scale)),
661
- half(__dequant_single(in.y.w, scale)));
662
- return out;
663
- }
664
- template <>
665
- inline Bfloat8_ fp8_convert<Bfloat8_, Uchar8_>(const thread Uchar8_ &in,
666
- float scale) {
667
- Bfloat8_ out;
668
- // first 4
669
- out.x.x.x = bfloat16_t(__dequant_single(in.x.x, scale));
670
- out.x.x.y = bfloat16_t(__dequant_single(in.x.y, scale));
671
- out.x.y.x = bfloat16_t(__dequant_single(in.x.z, scale));
672
- out.x.y.y = bfloat16_t(__dequant_single(in.x.w, scale));
673
- // second 4
674
- out.y.x.x = bfloat16_t(__dequant_single(in.y.x, scale));
675
- out.y.x.y = bfloat16_t(__dequant_single(in.y.y, scale));
676
- out.y.y.x = bfloat16_t(__dequant_single(in.y.z, scale));
677
- out.y.y.y = bfloat16_t(__dequant_single(in.y.w, scale));
678
- return out;
679
- }
680
-
681
- // ========================================== Dot product utilities
682
-
683
- // TODO(EricLBuehler): optimize with vectorization
684
- template <int THREAD_GROUP_SIZE, typename Vec, int N>
685
- inline float qk_dot_(const threadgroup Vec (&q)[N], const thread Vec (&k)[N]) {
686
- // Compute the parallel products for Q*K^T (treat vector lanes separately).
687
- using A_vec = typename FloatVec<Vec>::Type;
688
- A_vec qk_vec = mul<A_vec, Vec, Vec>(q[0], k[0]);
689
- #pragma unroll
690
- for (int ii = 1; ii < N; ++ii) {
691
- qk_vec = fma(q[ii], k[ii], qk_vec);
692
- }
693
-
694
- // Finalize the reduction across lanes.
695
- float qk = sum(qk_vec);
696
- #pragma unroll
697
- for (int mask = THREAD_GROUP_SIZE / 2; mask >= 1; mask /= 2) {
698
- qk += simd_shuffle_xor(qk, mask);
699
- }
700
- return qk;
701
- }
702
-
703
- template <typename T, int THREAD_GROUP_SIZE> struct Qk_dot {
704
- template <typename Vec, int N>
705
- static inline float dot(const threadgroup Vec (&q)[N],
706
- const thread Vec (&k)[N]) {
707
- return qk_dot_<THREAD_GROUP_SIZE>(q, k);
708
- }
709
- };
710
-
711
- // ========================================== Block sum utility
712
-
713
- // Utility function for attention softmax.
714
- template <int NUM_WARPS, int NUM_SIMD_LANES>
715
- inline float block_sum(threadgroup float *red_smem, float sum, uint simd_tid,
716
- uint simd_lid) {
717
- // Compute the sum per simdgroup.
718
- #pragma unroll
719
- for (int mask = NUM_SIMD_LANES / 2; mask >= 1; mask /= 2) {
720
- sum += simd_shuffle_xor(sum, mask);
721
- }
722
-
723
- // Simd leaders store the data to shared memory.
724
- if (simd_lid == 0) {
725
- red_smem[simd_tid] = sum;
726
- }
727
-
728
- // Make sure the data is in shared memory.
729
- threadgroup_barrier(mem_flags::mem_threadgroup);
730
-
731
- // The warps compute the final sums.
732
- if (simd_lid < NUM_WARPS) {
733
- sum = red_smem[simd_lid];
734
- }
735
-
736
- // Parallel reduction inside the simd group.
737
- #pragma unroll
738
- for (int mask = NUM_WARPS / 2; mask >= 1; mask /= 2) {
739
- sum += simd_shuffle_xor(sum, mask);
740
- }
741
-
742
- // Broadcast to other threads.
743
- return simd_shuffle(sum, 0);
744
- }
745
-
746
- // ========================================== Paged Attention kernel
747
-
748
- #define MAX(a, b) ((a) > (b) ? (a) : (b))
749
- #define MIN(a, b) ((a) < (b) ? (a) : (b))
750
- #define DIVIDE_ROUND_UP(a, b) (((a) + (b) - 1) / (b))
751
-
752
- constant bool use_partitioning [[function_constant(10)]];
753
- constant bool use_alibi [[function_constant(20)]];
754
- constant bool use_fp8_scales [[function_constant(30)]];
755
-
756
- template <typename T, typename CACHE_T, int HEAD_SIZE, int BLOCK_SIZE, int NUM_THREADS,
757
- int NUM_SIMD_LANES, int PARTITION_SIZE = 0>
758
- [[kernel]] void paged_attention(
759
- device float *exp_sums
760
- [[buffer(0)]], // [num_seqs, num_heads, max_num_partitions] - only used when
761
- // use_partitioning
762
- device float *max_logits
763
- [[buffer(1)]], // [num_seqs, num_heads, max_num_partitions] - only used when
764
- // use_partitioning
765
- device T *out
766
- [[buffer(2)]], // [num_seqs, num_heads, max_num_partitions, head_size]
767
- device const T *q [[buffer(3)]], // [num_seqs, num_heads, head_size]
768
- device const CACHE_T *k_cache
769
- [[buffer(4)]], // [num_blocks, num_kv_heads, head_size/x, block_size, x]
770
- device const CACHE_T *v_cache
771
- [[buffer(5)]], // [num_blocks, num_kv_heads, head_size, block_size]
772
- const device float *__restrict__ k_scale
773
- [[buffer(6)]], // [1] - only used when use_fp8_scales
774
- const device float *__restrict__ v_scale
775
- [[buffer(7)]], // [1] - only used when use_fp8_scales
776
- const constant int &num_kv_heads [[buffer(8)]], // [num_heads]
777
- const constant float &scale [[buffer(9)]],
778
- const constant float &softcapping [[buffer(10)]],
779
- device const uint32_t *block_tables
780
- [[buffer(11)]], // [num_seqs, max_num_blocks_per_seq]
781
- device const uint32_t *context_lens [[buffer(12)]], // [num_seqs]
782
- const constant int &max_num_blocks_per_seq [[buffer(13)]],
783
- device const float *alibi_slopes
784
- [[buffer(14)]], // [num_heads] - only used when use_alibi
785
- const constant int &q_stride [[buffer(15)]],
786
- const constant int &kv_block_stride [[buffer(16)]],
787
- const constant int &kv_head_stride [[buffer(17)]],
788
- threadgroup char *shared_mem [[threadgroup(0)]],
789
- uint3 threadgroup_position_in_grid [[threadgroup_position_in_grid]],
790
- uint3 threadgroups_per_grid [[threadgroups_per_grid]],
791
- uint3 thread_position_in_threadgroup [[thread_position_in_threadgroup]],
792
- uint simd_tid [[simdgroup_index_in_threadgroup]],
793
- uint simd_lid [[thread_index_in_simdgroup]]) {
794
- const int seq_idx = threadgroup_position_in_grid.y;
795
- const int partition_idx = threadgroup_position_in_grid.z;
796
- const int max_num_partitions = threadgroups_per_grid.z;
797
- const int thread_idx = thread_position_in_threadgroup.x;
798
- constexpr bool USE_PARTITIONING = PARTITION_SIZE > 0;
799
- const uint32_t context_len = context_lens[seq_idx];
800
- if (USE_PARTITIONING && partition_idx * PARTITION_SIZE >= context_len) {
801
- // No work to do. Terminate the thread block.
802
- return;
803
- }
804
-
805
- const int num_context_blocks = DIVIDE_ROUND_UP(context_len, BLOCK_SIZE);
806
- const int num_blocks_per_partition =
807
- USE_PARTITIONING ? PARTITION_SIZE / BLOCK_SIZE : num_context_blocks;
808
-
809
- // [start_block_idx, end_block_idx) is the range of blocks to process.
810
- const int start_block_idx =
811
- USE_PARTITIONING ? partition_idx * num_blocks_per_partition : 0;
812
- const int end_block_idx =
813
- MIN(start_block_idx + num_blocks_per_partition, num_context_blocks);
814
- const int num_blocks = end_block_idx - start_block_idx;
815
-
816
- // [start_token_idx, end_token_idx) is the range of tokens to process.
817
- const int start_token_idx = start_block_idx * BLOCK_SIZE;
818
- const int end_token_idx =
819
- MIN(start_token_idx + num_blocks * BLOCK_SIZE, context_len);
820
- const int num_tokens = end_token_idx - start_token_idx;
821
-
822
- constexpr int THREAD_GROUP_SIZE = MAX(NUM_SIMD_LANES / BLOCK_SIZE, 1);
823
- constexpr int NUM_THREAD_GROUPS =
824
- NUM_THREADS / THREAD_GROUP_SIZE; // Note: This assumes THREAD_GROUP_SIZE
825
- // divides NUM_THREADS
826
- assert(NUM_THREADS % THREAD_GROUP_SIZE == 0);
827
- constexpr int NUM_TOKENS_PER_THREAD_GROUP =
828
- DIVIDE_ROUND_UP(BLOCK_SIZE, NUM_SIMD_LANES);
829
- constexpr int NUM_WARPS = NUM_THREADS / NUM_SIMD_LANES;
830
- const int warp_idx = simd_tid;
831
- const int lane = simd_lid;
832
-
833
- const int head_idx = threadgroup_position_in_grid.x;
834
- const int num_heads = threadgroups_per_grid.x;
835
- const int num_queries_per_kv = num_heads / num_kv_heads;
836
- const int kv_head_idx = head_idx / num_queries_per_kv;
837
- const float alibi_slope = !use_alibi ? 0.f : alibi_slopes[head_idx];
838
-
839
- // A vector type to store a part of a key or a query.
840
- // The vector size is configured in such a way that the threads in a thread
841
- // group fetch or compute 16 bytes at a time. For example, if the size of a
842
- // thread group is 4 and the data type is half, then the vector size is 16 /
843
- // (4 * sizeof(half)) == 2.
844
- constexpr int VEC_SIZE = MAX(16 / (THREAD_GROUP_SIZE * sizeof(T)), 1);
845
- using K_vec = typename Vec<T, VEC_SIZE>::Type;
846
- using Q_vec = typename Vec<T, VEC_SIZE>::Type;
847
- using Quant_vec = typename Vec<CACHE_T, VEC_SIZE>::Type;
848
-
849
- constexpr int NUM_ELEMS_PER_THREAD = HEAD_SIZE / THREAD_GROUP_SIZE;
850
- constexpr int NUM_VECS_PER_THREAD = NUM_ELEMS_PER_THREAD / VEC_SIZE;
851
-
852
- const int thread_group_idx = thread_idx / THREAD_GROUP_SIZE;
853
- const int thread_group_offset = thread_idx % THREAD_GROUP_SIZE;
854
-
855
- // Load the query to registers.
856
- // Each thread in a thread group has a different part of the query.
857
- // For example, if the thread group size is 4, then the first thread in the
858
- // group has 0, 4, 8, ... th vectors of the query, and the second thread has
859
- // 1, 5, 9, ... th vectors of the query, and so on.
860
- const device T *q_ptr = q + seq_idx * q_stride + head_idx * HEAD_SIZE;
861
- threadgroup Q_vec q_vecs[THREAD_GROUP_SIZE][NUM_VECS_PER_THREAD];
862
- #pragma unroll
863
- for (int i = thread_group_idx; i < NUM_VECS_PER_THREAD;
864
- i += NUM_THREAD_GROUPS) {
865
- const int vec_idx = thread_group_offset + i * THREAD_GROUP_SIZE;
866
- q_vecs[thread_group_offset][i] =
867
- *reinterpret_cast<const device Q_vec *>(q_ptr + vec_idx * VEC_SIZE);
868
- }
869
- threadgroup_barrier(mem_flags::mem_threadgroup);
870
-
871
- // Use fp32 on softmax logits for better accuracy
872
- threadgroup float *logits = reinterpret_cast<threadgroup float *>(shared_mem);
873
- // Workspace for reduction
874
- threadgroup float red_smem[2 * NUM_WARPS];
875
-
876
- // x == THREAD_GROUP_SIZE * VEC_SIZE
877
- // Each thread group fetches x elements from the key at a time.
878
- constexpr int x = 16 / sizeof(CACHE_T);
879
- float qk_max = -FLT_MAX;
880
-
881
- // Iterate over the key blocks.
882
- // Each warp fetches a block of keys for each iteration.
883
- // Each thread group in a warp fetches a key from the block, and computes
884
- // dot product with the query.
885
- const device uint32_t *block_table =
886
- block_tables + seq_idx * max_num_blocks_per_seq;
887
- for (int block_idx = start_block_idx + warp_idx; block_idx < end_block_idx;
888
- block_idx += NUM_WARPS) {
889
- // NOTE: The block number is stored in int32. However, we cast it to int64
890
- // because int32 can lead to overflow when this variable is multiplied by
891
- // large numbers (e.g., kv_block_stride).
892
- const int64_t physical_block_number =
893
- static_cast<int64_t>(block_table[block_idx]);
894
-
895
- // Load a key to registers.
896
- // Each thread in a thread group has a different part of the key.
897
- // For example, if the thread group size is 4, then the first thread in the
898
- // group has 0, 4, 8, ... th vectors of the key, and the second thread has
899
- // 1, 5, 9, ... th vectors of the key, and so on.
900
- for (int i = 0; i < NUM_TOKENS_PER_THREAD_GROUP; i++) {
901
- const int physical_block_offset =
902
- (thread_group_idx + i * NUM_SIMD_LANES) % BLOCK_SIZE;
903
- const int token_idx = block_idx * BLOCK_SIZE + physical_block_offset;
904
- K_vec k_vecs[NUM_VECS_PER_THREAD];
905
-
906
- #pragma unroll
907
- for (int j = 0; j < NUM_VECS_PER_THREAD; j++) {
908
- const device CACHE_T *k_ptr =
909
- k_cache + physical_block_number * kv_block_stride +
910
- kv_head_idx * kv_head_stride + physical_block_offset * x;
911
- const int vec_idx = thread_group_offset + j * THREAD_GROUP_SIZE;
912
- const int offset1 = (vec_idx * VEC_SIZE) / x;
913
- const int offset2 = (vec_idx * VEC_SIZE) % x;
914
-
915
- if constexpr (is_uchar<CACHE_T>()) {
916
- // FP8 support
917
- Quant_vec k_vec_quant = *reinterpret_cast<const device Quant_vec *>(
918
- k_ptr + offset1 * BLOCK_SIZE * x + offset2);
919
- k_vecs[j] = fp8_convert<K_vec, Quant_vec>(k_vec_quant, *k_scale);
920
- } else {
921
- // Non-FP8 default
922
- k_vecs[j] = *reinterpret_cast<const device K_vec *>(
923
- k_ptr + offset1 * BLOCK_SIZE * x + offset2);
924
- }
925
- }
926
-
927
- // Compute dot product.
928
- // This includes a reduction across the threads in the same thread group.
929
- float qk = scale * Qk_dot<T, THREAD_GROUP_SIZE>::dot(
930
- q_vecs[thread_group_offset], k_vecs);
931
-
932
- // Apply softcapping
933
- if (softcapping != 1.0) {
934
- qk = precise::tanh(qk / softcapping) * softcapping;
935
- }
936
-
937
- // Add the ALiBi bias if slopes are given.
938
- if (use_alibi && alibi_slope != 0) {
939
- // Compute bias with explicit float precision to minimize precision loss
940
- int position_offset = token_idx - int(context_len) + 1;
941
- float alibi_bias = alibi_slope * float(position_offset);
942
- qk += alibi_bias;
943
- }
944
-
945
- if (thread_group_offset == 0) {
946
- // Store the partial reductions to shared memory.
947
- // NOTE: It is required to zero out the masked logits.
948
- const bool mask = token_idx >= context_len;
949
- logits[token_idx - start_token_idx] = mask ? 0.f : qk;
950
- // Update the max value.
951
- qk_max = mask ? qk_max : max(qk_max, qk);
952
- }
953
- }
954
- }
955
-
956
- // Perform reduction across the threads in the same warp to get the
957
- // max qk value for each "warp" (not across the thread block yet).
958
- // The 0-th thread of each thread group already has its max qk value.
959
- #pragma unroll
960
- for (int mask = NUM_SIMD_LANES / 2; mask >= THREAD_GROUP_SIZE; mask /= 2) {
961
- qk_max = max(qk_max, simd_shuffle_xor(qk_max, mask));
962
- }
963
- if (lane == 0) {
964
- red_smem[warp_idx] = qk_max;
965
- }
966
- threadgroup_barrier(mem_flags::mem_threadgroup);
967
-
968
- // Get the max qk value for the sequence.
969
- qk_max = lane < NUM_WARPS ? red_smem[lane] : -FLT_MAX;
970
- #pragma unroll
971
- for (int mask = NUM_WARPS / 2; mask >= 1; mask /= 2) {
972
- qk_max = max(qk_max, simd_shuffle_xor(qk_max, mask));
973
- }
974
- // Broadcast the max qk value to all threads.
975
- qk_max = simd_shuffle(qk_max, 0);
976
-
977
- // Get the sum of the exp values.
978
- float exp_sum = 0.f;
979
- for (int i = thread_idx; i < num_tokens; i += NUM_THREADS) {
980
- float val = exp(logits[i] - qk_max);
981
- logits[i] = val;
982
- exp_sum += val;
983
- }
984
- exp_sum = block_sum<NUM_WARPS, NUM_SIMD_LANES>(&red_smem[NUM_WARPS], exp_sum,
985
- simd_tid, simd_lid);
986
-
987
- // Compute softmax.
988
- const float inv_sum = divide(1.f, exp_sum + 1e-6f);
989
- for (int i = thread_idx; i < num_tokens; i += NUM_THREADS) {
990
- logits[i] *= inv_sum;
991
- }
992
- threadgroup_barrier(mem_flags::mem_threadgroup);
993
-
994
- // If partitioning is enabled, store the max logit and exp_sum.
995
- if (USE_PARTITIONING && thread_idx == 0 && use_partitioning) {
996
- device float *max_logits_ptr =
997
- max_logits + seq_idx * num_heads * max_num_partitions +
998
- head_idx * max_num_partitions + partition_idx;
999
- *max_logits_ptr = qk_max;
1000
- device float *exp_sums_ptr = exp_sums +
1001
- seq_idx * num_heads * max_num_partitions +
1002
- head_idx * max_num_partitions + partition_idx;
1003
- *exp_sums_ptr = exp_sum;
1004
- }
1005
-
1006
- // Each thread will fetch 16 bytes from the value cache at a time.
1007
- constexpr int V_VEC_SIZE = MIN(16 / sizeof(T), BLOCK_SIZE);
1008
- using V_vec = typename Vec<T, V_VEC_SIZE>::Type;
1009
- using L_vec = typename Vec<T, V_VEC_SIZE>::Type;
1010
- using Float_L_vec = typename FloatVec<L_vec>::Type;
1011
- using V_quant_vec = typename Vec<CACHE_T, V_VEC_SIZE>::Type;
1012
-
1013
- constexpr int NUM_V_VECS_PER_ROW = BLOCK_SIZE / V_VEC_SIZE;
1014
- constexpr int NUM_ROWS_PER_ITER = NUM_SIMD_LANES / NUM_V_VECS_PER_ROW;
1015
- constexpr int NUM_ROWS_PER_THREAD =
1016
- DIVIDE_ROUND_UP(HEAD_SIZE, NUM_ROWS_PER_ITER);
1017
-
1018
- // NOTE: We use FP32 for the accumulator for better accuracy.
1019
- float accs[NUM_ROWS_PER_THREAD];
1020
- #pragma unroll
1021
- for (int i = 0; i < NUM_ROWS_PER_THREAD; i++) {
1022
- accs[i] = 0.f;
1023
- }
1024
-
1025
- T zero_value = 0;
1026
- for (int block_idx = start_block_idx + warp_idx; block_idx < end_block_idx;
1027
- block_idx += NUM_WARPS) {
1028
- // NOTE: The block number is stored in int32. However, we cast it to int64
1029
- // because int32 can lead to overflow when this variable is multiplied by
1030
- // large numbers (e.g., kv_block_stride).
1031
- const int64_t physical_block_number =
1032
- static_cast<int64_t>(block_table[block_idx]);
1033
- const int physical_block_offset = (lane % NUM_V_VECS_PER_ROW) * V_VEC_SIZE;
1034
- const int token_idx = block_idx * BLOCK_SIZE + physical_block_offset;
1035
- L_vec logits_vec;
1036
- Float_L_vec logits_float_vec = *reinterpret_cast<threadgroup Float_L_vec *>(
1037
- logits + token_idx - start_token_idx);
1038
- from_float(logits_vec, logits_float_vec);
1039
-
1040
- const device CACHE_T *v_ptr = v_cache + physical_block_number * kv_block_stride +
1041
- kv_head_idx * kv_head_stride;
1042
- #pragma unroll
1043
- for (int i = 0; i < NUM_ROWS_PER_THREAD; i++) {
1044
- const int row_idx = lane / NUM_V_VECS_PER_ROW + i * NUM_ROWS_PER_ITER;
1045
- if (row_idx < HEAD_SIZE) {
1046
- const int offset = row_idx * BLOCK_SIZE + physical_block_offset;
1047
- // NOTE: When v_vec contains the tokens that are out of the context,
1048
- // we should explicitly zero out the values since they may contain NaNs.
1049
- // See
1050
- // https://github.com/vllm-project/vllm/issues/641#issuecomment-1682544472
1051
- V_vec v_vec;
1052
-
1053
- if constexpr (is_uchar<CACHE_T>()) {
1054
- // FP8 support
1055
- V_quant_vec v_quant_vec =
1056
- *reinterpret_cast<const device V_quant_vec *>(v_ptr + offset);
1057
- v_vec = fp8_convert<V_vec, V_quant_vec>(v_quant_vec, *v_scale);
1058
- } else {
1059
- // Non-FP8 default
1060
- v_vec = *reinterpret_cast<const device V_vec *>(v_ptr + offset);
1061
- }
1062
-
1063
- if (block_idx == num_context_blocks - 1) {
1064
- thread T *v_vec_ptr = reinterpret_cast<thread T *>(&v_vec);
1065
- #pragma unroll
1066
- for (int j = 0; j < V_VEC_SIZE; j++) {
1067
- v_vec_ptr[j] =
1068
- token_idx + j < context_len ? v_vec_ptr[j] : zero_value;
1069
- }
1070
- }
1071
- accs[i] += dot(logits_vec, v_vec);
1072
- }
1073
- }
1074
- }
1075
-
1076
- // Perform reduction within each warp.
1077
- #pragma unroll
1078
- for (int i = 0; i < NUM_ROWS_PER_THREAD; i++) {
1079
- float acc = accs[i];
1080
- #pragma unroll
1081
- for (int mask = NUM_V_VECS_PER_ROW / 2; mask >= 1; mask /= 2) {
1082
- acc += simd_shuffle_xor(acc, mask);
1083
- }
1084
- accs[i] = acc;
1085
- }
1086
-
1087
- // NOTE: A barrier is required because the shared memory space for logits
1088
- // is reused for the output.
1089
- threadgroup_barrier(mem_flags::mem_threadgroup);
1090
-
1091
- // Perform reduction across warps.
1092
- threadgroup float *out_smem =
1093
- reinterpret_cast<threadgroup float *>(shared_mem);
1094
- #pragma unroll
1095
- for (int i = NUM_WARPS; i > 1; i /= 2) {
1096
- int mid = i / 2;
1097
- // Upper warps write to shared memory.
1098
- if (warp_idx >= mid && warp_idx < i) {
1099
- threadgroup float *dst = &out_smem[(warp_idx - mid) * HEAD_SIZE];
1100
- #pragma unroll
1101
- for (int i = 0; i < NUM_ROWS_PER_THREAD; i++) {
1102
- const int row_idx = lane / NUM_V_VECS_PER_ROW + i * NUM_ROWS_PER_ITER;
1103
- if (row_idx < HEAD_SIZE && lane % NUM_V_VECS_PER_ROW == 0) {
1104
- dst[row_idx] = accs[i];
1105
- }
1106
- }
1107
- }
1108
- threadgroup_barrier(mem_flags::mem_threadgroup);
1109
-
1110
- // Lower warps update the output.
1111
- if (warp_idx < mid) {
1112
- const threadgroup float *src = &out_smem[warp_idx * HEAD_SIZE];
1113
- #pragma unroll
1114
- for (int i = 0; i < NUM_ROWS_PER_THREAD; i++) {
1115
- const int row_idx = lane / NUM_V_VECS_PER_ROW + i * NUM_ROWS_PER_ITER;
1116
- if (row_idx < HEAD_SIZE && lane % NUM_V_VECS_PER_ROW == 0) {
1117
- accs[i] += src[row_idx];
1118
- }
1119
- }
1120
- }
1121
- threadgroup_barrier(mem_flags::mem_threadgroup);
1122
- }
1123
-
1124
- // Write the final output.
1125
- if (warp_idx == 0) {
1126
- device T *out_ptr =
1127
- out + seq_idx * num_heads * max_num_partitions * HEAD_SIZE +
1128
- head_idx * max_num_partitions * HEAD_SIZE + partition_idx * HEAD_SIZE;
1129
- #pragma unroll
1130
- for (int i = 0; i < NUM_ROWS_PER_THREAD; i++) {
1131
- const int row_idx = lane / NUM_V_VECS_PER_ROW + i * NUM_ROWS_PER_ITER;
1132
- if (row_idx < HEAD_SIZE && lane % NUM_V_VECS_PER_ROW == 0) {
1133
- *(out_ptr + row_idx) = T(accs[i]);
1134
- }
1135
- }
1136
- }
1137
- }
1138
-
1139
- template <typename T, int HEAD_SIZE, int NUM_THREADS, int NUM_SIMD_LANES,
1140
- int PARTITION_SIZE = 0>
1141
- [[kernel]] void paged_attention_v2_reduce(
1142
- device T *out [[buffer(0)]], const device float *exp_sums [[buffer(1)]],
1143
- const device float *max_logits [[buffer(2)]],
1144
- const device T *tmp_out [[buffer(3)]],
1145
- device uint32_t *context_lens [[buffer(4)]],
1146
- const constant int &max_num_partitions [[buffer(5)]],
1147
- threadgroup char *shared_mem [[threadgroup(0)]],
1148
- uint3 threadgroup_position_in_grid [[threadgroup_position_in_grid]],
1149
- uint3 threadgroups_per_grid [[threadgroups_per_grid]],
1150
- uint3 thread_position_in_threadgroup [[thread_position_in_threadgroup]],
1151
- uint3 threads_per_threadgroup [[threads_per_threadgroup]],
1152
- uint simd_tid [[simdgroup_index_in_threadgroup]],
1153
- uint simd_lid [[thread_index_in_simdgroup]]) {
1154
- const int num_heads = threadgroups_per_grid.x;
1155
- const int head_idx = threadgroup_position_in_grid.x;
1156
- const int seq_idx = threadgroup_position_in_grid.y;
1157
- const uint32_t context_len = context_lens[seq_idx];
1158
- const int num_partitions = DIVIDE_ROUND_UP(context_len, PARTITION_SIZE);
1159
- if (num_partitions == 1) {
1160
- // No need to reduce. Only copy tmp_out to out.
1161
- device T *out_ptr =
1162
- out + seq_idx * num_heads * HEAD_SIZE + head_idx * HEAD_SIZE;
1163
- const device T *tmp_out_ptr =
1164
- tmp_out + seq_idx * num_heads * max_num_partitions * HEAD_SIZE +
1165
- head_idx * max_num_partitions * HEAD_SIZE;
1166
- for (int i = thread_position_in_threadgroup.x; i < HEAD_SIZE;
1167
- i += threads_per_threadgroup.x) {
1168
- out_ptr[i] = tmp_out_ptr[i];
1169
- }
1170
- // Terminate the thread block.
1171
- return;
1172
- }
1173
-
1174
- constexpr int NUM_WARPS = NUM_THREADS / NUM_SIMD_LANES;
1175
- const int warp_idx = simd_tid;
1176
- const int lane = simd_lid;
1177
-
1178
- // Workspace for reduction.
1179
- threadgroup float red_smem[2 * NUM_WARPS];
1180
-
1181
- // Load max logits to shared memory.
1182
- threadgroup float *shared_max_logits =
1183
- reinterpret_cast<threadgroup float *>(shared_mem);
1184
- const device float *max_logits_ptr =
1185
- max_logits + seq_idx * num_heads * max_num_partitions +
1186
- head_idx * max_num_partitions;
1187
- float max_logit = -FLT_MAX;
1188
- for (int i = thread_position_in_threadgroup.x; i < num_partitions;
1189
- i += threads_per_threadgroup.x) {
1190
- const float l = max_logits_ptr[i];
1191
- shared_max_logits[i] = l;
1192
- max_logit = max(max_logit, l);
1193
- }
1194
- threadgroup_barrier(mem_flags::mem_threadgroup);
1195
-
1196
- // Get the global max logit.
1197
- // Reduce within the warp.
1198
- #pragma unroll
1199
- for (int mask = NUM_SIMD_LANES / 2; mask >= 1; mask /= 2) {
1200
- max_logit = max(max_logit, simd_shuffle_xor(max_logit, mask));
1201
- }
1202
- if (lane == 0) {
1203
- red_smem[warp_idx] = max_logit;
1204
- }
1205
- threadgroup_barrier(mem_flags::mem_threadgroup);
1206
- // Reduce across warps.
1207
- max_logit = lane < NUM_WARPS ? red_smem[lane] : -FLT_MAX;
1208
- #pragma unroll
1209
- for (int mask = NUM_WARPS / 2; mask >= 1; mask /= 2) {
1210
- max_logit = max(max_logit, simd_shuffle_xor(max_logit, mask));
1211
- }
1212
- // Broadcast the max value to all threads.
1213
- max_logit = simd_shuffle(max_logit, 0);
1214
-
1215
- // Load rescaled exp sums to shared memory.
1216
- threadgroup float *shared_exp_sums = reinterpret_cast<threadgroup float *>(
1217
- shared_mem + sizeof(float) * num_partitions);
1218
- const device float *exp_sums_ptr = exp_sums +
1219
- seq_idx * num_heads * max_num_partitions +
1220
- head_idx * max_num_partitions;
1221
- float global_exp_sum = 0.0f;
1222
- for (int i = thread_position_in_threadgroup.x; i < num_partitions;
1223
- i += threads_per_threadgroup.x) {
1224
- float l = shared_max_logits[i];
1225
- float rescaled_exp_sum = exp_sums_ptr[i] * exp(l - max_logit);
1226
- global_exp_sum += rescaled_exp_sum;
1227
- shared_exp_sums[i] = rescaled_exp_sum;
1228
- }
1229
- threadgroup_barrier(mem_flags::mem_threadgroup);
1230
- global_exp_sum = block_sum<NUM_WARPS, NUM_SIMD_LANES>(
1231
- &red_smem[NUM_WARPS], global_exp_sum, simd_tid, simd_lid);
1232
- const float inv_global_exp_sum = divide(1.0f, global_exp_sum + 1e-6f);
1233
-
1234
- // Aggregate tmp_out to out.
1235
- const device T *tmp_out_ptr =
1236
- tmp_out + seq_idx * num_heads * max_num_partitions * HEAD_SIZE +
1237
- head_idx * max_num_partitions * HEAD_SIZE;
1238
- device T *out_ptr =
1239
- out + seq_idx * num_heads * HEAD_SIZE + head_idx * HEAD_SIZE;
1240
- #pragma unroll
1241
- for (int i = thread_position_in_threadgroup.x; i < HEAD_SIZE;
1242
- i += NUM_THREADS) {
1243
- float acc = 0.0f;
1244
- for (int j = 0; j < num_partitions; ++j) {
1245
- acc += float(tmp_out_ptr[j * HEAD_SIZE + i]) * shared_exp_sums[j] *
1246
- inv_global_exp_sum;
1247
- }
1248
- out_ptr[i] = T(acc);
1249
- }
1250
- }
1251
-
1252
- #define instantiate_paged_attention_inner(type, cache_type, head_size, \
1253
- block_size, num_threads, \
1254
- num_simd_lanes, partition_size) \
1255
- template [[host_name("paged_attention_" #type "_cache_" #cache_type \
1256
- "_hs" #head_size "_bs" #block_size "_nt" #num_threads \
1257
- "_nsl" #num_simd_lanes \
1258
- "_ps" #partition_size)]] [[kernel]] void \
1259
- paged_attention<type, cache_type, head_size, block_size, num_threads, \
1260
- num_simd_lanes, partition_size>( \
1261
- device float *exp_sums [[buffer(0)]], \
1262
- device float *max_logits [[buffer(1)]], \
1263
- device type *out [[buffer(2)]], device const type *q [[buffer(3)]], \
1264
- device const cache_type *k_cache [[buffer(4)]], \
1265
- device const cache_type *v_cache [[buffer(5)]], \
1266
- const device float *__restrict__ k_scale [[buffer(6)]], \
1267
- const device float *__restrict__ v_scale [[buffer(7)]], \
1268
- const constant int &num_kv_heads [[buffer(8)]], \
1269
- const constant float &scale [[buffer(9)]], \
1270
- const constant float &softcapping [[buffer(10)]], \
1271
- device const uint32_t *block_tables [[buffer(11)]], \
1272
- device const uint32_t *context_lens [[buffer(12)]], \
1273
- const constant int &max_num_blocks_per_seq [[buffer(13)]], \
1274
- device const float *alibi_slopes [[buffer(14)]], \
1275
- const constant int &q_stride [[buffer(15)]], \
1276
- const constant int &kv_block_stride [[buffer(16)]], \
1277
- const constant int &kv_head_stride [[buffer(17)]], \
1278
- threadgroup char *shared_mem [[threadgroup(0)]], \
1279
- uint3 threadgroup_position_in_grid [[threadgroup_position_in_grid]], \
1280
- uint3 threadgroups_per_grid [[threadgroups_per_grid]], \
1281
- uint3 thread_position_in_threadgroup [[thread_position_in_threadgroup]], \
1282
- uint simd_tid [[simdgroup_index_in_threadgroup]], \
1283
- uint simd_lid [[thread_index_in_simdgroup]]);
1284
-
1285
- #define instantiate_paged_attention_v2_reduce_inner( \
1286
- type, head_size, num_threads, num_simd_lanes, partition_size) \
1287
- template [[host_name("paged_attention_v2_reduce_" #type "_hs" #head_size \
1288
- "_nt" #num_threads "_nsl" #num_simd_lanes \
1289
- "_ps" #partition_size)]] [[kernel]] void \
1290
- paged_attention_v2_reduce<type, head_size, num_threads, num_simd_lanes, \
1291
- partition_size>( \
1292
- device type * out [[buffer(0)]], \
1293
- const device float *exp_sums [[buffer(1)]], \
1294
- const device float *max_logits [[buffer(2)]], \
1295
- const device type *tmp_out [[buffer(3)]], \
1296
- device uint32_t *context_lens [[buffer(4)]], \
1297
- const constant int &max_num_partitions [[buffer(5)]], \
1298
- threadgroup char *shared_mem [[threadgroup(0)]], \
1299
- uint3 threadgroup_position_in_grid [[threadgroup_position_in_grid]], \
1300
- uint3 threadgroups_per_grid [[threadgroups_per_grid]], \
1301
- uint3 thread_position_in_threadgroup [[thread_position_in_threadgroup]], \
1302
- uint3 threads_per_threadgroup [[threads_per_threadgroup]], \
1303
- uint simd_tid [[simdgroup_index_in_threadgroup]], \
1304
- uint simd_lid [[thread_index_in_simdgroup]]);
1305
-
1306
- #define instantiate_paged_attention_heads( \
1307
- type, cache_type, block_size, num_threads, num_simd_lanes, partition_size) \
1308
- instantiate_paged_attention_inner(type, cache_type, 32, block_size, \
1309
- num_threads, num_simd_lanes, \
1310
- partition_size); \
1311
- instantiate_paged_attention_inner(type, cache_type, 64, block_size, \
1312
- num_threads, num_simd_lanes, \
1313
- partition_size); \
1314
- instantiate_paged_attention_inner(type, cache_type, 80, block_size, \
1315
- num_threads, num_simd_lanes, \
1316
- partition_size); \
1317
- instantiate_paged_attention_inner(type, cache_type, 96, block_size, \
1318
- num_threads, num_simd_lanes, \
1319
- partition_size); \
1320
- instantiate_paged_attention_inner(type, cache_type, 112, block_size, \
1321
- num_threads, num_simd_lanes, \
1322
- partition_size); \
1323
- instantiate_paged_attention_inner(type, cache_type, 120, block_size, \
1324
- num_threads, num_simd_lanes, \
1325
- partition_size); \
1326
- instantiate_paged_attention_inner(type, cache_type, 128, block_size, \
1327
- num_threads, num_simd_lanes, \
1328
- partition_size); \
1329
- instantiate_paged_attention_inner(type, cache_type, 192, block_size, \
1330
- num_threads, num_simd_lanes, \
1331
- partition_size); \
1332
- instantiate_paged_attention_inner(type, cache_type, 256, block_size, \
1333
- num_threads, num_simd_lanes, \
1334
- partition_size);
1335
-
1336
- #define instantiate_paged_attention_v2_reduce_heads( \
1337
- type, num_threads, num_simd_lanes, partition_size) \
1338
- instantiate_paged_attention_v2_reduce_inner(type, 32, num_threads, \
1339
- num_simd_lanes, partition_size); \
1340
- instantiate_paged_attention_v2_reduce_inner(type, 64, num_threads, \
1341
- num_simd_lanes, partition_size); \
1342
- instantiate_paged_attention_v2_reduce_inner(type, 80, num_threads, \
1343
- num_simd_lanes, partition_size); \
1344
- instantiate_paged_attention_v2_reduce_inner(type, 96, num_threads, \
1345
- num_simd_lanes, partition_size); \
1346
- instantiate_paged_attention_v2_reduce_inner(type, 112, num_threads, \
1347
- num_simd_lanes, partition_size); \
1348
- instantiate_paged_attention_v2_reduce_inner(type, 120, num_threads, \
1349
- num_simd_lanes, partition_size); \
1350
- instantiate_paged_attention_v2_reduce_inner(type, 128, num_threads, \
1351
- num_simd_lanes, partition_size); \
1352
- instantiate_paged_attention_v2_reduce_inner(type, 192, num_threads, \
1353
- num_simd_lanes, partition_size); \
1354
- instantiate_paged_attention_v2_reduce_inner(type, 256, num_threads, \
1355
- num_simd_lanes, partition_size);
1356
-
1357
- #define instantiate_paged_attention_block_size(type, cache_type, num_threads, \
1358
- num_simd_lanes, partition_size) \
1359
- instantiate_paged_attention_heads(type, cache_type, 8, num_threads, \
1360
- num_simd_lanes, partition_size); \
1361
- instantiate_paged_attention_heads(type, cache_type, 16, num_threads, \
1362
- num_simd_lanes, partition_size); \
1363
- instantiate_paged_attention_heads(type, cache_type, 32, num_threads, \
1364
- num_simd_lanes, partition_size);
1365
-
1366
- // TODO: tune num_threads = 256
1367
- // NOTE: partition_size = 0
1368
- #define instantiate_paged_attention_v1(type, cache_type, num_simd_lanes) \
1369
- instantiate_paged_attention_block_size(type, cache_type, 256, \
1370
- num_simd_lanes, 0);
1371
-
1372
- // TODO: tune num_threads = 256
1373
- // NOTE: partition_size = 512
1374
- #define instantiate_paged_attention_v2(type, cache_type, num_simd_lanes) \
1375
- instantiate_paged_attention_block_size(type, cache_type, 256, \
1376
- num_simd_lanes, 512);
1377
-
1378
- // TODO: tune num_threads = 256
1379
- // NOTE: partition_size = 512
1380
- #define instantiate_paged_attention_v2_reduce(type, num_simd_lanes) \
1381
- instantiate_paged_attention_v2_reduce_heads(type, 256, num_simd_lanes, 512);
1382
-
1383
- instantiate_paged_attention_v1(float, float, 32);
1384
- instantiate_paged_attention_v1(bfloat16_t, bfloat16_t, 32);
1385
- instantiate_paged_attention_v1(half, half, 32);
1386
-
1387
- instantiate_paged_attention_v1(float, uchar, 32);
1388
- instantiate_paged_attention_v1(bfloat16_t, uchar, 32);
1389
- instantiate_paged_attention_v1(half, uchar, 32);
1390
-
1391
- instantiate_paged_attention_v2_reduce(float, 32);
1392
- instantiate_paged_attention_v2_reduce(bfloat16_t, 32);
1393
- instantiate_paged_attention_v2_reduce(half, 32);
1394
-
1395
- instantiate_paged_attention_v2(float, float, 32);
1396
- instantiate_paged_attention_v2(bfloat16_t, bfloat16_t, 32);
1397
- instantiate_paged_attention_v2(half, half, 32);
1398
-
1399
- instantiate_paged_attention_v2(float, uchar, 32);
1400
- instantiate_paged_attention_v2(bfloat16_t, uchar, 32);
1401
- instantiate_paged_attention_v2(half, uchar, 32);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
paged-attention-metal/cache.mm DELETED
@@ -1,562 +0,0 @@
1
- #include <ATen/mps/MPSDevice.h>
2
- #include <ATen/mps/MPSStream.h>
3
- #include <torch/torch.h>
4
-
5
- #import <Foundation/Foundation.h>
6
- #import <Metal/Metal.h>
7
- #include <dlfcn.h>
8
- #include <mach-o/dyld.h>
9
- #include <string>
10
-
11
- static inline id<MTLBuffer> getMTLBufferStorage(const torch::Tensor &tensor) {
12
- return __builtin_bit_cast(id<MTLBuffer>, tensor.storage().data());
13
- }
14
-
15
- static std::string getModuleDirectory() {
16
- Dl_info dl_info;
17
- if (dladdr((void *)getModuleDirectory, &dl_info)) {
18
- std::string path(dl_info.dli_fname);
19
- size_t pos = path.find_last_of('/');
20
- if (pos != std::string::npos) {
21
- return path.substr(0, pos);
22
- }
23
- }
24
- return ".";
25
- }
26
-
27
- void swap_blocks(torch::Tensor &src, torch::Tensor &dst,
28
- const torch::Tensor &block_mapping) {
29
- TORCH_CHECK(block_mapping.device().is_cpu(), "block_mapping must be on CPU");
30
-
31
- const int64_t block_size_in_bytes = src.element_size() * src[0].numel();
32
- const int64_t num_blocks = block_mapping.size(0);
33
-
34
- // Handle different device combinations
35
- if (src.device().is_mps() && dst.device().is_mps()) {
36
- // MPS to MPS: Use Metal blit encoder
37
- @autoreleasepool {
38
- at::mps::MPSStream *stream = at::mps::getCurrentMPSStream();
39
- TORCH_CHECK(stream, "Failed to get current MPS stream");
40
-
41
- id<MTLCommandBuffer> commandBuffer = stream->commandBuffer();
42
- TORCH_CHECK(commandBuffer, "Failed to get MPS command buffer");
43
-
44
- dispatch_queue_t serialQueue = stream->queue();
45
-
46
- dispatch_sync(serialQueue, ^{
47
- id<MTLBlitCommandEncoder> blitEncoder =
48
- [commandBuffer blitCommandEncoder];
49
- TORCH_CHECK(blitEncoder, "Failed to create blit command encoder");
50
-
51
- id<MTLBuffer> srcBuf = getMTLBufferStorage(src);
52
- id<MTLBuffer> dstBuf = getMTLBufferStorage(dst);
53
-
54
- for (int64_t i = 0; i < num_blocks; ++i) {
55
- int64_t src_block_number = block_mapping[i][0].item<int64_t>();
56
- int64_t dst_block_number = block_mapping[i][1].item<int64_t>();
57
- NSUInteger src_offset = src_block_number * block_size_in_bytes;
58
- NSUInteger dst_offset = dst_block_number * block_size_in_bytes;
59
-
60
- [blitEncoder copyFromBuffer:srcBuf
61
- sourceOffset:src_offset
62
- toBuffer:dstBuf
63
- destinationOffset:dst_offset
64
- size:block_size_in_bytes];
65
- }
66
-
67
- [blitEncoder endEncoding];
68
- stream->synchronize(at::mps::SyncType::COMMIT);
69
- });
70
- }
71
- } else {
72
- // Cross-device transfers (MPS-CPU, CPU-MPS, CPU-CPU): Use PyTorch's copy
73
- for (int64_t i = 0; i < num_blocks; ++i) {
74
- int64_t src_block_number = block_mapping[i][0].item<int64_t>();
75
- int64_t dst_block_number = block_mapping[i][1].item<int64_t>();
76
-
77
- // Copy the entire block
78
- dst[dst_block_number].copy_(src[src_block_number]);
79
- }
80
- }
81
- }
82
-
83
- void copy_blocks(const std::vector<torch::Tensor> &key_caches,
84
- const std::vector<torch::Tensor> &value_caches,
85
- const torch::Tensor &block_mapping) {
86
- const int64_t num_layers = key_caches.size();
87
- TORCH_CHECK(num_layers == static_cast<int64_t>(value_caches.size()),
88
- "key_caches and value_caches must have the same length");
89
- if (num_layers == 0) {
90
- return;
91
- }
92
-
93
- // --- Preconditions --------------------------------------------------
94
- torch::Device dev = key_caches[0].device();
95
- TORCH_CHECK(dev.is_mps(), "copy_blocks: expected MPS tensors");
96
-
97
- // Move block_mapping to CPU if it's on MPS
98
- torch::Tensor block_mapping_cpu = block_mapping;
99
- if (block_mapping.device().is_mps()) {
100
- block_mapping_cpu = block_mapping.cpu();
101
- }
102
-
103
- for (int64_t i = 0; i < num_layers; ++i) {
104
- TORCH_CHECK(key_caches[i].device() == dev &&
105
- value_caches[i].device() == dev,
106
- "All cache tensors must be on the same MPS device");
107
- TORCH_CHECK(key_caches[i].dtype() == value_caches[i].dtype(),
108
- "Key/value cache dtype mismatch at layer ", i);
109
- }
110
-
111
- const int64_t num_pairs = block_mapping.size(0);
112
- const int32_t numel_per_block =
113
- static_cast<int32_t>(key_caches[0][0].numel());
114
-
115
- @autoreleasepool {
116
- at::mps::MPSStream *stream = at::mps::getCurrentMPSStream();
117
- TORCH_CHECK(stream, "Failed to get current MPS stream");
118
-
119
- id<MTLDevice> device = stream->device();
120
- id<MTLCommandBuffer> cmdBuf = stream->commandBuffer();
121
- TORCH_CHECK(cmdBuf, "Failed to get command buffer");
122
-
123
- // Construct the full path to the metallib file
124
- std::string moduleDir = getModuleDirectory();
125
- std::string metallibPath = moduleDir + "/" + METALLIB_PATH;
126
-
127
- NSString *metallibPathStr =
128
- [NSString stringWithUTF8String:metallibPath.c_str()];
129
- NSURL *metallibURL = [NSURL fileURLWithPath:metallibPathStr];
130
- NSError *error = nil;
131
- id<MTLLibrary> lib = [device newLibraryWithURL:metallibURL error:&error];
132
- if (!lib) {
133
- NSLog(@"[cache.mm] Failed to load pre-compiled Metal library at %@: %@",
134
- metallibPathStr, error.localizedDescription);
135
- }
136
-
137
- // Process each layer separately
138
- for (int64_t layer_idx = 0; layer_idx < num_layers; ++layer_idx) {
139
- NSString *kernName = nil;
140
- switch (key_caches[layer_idx].scalar_type()) {
141
- case torch::kFloat:
142
- kernName = @"copy_blocks_float";
143
- break;
144
- case torch::kHalf:
145
- kernName = @"copy_blocks_half";
146
- break;
147
- case torch::kBFloat16:
148
- kernName = @"copy_blocks_bfloat16_t";
149
- break;
150
- case torch::kUInt8:
151
- kernName = @"copy_blocks_uchar";
152
- break;
153
- default:
154
- TORCH_CHECK(false, "Unsupported dtype for copy_blocks");
155
- }
156
-
157
- id<MTLFunction> fn = [lib newFunctionWithName:kernName];
158
- TORCH_CHECK(fn, "Missing Metal kernel function: ", kernName.UTF8String);
159
-
160
- id<MTLComputePipelineState> pso =
161
- [device newComputePipelineStateWithFunction:fn error:&error];
162
- TORCH_CHECK(pso, error.localizedDescription.UTF8String);
163
-
164
- dispatch_queue_t q = stream->queue();
165
- dispatch_sync(q, ^{
166
- id<MTLComputeCommandEncoder> enc = [cmdBuf computeCommandEncoder];
167
- TORCH_CHECK(enc, "Failed to create compute encoder");
168
-
169
- [enc setComputePipelineState:pso];
170
-
171
- // Set key and value cache buffers
172
- [enc setBuffer:getMTLBufferStorage(key_caches[layer_idx])
173
- offset:key_caches[layer_idx].storage_offset() *
174
- key_caches[layer_idx].element_size()
175
- atIndex:0];
176
- [enc setBuffer:getMTLBufferStorage(value_caches[layer_idx])
177
- offset:value_caches[layer_idx].storage_offset() *
178
- value_caches[layer_idx].element_size()
179
- atIndex:1];
180
-
181
- // Set block mapping buffer
182
- id<MTLBuffer> mappingBuf =
183
- [device newBufferWithBytes:block_mapping_cpu.data_ptr<int64_t>()
184
- length:num_pairs * 2 * sizeof(int64_t)
185
- options:MTLResourceStorageModeShared];
186
- [enc setBuffer:mappingBuf offset:0 atIndex:2];
187
-
188
- // Set numel_per_block as buffer
189
- id<MTLBuffer> numelBuf =
190
- [device newBufferWithBytes:&numel_per_block
191
- length:sizeof(int32_t)
192
- options:MTLResourceStorageModeShared];
193
- [enc setBuffer:numelBuf offset:0 atIndex:3];
194
-
195
- const uint32_t threadsPerThreadgroup =
196
- std::min<uint32_t>(256, numel_per_block);
197
- MTLSize tg = MTLSizeMake(threadsPerThreadgroup, 1, 1);
198
- MTLSize grid = MTLSizeMake(threadsPerThreadgroup * num_pairs, 1, 1);
199
-
200
- [enc dispatchThreads:grid threadsPerThreadgroup:tg];
201
- [enc endEncoding];
202
- });
203
- }
204
-
205
- stream->synchronize(at::mps::SyncType::COMMIT);
206
- }
207
- }
208
-
209
- void reshape_and_cache(
210
- torch::Tensor &key, // [num_tokens, num_heads, head_size]
211
- torch::Tensor &value, // [num_tokens, num_heads, head_size]
212
- torch::Tensor
213
- &key_cache, // [num_blocks, num_heads, head_size/x, block_size, x]
214
- torch::Tensor
215
- &value_cache, // [num_blocks, num_heads, head_size, block_size]
216
- torch::Tensor &slot_mapping, // [num_tokens]
217
- const std::string &kv_cache_dtype, torch::Tensor &k_scale,
218
- torch::Tensor &v_scale) {
219
-
220
- // Determine cache dtype and FP8 usage
221
- torch::ScalarType cache_dtype = key_cache.scalar_type();
222
- bool use_fp8_scales = (kv_cache_dtype == "fp8" || kv_cache_dtype == "fp8_e4m3");
223
- if (use_fp8_scales) {
224
- TORCH_CHECK(cache_dtype == torch::kUInt8, "FP8 cache requires UInt8 tensor type");
225
- TORCH_CHECK(k_scale.numel() == 1 && v_scale.numel() == 1, "FP8 scales must be scalars");
226
- TORCH_CHECK(k_scale.scalar_type() == torch::kFloat32 && v_scale.scalar_type() == torch::kFloat32,
227
- "FP8 scales must be float32");
228
- }
229
-
230
- TORCH_CHECK(key.device().is_mps() && value.device().is_mps() &&
231
- key_cache.device().is_mps() && value_cache.device().is_mps(),
232
- "All tensors must be on MPS device");
233
-
234
- // Move slot_mapping to CPU if it's on MPS
235
- torch::Tensor slot_mapping_cpu = slot_mapping;
236
- if (slot_mapping.device().is_mps()) {
237
- slot_mapping_cpu = slot_mapping.cpu();
238
- }
239
-
240
- const int64_t num_tokens = key.size(0);
241
- const int64_t num_heads = key.size(1);
242
- const int64_t head_size = key.size(2);
243
- const int64_t block_size = key_cache.size(3);
244
- const int64_t x = key_cache.size(4);
245
-
246
- const int32_t key_stride = key.stride(0);
247
- const int32_t value_stride = value.stride(0);
248
-
249
- @autoreleasepool {
250
- at::mps::MPSStream *stream = at::mps::getCurrentMPSStream();
251
- TORCH_CHECK(stream, "Failed to get current MPS stream");
252
-
253
- id<MTLDevice> device = stream->device();
254
- id<MTLCommandBuffer> cmdBuf = stream->commandBuffer();
255
- TORCH_CHECK(cmdBuf, "Failed to get command buffer");
256
-
257
- // Construct the full path to the metallib file
258
- std::string moduleDir = getModuleDirectory();
259
- std::string metallibPath = moduleDir + "/" + METALLIB_PATH;
260
-
261
- NSString *metallibPathStr =
262
- [NSString stringWithUTF8String:metallibPath.c_str()];
263
- NSURL *metallibURL = [NSURL fileURLWithPath:metallibPathStr];
264
- NSError *error = nil;
265
- id<MTLLibrary> lib = [device newLibraryWithURL:metallibURL error:&error];
266
- if (!lib) {
267
- NSLog(@"[cache.mm] Failed to load pre-compiled Metal library at %@: %@",
268
- metallibPathStr, error.localizedDescription);
269
- }
270
-
271
- NSString *kernName = nil;
272
- std::string kv_dtype_str, cache_dtype_str;
273
-
274
- // Get KV dtype string
275
- switch (key.scalar_type()) {
276
- case torch::kFloat:
277
- kv_dtype_str = "float";
278
- break;
279
- case torch::kHalf:
280
- kv_dtype_str = "half";
281
- break;
282
- case torch::kBFloat16:
283
- kv_dtype_str = "bfloat16_t";
284
- break;
285
- default:
286
- TORCH_CHECK(false, "Unsupported dtype for reshape_and_cache");
287
- }
288
-
289
- // Get cache dtype string
290
- switch (cache_dtype) {
291
- case torch::kFloat:
292
- cache_dtype_str = "float";
293
- break;
294
- case torch::kHalf:
295
- cache_dtype_str = "half";
296
- break;
297
- case torch::kBFloat16:
298
- cache_dtype_str = "bfloat16_t";
299
- break;
300
- case torch::kUInt8:
301
- cache_dtype_str = "uchar";
302
- break;
303
- default:
304
- TORCH_CHECK(false, "Unsupported cache dtype for reshape_and_cache");
305
- }
306
-
307
- std::string kernName_str = "reshape_and_cache_kv_" + kv_dtype_str + "_cache_" + cache_dtype_str;
308
- kernName = [NSString stringWithUTF8String:kernName_str.c_str()];
309
-
310
- // Create function constants for FP8 support
311
- MTLFunctionConstantValues *constants = [[MTLFunctionConstantValues alloc] init];
312
- [constants setConstantValue:&use_fp8_scales type:MTLDataTypeBool atIndex:10];
313
-
314
- id<MTLFunction> fn = [lib newFunctionWithName:kernName constantValues:constants error:&error];
315
- TORCH_CHECK(fn, "Missing Metal kernel function: ", kernName.UTF8String,
316
- error ? [NSString stringWithFormat:@": %@", error.localizedDescription].UTF8String : "");
317
-
318
- id<MTLComputePipelineState> pso =
319
- [device newComputePipelineStateWithFunction:fn error:&error];
320
- TORCH_CHECK(pso, error.localizedDescription.UTF8String);
321
-
322
- dispatch_queue_t q = stream->queue();
323
- dispatch_sync(q, ^{
324
- id<MTLComputeCommandEncoder> enc = [cmdBuf computeCommandEncoder];
325
- TORCH_CHECK(enc, "Failed to create compute encoder");
326
-
327
- [enc setComputePipelineState:pso];
328
-
329
- // Set tensor buffers
330
- [enc setBuffer:getMTLBufferStorage(key)
331
- offset:key.storage_offset() * key.element_size()
332
- atIndex:0];
333
- [enc setBuffer:getMTLBufferStorage(value)
334
- offset:value.storage_offset() * value.element_size()
335
- atIndex:1];
336
- [enc setBuffer:getMTLBufferStorage(key_cache)
337
- offset:key_cache.storage_offset() * key_cache.element_size()
338
- atIndex:2];
339
- [enc setBuffer:getMTLBufferStorage(value_cache)
340
- offset:value_cache.storage_offset() * value_cache.element_size()
341
- atIndex:3];
342
-
343
- // Set slot mapping buffer
344
- id<MTLBuffer> slotMappingBuf =
345
- [device newBufferWithBytes:slot_mapping_cpu.data_ptr<int64_t>()
346
- length:num_tokens * sizeof(int64_t)
347
- options:MTLResourceStorageModeShared];
348
- [enc setBuffer:slotMappingBuf offset:0 atIndex:4];
349
-
350
- // k_scale and v_scale buffers (for FP8)
351
- if (use_fp8_scales) {
352
- [enc setBuffer:getMTLBufferStorage(k_scale)
353
- offset:k_scale.storage_offset() * k_scale.element_size()
354
- atIndex:5];
355
- [enc setBuffer:getMTLBufferStorage(v_scale)
356
- offset:v_scale.storage_offset() * v_scale.element_size()
357
- atIndex:6];
358
- } else {
359
- // For non-FP8, we still need to increment buffer indices
360
- // The Metal kernel expects buffers at indices 5 and 6 even if unused
361
- }
362
-
363
- // Set parameters as individual buffers (matching mistralrs pattern)
364
- id<MTLBuffer> keyStrideBuf =
365
- [device newBufferWithBytes:&key_stride
366
- length:sizeof(int32_t)
367
- options:MTLResourceStorageModeShared];
368
- [enc setBuffer:keyStrideBuf offset:0 atIndex:7];
369
-
370
- id<MTLBuffer> valueStrideBuf =
371
- [device newBufferWithBytes:&value_stride
372
- length:sizeof(int32_t)
373
- options:MTLResourceStorageModeShared];
374
- [enc setBuffer:valueStrideBuf offset:0 atIndex:8];
375
-
376
- const int32_t num_heads_i32 = static_cast<int32_t>(num_heads);
377
- id<MTLBuffer> numHeadsBuf =
378
- [device newBufferWithBytes:&num_heads_i32
379
- length:sizeof(int32_t)
380
- options:MTLResourceStorageModeShared];
381
- [enc setBuffer:numHeadsBuf offset:0 atIndex:9];
382
-
383
- const int32_t head_size_i32 = static_cast<int32_t>(head_size);
384
- id<MTLBuffer> headSizeBuf =
385
- [device newBufferWithBytes:&head_size_i32
386
- length:sizeof(int32_t)
387
- options:MTLResourceStorageModeShared];
388
- [enc setBuffer:headSizeBuf offset:0 atIndex:10];
389
-
390
- const int32_t block_size_i32 = static_cast<int32_t>(block_size);
391
- id<MTLBuffer> blockSizeBuf =
392
- [device newBufferWithBytes:&block_size_i32
393
- length:sizeof(int32_t)
394
- options:MTLResourceStorageModeShared];
395
- [enc setBuffer:blockSizeBuf offset:0 atIndex:11];
396
-
397
- const int32_t x_i32 = static_cast<int32_t>(x);
398
- id<MTLBuffer> xBuf =
399
- [device newBufferWithBytes:&x_i32
400
- length:sizeof(int32_t)
401
- options:MTLResourceStorageModeShared];
402
- [enc setBuffer:xBuf offset:0 atIndex:12];
403
-
404
- const uint64_t threads_per_threadgroup =
405
- std::min<uint64_t>(512, num_heads * head_size);
406
- MTLSize tg = MTLSizeMake(threads_per_threadgroup, 1, 1);
407
- MTLSize grid = MTLSizeMake(num_tokens, 1, 1);
408
-
409
- [enc dispatchThreadgroups:grid threadsPerThreadgroup:tg];
410
- [enc endEncoding];
411
- });
412
-
413
- stream->synchronize(at::mps::SyncType::COMMIT);
414
- }
415
- }
416
-
417
- void reshape_and_cache_flash(
418
- torch::Tensor &key, // [num_tokens, num_heads, head_size]
419
- torch::Tensor &value, // [num_tokens, num_heads, head_size]
420
- torch::Tensor &key_cache, // [num_blocks, block_size, num_heads, head_size]
421
- torch::Tensor
422
- &value_cache, // [num_blocks, block_size, num_heads, head_size]
423
- torch::Tensor &slot_mapping, // [num_tokens]
424
- const std::string &kv_cache_dtype, torch::Tensor &k_scale,
425
- torch::Tensor &v_scale) {
426
-
427
- TORCH_CHECK(key.device().is_mps() && value.device().is_mps() &&
428
- key_cache.device().is_mps() && value_cache.device().is_mps(),
429
- "All tensors must be on MPS device");
430
-
431
- // Move slot_mapping to CPU if it's on MPS
432
- torch::Tensor slot_mapping_cpu = slot_mapping;
433
- if (slot_mapping.device().is_mps()) {
434
- slot_mapping_cpu = slot_mapping.cpu();
435
- }
436
-
437
- const int64_t num_tokens = key.size(0);
438
- const int64_t num_heads = key.size(1);
439
- const int64_t head_size = key.size(2);
440
- const int64_t block_size = key_cache.size(1);
441
-
442
- const int32_t key_stride = key.stride(0);
443
- const int32_t value_stride = value.stride(0);
444
-
445
- @autoreleasepool {
446
- at::mps::MPSStream *stream = at::mps::getCurrentMPSStream();
447
- TORCH_CHECK(stream, "Failed to get current MPS stream");
448
-
449
- id<MTLDevice> device = stream->device();
450
- id<MTLCommandBuffer> cmdBuf = stream->commandBuffer();
451
- TORCH_CHECK(cmdBuf, "Failed to get command buffer");
452
-
453
- // Construct the full path to the metallib file
454
- std::string moduleDir = getModuleDirectory();
455
- std::string metallibPath = moduleDir + "/" + METALLIB_PATH;
456
-
457
- NSString *metallibPathStr =
458
- [NSString stringWithUTF8String:metallibPath.c_str()];
459
- NSURL *metallibURL = [NSURL fileURLWithPath:metallibPathStr];
460
- NSError *error = nil;
461
- id<MTLLibrary> lib = [device newLibraryWithURL:metallibURL error:&error];
462
- if (!lib) {
463
- NSLog(@"[cache.mm] Failed to load pre-compiled Metal library at %@: %@",
464
- metallibPathStr, error.localizedDescription);
465
- }
466
-
467
- NSString *kernName = nil;
468
- switch (key.scalar_type()) {
469
- case torch::kFloat:
470
- kernName = @"reshape_and_cache_flash_float";
471
- break;
472
- case torch::kHalf:
473
- kernName = @"reshape_and_cache_flash_half";
474
- break;
475
- case torch::kBFloat16:
476
- kernName = @"reshape_and_cache_flash_bfloat16_t";
477
- break;
478
- default:
479
- TORCH_CHECK(false, "Unsupported dtype for reshape_and_cache_flash");
480
- }
481
-
482
- id<MTLFunction> fn = [lib newFunctionWithName:kernName];
483
- TORCH_CHECK(fn, "Missing Metal kernel function: ", kernName.UTF8String);
484
-
485
- id<MTLComputePipelineState> pso =
486
- [device newComputePipelineStateWithFunction:fn error:&error];
487
- TORCH_CHECK(pso, error.localizedDescription.UTF8String);
488
-
489
- dispatch_queue_t q = stream->queue();
490
- dispatch_sync(q, ^{
491
- id<MTLComputeCommandEncoder> enc = [cmdBuf computeCommandEncoder];
492
- TORCH_CHECK(enc, "Failed to create compute encoder");
493
-
494
- [enc setComputePipelineState:pso];
495
-
496
- // Set tensor buffers
497
- [enc setBuffer:getMTLBufferStorage(key)
498
- offset:key.storage_offset() * key.element_size()
499
- atIndex:0];
500
- [enc setBuffer:getMTLBufferStorage(value)
501
- offset:value.storage_offset() * value.element_size()
502
- atIndex:1];
503
- [enc setBuffer:getMTLBufferStorage(key_cache)
504
- offset:key_cache.storage_offset() * key_cache.element_size()
505
- atIndex:2];
506
- [enc setBuffer:getMTLBufferStorage(value_cache)
507
- offset:value_cache.storage_offset() * value_cache.element_size()
508
- atIndex:3];
509
-
510
- // Set slot mapping buffer
511
- id<MTLBuffer> slotMappingBuf =
512
- [device newBufferWithBytes:slot_mapping_cpu.data_ptr<int64_t>()
513
- length:num_tokens * sizeof(int64_t)
514
- options:MTLResourceStorageModeShared];
515
- [enc setBuffer:slotMappingBuf offset:0 atIndex:4];
516
-
517
- // Set parameters as individual buffers
518
- id<MTLBuffer> keyStrideBuf =
519
- [device newBufferWithBytes:&key_stride
520
- length:sizeof(int32_t)
521
- options:MTLResourceStorageModeShared];
522
- [enc setBuffer:keyStrideBuf offset:0 atIndex:5];
523
-
524
- id<MTLBuffer> valueStrideBuf =
525
- [device newBufferWithBytes:&value_stride
526
- length:sizeof(int32_t)
527
- options:MTLResourceStorageModeShared];
528
- [enc setBuffer:valueStrideBuf offset:0 atIndex:6];
529
-
530
- const int32_t num_heads_i32 = static_cast<int32_t>(num_heads);
531
- id<MTLBuffer> numHeadsBuf =
532
- [device newBufferWithBytes:&num_heads_i32
533
- length:sizeof(int32_t)
534
- options:MTLResourceStorageModeShared];
535
- [enc setBuffer:numHeadsBuf offset:0 atIndex:7];
536
-
537
- const int32_t head_size_i32 = static_cast<int32_t>(head_size);
538
- id<MTLBuffer> headSizeBuf =
539
- [device newBufferWithBytes:&head_size_i32
540
- length:sizeof(int32_t)
541
- options:MTLResourceStorageModeShared];
542
- [enc setBuffer:headSizeBuf offset:0 atIndex:8];
543
-
544
- const int32_t block_size_i32 = static_cast<int32_t>(block_size);
545
- id<MTLBuffer> blockSizeBuf =
546
- [device newBufferWithBytes:&block_size_i32
547
- length:sizeof(int32_t)
548
- options:MTLResourceStorageModeShared];
549
- [enc setBuffer:blockSizeBuf offset:0 atIndex:9];
550
-
551
- const uint64_t threads_per_threadgroup =
552
- std::min<uint64_t>(512, num_heads * head_size);
553
- MTLSize tg = MTLSizeMake(threads_per_threadgroup, 1, 1);
554
- MTLSize grid = MTLSizeMake(num_tokens, 1, 1);
555
-
556
- [enc dispatchThreadgroups:grid threadsPerThreadgroup:tg];
557
- [enc endEncoding];
558
- });
559
-
560
- stream->synchronize(at::mps::SyncType::COMMIT);
561
- }
562
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
paged-attention-metal/cache/copy_blocks.metal DELETED
@@ -1,51 +0,0 @@
1
- #include "../utils.metal"
2
- #include <metal_stdlib>
3
-
4
- using namespace metal;
5
-
6
- template <typename T>
7
- [[kernel]] void copy_blocks(device T *key_cache [[buffer(0)]],
8
- device T *value_cache [[buffer(1)]],
9
- const device int64_t *block_mapping [[buffer(2)]],
10
- device const int &numel_per_block,
11
- uint tgid [[threadgroup_position_in_grid]],
12
- uint tid [[thread_position_in_threadgroup]],
13
- uint threads_per_threadgroup
14
- [[threads_per_threadgroup]]) {
15
- const int pair_idx = tgid;
16
-
17
- int64_t src_block_number = block_mapping[2 * pair_idx];
18
- int64_t dst_block_number = block_mapping[2 * pair_idx + 1];
19
-
20
- const int64_t src_block_offset = src_block_number * numel_per_block;
21
- const int64_t dst_block_offset = dst_block_number * numel_per_block;
22
-
23
- // Copy key cache blocks
24
- for (int i = tid; i < numel_per_block; i += threads_per_threadgroup) {
25
- int64_t src_offset = src_block_offset + i;
26
- int64_t dst_offset = dst_block_offset + i;
27
- key_cache[dst_offset] = key_cache[src_offset];
28
- }
29
-
30
- // Copy value cache blocks
31
- for (int i = tid; i < numel_per_block; i += threads_per_threadgroup) {
32
- int64_t src_offset = src_block_offset + i;
33
- int64_t dst_offset = dst_block_offset + i;
34
- value_cache[dst_offset] = value_cache[src_offset];
35
- }
36
- }
37
-
38
- #define instantiate_copy_blocks(type) \
39
- template [[host_name("copy_blocks_" #type)]] [[kernel]] void \
40
- copy_blocks<type>(device type * key_cache [[buffer(0)]], \
41
- device type * value_cache [[buffer(1)]], \
42
- const device int64_t *block_mapping [[buffer(2)]], \
43
- device const int &numel_per_block, \
44
- uint tgid [[threadgroup_position_in_grid]], \
45
- uint tid [[thread_position_in_threadgroup]], \
46
- uint threads_per_threadgroup [[threads_per_threadgroup]]);
47
-
48
- instantiate_copy_blocks(float);
49
- instantiate_copy_blocks(bfloat16_t);
50
- instantiate_copy_blocks(half);
51
- instantiate_copy_blocks(uchar);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
paged-attention-metal/cache/reshape_and_cache.metal DELETED
@@ -1,193 +0,0 @@
1
- #include "../utils.metal"
2
- #include "../float8.metal"
3
- #include <metal_stdlib>
4
-
5
- using namespace metal;
6
-
7
- template <typename KV_T, typename CACHE_T>
8
- inline CACHE_T to_cache(KV_T v) = delete;
9
-
10
- template <> inline uchar to_cache<float, uchar>(float v) {
11
- return float_to_fp8_e4m3(v);
12
- }
13
-
14
- template <> inline uchar to_cache<bfloat16_t, uchar>(bfloat16_t v) {
15
- return float_to_fp8_e4m3((float)v);
16
- }
17
-
18
- template <> inline uchar to_cache<half, uchar>(half v) {
19
- return float_to_fp8_e4m3((float)v);
20
- }
21
-
22
- template <> inline float to_cache<float, float>(float v) { return v; }
23
-
24
- template <> inline bfloat16_t to_cache<bfloat16_t, bfloat16_t>(bfloat16_t v) {
25
- return v;
26
- }
27
-
28
- template <> inline half to_cache<half, half>(half v) { return v; }
29
-
30
- constant bool use_fp8_scales [[function_constant(10)]];
31
-
32
- template <typename KV_T, typename CACHE_T>
33
- [[kernel]] void reshape_and_cache(
34
- const device KV_T *__restrict__ key
35
- [[buffer(0)]], // [num_tokens, num_heads, head_size]
36
- const device KV_T *__restrict__ value
37
- [[buffer(1)]], // [num_tokens, num_heads, head_size]
38
- device CACHE_T *__restrict__ key_cache
39
- [[buffer(2)]], // [num_blocks, num_heads, head_size/x, block_size, x]
40
- device CACHE_T *__restrict__ value_cache
41
- [[buffer(3)]], // [num_blocks, num_heads, head_size, block_size]
42
- const device int64_t *__restrict__ slot_mapping
43
- [[buffer(4)]], // [num_tokens]
44
- const device float *__restrict__ k_scale
45
- [[buffer(5)]], // [1] - only used when use_fp8_scales
46
- const device float *__restrict__ v_scale
47
- [[buffer(6)]], // [1] - only used when use_fp8_scales
48
- device const int &key_stride [[buffer(7)]],
49
- device const int &value_stride [[buffer(8)]],
50
- device const int &num_heads [[buffer(9)]],
51
- device const int &head_size [[buffer(10)]],
52
- device const int &block_size [[buffer(11)]],
53
- device const int &x [[buffer(12)]],
54
- uint gid [[threadgroup_position_in_grid]],
55
- uint tid [[thread_position_in_threadgroup]],
56
- uint threads_per_threadgroup [[threads_per_threadgroup]]) {
57
- const int64_t token_idx = gid;
58
- const int64_t slot_idx = slot_mapping[token_idx];
59
- if (slot_idx < 0) {
60
- // Padding token that should be ignored.
61
- return;
62
- }
63
-
64
- const int64_t block_idx = slot_idx / block_size;
65
- const int64_t block_offset = slot_idx % block_size;
66
-
67
- const int n = num_heads * head_size;
68
- for (int i = tid; i < n; i += threads_per_threadgroup) {
69
- const int64_t src_key_idx = token_idx * key_stride + i;
70
- const int64_t src_value_idx = token_idx * value_stride + i;
71
-
72
- const int head_idx = i / head_size;
73
- const int head_offset = i % head_size;
74
- const int x_idx = head_offset / x;
75
- const int x_offset = head_offset % x;
76
-
77
- const int64_t tgt_key_idx =
78
- block_idx * num_heads * (head_size / x) * block_size * x +
79
- head_idx * (head_size / x) * block_size * x + x_idx * block_size * x +
80
- block_offset * x + x_offset;
81
- const int64_t tgt_value_idx =
82
- block_idx * num_heads * head_size * block_size +
83
- head_idx * head_size * block_size + head_offset * block_size +
84
- block_offset;
85
-
86
- if (use_fp8_scales) {
87
- key_cache[tgt_key_idx] =
88
- to_cache<KV_T, CACHE_T>(KV_T((float)key[src_key_idx] / *k_scale));
89
- value_cache[tgt_value_idx] =
90
- to_cache<KV_T, CACHE_T>(KV_T((float)value[src_value_idx] / *v_scale));
91
- } else {
92
- key_cache[tgt_key_idx] = to_cache<KV_T, CACHE_T>(key[src_key_idx]);
93
- value_cache[tgt_value_idx] = to_cache<KV_T, CACHE_T>(value[src_value_idx]);
94
- }
95
- }
96
- }
97
-
98
- #define instantiate_reshape_and_cache(kv_type, cache_type) \
99
- template [[host_name("reshape_and_cache_kv_" #kv_type \
100
- "_cache_" #cache_type)]] [[kernel]] void \
101
- reshape_and_cache<kv_type, cache_type>( \
102
- const device kv_type *__restrict__ key [[buffer(0)]], \
103
- const device kv_type *__restrict__ value [[buffer(1)]], \
104
- device cache_type *__restrict__ key_cache [[buffer(2)]], \
105
- device cache_type *__restrict__ value_cache [[buffer(3)]], \
106
- const device int64_t *__restrict__ slot_mapping [[buffer(4)]], \
107
- const device float *__restrict__ k_scale [[buffer(5)]], \
108
- const device float *__restrict__ v_scale [[buffer(6)]], \
109
- device const int &key_stride [[buffer(7)]], \
110
- device const int &value_stride [[buffer(8)]], \
111
- device const int &num_heads [[buffer(9)]], \
112
- device const int &head_size [[buffer(10)]], \
113
- device const int &block_size [[buffer(11)]], \
114
- device const int &x [[buffer(12)]], \
115
- uint gid [[threadgroup_position_in_grid]], \
116
- uint tid [[thread_position_in_threadgroup]], \
117
- uint threads_per_threadgroup [[threads_per_threadgroup]]);
118
-
119
- instantiate_reshape_and_cache(float, float);
120
- instantiate_reshape_and_cache(bfloat16_t, bfloat16_t);
121
- instantiate_reshape_and_cache(half, half);
122
-
123
- instantiate_reshape_and_cache(float, uchar);
124
- instantiate_reshape_and_cache(bfloat16_t, uchar);
125
- instantiate_reshape_and_cache(half, uchar);
126
-
127
- // Flash version with different cache layout: [num_blocks, block_size,
128
- // num_heads, head_size]
129
- template <typename T>
130
- [[kernel]] void reshape_and_cache_flash(
131
- const device T *__restrict__ key
132
- [[buffer(0)]], // [num_tokens, num_heads, head_size]
133
- const device T *__restrict__ value
134
- [[buffer(1)]], // [num_tokens, num_heads, head_size]
135
- device T *__restrict__ key_cache
136
- [[buffer(2)]], // [num_blocks, block_size, num_heads, head_size]
137
- device T *__restrict__ value_cache
138
- [[buffer(3)]], // [num_blocks, block_size, num_heads, head_size]
139
- const device int64_t *__restrict__ slot_mapping
140
- [[buffer(4)]], // [num_tokens]
141
- device const int &key_stride, device const int &value_stride,
142
- device const int &num_heads, device const int &head_size,
143
- device const int &block_size, uint gid [[threadgroup_position_in_grid]],
144
- uint tid [[thread_position_in_threadgroup]],
145
- uint threads_per_threadgroup [[threads_per_threadgroup]]) {
146
- const int64_t token_idx = gid;
147
- const int64_t slot_idx = slot_mapping[token_idx];
148
- if (slot_idx < 0) {
149
- // Padding token that should be ignored.
150
- return;
151
- }
152
-
153
- const int64_t block_idx = slot_idx / block_size;
154
- const int64_t block_offset = slot_idx % block_size;
155
-
156
- const int n = num_heads * head_size;
157
- for (int i = tid; i < n; i += threads_per_threadgroup) {
158
- const int64_t src_key_idx = token_idx * key_stride + i;
159
- const int64_t src_value_idx = token_idx * value_stride + i;
160
-
161
- const int head_idx = i / head_size;
162
- const int head_offset = i % head_size;
163
-
164
- // Flash cache layout: [num_blocks, block_size, num_heads, head_size]
165
- const int64_t tgt_key_idx = block_idx * block_size * num_heads * head_size +
166
- block_offset * num_heads * head_size +
167
- head_idx * head_size + head_offset;
168
- const int64_t tgt_value_idx =
169
- block_idx * block_size * num_heads * head_size +
170
- block_offset * num_heads * head_size + head_idx * head_size +
171
- head_offset;
172
- key_cache[tgt_key_idx] = key[src_key_idx];
173
- value_cache[tgt_value_idx] = value[src_value_idx];
174
- }
175
- }
176
-
177
- #define instantiate_reshape_and_cache_flash(type) \
178
- template [[host_name("reshape_and_cache_flash_" #type)]] [[kernel]] void \
179
- reshape_and_cache_flash<type>( \
180
- const device type *__restrict__ key [[buffer(0)]], \
181
- const device type *__restrict__ value [[buffer(1)]], \
182
- device type *__restrict__ key_cache [[buffer(2)]], \
183
- device type *__restrict__ value_cache [[buffer(3)]], \
184
- const device int64_t *__restrict__ slot_mapping [[buffer(4)]], \
185
- device const int &key_stride, device const int &value_stride, \
186
- device const int &num_heads, device const int &head_size, \
187
- device const int &block_size, uint gid [[threadgroup_position_in_grid]], \
188
- uint tid [[thread_position_in_threadgroup]], \
189
- uint threads_per_threadgroup [[threads_per_threadgroup]]);
190
-
191
- instantiate_reshape_and_cache_flash(float);
192
- instantiate_reshape_and_cache_flash(bfloat16_t);
193
- instantiate_reshape_and_cache_flash(half);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
paged-attention-metal/convert_fp8.metal DELETED
@@ -1,77 +0,0 @@
1
- #include "float8.metal"
2
- #include "utils.metal"
3
- #include <metal_stdlib>
4
-
5
- using namespace metal;
6
-
7
- // Convert between different precision formats for cache tensors
8
- // This kernel handles conversions like float->fp8, fp8->float, etc.
9
-
10
- template <typename SRC_T, typename DST_T>
11
- [[kernel]] void convert_fp8_kernel(
12
- const device SRC_T *__restrict__ src [[buffer(0)]],
13
- device DST_T *__restrict__ dst [[buffer(1)]],
14
- const device float &scale [[buffer(2)]],
15
- const device uint32_t &num_elements [[buffer(3)]],
16
- uint gid [[thread_position_in_grid]]) {
17
-
18
- if (gid >= num_elements) {
19
- return;
20
- }
21
-
22
- // Load source value
23
- SRC_T src_val = src[gid];
24
-
25
- // Convert based on source and destination types
26
- if constexpr (is_same_v<SRC_T, uchar> && !is_same_v<DST_T, uchar>) {
27
- // FP8 -> higher precision (dequantization)
28
- float fp32_val = fp8_e4m3_to_float(src_val) * scale;
29
- dst[gid] = static_cast<DST_T>(fp32_val);
30
- } else if constexpr (!is_same_v<SRC_T, uchar> && is_same_v<DST_T, uchar>) {
31
- // Higher precision -> FP8 (quantization)
32
- float fp32_val = static_cast<float>(src_val) / scale;
33
- dst[gid] = float_to_fp8_e4m3(fp32_val);
34
- } else if constexpr (is_same_v<SRC_T, uchar> && is_same_v<DST_T, uchar>) {
35
- // FP8 -> FP8 (with rescaling)
36
- float fp32_val = fp8_e4m3_to_float(src_val) * scale;
37
- dst[gid] = float_to_fp8_e4m3(fp32_val);
38
- } else {
39
- // Regular precision -> regular precision (with scaling)
40
- float fp32_val = static_cast<float>(src_val) * scale;
41
- dst[gid] = static_cast<DST_T>(fp32_val);
42
- }
43
- }
44
-
45
- // Instantiate all required combinations
46
- #define INSTANTIATE_CONVERT_FP8(src_type, dst_type) \
47
- template [[host_name("convert_fp8_" #src_type "_to_" #dst_type)]] \
48
- [[kernel]] void convert_fp8_kernel<src_type, dst_type>( \
49
- const device src_type *__restrict__ src [[buffer(0)]], \
50
- device dst_type *__restrict__ dst [[buffer(1)]], \
51
- const device float &scale [[buffer(2)]], \
52
- const device uint32_t &num_elements [[buffer(3)]], \
53
- uint gid [[thread_position_in_grid]]);
54
-
55
- // FP8 to other formats (dequantization)
56
- INSTANTIATE_CONVERT_FP8(uchar, float);
57
- INSTANTIATE_CONVERT_FP8(uchar, half);
58
- INSTANTIATE_CONVERT_FP8(uchar, bfloat16_t);
59
-
60
- // Other formats to FP8 (quantization)
61
- INSTANTIATE_CONVERT_FP8(float, uchar);
62
- INSTANTIATE_CONVERT_FP8(half, uchar);
63
- INSTANTIATE_CONVERT_FP8(bfloat16_t, uchar);
64
-
65
- // FP8 to FP8 (rescaling)
66
- INSTANTIATE_CONVERT_FP8(uchar, uchar);
67
-
68
- // Regular precision conversions with scaling
69
- INSTANTIATE_CONVERT_FP8(float, float);
70
- INSTANTIATE_CONVERT_FP8(float, half);
71
- INSTANTIATE_CONVERT_FP8(float, bfloat16_t);
72
- INSTANTIATE_CONVERT_FP8(half, float);
73
- INSTANTIATE_CONVERT_FP8(half, half);
74
- INSTANTIATE_CONVERT_FP8(half, bfloat16_t);
75
- INSTANTIATE_CONVERT_FP8(bfloat16_t, float);
76
- INSTANTIATE_CONVERT_FP8(bfloat16_t, half);
77
- INSTANTIATE_CONVERT_FP8(bfloat16_t, bfloat16_t);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
paged-attention-metal/convert_fp8.mm DELETED
@@ -1,138 +0,0 @@
1
- #include <ATen/mps/MPSDevice.h>
2
- #include <ATen/mps/MPSStream.h>
3
- #include <torch/torch.h>
4
-
5
- #import <Foundation/Foundation.h>
6
- #import <Metal/Metal.h>
7
- #include <algorithm>
8
- #include <dlfcn.h>
9
- #include <mach-o/dyld.h>
10
- #include <string>
11
- #include <vector>
12
-
13
- static inline id<MTLBuffer> getMTLBufferStorage(const torch::Tensor &tensor) {
14
- return __builtin_bit_cast(id<MTLBuffer>, tensor.storage().data());
15
- }
16
-
17
- static std::string getModuleDirectory() {
18
- Dl_info dl_info;
19
- if (dladdr((void *)getModuleDirectory, &dl_info)) {
20
- std::string path(dl_info.dli_fname);
21
- size_t pos = path.find_last_of('/');
22
- if (pos != std::string::npos) {
23
- return path.substr(0, pos);
24
- }
25
- }
26
- return ".";
27
- }
28
-
29
- // Helper function to get conversion kernel name
30
- static std::string getConvertKernelName(torch::ScalarType src_dtype, torch::ScalarType dst_dtype) {
31
- std::string src_str, dst_str;
32
-
33
- auto dtype_to_string = [](torch::ScalarType dtype) -> std::string {
34
- switch (dtype) {
35
- case torch::kFloat: return "float";
36
- case torch::kHalf: return "half";
37
- case torch::kBFloat16: return "bfloat16_t";
38
- case torch::kUInt8: return "uchar";
39
- default:
40
- TORCH_CHECK(false, "Unsupported dtype for convert_fp8: ", dtype);
41
- }
42
- };
43
-
44
- src_str = dtype_to_string(src_dtype);
45
- dst_str = dtype_to_string(dst_dtype);
46
-
47
- return "convert_fp8_" + src_str + "_to_" + dst_str;
48
- }
49
-
50
- void convert_fp8(torch::Tensor &dst_cache, torch::Tensor &src_cache,
51
- const double scale, const std::string &kv_cache_dtype) {
52
- // Validate input tensors
53
- TORCH_CHECK(src_cache.device().is_mps() && dst_cache.device().is_mps(),
54
- "Both tensors must be on MPS device");
55
- TORCH_CHECK(src_cache.device() == dst_cache.device(),
56
- "Source and destination tensors must be on the same device");
57
- TORCH_CHECK(src_cache.numel() == dst_cache.numel(),
58
- "Source and destination tensors must have the same number of elements");
59
- TORCH_CHECK(src_cache.is_contiguous() && dst_cache.is_contiguous(),
60
- "Both tensors must be contiguous");
61
-
62
- const uint32_t num_elements = static_cast<uint32_t>(src_cache.numel());
63
- if (num_elements == 0) {
64
- return; // Nothing to convert
65
- }
66
-
67
- // Determine conversion kernel name
68
- std::string kernel_name = getConvertKernelName(src_cache.scalar_type(), dst_cache.scalar_type());
69
-
70
- @autoreleasepool {
71
- at::mps::MPSStream *stream = at::mps::getCurrentMPSStream();
72
- TORCH_CHECK(stream, "Failed to get current MPS stream");
73
-
74
- id<MTLDevice> device = stream->device();
75
- id<MTLCommandBuffer> cmdBuf = stream->commandBuffer();
76
- TORCH_CHECK(cmdBuf, "Failed to get command buffer");
77
-
78
- // Load Metal library
79
- std::string moduleDir = getModuleDirectory();
80
- std::string metallibPath = moduleDir + "/" + METALLIB_PATH;
81
- NSString *metallibPathStr = [NSString stringWithUTF8String:metallibPath.c_str()];
82
- NSURL *metallibURL = [NSURL fileURLWithPath:metallibPathStr];
83
- NSError *error = nil;
84
- id<MTLLibrary> lib = [device newLibraryWithURL:metallibURL error:&error];
85
- TORCH_CHECK(lib, "Failed to load Metal library at ", metallibPath, ": ",
86
- error ? error.localizedDescription.UTF8String : "unknown error");
87
-
88
- // Create kernel function
89
- NSString *kernelNameStr = [NSString stringWithUTF8String:kernel_name.c_str()];
90
- id<MTLFunction> fn = [lib newFunctionWithName:kernelNameStr];
91
- TORCH_CHECK(fn, "Failed to find Metal kernel function: ", kernel_name);
92
-
93
- id<MTLComputePipelineState> pso = [device newComputePipelineStateWithFunction:fn error:&error];
94
- TORCH_CHECK(pso, "Failed to create compute pipeline state: ",
95
- error ? error.localizedDescription.UTF8String : "unknown error");
96
-
97
- dispatch_queue_t q = stream->queue();
98
- dispatch_sync(q, ^{
99
- id<MTLComputeCommandEncoder> enc = [cmdBuf computeCommandEncoder];
100
- TORCH_CHECK(enc, "Failed to create compute encoder");
101
-
102
- [enc setComputePipelineState:pso];
103
-
104
- // Set buffers
105
- [enc setBuffer:getMTLBufferStorage(src_cache)
106
- offset:src_cache.storage_offset() * src_cache.element_size()
107
- atIndex:0];
108
- [enc setBuffer:getMTLBufferStorage(dst_cache)
109
- offset:dst_cache.storage_offset() * dst_cache.element_size()
110
- atIndex:1];
111
-
112
- // Set scale parameter
113
- float scale_f32 = static_cast<float>(scale);
114
- id<MTLBuffer> scaleBuf = [device newBufferWithBytes:&scale_f32
115
- length:sizeof(float)
116
- options:MTLResourceStorageModeShared];
117
- [enc setBuffer:scaleBuf offset:0 atIndex:2];
118
-
119
- // Set num_elements parameter
120
- id<MTLBuffer> numElementsBuf = [device newBufferWithBytes:&num_elements
121
- length:sizeof(uint32_t)
122
- options:MTLResourceStorageModeShared];
123
- [enc setBuffer:numElementsBuf offset:0 atIndex:3];
124
-
125
- // Dispatch threads
126
- const uint32_t threads_per_threadgroup = std::min<uint32_t>(1024, num_elements);
127
- const uint32_t threadgroups = (num_elements + threads_per_threadgroup - 1) / threads_per_threadgroup;
128
-
129
- MTLSize threadsPerThreadgroup = MTLSizeMake(threads_per_threadgroup, 1, 1);
130
- MTLSize threadgroupsPerGrid = MTLSizeMake(threadgroups, 1, 1);
131
-
132
- [enc dispatchThreadgroups:threadgroupsPerGrid threadsPerThreadgroup:threadsPerThreadgroup];
133
- [enc endEncoding];
134
- });
135
-
136
- stream->synchronize(at::mps::SyncType::COMMIT);
137
- }
138
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
paged-attention-metal/device.mm DELETED
@@ -1,17 +0,0 @@
1
- #include "../torch-ext/torch_binding.h"
2
- #import <Metal/Metal.h>
3
- #include <torch/torch.h>
4
-
5
- int64_t get_device_attribute(int64_t attribute, int64_t device_id) {
6
- TORCH_CHECK(false, "get_device_attribute is not supported on Metal");
7
- }
8
-
9
- int64_t get_max_shared_memory_per_block_device_attribute(int64_t device_id) {
10
- // On macOS you can have multiple GPUs; fetch the N-th one.
11
- NSArray<id<MTLDevice>> *all = MTLCopyAllDevices();
12
- TORCH_CHECK(device_id >= 0 && device_id < (int64_t)all.count,
13
- "Invalid Metal device index");
14
-
15
- id<MTLDevice> dev = all[device_id];
16
- return static_cast<int64_t>(dev.maxThreadgroupMemoryLength);
17
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
paged-attention-metal/float8.metal DELETED
@@ -1,122 +0,0 @@
1
- #include <metal_stdlib>
2
- using namespace metal;
3
-
4
- // Helpers ------------------------------------------------------------
5
- static inline uint as_bits(float x) { return as_type<uint>(x); }
6
- static inline float from_bits(uint b) { return as_type<float>(b); }
7
-
8
- // -------------------------------------------------------------------
9
- // FP8 E4M3 (bias = 7)
10
- // -------------------------------------------------------------------
11
- inline float fp8_e4m3_to_float(uchar v) {
12
- const uint s = v >> 7;
13
- const uint exp = (v >> 3) & 0xF;
14
- const uint man = v & 0x7;
15
-
16
- if (exp == 0) { // zero / sub-normal
17
- if (man == 0)
18
- return s ? -0.f : 0.f;
19
- const float m = float(man) / 8.f; // already scaled by 2^-3
20
- float val = ldexp(m, 1 - 7); // 2^(1-bias) = 2^-6
21
- return s ? -val : val;
22
- }
23
-
24
- if (exp == 0xF) { // Inf / NaN (E4M3FN keeps only NaN)
25
- if (man != 0)
26
- return NAN;
27
- return s ? -INFINITY : INFINITY;
28
- }
29
-
30
- const float m = 1.f + float(man) / 8.f;
31
- float val = ldexp(m, int(exp) - 7);
32
- return s ? -val : val;
33
- }
34
-
35
- // -------------------------------------------------------------------
36
- // FP8 E5M2 (bias = 15)
37
- // -------------------------------------------------------------------
38
- inline float fp8_e5m2_to_float(uchar v) {
39
- const uint s = v >> 7;
40
- const uint exp = (v >> 2) & 0x1F;
41
- const uint man = v & 0x3;
42
-
43
- if (exp == 0) {
44
- if (man == 0)
45
- return s ? -0.f : 0.f;
46
- const float m = float(man) / 4.f;
47
- float val = ldexp(m, 1 - 15); // 2^(1-bias) = 2^-14
48
- return s ? -val : val;
49
- }
50
-
51
- if (exp == 0x1F) {
52
- if (man != 0)
53
- return NAN;
54
- return s ? -INFINITY : INFINITY;
55
- }
56
-
57
- const float m = 1.f + float(man) / 4.f;
58
- float val = ldexp(m, int(exp) - 15);
59
- return s ? -val : val;
60
- }
61
-
62
- // -------------------------------------------------------------------
63
- // Encoding helpers (round-to-nearest-even, gradual under-flow, sat-to-∞)
64
- // -------------------------------------------------------------------
65
- namespace detail {
66
- template <int EXP_BITS, int MAN_BITS, int BIAS>
67
- inline uchar fp32_to_fp8(float f) {
68
- const uint bits = as_bits(f);
69
- const uint s = bits >> 31;
70
- const uint abs = bits & 0x7FFFFFFF;
71
-
72
- // NaN propagates, Inf saturates
73
- if (abs >= 0x7F800000u) {
74
- return uchar((s << 7) | (((1u << EXP_BITS) - 1u) << MAN_BITS) |
75
- (abs != 0x7F800000u));
76
- }
77
-
78
- int e = int((abs >> 23) & 0xFF) - 127; // unbiased exponent
79
- uint m = abs & 0x7FFFFFu; // 23-bit mantissa
80
- const int EXP_MAX = (1 << EXP_BITS) - 2; // last finite exponent
81
-
82
- // ---------- Normal path -------------------------------------------------
83
- int e_fp8 = e + BIAS;
84
- if (e_fp8 >= 1 && e_fp8 <= EXP_MAX) {
85
- // round-to-nearest-even
86
- const int shift = 23 - MAN_BITS;
87
- uint mant = m >> shift;
88
- const uint lsb = mant & 1u;
89
- const uint round = (m >> (shift - 1)) & 1u;
90
- const uint sticky = (m & ((1u << (shift - 1)) - 1u)) != 0u;
91
- mant += (round & (sticky | lsb));
92
- if (mant >> MAN_BITS) { // mantissa overflow
93
- mant = 0;
94
- ++e_fp8;
95
- if (e_fp8 > EXP_MAX)
96
- return uchar((s << 7) | (((1u << EXP_BITS) - 1u) << MAN_BITS)); // ∞
97
- }
98
- return uchar((s << 7) | (uint(e_fp8) << MAN_BITS) |
99
- (mant & ((1u << MAN_BITS) - 1u)));
100
- }
101
-
102
- // ---------- Sub-normal / under-flow ------------------------------------
103
- if (e_fp8 < 1 - MAN_BITS) // too small -> ±0
104
- return uchar(s << 7);
105
-
106
- // shift so that exponent becomes 1
107
- int rshift = (1 - e_fp8) + (23 - MAN_BITS);
108
- uint mant = (0x800000u | m); // implicit 1
109
- uint rounded = (mant + (1u << (rshift - 1))) >> rshift;
110
- if (rounded == 0)
111
- return uchar(s << 7); // rounds to zero
112
-
113
- return uchar((s << 7) | (rounded & ((1u << MAN_BITS) - 1u)));
114
- }
115
- } // namespace detail
116
-
117
- inline uchar float_to_fp8_e4m3(float f) {
118
- return detail::fp32_to_fp8<4, 3, 7>(f);
119
- }
120
- inline uchar float_to_fp8_e5m2(float f) {
121
- return detail::fp32_to_fp8<5, 2, 15>(f);
122
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
paged-attention-metal/paged_attention.mm DELETED
@@ -1,693 +0,0 @@
1
- #include <ATen/mps/MPSDevice.h>
2
- #include <ATen/mps/MPSStream.h>
3
- #include <torch/torch.h>
4
-
5
- #import <Foundation/Foundation.h>
6
- #import <Metal/Metal.h>
7
- #include <algorithm>
8
- #include <dlfcn.h>
9
- #include <mach-o/dyld.h>
10
- #include <string>
11
- #include <vector>
12
-
13
- static inline id<MTLBuffer> getMTLBufferStorage(const torch::Tensor &tensor) {
14
- return __builtin_bit_cast(id<MTLBuffer>, tensor.storage().data());
15
- }
16
-
17
- static std::string getModuleDirectory() {
18
- Dl_info dl_info;
19
- if (dladdr((void *)getModuleDirectory, &dl_info)) {
20
- std::string path(dl_info.dli_fname);
21
- size_t pos = path.find_last_of('/');
22
- if (pos != std::string::npos) {
23
- return path.substr(0, pos);
24
- }
25
- }
26
- return ".";
27
- }
28
-
29
- // Helper function to get kernel name based on dtype and parameters
30
- static std::string getKernelName(const std::string &base_name,
31
- torch::ScalarType dtype,
32
- torch::ScalarType cache_dtype,
33
- int head_size,
34
- int block_size, int num_threads,
35
- int num_simd_lanes, int partition_size = 0) {
36
- std::string dtype_str;
37
- switch (dtype) {
38
- case torch::kFloat:
39
- dtype_str = "float";
40
- break;
41
- case torch::kHalf:
42
- dtype_str = "half";
43
- break;
44
- case torch::kBFloat16:
45
- dtype_str = "bfloat16_t";
46
- break;
47
- default:
48
- TORCH_CHECK(false, "Unsupported dtype for paged attention: ", dtype);
49
- }
50
-
51
- std::string cache_dtype_str;
52
- switch (cache_dtype) {
53
- case torch::kFloat:
54
- cache_dtype_str = "float";
55
- break;
56
- case torch::kHalf:
57
- cache_dtype_str = "half";
58
- break;
59
- case torch::kBFloat16:
60
- cache_dtype_str = "bfloat16_t";
61
- break;
62
- case torch::kUInt8:
63
- cache_dtype_str = "uchar";
64
- break;
65
- default:
66
- TORCH_CHECK(false, "Unsupported cache dtype for paged attention: ", cache_dtype);
67
- }
68
-
69
- std::string kernel_name =
70
- base_name + "_" + dtype_str + "_cache_" + cache_dtype_str + "_hs" + std::to_string(head_size) + "_bs" +
71
- std::to_string(block_size) + "_nt" + std::to_string(num_threads) +
72
- "_nsl" + std::to_string(num_simd_lanes);
73
-
74
- if (partition_size >= 0) {
75
- kernel_name += "_ps" + std::to_string(partition_size);
76
- }
77
-
78
- return kernel_name;
79
- }
80
-
81
- // Helper function to calculate shared memory size
82
- static size_t calculateSharedMemorySize(int max_seq_len, int head_size,
83
- int num_threads, int num_simd_lanes) {
84
- // Logits storage: max_seq_len * sizeof(float)
85
- size_t logits_size = max_seq_len * sizeof(float);
86
-
87
- // Reduction workspace: 2 * (num_threads / num_simd_lanes) * sizeof(float)
88
- size_t reduction_size = 2 * (num_threads / num_simd_lanes) * sizeof(float);
89
-
90
- // Output workspace for cross-warp reduction: head_size * sizeof(float)
91
- size_t output_size = head_size * sizeof(float);
92
- return std::max(logits_size + reduction_size, output_size);
93
- }
94
-
95
- // Helper function to get supported configurations
96
- static bool isValidConfiguration(int head_size, int block_size) {
97
- // Supported head sizes from the Metal kernel instantiations
98
- std::vector<int> supported_head_sizes = {32, 64, 80, 96, 112,
99
- 120, 128, 192, 256};
100
- std::vector<int> supported_block_sizes = {8, 16, 32};
101
-
102
- return std::find(supported_head_sizes.begin(), supported_head_sizes.end(),
103
- head_size) != supported_head_sizes.end() &&
104
- std::find(supported_block_sizes.begin(), supported_block_sizes.end(),
105
- block_size) != supported_block_sizes.end();
106
- }
107
-
108
- void paged_attention_v1(
109
- torch::Tensor &out, // [num_seqs, num_heads, head_size]
110
- torch::Tensor &query, // [num_seqs, num_heads, head_size]
111
- torch::Tensor
112
- &key_cache, // [num_blocks, num_heads, head_size/x, block_size, x]
113
- torch::Tensor
114
- &value_cache, // [num_blocks, num_heads, head_size, block_size]
115
- int64_t num_kv_heads, // [num_heads]
116
- double scale,
117
- torch::Tensor &block_tables, // [num_seqs, max_num_blocks_per_seq]
118
- torch::Tensor &seq_lens, // [num_seqs]
119
- int64_t block_size, int64_t max_seq_len,
120
- const std::optional<torch::Tensor> &alibi_slopes,
121
- const std::string &kv_cache_dtype, torch::Tensor &k_scale,
122
- torch::Tensor &v_scale, const int64_t tp_rank,
123
- const int64_t blocksparse_local_blocks,
124
- const int64_t blocksparse_vert_stride, const int64_t blocksparse_block_size,
125
- const int64_t blocksparse_head_sliding_step) {
126
- const bool is_block_sparse = (blocksparse_vert_stride > 1);
127
-
128
- // Validate block sparse is not supported yet
129
- // TODO: support blocksparse.
130
- TORCH_CHECK(
131
- !is_block_sparse,
132
- "Block sparse attention is not yet supported in Metal implementation");
133
-
134
- // Determine cache dtype based on kv_cache_dtype
135
- torch::ScalarType cache_dtype = key_cache.scalar_type();
136
- bool use_fp8_scales = (kv_cache_dtype == "fp8" || kv_cache_dtype == "fp8_e4m3");
137
- if (use_fp8_scales) {
138
- TORCH_CHECK(cache_dtype == torch::kUInt8, "FP8 cache requires UInt8 tensor type");
139
- TORCH_CHECK(k_scale.numel() == 1 && v_scale.numel() == 1, "FP8 scales must be scalars");
140
- TORCH_CHECK(k_scale.scalar_type() == torch::kFloat32 && v_scale.scalar_type() == torch::kFloat32,
141
- "FP8 scales must be float32");
142
- }
143
-
144
- // Validate input tensors
145
- TORCH_CHECK(out.device().is_mps() && query.device().is_mps() &&
146
- key_cache.device().is_mps() &&
147
- value_cache.device().is_mps() &&
148
- block_tables.device().is_mps() && seq_lens.device().is_mps(),
149
- "All tensors must be on MPS device");
150
-
151
- const int64_t num_seqs = query.size(0);
152
- const int64_t num_heads = query.size(1);
153
- const int64_t head_size = query.size(2);
154
- const int64_t max_num_blocks_per_seq = block_tables.size(1);
155
-
156
- // Validate configurations
157
- TORCH_CHECK(isValidConfiguration(head_size, block_size),
158
- "Unsupported head_size/block_size combination: ", head_size, "/",
159
- block_size);
160
-
161
- // For v1, no partitioning - each sequence processed by one threadgroup
162
- // Kernel configuration (should match the instantiated kernels)
163
- const int num_threads = 256;
164
- const int num_simd_lanes = 32;
165
- const int partition_size = 0; // v1 doesn't use partitioning
166
-
167
- // Calculate shared memory requirements (from mistral.rs)
168
- const int num_simds = num_threads / num_simd_lanes;
169
- const int padded_max_context_len =
170
- ((max_seq_len + block_size - 1) / block_size) * block_size;
171
- const int logits_size = padded_max_context_len * sizeof(float);
172
- const int outputs_size = (num_simds / 2) * head_size * sizeof(float);
173
- const size_t shared_memory_size = std::max(logits_size, outputs_size);
174
-
175
- // Get kernel name - v1 kernels have partition_size=0 in their name
176
- std::string kernel_name =
177
- getKernelName("paged_attention", query.scalar_type(), cache_dtype, head_size,
178
- block_size, num_threads, num_simd_lanes, partition_size);
179
-
180
- @autoreleasepool {
181
- id<MTLDevice> device = MTLCreateSystemDefaultDevice();
182
-
183
- // Load Metal library
184
- std::string moduleDir = getModuleDirectory();
185
- std::string metallibPath = moduleDir + "/" + METALLIB_PATH;
186
- NSString *metallibPathStr =
187
- [NSString stringWithUTF8String:metallibPath.c_str()];
188
- NSURL *metallibURL = [NSURL fileURLWithPath:metallibPathStr];
189
- NSError *error = nil;
190
- id<MTLLibrary> lib = [device newLibraryWithURL:metallibURL error:&error];
191
- TORCH_CHECK(lib, "Failed to load Metal library at ", metallibPath, ": ",
192
- error ? error.localizedDescription.UTF8String
193
- : "unknown error");
194
-
195
- // Create function constants for conditional compilation
196
- MTLFunctionConstantValues *constants =
197
- [[MTLFunctionConstantValues alloc] init];
198
- bool use_partitioning = false;
199
- bool use_alibi = alibi_slopes.has_value();
200
- [constants setConstantValue:&use_partitioning
201
- type:MTLDataTypeBool
202
- atIndex:10];
203
- [constants setConstantValue:&use_alibi type:MTLDataTypeBool atIndex:20];
204
- [constants setConstantValue:&use_fp8_scales type:MTLDataTypeBool atIndex:30];
205
-
206
- NSString *kernelNameStr =
207
- [NSString stringWithUTF8String:kernel_name.c_str()];
208
- id<MTLFunction> fn = [lib newFunctionWithName:kernelNameStr
209
- constantValues:constants
210
- error:&error];
211
- TORCH_CHECK(
212
- fn, "Failed to create Metal function '", kernel_name,
213
- "': ", error ? error.localizedDescription.UTF8String : "unknown error");
214
-
215
- id<MTLComputePipelineState> pso =
216
- [device newComputePipelineStateWithFunction:fn error:&error];
217
- TORCH_CHECK(pso, "Failed to create compute pipeline state: ",
218
- error ? error.localizedDescription.UTF8String
219
- : "unknown error");
220
-
221
- // Setup command buffer and encoder
222
- at::mps::MPSStream *stream = at::mps::getCurrentMPSStream();
223
- TORCH_CHECK(stream, "Failed to get current MPS stream");
224
-
225
- id<MTLCommandBuffer> cmdBuf = stream->commandBuffer();
226
- TORCH_CHECK(cmdBuf, "Failed to get MPS command buffer");
227
-
228
- dispatch_queue_t q = stream->queue();
229
- dispatch_sync(q, ^{
230
- id<MTLComputeCommandEncoder> enc = [cmdBuf computeCommandEncoder];
231
- TORCH_CHECK(enc, "Failed to create compute command encoder");
232
-
233
- [enc setComputePipelineState:pso];
234
-
235
- // Set threadgroup memory
236
- [enc setThreadgroupMemoryLength:shared_memory_size atIndex:0];
237
-
238
- // Buffer arguments (matching the Metal kernel signature)
239
- int buffer_idx = 0;
240
-
241
- // Skip exp_sums and max_logits for v1 (buffers 0, 1)
242
- buffer_idx = 2;
243
-
244
- // out buffer
245
- [enc setBuffer:getMTLBufferStorage(out)
246
- offset:out.storage_offset() * out.element_size()
247
- atIndex:buffer_idx++];
248
-
249
- // query buffer
250
- [enc setBuffer:getMTLBufferStorage(query)
251
- offset:query.storage_offset() * query.element_size()
252
- atIndex:buffer_idx++];
253
-
254
- // key_cache buffer
255
- [enc setBuffer:getMTLBufferStorage(key_cache)
256
- offset:key_cache.storage_offset() * key_cache.element_size()
257
- atIndex:buffer_idx++];
258
-
259
- // value_cache buffer
260
- [enc setBuffer:getMTLBufferStorage(value_cache)
261
- offset:value_cache.storage_offset() * value_cache.element_size()
262
- atIndex:buffer_idx++];
263
-
264
- // k_scale and v_scale (for FP8)
265
- if (use_fp8_scales) {
266
- [enc setBuffer:getMTLBufferStorage(k_scale)
267
- offset:k_scale.storage_offset() * k_scale.element_size()
268
- atIndex:buffer_idx++];
269
- [enc setBuffer:getMTLBufferStorage(v_scale)
270
- offset:v_scale.storage_offset() * v_scale.element_size()
271
- atIndex:buffer_idx++];
272
- } else {
273
- buffer_idx += 2; // Skip k_scale and v_scale buffer slots
274
- }
275
-
276
- // num_kv_heads
277
- int32_t num_kv_heads_i32 = static_cast<int32_t>(num_kv_heads);
278
- [enc setBytes:&num_kv_heads_i32
279
- length:sizeof(int32_t)
280
- atIndex:buffer_idx++];
281
-
282
- // scale
283
- float scale_f32 = static_cast<float>(scale);
284
- [enc setBytes:&scale_f32 length:sizeof(float) atIndex:buffer_idx++];
285
-
286
- // softcapping (default to 1.0 for no capping)
287
- float softcapping = 1.0f;
288
- [enc setBytes:&softcapping length:sizeof(float) atIndex:buffer_idx++];
289
-
290
- // block_tables buffer
291
- [enc setBuffer:getMTLBufferStorage(block_tables)
292
- offset:block_tables.storage_offset() * block_tables.element_size()
293
- atIndex:buffer_idx++];
294
-
295
- // seq_lens buffer (context_lens in kernel)
296
- [enc setBuffer:getMTLBufferStorage(seq_lens)
297
- offset:seq_lens.storage_offset() * seq_lens.element_size()
298
- atIndex:buffer_idx++];
299
-
300
- // max_num_blocks_per_seq
301
- int32_t max_num_blocks_per_seq_i32 =
302
- static_cast<int32_t>(max_num_blocks_per_seq);
303
- [enc setBytes:&max_num_blocks_per_seq_i32
304
- length:sizeof(int32_t)
305
- atIndex:buffer_idx++];
306
-
307
- // alibi_slopes (optional)
308
- if (use_alibi) {
309
- [enc setBuffer:getMTLBufferStorage(alibi_slopes.value())
310
- offset:alibi_slopes.value().storage_offset() *
311
- alibi_slopes.value().element_size()
312
- atIndex:buffer_idx++];
313
- } else {
314
- buffer_idx++; // Skip this buffer slot
315
- }
316
-
317
- // Stride parameters
318
- int32_t q_stride = static_cast<int32_t>(query.stride(0));
319
- int32_t kv_block_stride = static_cast<int32_t>(key_cache.stride(0));
320
- int32_t kv_head_stride = static_cast<int32_t>(key_cache.stride(1));
321
-
322
- [enc setBytes:&q_stride length:sizeof(int32_t) atIndex:buffer_idx++];
323
- [enc setBytes:&kv_block_stride
324
- length:sizeof(int32_t)
325
- atIndex:buffer_idx++];
326
- [enc setBytes:&kv_head_stride
327
- length:sizeof(int32_t)
328
- atIndex:buffer_idx++];
329
-
330
- // Dispatch configuration
331
- // Grid: (num_heads, num_seqs, 1) - no partitioning for v1
332
- MTLSize grid = MTLSizeMake(num_heads, num_seqs, 1);
333
- MTLSize threadgroup = MTLSizeMake(num_threads, 1, 1);
334
-
335
- [enc dispatchThreadgroups:grid threadsPerThreadgroup:threadgroup];
336
- [enc endEncoding];
337
-
338
- stream->synchronize(at::mps::SyncType::COMMIT);
339
- });
340
- }
341
- }
342
-
343
- void paged_attention_v2(
344
- torch::Tensor &out, // [num_seqs, num_heads, head_size]
345
- torch::Tensor &exp_sums, // [num_seqs, num_heads, max_num_partitions]
346
- torch::Tensor &max_logits, // [num_seqs, num_heads, max_num_partitions]
347
- torch::Tensor
348
- &tmp_out, // [num_seqs, num_heads, max_num_partitions, head_size]
349
- torch::Tensor &query, // [num_seqs, num_heads, head_size]
350
- torch::Tensor
351
- &key_cache, // [num_blocks, num_heads, head_size/x, block_size, x]
352
- torch::Tensor
353
- &value_cache, // [num_blocks, num_heads, head_size, block_size]
354
- int64_t num_kv_heads, // [num_heads]
355
- double scale,
356
- torch::Tensor &block_tables, // [num_seqs, max_num_blocks_per_seq]
357
- torch::Tensor &seq_lens, // [num_seqs]
358
- int64_t block_size, int64_t max_seq_len,
359
- const std::optional<torch::Tensor> &alibi_slopes,
360
- const std::string &kv_cache_dtype, torch::Tensor &k_scale,
361
- torch::Tensor &v_scale, const int64_t tp_rank,
362
- const int64_t blocksparse_local_blocks,
363
- const int64_t blocksparse_vert_stride, const int64_t blocksparse_block_size,
364
- const int64_t blocksparse_head_sliding_step) {
365
- const bool is_block_sparse = (blocksparse_vert_stride > 1);
366
-
367
- // TODO: support blocksparse.
368
- // Validate block sparse is not supported yet
369
- TORCH_CHECK(
370
- !is_block_sparse,
371
- "Block sparse attention is not yet supported in Metal implementation");
372
-
373
- // Determine cache dtype based on kv_cache_dtype
374
- torch::ScalarType cache_dtype = key_cache.scalar_type();
375
- bool use_fp8_scales = (kv_cache_dtype == "fp8" || kv_cache_dtype == "fp8_e4m3");
376
- if (use_fp8_scales) {
377
- TORCH_CHECK(cache_dtype == torch::kUInt8, "FP8 cache requires UInt8 tensor type");
378
- TORCH_CHECK(k_scale.numel() == 1 && v_scale.numel() == 1, "FP8 scales must be scalars");
379
- TORCH_CHECK(k_scale.scalar_type() == torch::kFloat32 && v_scale.scalar_type() == torch::kFloat32,
380
- "FP8 scales must be float32");
381
- }
382
-
383
- // Validate input tensors
384
- TORCH_CHECK(out.device().is_mps() && query.device().is_mps() &&
385
- key_cache.device().is_mps() &&
386
- value_cache.device().is_mps() && exp_sums.device().is_mps() &&
387
- max_logits.device().is_mps() && tmp_out.device().is_mps() &&
388
- block_tables.device().is_mps() && seq_lens.device().is_mps(),
389
- "All tensors must be on MPS device");
390
-
391
- const int64_t num_seqs = query.size(0);
392
- const int64_t num_heads = query.size(1);
393
- const int64_t head_size = query.size(2);
394
- const int64_t max_num_blocks_per_seq = block_tables.size(1);
395
- const int64_t max_num_partitions = exp_sums.size(2);
396
-
397
- // Validate configurations
398
- TORCH_CHECK(isValidConfiguration(head_size, block_size),
399
- "Unsupported head_size/block_size combination: ", head_size, "/",
400
- block_size);
401
-
402
- // For v2, use partitioning (matching the instantiated kernels)
403
- const int num_threads = 256;
404
- const int num_simd_lanes = 32;
405
- const int partition_size = 512; // v2 uses partitioning
406
-
407
- // Calculate shared memory requirements (from mistral.rs)
408
- const int num_simds = num_threads / num_simd_lanes;
409
- const int logits_size = partition_size * sizeof(float);
410
- const int outputs_size = (num_simds / 2) * head_size * sizeof(float);
411
- const size_t shared_memory_size = std::max(logits_size, outputs_size);
412
-
413
- // Get kernel names
414
- std::string kernel_name =
415
- getKernelName("paged_attention", query.scalar_type(), cache_dtype, head_size,
416
- block_size, num_threads, num_simd_lanes, partition_size);
417
- // Reduce kernel doesn't have block_size in its name
418
- std::string reduce_kernel_name = "paged_attention_v2_reduce";
419
- switch (query.scalar_type()) {
420
- case torch::kFloat:
421
- reduce_kernel_name += "_float";
422
- break;
423
- case torch::kHalf:
424
- reduce_kernel_name += "_half";
425
- break;
426
- case torch::kBFloat16:
427
- reduce_kernel_name += "_bfloat16_t";
428
- break;
429
- default:
430
- TORCH_CHECK(false,
431
- "Unsupported dtype for paged attention: ", query.scalar_type());
432
- }
433
- reduce_kernel_name += "_hs" + std::to_string(head_size) + "_nt" +
434
- std::to_string(num_threads) + "_nsl" +
435
- std::to_string(num_simd_lanes) + "_ps" +
436
- std::to_string(partition_size);
437
-
438
- @autoreleasepool {
439
- id<MTLDevice> device = MTLCreateSystemDefaultDevice();
440
-
441
- // Load Metal library
442
- std::string moduleDir = getModuleDirectory();
443
- std::string metallibPath = moduleDir + "/" + METALLIB_PATH;
444
- NSString *metallibPathStr =
445
- [NSString stringWithUTF8String:metallibPath.c_str()];
446
- NSURL *metallibURL = [NSURL fileURLWithPath:metallibPathStr];
447
- NSError *error = nil;
448
- id<MTLLibrary> lib = [device newLibraryWithURL:metallibURL error:&error];
449
- TORCH_CHECK(lib, "Failed to load Metal library at ", metallibPath, ": ",
450
- error ? error.localizedDescription.UTF8String
451
- : "unknown error");
452
-
453
- // Setup command buffer and queue
454
- at::mps::MPSStream *stream = at::mps::getCurrentMPSStream();
455
- TORCH_CHECK(stream, "Failed to get current MPS stream");
456
-
457
- id<MTLCommandBuffer> cmdBuf = stream->commandBuffer();
458
- TORCH_CHECK(cmdBuf, "Failed to get MPS command buffer");
459
-
460
- dispatch_queue_t q = stream->queue();
461
- dispatch_sync(q, ^{
462
- // ==================================================================
463
- // Phase 1: Main paged attention kernel with partitioning
464
- // ==================================================================
465
-
466
- // Create function constants for main kernel
467
- MTLFunctionConstantValues *mainConstants =
468
- [[MTLFunctionConstantValues alloc] init];
469
- bool use_partitioning = true;
470
- bool use_alibi = alibi_slopes.has_value();
471
- [mainConstants setConstantValue:&use_partitioning
472
- type:MTLDataTypeBool
473
- atIndex:10];
474
- [mainConstants setConstantValue:&use_alibi
475
- type:MTLDataTypeBool
476
- atIndex:20];
477
- [mainConstants setConstantValue:&use_fp8_scales
478
- type:MTLDataTypeBool
479
- atIndex:30];
480
-
481
- NSString *kernelNameStr =
482
- [NSString stringWithUTF8String:kernel_name.c_str()];
483
- NSError *mainError = nil;
484
- id<MTLFunction> mainFn = [lib newFunctionWithName:kernelNameStr
485
- constantValues:mainConstants
486
- error:&mainError];
487
- TORCH_CHECK(mainFn, "Failed to create Metal function '", kernel_name,
488
- "': ",
489
- mainError ? mainError.localizedDescription.UTF8String
490
- : "unknown error");
491
-
492
- NSError *psoError = nil;
493
- id<MTLComputePipelineState> mainPso =
494
- [device newComputePipelineStateWithFunction:mainFn error:&psoError];
495
- TORCH_CHECK(mainPso, "Failed to create compute pipeline state: ",
496
- psoError ? psoError.localizedDescription.UTF8String
497
- : "unknown error");
498
-
499
- id<MTLComputeCommandEncoder> enc = [cmdBuf computeCommandEncoder];
500
- TORCH_CHECK(enc, "Failed to create compute command encoder");
501
-
502
- [enc setComputePipelineState:mainPso];
503
- [enc setThreadgroupMemoryLength:shared_memory_size atIndex:0];
504
-
505
- // Set buffers for main kernel
506
- int buffer_idx = 0;
507
-
508
- // exp_sums buffer
509
- [enc setBuffer:getMTLBufferStorage(exp_sums)
510
- offset:exp_sums.storage_offset() * exp_sums.element_size()
511
- atIndex:buffer_idx++];
512
-
513
- // max_logits buffer
514
- [enc setBuffer:getMTLBufferStorage(max_logits)
515
- offset:max_logits.storage_offset() * max_logits.element_size()
516
- atIndex:buffer_idx++];
517
-
518
- // tmp_out buffer
519
- [enc setBuffer:getMTLBufferStorage(tmp_out)
520
- offset:tmp_out.storage_offset() * tmp_out.element_size()
521
- atIndex:buffer_idx++];
522
-
523
- // query buffer
524
- [enc setBuffer:getMTLBufferStorage(query)
525
- offset:query.storage_offset() * query.element_size()
526
- atIndex:buffer_idx++];
527
-
528
- // key_cache buffer
529
- [enc setBuffer:getMTLBufferStorage(key_cache)
530
- offset:key_cache.storage_offset() * key_cache.element_size()
531
- atIndex:buffer_idx++];
532
-
533
- // value_cache buffer
534
- [enc setBuffer:getMTLBufferStorage(value_cache)
535
- offset:value_cache.storage_offset() * value_cache.element_size()
536
- atIndex:buffer_idx++];
537
-
538
- // k_scale and v_scale (for FP8)
539
- if (use_fp8_scales) {
540
- [enc setBuffer:getMTLBufferStorage(k_scale)
541
- offset:k_scale.storage_offset() * k_scale.element_size()
542
- atIndex:buffer_idx++];
543
- [enc setBuffer:getMTLBufferStorage(v_scale)
544
- offset:v_scale.storage_offset() * v_scale.element_size()
545
- atIndex:buffer_idx++];
546
- } else {
547
- buffer_idx += 2; // Skip k_scale and v_scale buffer slots
548
- }
549
-
550
- // num_kv_heads
551
- int32_t num_kv_heads_i32 = static_cast<int32_t>(num_kv_heads);
552
- [enc setBytes:&num_kv_heads_i32
553
- length:sizeof(int32_t)
554
- atIndex:buffer_idx++];
555
-
556
- // scale
557
- float scale_f32 = static_cast<float>(scale);
558
- [enc setBytes:&scale_f32 length:sizeof(float) atIndex:buffer_idx++];
559
-
560
- // softcapping (default to 1.0 for no capping)
561
- float softcapping = 1.0f;
562
- [enc setBytes:&softcapping length:sizeof(float) atIndex:buffer_idx++];
563
-
564
- // block_tables buffer
565
- [enc setBuffer:getMTLBufferStorage(block_tables)
566
- offset:block_tables.storage_offset() * block_tables.element_size()
567
- atIndex:buffer_idx++];
568
-
569
- // seq_lens buffer (context_lens in kernel)
570
- [enc setBuffer:getMTLBufferStorage(seq_lens)
571
- offset:seq_lens.storage_offset() * seq_lens.element_size()
572
- atIndex:buffer_idx++];
573
-
574
- // max_num_blocks_per_seq
575
- int32_t max_num_blocks_per_seq_i32 =
576
- static_cast<int32_t>(max_num_blocks_per_seq);
577
- [enc setBytes:&max_num_blocks_per_seq_i32
578
- length:sizeof(int32_t)
579
- atIndex:buffer_idx++];
580
-
581
- // alibi_slopes (optional)
582
- if (use_alibi) {
583
- [enc setBuffer:getMTLBufferStorage(alibi_slopes.value())
584
- offset:alibi_slopes.value().storage_offset() *
585
- alibi_slopes.value().element_size()
586
- atIndex:buffer_idx++];
587
- } else {
588
- buffer_idx++; // Skip this buffer slot
589
- }
590
-
591
- // Stride parameters
592
- int32_t q_stride = static_cast<int32_t>(query.stride(0));
593
- int32_t kv_block_stride = static_cast<int32_t>(key_cache.stride(0));
594
- int32_t kv_head_stride = static_cast<int32_t>(key_cache.stride(1));
595
-
596
- [enc setBytes:&q_stride length:sizeof(int32_t) atIndex:buffer_idx++];
597
- [enc setBytes:&kv_block_stride
598
- length:sizeof(int32_t)
599
- atIndex:buffer_idx++];
600
- [enc setBytes:&kv_head_stride
601
- length:sizeof(int32_t)
602
- atIndex:buffer_idx++];
603
-
604
- // Dispatch main kernel
605
- // Grid: (num_heads, num_seqs, max_num_partitions) - with partitioning for
606
- // v2
607
- MTLSize mainGrid = MTLSizeMake(num_heads, num_seqs, max_num_partitions);
608
- MTLSize mainThreadgroup = MTLSizeMake(num_threads, 1, 1);
609
-
610
- [enc dispatchThreadgroups:mainGrid threadsPerThreadgroup:mainThreadgroup];
611
- [enc endEncoding];
612
-
613
- // ==================================================================
614
- // Phase 2: Reduction kernel to combine partitions
615
- // ==================================================================
616
-
617
- // Create reduction kernel
618
- NSString *reduceKernelNameStr =
619
- [NSString stringWithUTF8String:reduce_kernel_name.c_str()];
620
- id<MTLFunction> reduceFn = [lib newFunctionWithName:reduceKernelNameStr];
621
- TORCH_CHECK(reduceFn, "Failed to create Metal function '",
622
- reduce_kernel_name, "'");
623
-
624
- NSError *reducePsoError = nil;
625
- id<MTLComputePipelineState> reducePso =
626
- [device newComputePipelineStateWithFunction:reduceFn
627
- error:&reducePsoError];
628
- TORCH_CHECK(
629
- reducePso, "Failed to create compute pipeline state for reduction: ",
630
- reducePsoError ? reducePsoError.localizedDescription.UTF8String
631
- : "unknown error");
632
-
633
- // Calculate shared memory for reduction kernel
634
- size_t reduce_shared_memory_size =
635
- max_num_partitions * sizeof(float) * 2; // max_logits + exp_sums
636
-
637
- id<MTLComputeCommandEncoder> reduceEnc = [cmdBuf computeCommandEncoder];
638
- TORCH_CHECK(reduceEnc,
639
- "Failed to create compute command encoder for reduction");
640
-
641
- [reduceEnc setComputePipelineState:reducePso];
642
- [reduceEnc setThreadgroupMemoryLength:reduce_shared_memory_size
643
- atIndex:0];
644
-
645
- // Set buffers for reduction kernel
646
- buffer_idx = 0;
647
-
648
- // out buffer (final output)
649
- [reduceEnc setBuffer:getMTLBufferStorage(out)
650
- offset:out.storage_offset() * out.element_size()
651
- atIndex:buffer_idx++];
652
-
653
- // exp_sums buffer
654
- [reduceEnc setBuffer:getMTLBufferStorage(exp_sums)
655
- offset:exp_sums.storage_offset() * exp_sums.element_size()
656
- atIndex:buffer_idx++];
657
-
658
- // max_logits buffer
659
- [reduceEnc
660
- setBuffer:getMTLBufferStorage(max_logits)
661
- offset:max_logits.storage_offset() * max_logits.element_size()
662
- atIndex:buffer_idx++];
663
-
664
- // tmp_out buffer
665
- [reduceEnc setBuffer:getMTLBufferStorage(tmp_out)
666
- offset:tmp_out.storage_offset() * tmp_out.element_size()
667
- atIndex:buffer_idx++];
668
-
669
- // seq_lens buffer (context_lens in kernel)
670
- [reduceEnc setBuffer:getMTLBufferStorage(seq_lens)
671
- offset:seq_lens.storage_offset() * seq_lens.element_size()
672
- atIndex:buffer_idx++];
673
-
674
- // max_num_partitions
675
- int32_t max_num_partitions_i32 = static_cast<int32_t>(max_num_partitions);
676
- [reduceEnc setBytes:&max_num_partitions_i32
677
- length:sizeof(int32_t)
678
- atIndex:buffer_idx++];
679
-
680
- // Dispatch reduction kernel
681
- // Grid: (num_heads, num_seqs) - one threadgroup per sequence/head
682
- // combination
683
- MTLSize reduceGrid = MTLSizeMake(num_heads, num_seqs, 1);
684
- MTLSize reduceThreadgroup = MTLSizeMake(num_threads, 1, 1);
685
-
686
- [reduceEnc dispatchThreadgroups:reduceGrid
687
- threadsPerThreadgroup:reduceThreadgroup];
688
- [reduceEnc endEncoding];
689
-
690
- stream->synchronize(at::mps::SyncType::COMMIT);
691
- });
692
- }
693
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
paged-attention-metal/utils.metal DELETED
@@ -1,246 +0,0 @@
1
- #include <metal_stdlib>
2
- using namespace metal;
3
-
4
- #if defined(__HAVE_BFLOAT__)
5
-
6
- typedef bfloat bfloat16_t;
7
-
8
- #else
9
-
10
- /////////////////////////////////////////////////////////////////////////////
11
- // Helpers
12
- /////////////////////////////////////////////////////////////////////////////
13
-
14
- constexpr METAL_FUNC uint16_t float_to_bfloat_bits(float x) {
15
- // Check for nan
16
- if ((as_type<uint32_t>(x) & ~_fp_encoding_traits<float>::sign_mask) >
17
- _fp_encoding_traits<float>::inf_mask) {
18
- return uint16_t(as_type<uint32_t>(0x7FC0));
19
- }
20
- // Take bits
21
- uint32_t float_bits = as_type<uint32_t>(x);
22
-
23
- // Round to nearest even
24
- float_bits += ((float_bits >> 16) & 1) + as_type<uint32_t>(0x7FFF);
25
-
26
- // Take upper 16 bits
27
- return float_bits >> 16;
28
- }
29
-
30
- constexpr METAL_FUNC float bfloat_bits_to_float(uint16_t x) {
31
- // Upper 16 bits are the data and lower 16 bits are 0s
32
- return as_type<float>((uint32_t)x << 16);
33
- }
34
-
35
- struct _MLX_BFloat16;
36
-
37
- template <typename T>
38
- static constexpr constant bool can_convert_to_bfloat =
39
- !is_same_v<T, _MLX_BFloat16> && is_convertible_v<T, float>;
40
-
41
- template <typename T>
42
- static constexpr constant bool can_convert_from_bfloat =
43
- !is_same_v<T, _MLX_BFloat16> && is_convertible_v<float, T>;
44
-
45
- /////////////////////////////////////////////////////////////////////////////
46
- // Bfloat struct
47
- /////////////////////////////////////////////////////////////////////////////
48
-
49
- struct _MLX_BFloat16 {
50
- /////////////////////////////////////////////////////////////////////////////
51
- // Constructors
52
- uint16_t bits_;
53
- _MLX_BFloat16() thread = default;
54
- _MLX_BFloat16() threadgroup = default;
55
- _MLX_BFloat16() device = default;
56
- _MLX_BFloat16() constant = default;
57
-
58
- struct bits_to_bfloat_struct {};
59
- static constexpr METAL_FUNC bits_to_bfloat_struct bits_to_bfloat() {
60
- return bits_to_bfloat_struct();
61
- }
62
- constexpr METAL_FUNC _MLX_BFloat16(uint16_t bits, bits_to_bfloat_struct)
63
- : bits_(bits) {}
64
-
65
- /////////////////////////////////////////////////////////////////////////////
66
- // Conversions to bfloat
67
-
68
- template <typename T,
69
- typename = typename enable_if<can_convert_to_bfloat<T>>::type>
70
- constexpr METAL_FUNC _MLX_BFloat16(T x) thread
71
- : bits_(float_to_bfloat_bits(static_cast<float>(x))) {}
72
-
73
- template <typename T,
74
- typename = typename enable_if<can_convert_to_bfloat<T>>::type>
75
- constexpr METAL_FUNC _MLX_BFloat16(T x) threadgroup
76
- : bits_(float_to_bfloat_bits(static_cast<float>(x))) {}
77
-
78
- template <typename T,
79
- typename = typename enable_if<can_convert_to_bfloat<T>>::type>
80
- constexpr METAL_FUNC _MLX_BFloat16(T x) device
81
- : bits_(float_to_bfloat_bits(static_cast<float>(x))) {}
82
-
83
- template <typename T,
84
- typename = typename enable_if<can_convert_to_bfloat<T>>::type>
85
- constexpr METAL_FUNC _MLX_BFloat16(T x) constant
86
- : bits_(float_to_bfloat_bits(static_cast<float>(x))) {}
87
-
88
- /////////////////////////////////////////////////////////////////////////////
89
- // Conversions from bfloat
90
-
91
- template <typename T,
92
- typename = typename enable_if<can_convert_from_bfloat<T>>::type>
93
- constexpr METAL_FUNC operator T() const thread {
94
- return static_cast<T>(bfloat_bits_to_float(bits_));
95
- }
96
-
97
- template <typename T,
98
- typename = typename enable_if<can_convert_from_bfloat<T>>::type>
99
- constexpr METAL_FUNC operator T() const threadgroup {
100
- return static_cast<T>(bfloat_bits_to_float(bits_));
101
- }
102
-
103
- template <typename T,
104
- typename = typename enable_if<can_convert_from_bfloat<T>>::type>
105
- constexpr METAL_FUNC operator T() const device {
106
- return static_cast<T>(bfloat_bits_to_float(bits_));
107
- }
108
-
109
- template <typename T,
110
- typename = typename enable_if<can_convert_from_bfloat<T>>::type>
111
- constexpr METAL_FUNC operator T() constant {
112
- return static_cast<T>(bfloat_bits_to_float(bits_));
113
- }
114
- };
115
-
116
- /////////////////////////////////////////////////////////////////////////////
117
- // Bfloat operators
118
- /////////////////////////////////////////////////////////////////////////////
119
-
120
- /////////////////////////////////////////////////////////////////////////////
121
- // Unary ops
122
- constexpr METAL_FUNC _MLX_BFloat16 operator-(_MLX_BFloat16 x) {
123
- return -static_cast<float>(x);
124
- }
125
-
126
- /////////////////////////////////////////////////////////////////////////////
127
- // Binary operators
128
- #define bfloat_binop_base(__op__, __operator__, otype, atype, btype, ctype) \
129
- constexpr METAL_FUNC otype __operator__(atype lhs, btype rhs) { \
130
- return static_cast<ctype>(lhs) __op__ static_cast<ctype>(rhs); \
131
- }
132
-
133
- #define bfloat_binop_helper(__op__, __operator__, otype, itype, ctype) \
134
- constexpr METAL_FUNC otype __operator__(_MLX_BFloat16 lhs, itype rhs) { \
135
- return static_cast<ctype>(lhs) __op__ static_cast<ctype>(rhs); \
136
- } \
137
- constexpr METAL_FUNC otype __operator__(itype lhs, _MLX_BFloat16 rhs) { \
138
- return static_cast<ctype>(lhs) __op__ static_cast<ctype>(rhs); \
139
- }
140
-
141
- /////////////////////////////////////////////////////////////////////////////
142
- // Arithmetic Operators
143
- #define bfloat_binop(_op_, _operator_) \
144
- bfloat_binop_base(_op_, _operator_, _MLX_BFloat16, _MLX_BFloat16, \
145
- _MLX_BFloat16, float); \
146
- bfloat_binop_helper(_op_, _operator_, float, float, float); \
147
- bfloat_binop_helper(_op_, _operator_, float, half, float); \
148
- bfloat_binop_helper(_op_, _operator_, _MLX_BFloat16, int32_t, float); \
149
- bfloat_binop_helper(_op_, _operator_, _MLX_BFloat16, uint32_t, float); \
150
- bfloat_binop_helper(_op_, _operator_, _MLX_BFloat16, int64_t, float); \
151
- bfloat_binop_helper(_op_, _operator_, _MLX_BFloat16, uint64_t, float);
152
-
153
- bfloat_binop(+, operator+);
154
- bfloat_binop(-, operator-);
155
- bfloat_binop(*, operator*);
156
- bfloat_binop(/, operator/);
157
-
158
- /////////////////////////////////////////////////////////////////////////////
159
- // Comparison ops
160
- #define bfloat_compop(__op__, __operator__) \
161
- bfloat_binop_base(__op__, __operator__, bool, _MLX_BFloat16, _MLX_BFloat16, \
162
- float); \
163
- bfloat_binop_helper(__op__, __operator__, bool, float, float); \
164
- bfloat_binop_helper(__op__, __operator__, bool, half, float); \
165
- bfloat_binop_helper(__op__, __operator__, bool, int32_t, float); \
166
- bfloat_binop_helper(__op__, __operator__, bool, uint32_t, float); \
167
- bfloat_binop_helper(__op__, __operator__, bool, int64_t, float); \
168
- bfloat_binop_helper(__op__, __operator__, bool, uint64_t, float);
169
-
170
- bfloat_compop(>, operator>);
171
- bfloat_compop(<, operator<);
172
- bfloat_compop(>=, operator>=);
173
- bfloat_compop(<=, operator<=);
174
- bfloat_compop(==, operator==);
175
- bfloat_compop(!=, operator!=);
176
-
177
- #undef bfloat_compop
178
- #undef bfloat_binop_base
179
- #undef bfloat_binop_helper
180
- #undef bfloat_binop
181
-
182
- /////////////////////////////////////////////////////////////////////////////
183
- // Inplace Operators
184
- #define bfloat_inplace_op_helper(__op__, __operator__, itype, addr_space) \
185
- constexpr METAL_FUNC addr_space _MLX_BFloat16 &__operator__( \
186
- addr_space _MLX_BFloat16 &lhs, itype rhs) { \
187
- lhs = static_cast<float>(lhs) __op__ static_cast<float>(rhs); \
188
- return lhs; \
189
- } \
190
- constexpr METAL_FUNC addr_space itype &__operator__(addr_space itype &lhs, \
191
- _MLX_BFloat16 rhs) { \
192
- lhs = static_cast<float>(lhs) __op__ static_cast<float>(rhs); \
193
- return lhs; \
194
- }
195
-
196
- #define bfloat_inplace_op_addr_space_helper(__op__, __operator__, itype) \
197
- bfloat_inplace_op_helper(__op__, __operator__, itype, device); \
198
- bfloat_inplace_op_helper(__op__, __operator__, itype, thread); \
199
- bfloat_inplace_op_helper(__op__, __operator__, itype, threadgroup);
200
-
201
- #define bfloat_inplace_op(itype) \
202
- bfloat_inplace_op_addr_space_helper(+, operator+=, itype); \
203
- bfloat_inplace_op_addr_space_helper(-, operator-=, itype); \
204
- bfloat_inplace_op_addr_space_helper(*, operator*=, itype); \
205
- bfloat_inplace_op_addr_space_helper(/, operator/=, itype);
206
-
207
- bfloat_inplace_op(float);
208
- bfloat_inplace_op(half);
209
- bfloat_inplace_op(int16_t);
210
- bfloat_inplace_op(int32_t);
211
- bfloat_inplace_op(int64_t);
212
- bfloat_inplace_op(uint16_t);
213
- bfloat_inplace_op(uint32_t);
214
- bfloat_inplace_op(uint64_t);
215
-
216
- #undef bfloat_inplace_op_helper
217
- #undef bfloat_inplace_op_addr_space_helper
218
- #undef bfloat_inplace_op
219
-
220
- #define bfloat_inplace_op_helper(__op__, __operator__, addr_space) \
221
- constexpr METAL_FUNC addr_space _MLX_BFloat16 &__operator__( \
222
- addr_space _MLX_BFloat16 &lhs, _MLX_BFloat16 rhs) { \
223
- lhs = static_cast<float>(lhs) __op__ static_cast<float>(rhs); \
224
- return lhs; \
225
- }
226
-
227
- #define bfloat_inplace_op_addr_space_helper(__op__, __operator__) \
228
- bfloat_inplace_op_helper(__op__, __operator__, device); \
229
- bfloat_inplace_op_helper(__op__, __operator__, thread); \
230
- bfloat_inplace_op_helper(__op__, __operator__, threadgroup);
231
-
232
- bfloat_inplace_op_addr_space_helper(+, operator+=);
233
- bfloat_inplace_op_addr_space_helper(-, operator-=);
234
- bfloat_inplace_op_addr_space_helper(*, operator*=);
235
- bfloat_inplace_op_addr_space_helper(/, operator/=);
236
-
237
- #undef bfloat_inplace_op_helper
238
- #undef bfloat_inplace_op_addr_space_helper
239
-
240
- /////////////////////////////////////////////////////////////////////////////
241
- // Bfloat typedef
242
- /////////////////////////////////////////////////////////////////////////////
243
-
244
- typedef struct _MLX_BFloat16 bfloat16_t;
245
-
246
- #endif
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
paged-attention/attention/attention_dtypes.h DELETED
@@ -1,7 +0,0 @@
1
- #pragma once
2
-
3
- #include "attention_generic.cuh"
4
- #include "dtype_float16.cuh"
5
- #include "dtype_float32.cuh"
6
- #include "dtype_bfloat16.cuh"
7
- #include "dtype_fp8.cuh"
 
 
 
 
 
 
 
 
paged-attention/attention/attention_generic.cuh DELETED
@@ -1,65 +0,0 @@
1
- /*
2
- * Adapted from
3
- * https://github.com/NVIDIA/FasterTransformer/blob/release/v5.3_tag/src/fastertransformer/kernels/decoder_masked_multihead_attention_utils.h
4
- * Copyright (c) 2023, The vLLM team.
5
- * Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved.
6
- *
7
- * Licensed under the Apache License, Version 2.0 (the "License");
8
- * you may not use this file except in compliance with the License.
9
- * You may obtain a copy of the License at
10
- *
11
- * http://www.apache.org/licenses/LICENSE-2.0
12
- *
13
- * Unless required by applicable law or agreed to in writing, software
14
- * distributed under the License is distributed on an "AS IS" BASIS,
15
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
- * See the License for the specific language governing permissions and
17
- * limitations under the License.
18
- */
19
- #pragma once
20
-
21
- #include <stdint.h>
22
-
23
- namespace vllm {
24
-
25
- // A vector type to store Q, K, V elements.
26
- template <typename T, int VEC_SIZE>
27
- struct Vec {};
28
-
29
- // A vector type to store FP32 accumulators.
30
- template <typename T>
31
- struct FloatVec {};
32
-
33
- // Template vector operations.
34
- template <typename Acc, typename A, typename B>
35
- inline __device__ Acc mul(A a, B b);
36
-
37
- template <typename T>
38
- inline __device__ float sum(T v);
39
-
40
- template <typename T>
41
- inline __device__ float dot(T a, T b) {
42
- return sum(mul<T, T, T>(a, b));
43
- }
44
-
45
- template <typename A, typename T>
46
- inline __device__ float dot(T a, T b) {
47
- return sum(mul<A, T, T>(a, b));
48
- }
49
-
50
- template <typename T>
51
- inline __device__ void zero(T& dst) {
52
- constexpr int WORDS = sizeof(T) / 4;
53
- union {
54
- T raw;
55
- uint32_t words[WORDS];
56
- } tmp;
57
-
58
- #pragma unroll
59
- for (int ii = 0; ii < WORDS; ++ii) {
60
- tmp.words[ii] = 0u;
61
- }
62
- dst = tmp.raw;
63
- }
64
-
65
- } // namespace vllm
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
paged-attention/attention/attention_kernels.cuh DELETED
@@ -1,670 +0,0 @@
1
- /*
2
- * Adapted from
3
- * https://github.com/NVIDIA/FasterTransformer/blob/release/v5.3_tag/src/fastertransformer/kernels/decoder_masked_multihead_attention/decoder_masked_multihead_attention_template.hpp
4
- * Copyright (c) 2023, The vLLM team.
5
- * Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved.
6
- *
7
- * Licensed under the Apache License, Version 2.0 (the "License");
8
- * you may not use this file except in compliance with the License.
9
- * You may obtain a copy of the License at
10
- *
11
- * http://www.apache.org/licenses/LICENSE-2.0
12
- *
13
- * Unless required by applicable law or agreed to in writing, software
14
- * distributed under the License is distributed on an "AS IS" BASIS,
15
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
- * See the License for the specific language governing permissions and
17
- * limitations under the License.
18
- */
19
-
20
- #include <torch/all.h>
21
- #include <ATen/cuda/CUDAContext.h>
22
- #include <c10/cuda/CUDAGuard.h>
23
- #include <algorithm>
24
-
25
- #include "attention_dtypes.h"
26
- #include "attention_utils.cuh"
27
- #include "cuda_compat.h"
28
-
29
- #ifdef USE_ROCM
30
- #include <hip/hip_bf16.h>
31
- #include "../quantization/fp8/amd/quant_utils.cuh"
32
- typedef __hip_bfloat16 __nv_bfloat16;
33
- #else
34
- #include "../quantization/fp8/nvidia/quant_utils.cuh"
35
- #endif
36
-
37
- #define MAX(a, b) ((a) > (b) ? (a) : (b))
38
- #define MIN(a, b) ((a) < (b) ? (a) : (b))
39
- #define DIVIDE_ROUND_UP(a, b) (((a) + (b) - 1) / (b))
40
-
41
- namespace vllm {
42
-
43
- // Utility function for attention softmax.
44
- template <int NUM_WARPS>
45
- inline __device__ float block_sum(float* red_smem, float sum) {
46
- // Decompose the thread index into warp / lane.
47
- int warp = threadIdx.x / WARP_SIZE;
48
- int lane = threadIdx.x % WARP_SIZE;
49
-
50
- // Compute the sum per warp.
51
- #pragma unroll
52
- for (int mask = WARP_SIZE / 2; mask >= 1; mask /= 2) {
53
- sum += VLLM_SHFL_XOR_SYNC(sum, mask);
54
- }
55
-
56
- // Warp leaders store the data to shared memory.
57
- if (lane == 0) {
58
- red_smem[warp] = sum;
59
- }
60
-
61
- // Make sure the data is in shared memory.
62
- __syncthreads();
63
-
64
- // The warps compute the final sums.
65
- if (lane < NUM_WARPS) {
66
- sum = red_smem[lane];
67
- }
68
-
69
- // Parallel reduction inside the warp.
70
- #pragma unroll
71
- for (int mask = NUM_WARPS / 2; mask >= 1; mask /= 2) {
72
- sum += VLLM_SHFL_XOR_SYNC(sum, mask);
73
- }
74
-
75
- // Broadcast to other threads.
76
- return VLLM_SHFL_SYNC(sum, 0);
77
- }
78
-
79
- // TODO(woosuk): Merge the last two dimensions of the grid.
80
- // Grid: (num_heads, num_seqs, max_num_partitions).
81
- template <typename scalar_t, typename cache_t, int HEAD_SIZE, int BLOCK_SIZE,
82
- int NUM_THREADS, vllm::Fp8KVCacheDataType KV_DTYPE,
83
- bool IS_BLOCK_SPARSE,
84
- int PARTITION_SIZE = 0> // Zero means no partitioning.
85
- __device__ void paged_attention_kernel(
86
- float* __restrict__ exp_sums, // [num_seqs, num_heads, max_num_partitions]
87
- float* __restrict__ max_logits, // [num_seqs, num_heads,
88
- // max_num_partitions]
89
- scalar_t* __restrict__ out, // [num_seqs, num_heads, max_num_partitions,
90
- // head_size]
91
- const scalar_t* __restrict__ q, // [num_seqs, num_heads, head_size]
92
- const cache_t* __restrict__ k_cache, // [num_blocks, num_kv_heads,
93
- // head_size/x, block_size, x]
94
- const cache_t* __restrict__ v_cache, // [num_blocks, num_kv_heads,
95
- // head_size, block_size]
96
- const int num_kv_heads, // [num_heads]
97
- const float scale,
98
- const int* __restrict__ block_tables, // [num_seqs, max_num_blocks_per_seq]
99
- const int* __restrict__ seq_lens, // [num_seqs]
100
- const int max_num_blocks_per_seq,
101
- const float* __restrict__ alibi_slopes, // [num_heads]
102
- const int q_stride, const int kv_block_stride, const int kv_head_stride,
103
- const float* k_scale, const float* v_scale, const int tp_rank,
104
- const int blocksparse_local_blocks, const int blocksparse_vert_stride,
105
- const int blocksparse_block_size, const int blocksparse_head_sliding_step) {
106
- const int seq_idx = blockIdx.y;
107
- const int partition_idx = blockIdx.z;
108
- const int max_num_partitions = gridDim.z;
109
- constexpr bool USE_PARTITIONING = PARTITION_SIZE > 0;
110
- const int seq_len = seq_lens[seq_idx];
111
- if (USE_PARTITIONING && partition_idx * PARTITION_SIZE >= seq_len) {
112
- // No work to do. Terminate the thread block.
113
- return;
114
- }
115
-
116
- const int num_seq_blocks = DIVIDE_ROUND_UP(seq_len, BLOCK_SIZE);
117
- const int num_blocks_per_partition =
118
- USE_PARTITIONING ? PARTITION_SIZE / BLOCK_SIZE : num_seq_blocks;
119
-
120
- // [start_block_idx, end_block_idx) is the range of blocks to process.
121
- const int start_block_idx =
122
- USE_PARTITIONING ? partition_idx * num_blocks_per_partition : 0;
123
- const int end_block_idx =
124
- MIN(start_block_idx + num_blocks_per_partition, num_seq_blocks);
125
- const int num_blocks = end_block_idx - start_block_idx;
126
-
127
- // [start_token_idx, end_token_idx) is the range of tokens to process.
128
- const int start_token_idx = start_block_idx * BLOCK_SIZE;
129
- const int end_token_idx =
130
- MIN(start_token_idx + num_blocks * BLOCK_SIZE, seq_len);
131
- const int num_tokens = end_token_idx - start_token_idx;
132
-
133
- constexpr int THREAD_GROUP_SIZE = MAX(WARP_SIZE / BLOCK_SIZE, 1);
134
- constexpr int NUM_THREAD_GROUPS =
135
- NUM_THREADS / THREAD_GROUP_SIZE; // Note: This assumes THREAD_GROUP_SIZE
136
- // divides NUM_THREADS
137
- assert(NUM_THREADS % THREAD_GROUP_SIZE == 0);
138
- constexpr int NUM_TOKENS_PER_THREAD_GROUP =
139
- DIVIDE_ROUND_UP(BLOCK_SIZE, WARP_SIZE);
140
- constexpr int NUM_WARPS = NUM_THREADS / WARP_SIZE;
141
- const int thread_idx = threadIdx.x;
142
- const int warp_idx = thread_idx / WARP_SIZE;
143
- const int lane = thread_idx % WARP_SIZE;
144
-
145
- const int head_idx = blockIdx.x;
146
- const int num_heads = gridDim.x;
147
- const int num_queries_per_kv = num_heads / num_kv_heads;
148
- const int kv_head_idx = head_idx / num_queries_per_kv;
149
- const float alibi_slope =
150
- alibi_slopes == nullptr ? 0.f : alibi_slopes[head_idx];
151
-
152
- // A vector type to store a part of a key or a query.
153
- // The vector size is configured in such a way that the threads in a thread
154
- // group fetch or compute 16 bytes at a time. For example, if the size of a
155
- // thread group is 4 and the data type is half, then the vector size is 16 /
156
- // (4 * sizeof(half)) == 2.
157
- constexpr int VEC_SIZE = MAX(16 / (THREAD_GROUP_SIZE * sizeof(scalar_t)), 1);
158
- using K_vec = typename Vec<scalar_t, VEC_SIZE>::Type;
159
- using Q_vec = typename Vec<scalar_t, VEC_SIZE>::Type;
160
- using Quant_vec = typename Vec<cache_t, VEC_SIZE>::Type;
161
-
162
- constexpr int NUM_ELEMS_PER_THREAD = HEAD_SIZE / THREAD_GROUP_SIZE;
163
- constexpr int NUM_VECS_PER_THREAD = NUM_ELEMS_PER_THREAD / VEC_SIZE;
164
-
165
- const int thread_group_idx = thread_idx / THREAD_GROUP_SIZE;
166
- const int thread_group_offset = thread_idx % THREAD_GROUP_SIZE;
167
-
168
- // Load the query to registers.
169
- // Each thread in a thread group has a different part of the query.
170
- // For example, if the thread group size is 4, then the first thread in
171
- // the group has 0, 4, 8, ... th vectors of the query, and the second thread
172
- // has 1, 5, 9, ... th vectors of the query, and so on. NOTE(woosuk): Because
173
- // q is split from a qkv tensor, it may not be contiguous.
174
- const scalar_t* q_ptr = q + seq_idx * q_stride + head_idx * HEAD_SIZE;
175
- __shared__ Q_vec q_vecs[THREAD_GROUP_SIZE][NUM_VECS_PER_THREAD];
176
- #pragma unroll
177
- for (int i = thread_group_idx; i < NUM_VECS_PER_THREAD;
178
- i += NUM_THREAD_GROUPS) {
179
- const int vec_idx = thread_group_offset + i * THREAD_GROUP_SIZE;
180
- q_vecs[thread_group_offset][i] =
181
- *reinterpret_cast<const Q_vec*>(q_ptr + vec_idx * VEC_SIZE);
182
- }
183
- __syncthreads(); // TODO(naed90): possible speedup if this is replaced with a
184
- // memory wall right before we use q_vecs
185
-
186
- // Memory planning.
187
- extern __shared__ char shared_mem[];
188
- // NOTE(woosuk): We use FP32 for the softmax logits for better accuracy.
189
- float* logits = reinterpret_cast<float*>(shared_mem);
190
- // Workspace for reduction.
191
- __shared__ float red_smem[2 * NUM_WARPS];
192
-
193
- // x == THREAD_GROUP_SIZE * VEC_SIZE
194
- // Each thread group fetches x elements from the key at a time.
195
- constexpr int x = 16 / sizeof(cache_t);
196
- float qk_max = -FLT_MAX;
197
-
198
- // Iterate over the key blocks.
199
- // Each warp fetches a block of keys for each iteration.
200
- // Each thread group in a warp fetches a key from the block, and computes
201
- // dot product with the query.
202
- const int* block_table = block_tables + seq_idx * max_num_blocks_per_seq;
203
-
204
- // blocksparse specific vars
205
- int bs_block_offset;
206
- int q_bs_block_id;
207
- if constexpr (IS_BLOCK_SPARSE) {
208
- // const int num_blocksparse_blocks = DIVIDE_ROUND_UP(seq_len,
209
- // blocksparse_block_size);
210
- q_bs_block_id = (seq_len - 1) / blocksparse_block_size;
211
- if (blocksparse_head_sliding_step >= 0)
212
- // sliding on q heads
213
- bs_block_offset =
214
- (tp_rank * num_heads + head_idx) * blocksparse_head_sliding_step + 1;
215
- else
216
- // sliding on kv heads
217
- bs_block_offset = (tp_rank * num_kv_heads + kv_head_idx) *
218
- (-blocksparse_head_sliding_step) +
219
- 1;
220
- }
221
-
222
- for (int block_idx = start_block_idx + warp_idx; block_idx < end_block_idx;
223
- block_idx += NUM_WARPS) {
224
- // NOTE(woosuk): The block number is stored in int32. However, we cast it to
225
- // int64 because int32 can lead to overflow when this variable is multiplied
226
- // by large numbers (e.g., kv_block_stride).
227
- // For blocksparse attention: skip computation on blocks that are not
228
- // attended
229
- if constexpr (IS_BLOCK_SPARSE) {
230
- const int k_bs_block_id = block_idx * BLOCK_SIZE / blocksparse_block_size;
231
- const bool is_remote =
232
- ((k_bs_block_id + bs_block_offset) % blocksparse_vert_stride == 0);
233
- const bool is_local =
234
- (k_bs_block_id > q_bs_block_id - blocksparse_local_blocks);
235
- if (!is_remote && !is_local) {
236
- for (int i = 0; i < NUM_TOKENS_PER_THREAD_GROUP; i++) {
237
- const int physical_block_offset =
238
- (thread_group_idx + i * WARP_SIZE) % BLOCK_SIZE;
239
- const int token_idx = block_idx * BLOCK_SIZE + physical_block_offset;
240
-
241
- if (thread_group_offset == 0) {
242
- // NOTE(linxihui): assign very large number to skipped tokens to
243
- // avoid contribution to the sumexp softmax normalizer. This will
244
- // not be used at computing sum(softmax*v) as the blocks will be
245
- // skipped.
246
- logits[token_idx - start_token_idx] = -FLT_MAX;
247
- }
248
- }
249
- continue;
250
- }
251
- }
252
- const int64_t physical_block_number =
253
- static_cast<int64_t>(block_table[block_idx]);
254
-
255
- // Load a key to registers.
256
- // Each thread in a thread group has a different part of the key.
257
- // For example, if the thread group size is 4, then the first thread in
258
- // the group has 0, 4, 8, ... th vectors of the key, and the second thread
259
- // has 1, 5, 9, ... th vectors of the key, and so on.
260
- for (int i = 0; i < NUM_TOKENS_PER_THREAD_GROUP; i++) {
261
- const int physical_block_offset =
262
- (thread_group_idx + i * WARP_SIZE) % BLOCK_SIZE;
263
- const int token_idx = block_idx * BLOCK_SIZE + physical_block_offset;
264
- K_vec k_vecs[NUM_VECS_PER_THREAD];
265
-
266
- #pragma unroll
267
- for (int j = 0; j < NUM_VECS_PER_THREAD; j++) {
268
- const cache_t* k_ptr =
269
- k_cache + physical_block_number * kv_block_stride +
270
- kv_head_idx * kv_head_stride + physical_block_offset * x;
271
- const int vec_idx = thread_group_offset + j * THREAD_GROUP_SIZE;
272
- const int offset1 = (vec_idx * VEC_SIZE) / x;
273
- const int offset2 = (vec_idx * VEC_SIZE) % x;
274
-
275
- if constexpr (KV_DTYPE == Fp8KVCacheDataType::kAuto) {
276
- k_vecs[j] = *reinterpret_cast<const K_vec*>(
277
- k_ptr + offset1 * BLOCK_SIZE * x + offset2);
278
- } else {
279
- // Vector conversion from Quant_vec to K_vec.
280
- Quant_vec k_vec_quant = *reinterpret_cast<const Quant_vec*>(
281
- k_ptr + offset1 * BLOCK_SIZE * x + offset2);
282
- k_vecs[j] = fp8::scaled_convert<K_vec, Quant_vec, KV_DTYPE>(
283
- k_vec_quant, *k_scale);
284
- }
285
- }
286
-
287
- // Compute dot product.
288
- // This includes a reduction across the threads in the same thread group.
289
- float qk = scale * Qk_dot<scalar_t, THREAD_GROUP_SIZE>::dot(
290
- q_vecs[thread_group_offset], k_vecs);
291
- // Add the ALiBi bias if slopes are given.
292
- qk += (alibi_slope != 0) ? alibi_slope * (token_idx - seq_len + 1) : 0;
293
-
294
- if (thread_group_offset == 0) {
295
- // Store the partial reductions to shared memory.
296
- // NOTE(woosuk): It is required to zero out the masked logits.
297
- const bool mask = token_idx >= seq_len;
298
- logits[token_idx - start_token_idx] = mask ? 0.f : qk;
299
- // Update the max value.
300
- qk_max = mask ? qk_max : fmaxf(qk_max, qk);
301
- }
302
- }
303
- }
304
-
305
- // Perform reduction across the threads in the same warp to get the
306
- // max qk value for each "warp" (not across the thread block yet).
307
- // The 0-th thread of each thread group already has its max qk value.
308
- #pragma unroll
309
- for (int mask = WARP_SIZE / 2; mask >= THREAD_GROUP_SIZE; mask /= 2) {
310
- qk_max = fmaxf(qk_max, VLLM_SHFL_XOR_SYNC(qk_max, mask));
311
- }
312
- if (lane == 0) {
313
- red_smem[warp_idx] = qk_max;
314
- }
315
- __syncthreads();
316
-
317
- // TODO(woosuk): Refactor this part.
318
- // Get the max qk value for the sequence.
319
- qk_max = lane < NUM_WARPS ? red_smem[lane] : -FLT_MAX;
320
- #pragma unroll
321
- for (int mask = NUM_WARPS / 2; mask >= 1; mask /= 2) {
322
- qk_max = fmaxf(qk_max, VLLM_SHFL_XOR_SYNC(qk_max, mask));
323
- }
324
- // Broadcast the max qk value to all threads.
325
- qk_max = VLLM_SHFL_SYNC(qk_max, 0);
326
-
327
- // Get the sum of the exp values.
328
- float exp_sum = 0.f;
329
- for (int i = thread_idx; i < num_tokens; i += NUM_THREADS) {
330
- float val = __expf(logits[i] - qk_max);
331
- logits[i] = val;
332
- exp_sum += val;
333
- }
334
- exp_sum = block_sum<NUM_WARPS>(&red_smem[NUM_WARPS], exp_sum);
335
-
336
- // Compute softmax.
337
- const float inv_sum = __fdividef(1.f, exp_sum + 1e-6f);
338
- for (int i = thread_idx; i < num_tokens; i += NUM_THREADS) {
339
- logits[i] *= inv_sum;
340
- }
341
- __syncthreads();
342
-
343
- // If partitioning is enabled, store the max logit and exp_sum.
344
- if (USE_PARTITIONING && thread_idx == 0) {
345
- float* max_logits_ptr = max_logits +
346
- seq_idx * num_heads * max_num_partitions +
347
- head_idx * max_num_partitions + partition_idx;
348
- *max_logits_ptr = qk_max;
349
- float* exp_sums_ptr = exp_sums + seq_idx * num_heads * max_num_partitions +
350
- head_idx * max_num_partitions + partition_idx;
351
- *exp_sums_ptr = exp_sum;
352
- }
353
-
354
- // Each thread will fetch 16 bytes from the value cache at a time.
355
- constexpr int V_VEC_SIZE = MIN(16 / sizeof(scalar_t), BLOCK_SIZE);
356
- using V_vec = typename Vec<scalar_t, V_VEC_SIZE>::Type;
357
- using L_vec = typename Vec<scalar_t, V_VEC_SIZE>::Type;
358
- using V_quant_vec = typename Vec<cache_t, V_VEC_SIZE>::Type;
359
- using Float_L_vec = typename FloatVec<L_vec>::Type;
360
-
361
- constexpr int NUM_V_VECS_PER_ROW = BLOCK_SIZE / V_VEC_SIZE;
362
- constexpr int NUM_ROWS_PER_ITER = WARP_SIZE / NUM_V_VECS_PER_ROW;
363
- constexpr int NUM_ROWS_PER_THREAD =
364
- DIVIDE_ROUND_UP(HEAD_SIZE, NUM_ROWS_PER_ITER);
365
-
366
- // NOTE(woosuk): We use FP32 for the accumulator for better accuracy.
367
- float accs[NUM_ROWS_PER_THREAD];
368
- #pragma unroll
369
- for (int i = 0; i < NUM_ROWS_PER_THREAD; i++) {
370
- accs[i] = 0.f;
371
- }
372
-
373
- scalar_t zero_value;
374
- zero(zero_value);
375
- for (int block_idx = start_block_idx + warp_idx; block_idx < end_block_idx;
376
- block_idx += NUM_WARPS) {
377
- // NOTE(woosuk): The block number is stored in int32. However, we cast it to
378
- // int64 because int32 can lead to overflow when this variable is multiplied
379
- // by large numbers (e.g., kv_block_stride).
380
- // For blocksparse attention: skip computation on blocks that are not
381
- // attended
382
- if constexpr (IS_BLOCK_SPARSE) {
383
- int v_bs_block_id = block_idx * BLOCK_SIZE / blocksparse_block_size;
384
- if (!((v_bs_block_id + bs_block_offset) % blocksparse_vert_stride == 0) &&
385
- !((v_bs_block_id > q_bs_block_id - blocksparse_local_blocks))) {
386
- continue;
387
- }
388
- }
389
- const int64_t physical_block_number =
390
- static_cast<int64_t>(block_table[block_idx]);
391
- const int physical_block_offset = (lane % NUM_V_VECS_PER_ROW) * V_VEC_SIZE;
392
- const int token_idx = block_idx * BLOCK_SIZE + physical_block_offset;
393
- L_vec logits_vec;
394
- from_float(logits_vec, *reinterpret_cast<Float_L_vec*>(logits + token_idx -
395
- start_token_idx));
396
-
397
- const cache_t* v_ptr = v_cache + physical_block_number * kv_block_stride +
398
- kv_head_idx * kv_head_stride;
399
- #pragma unroll
400
- for (int i = 0; i < NUM_ROWS_PER_THREAD; i++) {
401
- const int row_idx = lane / NUM_V_VECS_PER_ROW + i * NUM_ROWS_PER_ITER;
402
- if (row_idx < HEAD_SIZE) {
403
- const int offset = row_idx * BLOCK_SIZE + physical_block_offset;
404
- V_vec v_vec;
405
-
406
- if constexpr (KV_DTYPE == Fp8KVCacheDataType::kAuto) {
407
- v_vec = *reinterpret_cast<const V_vec*>(v_ptr + offset);
408
- } else {
409
- V_quant_vec v_quant_vec =
410
- *reinterpret_cast<const V_quant_vec*>(v_ptr + offset);
411
- // Vector conversion from V_quant_vec to V_vec.
412
- v_vec = fp8::scaled_convert<V_vec, V_quant_vec, KV_DTYPE>(v_quant_vec,
413
- *v_scale);
414
- }
415
- if (block_idx == num_seq_blocks - 1) {
416
- // NOTE(woosuk): When v_vec contains the tokens that are out of the
417
- // context, we should explicitly zero out the values since they may
418
- // contain NaNs. See
419
- // https://github.com/vllm-project/vllm/issues/641#issuecomment-1682544472
420
- scalar_t* v_vec_ptr = reinterpret_cast<scalar_t*>(&v_vec);
421
- #pragma unroll
422
- for (int j = 0; j < V_VEC_SIZE; j++) {
423
- v_vec_ptr[j] = token_idx + j < seq_len ? v_vec_ptr[j] : zero_value;
424
- }
425
- }
426
- accs[i] += dot(logits_vec, v_vec);
427
- }
428
- }
429
- }
430
-
431
- // Perform reduction within each warp.
432
- #pragma unroll
433
- for (int i = 0; i < NUM_ROWS_PER_THREAD; i++) {
434
- float acc = accs[i];
435
- #pragma unroll
436
- for (int mask = NUM_V_VECS_PER_ROW / 2; mask >= 1; mask /= 2) {
437
- acc += VLLM_SHFL_XOR_SYNC(acc, mask);
438
- }
439
- accs[i] = acc;
440
- }
441
-
442
- // NOTE(woosuk): A barrier is required because the shared memory space for
443
- // logits is reused for the output.
444
- __syncthreads();
445
-
446
- // Perform reduction across warps.
447
- float* out_smem = reinterpret_cast<float*>(shared_mem);
448
- #pragma unroll
449
- for (int i = NUM_WARPS; i > 1; i /= 2) {
450
- int mid = i / 2;
451
- // Upper warps write to shared memory.
452
- if (warp_idx >= mid && warp_idx < i) {
453
- float* dst = &out_smem[(warp_idx - mid) * HEAD_SIZE];
454
- #pragma unroll
455
- for (int i = 0; i < NUM_ROWS_PER_THREAD; i++) {
456
- const int row_idx = lane / NUM_V_VECS_PER_ROW + i * NUM_ROWS_PER_ITER;
457
- if (row_idx < HEAD_SIZE && lane % NUM_V_VECS_PER_ROW == 0) {
458
- dst[row_idx] = accs[i];
459
- }
460
- }
461
- }
462
- __syncthreads();
463
-
464
- // Lower warps update the output.
465
- if (warp_idx < mid) {
466
- const float* src = &out_smem[warp_idx * HEAD_SIZE];
467
- #pragma unroll
468
- for (int i = 0; i < NUM_ROWS_PER_THREAD; i++) {
469
- const int row_idx = lane / NUM_V_VECS_PER_ROW + i * NUM_ROWS_PER_ITER;
470
- if (row_idx < HEAD_SIZE && lane % NUM_V_VECS_PER_ROW == 0) {
471
- accs[i] += src[row_idx];
472
- }
473
- }
474
- }
475
- __syncthreads();
476
- }
477
-
478
- // Write the final output.
479
- if (warp_idx == 0) {
480
- scalar_t* out_ptr =
481
- out + seq_idx * num_heads * max_num_partitions * HEAD_SIZE +
482
- head_idx * max_num_partitions * HEAD_SIZE + partition_idx * HEAD_SIZE;
483
- #pragma unroll
484
- for (int i = 0; i < NUM_ROWS_PER_THREAD; i++) {
485
- const int row_idx = lane / NUM_V_VECS_PER_ROW + i * NUM_ROWS_PER_ITER;
486
- if (row_idx < HEAD_SIZE && lane % NUM_V_VECS_PER_ROW == 0) {
487
- from_float(*(out_ptr + row_idx), accs[i]);
488
- }
489
- }
490
- }
491
- }
492
-
493
- // Grid: (num_heads, num_seqs, 1).
494
- template <typename scalar_t, typename cache_t, int HEAD_SIZE, int BLOCK_SIZE,
495
- int NUM_THREADS, vllm::Fp8KVCacheDataType KV_DTYPE,
496
- bool IS_BLOCK_SPARSE>
497
- __global__ void paged_attention_v1_kernel(
498
- scalar_t* __restrict__ out, // [num_seqs, num_heads, head_size]
499
- const scalar_t* __restrict__ q, // [num_seqs, num_heads, head_size]
500
- const cache_t* __restrict__ k_cache, // [num_blocks, num_kv_heads,
501
- // head_size/x, block_size, x]
502
- const cache_t* __restrict__ v_cache, // [num_blocks, num_kv_heads,
503
- // head_size, block_size]
504
- const int num_kv_heads, // [num_heads]
505
- const float scale,
506
- const int* __restrict__ block_tables, // [num_seqs, max_num_blocks_per_seq]
507
- const int* __restrict__ seq_lens, // [num_seqs]
508
- const int max_num_blocks_per_seq,
509
- const float* __restrict__ alibi_slopes, // [num_heads]
510
- const int q_stride, const int kv_block_stride, const int kv_head_stride,
511
- const float* k_scale, const float* v_scale, const int tp_rank,
512
- const int blocksparse_local_blocks, const int blocksparse_vert_stride,
513
- const int blocksparse_block_size, const int blocksparse_head_sliding_step) {
514
- paged_attention_kernel<scalar_t, cache_t, HEAD_SIZE, BLOCK_SIZE, NUM_THREADS,
515
- KV_DTYPE, IS_BLOCK_SPARSE>(
516
- /* exp_sums */ nullptr, /* max_logits */ nullptr, out, q, k_cache,
517
- v_cache, num_kv_heads, scale, block_tables, seq_lens,
518
- max_num_blocks_per_seq, alibi_slopes, q_stride, kv_block_stride,
519
- kv_head_stride, k_scale, v_scale, tp_rank, blocksparse_local_blocks,
520
- blocksparse_vert_stride, blocksparse_block_size,
521
- blocksparse_head_sliding_step);
522
- }
523
-
524
- // Grid: (num_heads, num_seqs, max_num_partitions).
525
- template <typename scalar_t, typename cache_t, int HEAD_SIZE, int BLOCK_SIZE,
526
- int NUM_THREADS, vllm::Fp8KVCacheDataType KV_DTYPE,
527
- bool IS_BLOCK_SPARSE,
528
- int PARTITION_SIZE>
529
- __global__ void paged_attention_v2_kernel(
530
- float* __restrict__ exp_sums, // [num_seqs, num_heads, max_num_partitions]
531
- float* __restrict__ max_logits, // [num_seqs, num_heads,
532
- // max_num_partitions]
533
- scalar_t* __restrict__ tmp_out, // [num_seqs, num_heads,
534
- // max_num_partitions, head_size]
535
- const scalar_t* __restrict__ q, // [num_seqs, num_heads, head_size]
536
- const cache_t* __restrict__ k_cache, // [num_blocks, num_kv_heads,
537
- // head_size/x, block_size, x]
538
- const cache_t* __restrict__ v_cache, // [num_blocks, num_kv_heads,
539
- // head_size, block_size]
540
- const int num_kv_heads, // [num_heads]
541
- const float scale,
542
- const int* __restrict__ block_tables, // [num_seqs, max_num_blocks_per_seq]
543
- const int* __restrict__ seq_lens, // [num_seqs]
544
- const int max_num_blocks_per_seq,
545
- const float* __restrict__ alibi_slopes, // [num_heads]
546
- const int q_stride, const int kv_block_stride, const int kv_head_stride,
547
- const float* k_scale, const float* v_scale, const int tp_rank,
548
- const int blocksparse_local_blocks, const int blocksparse_vert_stride,
549
- const int blocksparse_block_size, const int blocksparse_head_sliding_step) {
550
- paged_attention_kernel<scalar_t, cache_t, HEAD_SIZE, BLOCK_SIZE, NUM_THREADS,
551
- KV_DTYPE, IS_BLOCK_SPARSE, PARTITION_SIZE>(
552
- exp_sums, max_logits, tmp_out, q, k_cache, v_cache, num_kv_heads, scale,
553
- block_tables, seq_lens, max_num_blocks_per_seq, alibi_slopes, q_stride,
554
- kv_block_stride, kv_head_stride, k_scale, v_scale, tp_rank,
555
- blocksparse_local_blocks, blocksparse_vert_stride, blocksparse_block_size,
556
- blocksparse_head_sliding_step);
557
- }
558
-
559
- // Grid: (num_heads, num_seqs).
560
- template <typename scalar_t, int HEAD_SIZE, int NUM_THREADS,
561
- int PARTITION_SIZE>
562
- __global__ void paged_attention_v2_reduce_kernel(
563
- scalar_t* __restrict__ out, // [num_seqs, num_heads, head_size]
564
- const float* __restrict__ exp_sums, // [num_seqs, num_heads,
565
- // max_num_partitions]
566
- const float* __restrict__ max_logits, // [num_seqs, num_heads,
567
- // max_num_partitions]
568
- const scalar_t* __restrict__ tmp_out, // [num_seqs, num_heads,
569
- // max_num_partitions, head_size]
570
- const int* __restrict__ seq_lens, // [num_seqs]
571
- const int max_num_partitions) {
572
- const int num_heads = gridDim.x;
573
- const int head_idx = blockIdx.x;
574
- const int seq_idx = blockIdx.y;
575
- const int seq_len = seq_lens[seq_idx];
576
- const int num_partitions = DIVIDE_ROUND_UP(seq_len, PARTITION_SIZE);
577
- if (num_partitions == 1) {
578
- // No need to reduce. Only copy tmp_out to out.
579
- scalar_t* out_ptr =
580
- out + seq_idx * num_heads * HEAD_SIZE + head_idx * HEAD_SIZE;
581
- const scalar_t* tmp_out_ptr =
582
- tmp_out + seq_idx * num_heads * max_num_partitions * HEAD_SIZE +
583
- head_idx * max_num_partitions * HEAD_SIZE;
584
- for (int i = threadIdx.x; i < HEAD_SIZE; i += blockDim.x) {
585
- out_ptr[i] = tmp_out_ptr[i];
586
- }
587
- // Terminate the thread block.
588
- return;
589
- }
590
-
591
- constexpr int NUM_WARPS = NUM_THREADS / WARP_SIZE;
592
- const int warp_idx = threadIdx.x / WARP_SIZE;
593
- const int lane = threadIdx.x % WARP_SIZE;
594
-
595
- // Size: 2 * num_partitions.
596
- extern __shared__ char shared_mem[];
597
- // Workspace for reduction.
598
- __shared__ float red_smem[2 * NUM_WARPS];
599
-
600
- // Load max logits to shared memory.
601
- float* shared_max_logits = reinterpret_cast<float*>(shared_mem);
602
- const float* max_logits_ptr = max_logits +
603
- seq_idx * num_heads * max_num_partitions +
604
- head_idx * max_num_partitions;
605
- float max_logit = -FLT_MAX;
606
- for (int i = threadIdx.x; i < num_partitions; i += blockDim.x) {
607
- const float l = max_logits_ptr[i];
608
- shared_max_logits[i] = l;
609
- max_logit = fmaxf(max_logit, l);
610
- }
611
- __syncthreads();
612
-
613
- // Get the global max logit.
614
- // Reduce within the warp.
615
- #pragma unroll
616
- for (int mask = WARP_SIZE / 2; mask >= 1; mask /= 2) {
617
- max_logit = fmaxf(max_logit, VLLM_SHFL_XOR_SYNC(max_logit, mask));
618
- }
619
- if (lane == 0) {
620
- red_smem[warp_idx] = max_logit;
621
- }
622
- __syncthreads();
623
- // Reduce across warps.
624
- max_logit = lane < NUM_WARPS ? red_smem[lane] : -FLT_MAX;
625
- #pragma unroll
626
- for (int mask = NUM_WARPS / 2; mask >= 1; mask /= 2) {
627
- max_logit = fmaxf(max_logit, VLLM_SHFL_XOR_SYNC(max_logit, mask));
628
- }
629
- // Broadcast the max value to all threads.
630
- max_logit = VLLM_SHFL_SYNC(max_logit, 0);
631
-
632
- // Load rescaled exp sums to shared memory.
633
- float* shared_exp_sums =
634
- reinterpret_cast<float*>(shared_mem + sizeof(float) * num_partitions);
635
- const float* exp_sums_ptr = exp_sums +
636
- seq_idx * num_heads * max_num_partitions +
637
- head_idx * max_num_partitions;
638
- float global_exp_sum = 0.0f;
639
- for (int i = threadIdx.x; i < num_partitions; i += blockDim.x) {
640
- float l = shared_max_logits[i];
641
- float rescaled_exp_sum = exp_sums_ptr[i] * expf(l - max_logit);
642
- global_exp_sum += rescaled_exp_sum;
643
- shared_exp_sums[i] = rescaled_exp_sum;
644
- }
645
- __syncthreads();
646
- global_exp_sum = block_sum<NUM_WARPS>(&red_smem[NUM_WARPS], global_exp_sum);
647
- const float inv_global_exp_sum = __fdividef(1.0f, global_exp_sum + 1e-6f);
648
-
649
- // Aggregate tmp_out to out.
650
- const scalar_t* tmp_out_ptr =
651
- tmp_out + seq_idx * num_heads * max_num_partitions * HEAD_SIZE +
652
- head_idx * max_num_partitions * HEAD_SIZE;
653
- scalar_t* out_ptr =
654
- out + seq_idx * num_heads * HEAD_SIZE + head_idx * HEAD_SIZE;
655
- #pragma unroll
656
- for (int i = threadIdx.x; i < HEAD_SIZE; i += NUM_THREADS) {
657
- float acc = 0.0f;
658
- for (int j = 0; j < num_partitions; ++j) {
659
- acc += to_float(tmp_out_ptr[j * HEAD_SIZE + i]) * shared_exp_sums[j] *
660
- inv_global_exp_sum;
661
- }
662
- from_float(out_ptr[i], acc);
663
- }
664
- }
665
-
666
- } // namespace vllm
667
-
668
- #undef MAX
669
- #undef MIN
670
- #undef DIVIDE_ROUND_UP
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
paged-attention/attention/attention_utils.cuh DELETED
@@ -1,57 +0,0 @@
1
- /*
2
- * Adapted from
3
- * https://github.com/NVIDIA/FasterTransformer/blob/release/v5.3_tag/src/fastertransformer/kernels/decoder_masked_multihead_attention/decoder_masked_multihead_attention_template.hpp
4
- * Copyright (c) 2023, The vLLM team.
5
- * Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved.
6
- *
7
- * Licensed under the Apache License, Version 2.0 (the "License");
8
- * you may not use this file except in compliance with the License.
9
- * You may obtain a copy of the License at
10
- *
11
- * http://www.apache.org/licenses/LICENSE-2.0
12
- *
13
- * Unless required by applicable law or agreed to in writing, software
14
- * distributed under the License is distributed on an "AS IS" BASIS,
15
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
- * See the License for the specific language governing permissions and
17
- * limitations under the License.
18
- */
19
- #pragma once
20
-
21
- #include "../cuda_compat.h"
22
- #include "attention_dtypes.h"
23
-
24
- #include <float.h>
25
- #include <type_traits>
26
-
27
- namespace vllm {
28
-
29
- // Q*K^T operation.
30
- template <int THREAD_GROUP_SIZE, typename Vec, int N>
31
- inline __device__ float qk_dot_(const Vec (&q)[N], const Vec (&k)[N]) {
32
- using A_vec = typename FloatVec<Vec>::Type;
33
- // Compute the parallel products for Q*K^T (treat vector lanes separately).
34
- A_vec qk_vec = mul<A_vec, Vec, Vec>(q[0], k[0]);
35
- #pragma unroll
36
- for (int ii = 1; ii < N; ++ii) {
37
- qk_vec = vllm::fma(q[ii], k[ii], qk_vec);
38
- }
39
-
40
- // Finalize the reduction across lanes.
41
- float qk = sum(qk_vec);
42
- #pragma unroll
43
- for (int mask = THREAD_GROUP_SIZE / 2; mask >= 1; mask /= 2) {
44
- qk += VLLM_SHFL_XOR_SYNC(qk, mask);
45
- }
46
- return qk;
47
- }
48
-
49
- template <typename T, int THREAD_GROUP_SIZE>
50
- struct Qk_dot {
51
- template <typename Vec, int N>
52
- static inline __device__ float dot(const Vec (&q)[N], const Vec (&k)[N]) {
53
- return qk_dot_<THREAD_GROUP_SIZE>(q, k);
54
- }
55
- };
56
-
57
- } // namespace vllm
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
paged-attention/attention/dtype_bfloat16.cuh DELETED
@@ -1,463 +0,0 @@
1
- /*
2
- * Adapted from
3
- * https://github.com/NVIDIA/FasterTransformer/blob/release/v5.3_tag/src/fastertransformer/kernels/decoder_masked_multihead_attention/decoder_masked_multihead_attention_template.hpp
4
- * and
5
- * https://github.com/NVIDIA/FasterTransformer/blob/release/v5.3_tag/src/fastertransformer/kernels/decoder_masked_multihead_attention_utils.h
6
- * Copyright (c) 2023, The vLLM team.
7
- * Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved.
8
- *
9
- * Licensed under the Apache License, Version 2.0 (the "License");
10
- * you may not use this file except in compliance with the License.
11
- * You may obtain a copy of the License at
12
- *
13
- * http://www.apache.org/licenses/LICENSE-2.0
14
- *
15
- * Unless required by applicable law or agreed to in writing, software
16
- * distributed under the License is distributed on an "AS IS" BASIS,
17
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
- * See the License for the specific language governing permissions and
19
- * limitations under the License.
20
- */
21
- #pragma once
22
-
23
- #include "attention_generic.cuh"
24
- #include "dtype_float32.cuh"
25
-
26
- #ifndef USE_ROCM
27
- #include <cuda_bf16.h>
28
- #include <cuda_fp16.h>
29
- #else
30
- #include <hip/hip_bf16.h>
31
- #include <hip/hip_fp16.h>
32
-
33
- typedef __hip_bfloat162 __nv_bfloat162;
34
- typedef __hip_bfloat16 __nv_bfloat16;
35
- #endif
36
-
37
- #include <stdint.h>
38
-
39
- namespace vllm {
40
-
41
- // Define custom BF16 vector data types.
42
- struct bf16_4_t {
43
- __nv_bfloat162 x;
44
- __nv_bfloat162 y;
45
- };
46
-
47
- struct bf16_8_t {
48
- __nv_bfloat162 x;
49
- __nv_bfloat162 y;
50
- __nv_bfloat162 z;
51
- __nv_bfloat162 w;
52
- };
53
-
54
- // BF16 vector types for Q, K, V.
55
- template <>
56
- struct Vec<__nv_bfloat16, 1> {
57
- using Type = __nv_bfloat16;
58
- };
59
- template <>
60
- struct Vec<__nv_bfloat16, 2> {
61
- using Type = __nv_bfloat162;
62
- };
63
- template <>
64
- struct Vec<__nv_bfloat16, 4> {
65
- using Type = bf16_4_t;
66
- };
67
- template <>
68
- struct Vec<__nv_bfloat16, 8> {
69
- using Type = bf16_8_t;
70
- };
71
-
72
- // FP32 accumulator vector types corresponding to Vec.
73
- template <>
74
- struct FloatVec<__nv_bfloat16> {
75
- using Type = float;
76
- };
77
- template <>
78
- struct FloatVec<__nv_bfloat162> {
79
- using Type = float2;
80
- };
81
- template <>
82
- struct FloatVec<bf16_4_t> {
83
- using Type = Float4_;
84
- };
85
- template <>
86
- struct FloatVec<bf16_8_t> {
87
- using Type = Float8_;
88
- };
89
-
90
- // Utility functions for type conversions.
91
- inline __device__ float2 bf1622float2(const __nv_bfloat162 val) {
92
- #if defined(__CUDA_ARCH__) && __CUDA_ARCH__ < 800
93
- assert(false);
94
- #else
95
- return __bfloat1622float2(val);
96
- #endif
97
- __builtin_unreachable(); // Suppress missing return statement warning
98
- }
99
-
100
- inline __device__ __nv_bfloat162 bf162bf162(const __nv_bfloat16 val) {
101
- #if defined(__CUDA_ARCH__) && __CUDA_ARCH__ < 800
102
- assert(false);
103
- #else
104
- return __bfloat162bfloat162(val);
105
- #endif
106
- __builtin_unreachable(); // Suppress missing return statement warning
107
- }
108
-
109
- // Vector addition.
110
- inline __device__ __nv_bfloat16 add(__nv_bfloat16 a, __nv_bfloat16 b) {
111
- #if defined(__CUDA_ARCH__) && __CUDA_ARCH__ < 800
112
- assert(false);
113
- #else
114
- #ifndef USE_ROCM
115
- return a + b;
116
- #else
117
- return __hadd(a, b);
118
- #endif
119
- #endif
120
- __builtin_unreachable(); // Suppress missing return statement warning
121
- }
122
-
123
- inline __device__ __nv_bfloat162 add(__nv_bfloat162 a, __nv_bfloat162 b) {
124
- #if defined(__CUDA_ARCH__) && __CUDA_ARCH__ < 800
125
- assert(false);
126
- #else
127
- return __hadd2(a, b);
128
- #endif
129
- __builtin_unreachable(); // Suppress missing return statement warning
130
- }
131
-
132
- inline __device__ bf16_4_t add(bf16_4_t a, bf16_4_t b) {
133
- bf16_4_t c;
134
- c.x = add(a.x, b.x);
135
- c.y = add(a.y, b.y);
136
- return c;
137
- }
138
-
139
- inline __device__ bf16_8_t add(bf16_8_t a, bf16_8_t b) {
140
- bf16_8_t c;
141
- c.x = add(a.x, b.x);
142
- c.y = add(a.y, b.y);
143
- c.z = add(a.z, b.z);
144
- c.w = add(a.w, b.w);
145
- return c;
146
- }
147
-
148
- inline __device__ float2 add(__nv_bfloat162 a, float2 fb) {
149
- float2 fa = bf1622float2(a);
150
- return add(fa, fb);
151
- }
152
-
153
- inline __device__ Float4_ add(bf16_4_t a, Float4_ fb) {
154
- Float4_ fc;
155
- fc.x = add(a.x, fb.x);
156
- fc.y = add(a.y, fb.y);
157
- return fc;
158
- }
159
-
160
- inline __device__ Float8_ add(bf16_8_t a, Float8_ fb) {
161
- Float8_ fc;
162
- fc.x = add(a.x, fb.x);
163
- fc.y = add(a.y, fb.y);
164
- fc.z = add(a.z, fb.z);
165
- fc.w = add(a.w, fb.w);
166
- return fc;
167
- }
168
-
169
- // Vector multiplication.
170
- template <>
171
- inline __device__ __nv_bfloat16 mul(__nv_bfloat16 a, __nv_bfloat16 b) {
172
- #if defined(__CUDA_ARCH__) && __CUDA_ARCH__ < 800
173
- assert(false);
174
- #else
175
- return __hmul(a, b);
176
- #endif
177
- __builtin_unreachable(); // Suppress missing return statement warning
178
- }
179
-
180
- template <>
181
- inline __device__ __nv_bfloat162 mul(__nv_bfloat162 a, __nv_bfloat162 b) {
182
- #if defined(__CUDA_ARCH__) && __CUDA_ARCH__ < 800
183
- assert(false);
184
- #else
185
- return __hmul2(a, b);
186
- #endif
187
- __builtin_unreachable(); // Suppress missing return statement warning
188
- }
189
-
190
- template <>
191
- inline __device__ __nv_bfloat162 mul(__nv_bfloat16 a, __nv_bfloat162 b) {
192
- return mul<__nv_bfloat162, __nv_bfloat162, __nv_bfloat162>(bf162bf162(a), b);
193
- }
194
-
195
- template <>
196
- inline __device__ bf16_4_t mul(bf16_4_t a, bf16_4_t b) {
197
- bf16_4_t c;
198
- c.x = mul<__nv_bfloat162, __nv_bfloat162, __nv_bfloat162>(a.x, b.x);
199
- c.y = mul<__nv_bfloat162, __nv_bfloat162, __nv_bfloat162>(a.y, b.y);
200
- return c;
201
- }
202
-
203
- template <>
204
- inline __device__ bf16_4_t mul(__nv_bfloat16 a, bf16_4_t b) {
205
- __nv_bfloat162 s = bf162bf162(a);
206
- bf16_4_t c;
207
- c.x = mul<__nv_bfloat162, __nv_bfloat162, __nv_bfloat162>(s, b.x);
208
- c.y = mul<__nv_bfloat162, __nv_bfloat162, __nv_bfloat162>(s, b.y);
209
- return c;
210
- }
211
-
212
- template <>
213
- inline __device__ bf16_8_t mul(bf16_8_t a, bf16_8_t b) {
214
- bf16_8_t c;
215
- c.x = mul<__nv_bfloat162, __nv_bfloat162, __nv_bfloat162>(a.x, b.x);
216
- c.y = mul<__nv_bfloat162, __nv_bfloat162, __nv_bfloat162>(a.y, b.y);
217
- c.z = mul<__nv_bfloat162, __nv_bfloat162, __nv_bfloat162>(a.z, b.z);
218
- c.w = mul<__nv_bfloat162, __nv_bfloat162, __nv_bfloat162>(a.w, b.w);
219
- return c;
220
- }
221
-
222
- template <>
223
- inline __device__ bf16_8_t mul(__nv_bfloat16 a, bf16_8_t b) {
224
- __nv_bfloat162 s = bf162bf162(a);
225
- bf16_8_t c;
226
- c.x = mul<__nv_bfloat162, __nv_bfloat162, __nv_bfloat162>(s, b.x);
227
- c.y = mul<__nv_bfloat162, __nv_bfloat162, __nv_bfloat162>(s, b.y);
228
- c.z = mul<__nv_bfloat162, __nv_bfloat162, __nv_bfloat162>(s, b.z);
229
- c.w = mul<__nv_bfloat162, __nv_bfloat162, __nv_bfloat162>(s, b.w);
230
- return c;
231
- }
232
-
233
- template <>
234
- inline __device__ float mul(__nv_bfloat16 a, __nv_bfloat16 b) {
235
- float fa = __bfloat162float(a);
236
- float fb = __bfloat162float(b);
237
- return fa * fb;
238
- }
239
-
240
- template <>
241
- inline __device__ float2 mul(__nv_bfloat162 a, __nv_bfloat162 b) {
242
- float2 fa = bf1622float2(a);
243
- float2 fb = bf1622float2(b);
244
- return mul<float2, float2, float2>(fa, fb);
245
- }
246
-
247
- template <>
248
- inline __device__ float2 mul(__nv_bfloat16 a, __nv_bfloat162 b) {
249
- return mul<float2, __nv_bfloat162, __nv_bfloat162>(bf162bf162(a), b);
250
- }
251
-
252
- template <>
253
- inline __device__ Float4_ mul(bf16_4_t a, bf16_4_t b) {
254
- Float4_ fc;
255
- fc.x = mul<float2, __nv_bfloat162, __nv_bfloat162>(a.x, b.x);
256
- fc.y = mul<float2, __nv_bfloat162, __nv_bfloat162>(a.y, b.y);
257
- return fc;
258
- }
259
-
260
- template <>
261
- inline __device__ Float4_ mul(__nv_bfloat16 a, bf16_4_t b) {
262
- __nv_bfloat162 s = bf162bf162(a);
263
- Float4_ fc;
264
- fc.x = mul<float2, __nv_bfloat162, __nv_bfloat162>(s, b.x);
265
- fc.y = mul<float2, __nv_bfloat162, __nv_bfloat162>(s, b.y);
266
- return fc;
267
- }
268
-
269
- template <>
270
- inline __device__ Float8_ mul(bf16_8_t a, bf16_8_t b) {
271
- Float8_ fc;
272
- fc.x = mul<float2, __nv_bfloat162, __nv_bfloat162>(a.x, b.x);
273
- fc.y = mul<float2, __nv_bfloat162, __nv_bfloat162>(a.y, b.y);
274
- fc.z = mul<float2, __nv_bfloat162, __nv_bfloat162>(a.z, b.z);
275
- fc.w = mul<float2, __nv_bfloat162, __nv_bfloat162>(a.w, b.w);
276
- return fc;
277
- }
278
-
279
- template <>
280
- inline __device__ Float8_ mul(__nv_bfloat16 a, bf16_8_t b) {
281
- __nv_bfloat162 s = bf162bf162(a);
282
- Float8_ fc;
283
- fc.x = mul<float2, __nv_bfloat162, __nv_bfloat162>(s, b.x);
284
- fc.y = mul<float2, __nv_bfloat162, __nv_bfloat162>(s, b.y);
285
- fc.z = mul<float2, __nv_bfloat162, __nv_bfloat162>(s, b.z);
286
- fc.w = mul<float2, __nv_bfloat162, __nv_bfloat162>(s, b.w);
287
- return fc;
288
- }
289
-
290
- // Vector fused multiply-add.
291
- inline __device__ __nv_bfloat162 fma(__nv_bfloat162 a, __nv_bfloat162 b,
292
- __nv_bfloat162 c) {
293
- #if defined(__CUDA_ARCH__) && __CUDA_ARCH__ < 800
294
- assert(false);
295
- #else
296
- return __hfma2(a, b, c);
297
- #endif
298
- __builtin_unreachable(); // Suppress missing return statement warning
299
- }
300
-
301
- inline __device__ __nv_bfloat162 fma(__nv_bfloat16 a, __nv_bfloat162 b,
302
- __nv_bfloat162 c) {
303
- #if defined(__CUDA_ARCH__) && __CUDA_ARCH__ < 800
304
- assert(false);
305
- #else
306
- return __hfma2(bf162bf162(a), b, c);
307
- #endif
308
- __builtin_unreachable(); // Suppress missing return statement warning
309
- }
310
-
311
- inline __device__ bf16_4_t fma(bf16_4_t a, bf16_4_t b, bf16_4_t c) {
312
- bf16_4_t d;
313
- d.x = fma(a.x, b.x, c.x);
314
- d.y = fma(a.y, b.y, c.y);
315
- return d;
316
- }
317
-
318
- inline __device__ bf16_4_t fma(__nv_bfloat16 a, bf16_4_t b, bf16_4_t c) {
319
- __nv_bfloat162 s = bf162bf162(a);
320
- bf16_4_t d;
321
- d.x = fma(s, b.x, c.x);
322
- d.y = fma(s, b.y, c.y);
323
- return d;
324
- }
325
-
326
- inline __device__ bf16_8_t fma(bf16_8_t a, bf16_8_t b, bf16_8_t c) {
327
- bf16_8_t d;
328
- d.x = fma(a.x, b.x, c.x);
329
- d.y = fma(a.y, b.y, c.y);
330
- d.z = fma(a.z, b.z, c.z);
331
- d.w = fma(a.w, b.w, c.w);
332
- return d;
333
- }
334
-
335
- inline __device__ bf16_8_t fma(__nv_bfloat16 a, bf16_8_t b, bf16_8_t c) {
336
- __nv_bfloat162 s = bf162bf162(a);
337
- bf16_8_t d;
338
- d.x = fma(s, b.x, c.x);
339
- d.y = fma(s, b.y, c.y);
340
- d.z = fma(s, b.z, c.z);
341
- d.w = fma(s, b.w, c.w);
342
- return d;
343
- }
344
-
345
- inline __device__ float fma(__nv_bfloat16 a, __nv_bfloat16 b, float fc) {
346
- return __bfloat162float(a) * __bfloat162float(b) + fc;
347
- }
348
-
349
- inline __device__ float2 fma(__nv_bfloat162 a, __nv_bfloat162 b, float2 fc) {
350
- float2 fa = bf1622float2(a);
351
- float2 fb = bf1622float2(b);
352
- return fma(fa, fb, fc);
353
- }
354
-
355
- inline __device__ float2 fma(__nv_bfloat16 a, __nv_bfloat162 b, float2 fc) {
356
- return fma(bf162bf162(a), b, fc);
357
- }
358
-
359
- inline __device__ Float4_ fma(bf16_4_t a, bf16_4_t b, Float4_ fc) {
360
- Float4_ fd;
361
- fd.x = fma(a.x, b.x, fc.x);
362
- fd.y = fma(a.y, b.y, fc.y);
363
- return fd;
364
- }
365
-
366
- inline __device__ Float4_ fma(__nv_bfloat16 a, bf16_4_t b, Float4_ fc) {
367
- __nv_bfloat162 s = bf162bf162(a);
368
- Float4_ fd;
369
- fd.x = fma(s, b.x, fc.x);
370
- fd.y = fma(s, b.y, fc.y);
371
- return fd;
372
- }
373
-
374
- inline __device__ Float8_ fma(bf16_8_t a, bf16_8_t b, Float8_ fc) {
375
- Float8_ fd;
376
- fd.x = fma(a.x, b.x, fc.x);
377
- fd.y = fma(a.y, b.y, fc.y);
378
- fd.z = fma(a.z, b.z, fc.z);
379
- fd.w = fma(a.w, b.w, fc.w);
380
- return fd;
381
- }
382
-
383
- inline __device__ Float8_ fma(__nv_bfloat16 a, bf16_8_t b, Float8_ fc) {
384
- __nv_bfloat162 s = bf162bf162(a);
385
- Float8_ fd;
386
- fd.x = fma(s, b.x, fc.x);
387
- fd.y = fma(s, b.y, fc.y);
388
- fd.z = fma(s, b.z, fc.z);
389
- fd.w = fma(s, b.w, fc.w);
390
- return fd;
391
- }
392
-
393
- // Vector sum.
394
- template <>
395
- inline __device__ float sum(__nv_bfloat16 v) {
396
- return __bfloat162float(v);
397
- }
398
-
399
- template <>
400
- inline __device__ float sum(__nv_bfloat162 v) {
401
- float2 vf = bf1622float2(v);
402
- return vf.x + vf.y;
403
- }
404
-
405
- template <>
406
- inline __device__ float sum(bf16_4_t v) {
407
- return sum(v.x) + sum(v.y);
408
- }
409
-
410
- template <>
411
- inline __device__ float sum(bf16_8_t v) {
412
- return sum(v.x) + sum(v.y) + sum(v.z) + sum(v.w);
413
- }
414
-
415
- // From float32 to bfloat16.
416
- inline __device__ void from_float(__nv_bfloat16& dst, float src) {
417
- dst = __float2bfloat16(src);
418
- }
419
-
420
- inline __device__ void from_float(__nv_bfloat162& dst, float2 src) {
421
- #if defined(__CUDA_ARCH__) && __CUDA_ARCH__ < 800
422
- assert(false);
423
- #else
424
- dst = __float22bfloat162_rn(src);
425
- #endif
426
- }
427
-
428
- inline __device__ void from_float(bf16_4_t& dst, Float4_ src) {
429
- #if defined(__CUDA_ARCH__) && __CUDA_ARCH__ < 800
430
- assert(false);
431
- #else
432
- dst.x = __float22bfloat162_rn(src.x);
433
- dst.y = __float22bfloat162_rn(src.y);
434
- #endif
435
- }
436
-
437
- inline __device__ void from_float(bf16_8_t& dst, Float8_ src) {
438
- #if defined(__CUDA_ARCH__) && __CUDA_ARCH__ < 800
439
- assert(false);
440
- #else
441
- dst.x = __float22bfloat162_rn(src.x);
442
- dst.y = __float22bfloat162_rn(src.y);
443
- dst.z = __float22bfloat162_rn(src.z);
444
- dst.w = __float22bfloat162_rn(src.w);
445
- #endif
446
- }
447
-
448
- // From bfloat16 to float32.
449
- inline __device__ float to_float(__nv_bfloat16 u) {
450
- return __bfloat162float(u);
451
- }
452
-
453
- // Zero-out a variable.
454
- inline __device__ void zero(__nv_bfloat16& dst) {
455
- #if defined(__CUDA_ARCH__) && __CUDA_ARCH__ < 800
456
- assert(false);
457
- #else
458
- // Same as CUDART_ZERO_BF16 introduced in CUDA 12.2.
459
- dst = __ushort_as_bfloat16((unsigned short)0x0000U);
460
- #endif
461
- }
462
-
463
- } // namespace vllm
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
paged-attention/attention/dtype_float16.cuh DELETED
@@ -1,504 +0,0 @@
1
- /*
2
- * Adapted from
3
- * https://github.com/NVIDIA/FasterTransformer/blob/release/v5.3_tag/src/fastertransformer/kernels/decoder_masked_multihead_attention/decoder_masked_multihead_attention_template.hpp
4
- * and
5
- * https://github.com/NVIDIA/FasterTransformer/blob/release/v5.3_tag/src/fastertransformer/kernels/decoder_masked_multihead_attention_utils.h
6
- * Copyright (c) 2023, The vLLM team.
7
- * Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved.
8
- *
9
- * Licensed under the Apache License, Version 2.0 (the "License");
10
- * you may not use this file except in compliance with the License.
11
- * You may obtain a copy of the License at
12
- *
13
- * http://www.apache.org/licenses/LICENSE-2.0
14
- *
15
- * Unless required by applicable law or agreed to in writing, software
16
- * distributed under the License is distributed on an "AS IS" BASIS,
17
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
- * See the License for the specific language governing permissions and
19
- * limitations under the License.
20
- */
21
- #pragma once
22
-
23
- #include "attention_generic.cuh"
24
- #include "dtype_float32.cuh"
25
-
26
- #ifdef USE_ROCM
27
- #include <hip/hip_fp16.h>
28
- #endif
29
-
30
- #include <stdint.h>
31
-
32
- namespace vllm {
33
-
34
- // FP16 vector types for Q, K, V.
35
- template <>
36
- struct Vec<uint16_t, 1> {
37
- using Type = uint16_t;
38
- };
39
- template <>
40
- struct Vec<uint16_t, 2> {
41
- using Type = uint32_t;
42
- };
43
- template <>
44
- struct Vec<uint16_t, 4> {
45
- using Type = uint2;
46
- };
47
- template <>
48
- struct Vec<uint16_t, 8> {
49
- using Type = uint4;
50
- };
51
-
52
- // FP32 accumulator vector types corresponding to Vec.
53
- template <>
54
- struct FloatVec<uint16_t> {
55
- using Type = float;
56
- };
57
- template <>
58
- struct FloatVec<uint32_t> {
59
- using Type = float2;
60
- };
61
- template <>
62
- struct FloatVec<uint2> {
63
- using Type = Float4_;
64
- };
65
- template <>
66
- struct FloatVec<uint4> {
67
- using Type = Float8_;
68
- };
69
-
70
- // Utility functions for type conversions.
71
- inline __device__ uint32_t h0_h0(uint16_t a) {
72
- #ifndef USE_ROCM
73
- uint32_t b;
74
- asm volatile("mov.b32 %0, {%1, %1};" : "=r"(b) : "h"(a));
75
- return b;
76
- #else
77
- union {
78
- uint32_t u32;
79
- uint16_t u16[2];
80
- } tmp;
81
- tmp.u16[0] = a;
82
- tmp.u16[1] = a;
83
- return tmp.u32;
84
- #endif
85
- }
86
-
87
- inline __device__ float half_to_float(uint16_t h) {
88
- float f;
89
- #ifndef USE_ROCM
90
- asm volatile("cvt.f32.f16 %0, %1;\n" : "=f"(f) : "h"(h));
91
- #else
92
- asm volatile("v_cvt_f32_f16 %0, %1;" : "=v"(f) : "v"(h));
93
- #endif
94
- return f;
95
- }
96
-
97
- inline __device__ float2 half2_to_float2(uint32_t v) {
98
- #ifndef USE_ROCM
99
- uint16_t lo, hi;
100
- asm volatile("mov.b32 {%0, %1}, %2;\n" : "=h"(lo), "=h"(hi) : "r"(v));
101
- return make_float2(half_to_float(lo), half_to_float(hi));
102
- #else
103
- union {
104
- uint32_t u32;
105
- uint16_t u16[2];
106
- } tmp;
107
- tmp.u32 = v;
108
- float2 ret;
109
- ret.x = half_to_float(tmp.u16[0]);
110
- ret.y = half_to_float(tmp.u16[1]);
111
- return ret;
112
- #endif
113
- }
114
-
115
- inline __device__ uint16_t float_to_half(float f) {
116
- union {
117
- uint32_t u32;
118
- uint16_t u16[2];
119
- } tmp;
120
- #ifndef USE_ROCM
121
- asm volatile("cvt.rn.f16.f32 %0, %1;\n" : "=h"(tmp.u16[0]) : "f"(f));
122
- #else
123
- asm volatile("v_cvt_f16_f32 %0, %1;\n" : "=v"(tmp.u32) : "v"(f));
124
- #endif
125
- return tmp.u16[0];
126
- }
127
-
128
- inline __device__ uint32_t float2_to_half2(float2 f) {
129
- union {
130
- uint32_t u32;
131
- uint16_t u16[2];
132
- } tmp;
133
- #ifndef USE_ROCM
134
- #if defined(__CUDA_ARCH__) && __CUDA_ARCH__ >= 800
135
- asm volatile("cvt.rn.f16x2.f32 %0, %1, %2;\n"
136
- : "=r"(tmp.u32)
137
- : "f"(f.y), "f"(f.x));
138
- #else
139
- asm volatile("cvt.rn.f16.f32 %0, %1;\n" : "=h"(tmp.u16[0]) : "f"(f.x));
140
- asm volatile("cvt.rn.f16.f32 %0, %1;\n" : "=h"(tmp.u16[1]) : "f"(f.y));
141
- #endif
142
- #else
143
- tmp.u16[0] = float_to_half(f.x);
144
- tmp.u16[1] = float_to_half(f.y);
145
- #endif
146
- return tmp.u32;
147
- }
148
-
149
- // Vector addition.
150
- inline __device__ uint16_t add(uint16_t a, uint16_t b) {
151
- uint16_t c;
152
- #ifndef USE_ROCM
153
- asm volatile("add.f16 %0, %1, %2;\n" : "=h"(c) : "h"(a), "h"(b));
154
- #else
155
- asm volatile("v_add_f16 %0, %1, %2;\n" : "=v"(c) : "v"(a), "v"(b));
156
- #endif
157
- return c;
158
- }
159
-
160
- inline __device__ uint32_t add(uint32_t a, uint32_t b) {
161
- uint32_t c;
162
- #ifndef USE_ROCM
163
- asm volatile("add.f16x2 %0, %1, %2;\n" : "=r"(c) : "r"(a), "r"(b));
164
- #else
165
- asm volatile("v_pk_add_f16 %0, %1, %2;\n" : "=v"(c) : "v"(a), "v"(b));
166
- #endif
167
- return c;
168
- }
169
-
170
- inline __device__ uint2 add(uint2 a, uint2 b) {
171
- uint2 c;
172
- c.x = add(a.x, b.x);
173
- c.y = add(a.y, b.y);
174
- return c;
175
- }
176
-
177
- inline __device__ uint4 add(uint4 a, uint4 b) {
178
- uint4 c;
179
- c.x = add(a.x, b.x);
180
- c.y = add(a.y, b.y);
181
- c.z = add(a.z, b.z);
182
- c.w = add(a.w, b.w);
183
- return c;
184
- }
185
-
186
- inline __device__ float2 add(uint32_t a, float2 fb) {
187
- float2 fa = half2_to_float2(a);
188
- return add(fa, fb);
189
- }
190
-
191
- inline __device__ Float4_ add(uint2 a, Float4_ fb) {
192
- Float4_ fc;
193
- fc.x = add(a.x, fb.x);
194
- fc.y = add(a.y, fb.y);
195
- return fc;
196
- }
197
-
198
- inline __device__ Float8_ add(uint4 a, Float8_ fb) {
199
- Float8_ fc;
200
- fc.x = add(a.x, fb.x);
201
- fc.y = add(a.y, fb.y);
202
- fc.z = add(a.z, fb.z);
203
- fc.w = add(a.w, fb.w);
204
- return fc;
205
- }
206
-
207
- // Vector multiplication.
208
- template <>
209
- inline __device__ uint16_t mul(uint16_t a, uint16_t b) {
210
- uint16_t c;
211
- #ifndef USE_ROCM
212
- asm volatile("mul.f16 %0, %1, %2;\n" : "=h"(c) : "h"(a), "h"(b));
213
- #else
214
- asm volatile("v_mul_f16 %0, %1, %2;\n" : "=v"(c) : "v"(a), "v"(b));
215
- #endif
216
- return c;
217
- }
218
-
219
- template <>
220
- inline __device__ uint32_t mul(uint32_t a, uint32_t b) {
221
- uint32_t c;
222
- #ifndef USE_ROCM
223
- asm volatile("mul.f16x2 %0, %1, %2;\n" : "=r"(c) : "r"(a), "r"(b));
224
- #else
225
- asm volatile("v_pk_mul_f16 %0, %1, %2;\n" : "=v"(c) : "v"(a), "v"(b));
226
- #endif
227
- return c;
228
- }
229
-
230
- template <>
231
- inline __device__ uint32_t mul(uint16_t a, uint32_t b) {
232
- return mul<uint32_t, uint32_t, uint32_t>(h0_h0(a), b);
233
- }
234
-
235
- template <>
236
- inline __device__ uint2 mul(uint2 a, uint2 b) {
237
- uint2 c;
238
- c.x = mul<uint32_t, uint32_t, uint32_t>(a.x, b.x);
239
- c.y = mul<uint32_t, uint32_t, uint32_t>(a.y, b.y);
240
- return c;
241
- }
242
-
243
- template <>
244
- inline __device__ uint2 mul(uint16_t a, uint2 b) {
245
- uint32_t s = h0_h0(a);
246
- uint2 c;
247
- c.x = mul<uint32_t, uint32_t, uint32_t>(s, b.x);
248
- c.y = mul<uint32_t, uint32_t, uint32_t>(s, b.y);
249
- return c;
250
- }
251
-
252
- template <>
253
- inline __device__ uint4 mul(uint4 a, uint4 b) {
254
- uint4 c;
255
- c.x = mul<uint32_t, uint32_t, uint32_t>(a.x, b.x);
256
- c.y = mul<uint32_t, uint32_t, uint32_t>(a.y, b.y);
257
- c.z = mul<uint32_t, uint32_t, uint32_t>(a.z, b.z);
258
- c.w = mul<uint32_t, uint32_t, uint32_t>(a.w, b.w);
259
- return c;
260
- }
261
-
262
- template <>
263
- inline __device__ uint4 mul(uint16_t a, uint4 b) {
264
- uint32_t s = h0_h0(a);
265
- uint4 c;
266
- c.x = mul<uint32_t, uint32_t, uint32_t>(s, b.x);
267
- c.y = mul<uint32_t, uint32_t, uint32_t>(s, b.y);
268
- c.z = mul<uint32_t, uint32_t, uint32_t>(s, b.z);
269
- c.w = mul<uint32_t, uint32_t, uint32_t>(s, b.w);
270
- return c;
271
- }
272
-
273
- template <>
274
- inline __device__ float mul(uint16_t a, uint16_t b) {
275
- float fa = half_to_float(a);
276
- float fb = half_to_float(b);
277
- return fa * fb;
278
- }
279
-
280
- template <>
281
- inline __device__ float2 mul(uint32_t a, uint32_t b) {
282
- float2 fa = half2_to_float2(a);
283
- float2 fb = half2_to_float2(b);
284
- return mul<float2, float2, float2>(fa, fb);
285
- }
286
-
287
- template <>
288
- inline __device__ float2 mul(uint16_t a, uint32_t b) {
289
- return mul<float2, uint32_t, uint32_t>(h0_h0(a), b);
290
- }
291
-
292
- template <>
293
- inline __device__ Float4_ mul(uint2 a, uint2 b) {
294
- Float4_ fc;
295
- fc.x = mul<float2, uint32_t, uint32_t>(a.x, b.x);
296
- fc.y = mul<float2, uint32_t, uint32_t>(a.y, b.y);
297
- return fc;
298
- }
299
-
300
- template <>
301
- inline __device__ Float4_ mul(uint16_t a, uint2 b) {
302
- uint32_t s = h0_h0(a);
303
- Float4_ fc;
304
- fc.x = mul<float2, uint32_t, uint32_t>(s, b.x);
305
- fc.y = mul<float2, uint32_t, uint32_t>(s, b.y);
306
- return fc;
307
- }
308
-
309
- template <>
310
- inline __device__ Float8_ mul(uint4 a, uint4 b) {
311
- Float8_ fc;
312
- fc.x = mul<float2, uint32_t, uint32_t>(a.x, b.x);
313
- fc.y = mul<float2, uint32_t, uint32_t>(a.y, b.y);
314
- fc.z = mul<float2, uint32_t, uint32_t>(a.z, b.z);
315
- fc.w = mul<float2, uint32_t, uint32_t>(a.w, b.w);
316
- return fc;
317
- }
318
-
319
- template <>
320
- inline __device__ Float8_ mul(uint16_t a, uint4 b) {
321
- uint32_t s = h0_h0(a);
322
- Float8_ fc;
323
- fc.x = mul<float2, uint32_t, uint32_t>(s, b.x);
324
- fc.y = mul<float2, uint32_t, uint32_t>(s, b.y);
325
- fc.z = mul<float2, uint32_t, uint32_t>(s, b.z);
326
- fc.w = mul<float2, uint32_t, uint32_t>(s, b.w);
327
- return fc;
328
- }
329
-
330
- // Vector fused multiply-add.
331
- inline __device__ uint32_t fma(uint32_t a, uint32_t b, uint32_t c) {
332
- uint32_t d;
333
- #ifndef USE_ROCM
334
- asm volatile("fma.rn.f16x2 %0, %1, %2, %3;\n"
335
- : "=r"(d)
336
- : "r"(a), "r"(b), "r"(c));
337
- #else
338
- asm volatile("v_pk_fma_f16 %0, %1, %2, %3;\n"
339
- : "=v"(d)
340
- : "v"(a), "v"(b), "v"(c));
341
- #endif
342
- return d;
343
- }
344
-
345
- inline __device__ uint32_t fma(uint16_t a, uint32_t b, uint32_t c) {
346
- return fma(h0_h0(a), b, c);
347
- }
348
-
349
- inline __device__ uint2 fma(uint2 a, uint2 b, uint2 c) {
350
- uint2 d;
351
- d.x = fma(a.x, b.x, c.x);
352
- d.y = fma(a.y, b.y, c.y);
353
- return d;
354
- }
355
-
356
- inline __device__ uint2 fma(uint16_t a, uint2 b, uint2 c) {
357
- uint32_t s = h0_h0(a);
358
- uint2 d;
359
- d.x = fma(s, b.x, c.x);
360
- d.y = fma(s, b.y, c.y);
361
- return d;
362
- }
363
-
364
- inline __device__ uint4 fma(uint4 a, uint4 b, uint4 c) {
365
- uint4 d;
366
- d.x = fma(a.x, b.x, c.x);
367
- d.y = fma(a.y, b.y, c.y);
368
- d.z = fma(a.z, b.z, c.z);
369
- d.w = fma(a.w, b.w, c.w);
370
- return d;
371
- }
372
-
373
- inline __device__ uint4 fma(uint16_t a, uint4 b, uint4 c) {
374
- uint32_t s = h0_h0(a);
375
- uint4 d;
376
- d.x = fma(s, b.x, c.x);
377
- d.y = fma(s, b.y, c.y);
378
- d.z = fma(s, b.z, c.z);
379
- d.w = fma(s, b.w, c.w);
380
- return d;
381
- }
382
-
383
- inline __device__ float fma(uint16_t a, uint16_t b, float fc) {
384
- float fa = half_to_float(a);
385
- float fb = half_to_float(b);
386
- return fa * fb + fc;
387
- }
388
-
389
- inline __device__ float2 fma(uint32_t a, uint32_t b, float2 fc) {
390
- float2 fa = half2_to_float2(a);
391
- float2 fb = half2_to_float2(b);
392
- return fma(fa, fb, fc);
393
- }
394
-
395
- inline __device__ float2 fma(uint16_t a, uint32_t b, float2 fc) {
396
- return fma(h0_h0(a), b, fc);
397
- }
398
-
399
- inline __device__ Float4_ fma(uint2 a, uint2 b, Float4_ fc) {
400
- Float4_ fd;
401
- fd.x = fma(a.x, b.x, fc.x);
402
- fd.y = fma(a.y, b.y, fc.y);
403
- return fd;
404
- }
405
-
406
- inline __device__ Float4_ fma(uint16_t a, uint2 b, Float4_ fc) {
407
- uint32_t s = h0_h0(a);
408
- Float4_ fd;
409
- fd.x = fma(s, b.x, fc.x);
410
- fd.y = fma(s, b.y, fc.y);
411
- return fd;
412
- }
413
-
414
- inline __device__ Float8_ fma(uint4 a, uint4 b, Float8_ fc) {
415
- Float8_ fd;
416
- fd.x = fma(a.x, b.x, fc.x);
417
- fd.y = fma(a.y, b.y, fc.y);
418
- fd.z = fma(a.z, b.z, fc.z);
419
- fd.w = fma(a.w, b.w, fc.w);
420
- return fd;
421
- }
422
-
423
- inline __device__ Float8_ fma(uint16_t a, uint4 b, Float8_ fc) {
424
- uint32_t s = h0_h0(a);
425
- Float8_ fd;
426
- fd.x = fma(s, b.x, fc.x);
427
- fd.y = fma(s, b.y, fc.y);
428
- fd.z = fma(s, b.z, fc.z);
429
- fd.w = fma(s, b.w, fc.w);
430
- return fd;
431
- }
432
-
433
- // Vector sum.
434
- template <>
435
- inline __device__ float sum(uint16_t v) {
436
- return half_to_float(v);
437
- }
438
-
439
- template <>
440
- inline __device__ float sum(uint32_t v) {
441
- float2 tmp = half2_to_float2(v);
442
- return tmp.x + tmp.y;
443
- }
444
-
445
- template <>
446
- inline __device__ float sum(uint2 v) {
447
- uint32_t c = add(v.x, v.y);
448
- return sum(c);
449
- }
450
-
451
- template <>
452
- inline __device__ float sum(uint4 v) {
453
- uint32_t c = add(v.x, v.y);
454
- c = add(c, v.z);
455
- c = add(c, v.w);
456
- return sum(c);
457
- }
458
-
459
- // From float32 to float16.
460
- inline __device__ void from_float(uint16_t& dst, float src) {
461
- dst = float_to_half(src);
462
- }
463
-
464
- inline __device__ void from_float(uint32_t& dst, float2 src) {
465
- dst = float2_to_half2(src);
466
- }
467
-
468
- inline __device__ void from_float(uint2& dst, Float4_ src) {
469
- dst.x = float2_to_half2(src.x);
470
- dst.y = float2_to_half2(src.y);
471
- }
472
-
473
- inline __device__ void from_float(uint4& dst, Float8_ src) {
474
- dst.x = float2_to_half2(src.x);
475
- dst.y = float2_to_half2(src.y);
476
- dst.z = float2_to_half2(src.z);
477
- dst.w = float2_to_half2(src.w);
478
- }
479
-
480
- // From float16 to float32.
481
- inline __device__ float to_float(uint16_t u) { return half_to_float(u); }
482
-
483
- inline __device__ float2 to_float(uint32_t u) { return half2_to_float2(u); }
484
-
485
- inline __device__ Float4_ to_float(uint2 u) {
486
- Float4_ tmp;
487
- tmp.x = half2_to_float2(u.x);
488
- tmp.y = half2_to_float2(u.y);
489
- return tmp;
490
- }
491
-
492
- inline __device__ Float8_ to_float(uint4 u) {
493
- Float8_ tmp;
494
- tmp.x = half2_to_float2(u.x);
495
- tmp.y = half2_to_float2(u.y);
496
- tmp.z = half2_to_float2(u.z);
497
- tmp.w = half2_to_float2(u.w);
498
- return tmp;
499
- }
500
-
501
- // Zero-out a variable.
502
- inline __device__ void zero(uint16_t& dst) { dst = uint16_t(0); }
503
-
504
- } // namespace vllm
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
paged-attention/attention/dtype_float32.cuh DELETED
@@ -1,251 +0,0 @@
1
- /*
2
- * Adapted from
3
- * https://github.com/NVIDIA/FasterTransformer/blob/release/v5.3_tag/src/fastertransformer/kernels/decoder_masked_multihead_attention/decoder_masked_multihead_attention_template.hpp
4
- * and
5
- * https://github.com/NVIDIA/FasterTransformer/blob/release/v5.3_tag/src/fastertransformer/kernels/decoder_masked_multihead_attention_utils.h
6
- * Copyright (c) 2023, The vLLM team.
7
- * Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved.
8
- *
9
- * Licensed under the Apache License, Version 2.0 (the "License");
10
- * you may not use this file except in compliance with the License.
11
- * You may obtain a copy of the License at
12
- *
13
- * http://www.apache.org/licenses/LICENSE-2.0
14
- *
15
- * Unless required by applicable law or agreed to in writing, software
16
- * distributed under the License is distributed on an "AS IS" BASIS,
17
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
- * See the License for the specific language governing permissions and
19
- * limitations under the License.
20
- */
21
- #pragma once
22
-
23
- #include "attention_generic.cuh"
24
-
25
- #include <stdint.h>
26
-
27
- namespace vllm {
28
-
29
- // Define custom FP32 vector data types.
30
- struct Float4_ {
31
- float2 x;
32
- float2 y;
33
- };
34
-
35
- struct Float8_ {
36
- float2 x;
37
- float2 y;
38
- float2 z;
39
- float2 w;
40
- };
41
-
42
- // FP32 vector types for Q, K, V.
43
- template <>
44
- struct Vec<float, 1> {
45
- using Type = float;
46
- };
47
- template <>
48
- struct Vec<float, 2> {
49
- using Type = float2;
50
- };
51
- template <>
52
- struct Vec<float, 4> {
53
- using Type = float4;
54
- };
55
-
56
- // FP32 accumulator vector types corresponding to Vec.
57
- template <>
58
- struct FloatVec<float> {
59
- using Type = float;
60
- };
61
- template <>
62
- struct FloatVec<float2> {
63
- using Type = float2;
64
- };
65
- template <>
66
- struct FloatVec<float4> {
67
- using Type = float4;
68
- };
69
-
70
- // Vector addition.
71
- inline __device__ float add(float a, float b) { return a + b; }
72
-
73
- inline __device__ float2 add(float2 a, float2 b) {
74
- float2 c;
75
- c.x = add(a.x, b.x);
76
- c.y = add(a.y, b.y);
77
- return c;
78
- }
79
-
80
- inline __device__ float4 add(float4 a, float4 b) {
81
- float4 c;
82
- c.x = add(a.x, b.x);
83
- c.y = add(a.y, b.y);
84
- c.z = add(a.z, b.z);
85
- c.w = add(a.w, b.w);
86
- return c;
87
- }
88
-
89
- // Vector multiplication.
90
- template <>
91
- inline __device__ float mul<float, float>(float a, float b) {
92
- return a * b;
93
- }
94
-
95
- template <>
96
- inline __device__ float2 mul(float2 a, float2 b) {
97
- float2 c;
98
- c.x = a.x * b.x;
99
- c.y = a.y * b.y;
100
- return c;
101
- }
102
-
103
- template <>
104
- inline __device__ float2 mul(float a, float2 b) {
105
- float2 c;
106
- c.x = a * b.x;
107
- c.y = a * b.y;
108
- return c;
109
- }
110
-
111
- template <>
112
- inline __device__ float4 mul(float4 a, float4 b) {
113
- float4 c;
114
- c.x = a.x * b.x;
115
- c.y = a.y * b.y;
116
- c.z = a.z * b.z;
117
- c.w = a.w * b.w;
118
- return c;
119
- }
120
-
121
- template <>
122
- inline __device__ float4 mul(float a, float4 b) {
123
- float4 c;
124
- c.x = a * b.x;
125
- c.y = a * b.y;
126
- c.z = a * b.z;
127
- c.w = a * b.w;
128
- return c;
129
- }
130
-
131
- // Vector fused multiply-add.
132
- inline __device__ float fma(float a, float b, float c) { return a * b + c; }
133
-
134
- inline __device__ float2 fma(float2 a, float2 b, float2 c) {
135
- float2 d;
136
- d.x = fma(a.x, b.x, c.x);
137
- d.y = fma(a.y, b.y, c.y);
138
- return d;
139
- }
140
-
141
- inline __device__ float2 fma(float a, float2 b, float2 c) {
142
- float2 d;
143
- d.x = fma(a, b.x, c.x);
144
- d.y = fma(a, b.y, c.y);
145
- return d;
146
- }
147
-
148
- inline __device__ float4 fma(float4 a, float4 b, float4 c) {
149
- float4 d;
150
- d.x = fma(a.x, b.x, c.x);
151
- d.y = fma(a.y, b.y, c.y);
152
- d.z = fma(a.z, b.z, c.z);
153
- d.w = fma(a.w, b.w, c.w);
154
- return d;
155
- }
156
-
157
- inline __device__ float4 fma(float a, float4 b, float4 c) {
158
- float4 d;
159
- d.x = fma(a, b.x, c.x);
160
- d.y = fma(a, b.y, c.y);
161
- d.z = fma(a, b.z, c.z);
162
- d.w = fma(a, b.w, c.w);
163
- return d;
164
- }
165
-
166
- inline __device__ Float4_ fma(float a, Float4_ b, Float4_ c) {
167
- Float4_ d;
168
- d.x = fma(a, b.x, c.x);
169
- d.y = fma(a, b.y, c.y);
170
- return d;
171
- }
172
-
173
- inline __device__ Float8_ fma(float a, Float8_ b, Float8_ c) {
174
- Float8_ d;
175
- d.x = fma(a, b.x, c.x);
176
- d.y = fma(a, b.y, c.y);
177
- d.z = fma(a, b.z, c.z);
178
- d.w = fma(a, b.w, c.w);
179
- return d;
180
- }
181
-
182
- // Vector sum.
183
- template <>
184
- inline __device__ float sum(float v) {
185
- return v;
186
- }
187
-
188
- template <>
189
- inline __device__ float sum(float2 v) {
190
- return v.x + v.y;
191
- }
192
-
193
- template <>
194
- inline __device__ float sum(float4 v) {
195
- return v.x + v.y + v.z + v.w;
196
- }
197
-
198
- template <>
199
- inline __device__ float sum(Float4_ v) {
200
- return v.x.x + v.x.y + v.y.x + v.y.y;
201
- }
202
-
203
- template <>
204
- inline __device__ float sum(Float8_ v) {
205
- return v.x.x + v.x.y + v.y.x + v.y.y + v.z.x + v.z.y + v.w.x + v.w.y;
206
- }
207
-
208
- // Vector dot product.
209
- inline __device__ float dot(float a, float b) { return a * b; }
210
-
211
- inline __device__ float dot(float2 a, float2 b) {
212
- float2 c = mul<float2, float2, float2>(a, b);
213
- return c.x + c.y;
214
- }
215
-
216
- inline __device__ float dot(Float4_ a, Float4_ b) {
217
- float2 acc = mul<float2, float2, float2>(a.x, b.x);
218
- acc = fma(a.y, b.y, acc);
219
- return acc.x + acc.y;
220
- }
221
-
222
- inline __device__ float dot(Float8_ a, Float8_ b) {
223
- float2 acc = mul<float2, float2, float2>(a.x, b.x);
224
- acc = fma(a.y, b.y, acc);
225
- acc = fma(a.z, b.z, acc);
226
- acc = fma(a.w, b.w, acc);
227
- return acc.x + acc.y;
228
- }
229
-
230
- // From float to float.
231
- inline __device__ void from_float(float& dst, float src) { dst = src; }
232
-
233
- inline __device__ void from_float(float2& dst, float2 src) { dst = src; }
234
-
235
- inline __device__ void from_float(float4& dst, float4 src) { dst = src; }
236
-
237
- // From float to float.
238
- inline __device__ float to_float(float u) { return u; }
239
-
240
- inline __device__ float2 to_float(float2 u) { return u; }
241
-
242
- inline __device__ float4 to_float(float4 u) { return u; }
243
-
244
- inline __device__ Float4_ to_float(Float4_ u) { return u; }
245
-
246
- inline __device__ Float8_ to_float(Float8_ u) { return u; }
247
-
248
- // Zero-out a variable.
249
- inline __device__ void zero(float& dst) { dst = 0.f; }
250
-
251
- } // namespace vllm
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
paged-attention/attention/dtype_fp8.cuh DELETED
@@ -1,41 +0,0 @@
1
- #pragma once
2
-
3
- #include "attention_generic.cuh"
4
-
5
- #include <stdint.h>
6
- #ifdef ENABLE_FP8
7
- #ifndef USE_ROCM
8
- #include <cuda_fp8.h>
9
- #endif // USE_ROCM
10
- #endif // ENABLE_FP8
11
-
12
- namespace vllm {
13
-
14
- enum class Fp8KVCacheDataType {
15
- kAuto = 0,
16
- kFp8E4M3 = 1,
17
- kFp8E5M2 = 2,
18
- };
19
-
20
- // fp8 vector types for quantization of kv cache
21
- template <>
22
- struct Vec<uint8_t, 1> {
23
- using Type = uint8_t;
24
- };
25
-
26
- template <>
27
- struct Vec<uint8_t, 2> {
28
- using Type = uint16_t;
29
- };
30
-
31
- template <>
32
- struct Vec<uint8_t, 4> {
33
- using Type = uint32_t;
34
- };
35
-
36
- template <>
37
- struct Vec<uint8_t, 8> {
38
- using Type = uint2;
39
- };
40
-
41
- } // namespace vllm
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
paged-attention/attention/paged_attention_v1.cu DELETED
@@ -1,187 +0,0 @@
1
- /*
2
- * Adapted from
3
- * https://github.com/NVIDIA/FasterTransformer/blob/release/v5.3_tag/src/fastertransformer/kernels/decoder_masked_multihead_attention/decoder_masked_multihead_attention_template.hpp
4
- * Copyright (c) 2023, The vLLM team.
5
- * Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved.
6
- *
7
- * Licensed under the Apache License, Version 2.0 (the "License");
8
- * you may not use this file except in compliance with the License.
9
- * You may obtain a copy of the License at
10
- *
11
- * http://www.apache.org/licenses/LICENSE-2.0
12
- *
13
- * Unless required by applicable law or agreed to in writing, software
14
- * distributed under the License is distributed on an "AS IS" BASIS,
15
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
- * See the License for the specific language governing permissions and
17
- * limitations under the License.
18
- */
19
-
20
- #include "attention_kernels.cuh"
21
- #include "cuda_compat.h"
22
-
23
- #define MAX(a, b) ((a) > (b) ? (a) : (b))
24
- #define MIN(a, b) ((a) < (b) ? (a) : (b))
25
- #define DIVIDE_ROUND_UP(a, b) (((a) + (b) - 1) / (b))
26
-
27
- #define LAUNCH_PAGED_ATTENTION_V1(HEAD_SIZE) \
28
- VLLM_DevFuncAttribute_SET_MaxDynamicSharedMemorySize( \
29
- ((void*)vllm::paged_attention_v1_kernel<T, CACHE_T, HEAD_SIZE, \
30
- BLOCK_SIZE, NUM_THREADS, \
31
- KV_DTYPE, IS_BLOCK_SPARSE>), \
32
- shared_mem_size); \
33
- vllm::paged_attention_v1_kernel<T, CACHE_T, HEAD_SIZE, BLOCK_SIZE, \
34
- NUM_THREADS, KV_DTYPE, IS_BLOCK_SPARSE> \
35
- <<<grid, block, shared_mem_size, stream>>>( \
36
- out_ptr, query_ptr, key_cache_ptr, value_cache_ptr, num_kv_heads, \
37
- scale, block_tables_ptr, seq_lens_ptr, max_num_blocks_per_seq, \
38
- alibi_slopes_ptr, q_stride, kv_block_stride, kv_head_stride, \
39
- k_scale_ptr, v_scale_ptr, tp_rank, blocksparse_local_blocks, \
40
- blocksparse_vert_stride, blocksparse_block_size, \
41
- blocksparse_head_sliding_step);
42
-
43
- // TODO(woosuk): Tune NUM_THREADS.
44
- template <typename T, typename CACHE_T, int BLOCK_SIZE,
45
- vllm::Fp8KVCacheDataType KV_DTYPE, bool IS_BLOCK_SPARSE,
46
- int NUM_THREADS = 128>
47
- void paged_attention_v1_launcher(
48
- torch::Tensor& out, torch::Tensor& query, torch::Tensor& key_cache,
49
- torch::Tensor& value_cache, int num_kv_heads, float scale,
50
- torch::Tensor& block_tables, torch::Tensor& seq_lens, int max_seq_len,
51
- const std::optional<torch::Tensor>& alibi_slopes, torch::Tensor& k_scale,
52
- torch::Tensor& v_scale, const int tp_rank,
53
- const int blocksparse_local_blocks, const int blocksparse_vert_stride,
54
- const int blocksparse_block_size, const int blocksparse_head_sliding_step) {
55
- int num_seqs = query.size(0);
56
- int num_heads = query.size(1);
57
- int head_size = query.size(2);
58
- int max_num_blocks_per_seq = block_tables.size(1);
59
- int q_stride = query.stride(0);
60
- int kv_block_stride = key_cache.stride(0);
61
- int kv_head_stride = key_cache.stride(1);
62
-
63
- // NOTE: alibi_slopes is optional.
64
- const float* alibi_slopes_ptr =
65
- alibi_slopes
66
- ? reinterpret_cast<const float*>(alibi_slopes.value().data_ptr())
67
- : nullptr;
68
-
69
- T* out_ptr = reinterpret_cast<T*>(out.data_ptr());
70
- T* query_ptr = reinterpret_cast<T*>(query.data_ptr());
71
- CACHE_T* key_cache_ptr = reinterpret_cast<CACHE_T*>(key_cache.data_ptr());
72
- CACHE_T* value_cache_ptr = reinterpret_cast<CACHE_T*>(value_cache.data_ptr());
73
- int* block_tables_ptr = block_tables.data_ptr<int>();
74
- int* seq_lens_ptr = seq_lens.data_ptr<int>();
75
- const float* k_scale_ptr = reinterpret_cast<const float*>(k_scale.data_ptr());
76
- const float* v_scale_ptr = reinterpret_cast<const float*>(v_scale.data_ptr());
77
-
78
- constexpr int NUM_WARPS = NUM_THREADS / WARP_SIZE;
79
- int padded_max_seq_len =
80
- DIVIDE_ROUND_UP(max_seq_len, BLOCK_SIZE) * BLOCK_SIZE;
81
- int logits_size = padded_max_seq_len * sizeof(float);
82
- int outputs_size = (NUM_WARPS / 2) * head_size * sizeof(float);
83
- // Python-side check in vllm.worker.worker._check_if_can_support_max_seq_len
84
- // Keep that in sync with the logic here!
85
- int shared_mem_size = std::max(logits_size, outputs_size);
86
-
87
- dim3 grid(num_heads, num_seqs, 1);
88
- dim3 block(NUM_THREADS);
89
- const at::cuda::OptionalCUDAGuard device_guard(device_of(query));
90
- const cudaStream_t stream = at::cuda::getCurrentCUDAStream();
91
- switch (head_size) {
92
- // NOTE(woosuk): To reduce the compilation time, we only compile for the
93
- // head sizes that we use in the model. However, we can easily extend this
94
- // to support any head size which is a multiple of 16.
95
- case 32:
96
- LAUNCH_PAGED_ATTENTION_V1(32);
97
- break;
98
- case 64:
99
- LAUNCH_PAGED_ATTENTION_V1(64);
100
- break;
101
- case 80:
102
- LAUNCH_PAGED_ATTENTION_V1(80);
103
- break;
104
- case 96:
105
- LAUNCH_PAGED_ATTENTION_V1(96);
106
- break;
107
- case 112:
108
- LAUNCH_PAGED_ATTENTION_V1(112);
109
- break;
110
- case 120:
111
- LAUNCH_PAGED_ATTENTION_V1(120);
112
- break;
113
- case 128:
114
- LAUNCH_PAGED_ATTENTION_V1(128);
115
- break;
116
- case 192:
117
- LAUNCH_PAGED_ATTENTION_V1(192);
118
- break;
119
- case 256:
120
- LAUNCH_PAGED_ATTENTION_V1(256);
121
- break;
122
- default:
123
- TORCH_CHECK(false, "Unsupported head size: ", head_size);
124
- break;
125
- }
126
- }
127
-
128
- #define CALL_V1_LAUNCHER(T, CACHE_T, BLOCK_SIZE, KV_DTYPE, IS_BLOCK_SPARSE) \
129
- paged_attention_v1_launcher<T, CACHE_T, BLOCK_SIZE, KV_DTYPE, \
130
- IS_BLOCK_SPARSE>( \
131
- out, query, key_cache, value_cache, num_kv_heads, scale, block_tables, \
132
- seq_lens, max_seq_len, alibi_slopes, k_scale, v_scale, tp_rank, \
133
- blocksparse_local_blocks, blocksparse_vert_stride, \
134
- blocksparse_block_size, blocksparse_head_sliding_step);
135
-
136
- #define CALL_V1_LAUNCHER_SPARSITY(T, CACHE_T, BLOCK_SIZE, IS_FP8_KV_CACHE) \
137
- if (is_block_sparse) { \
138
- CALL_V1_LAUNCHER(T, CACHE_T, BLOCK_SIZE, IS_FP8_KV_CACHE, true); \
139
- } else { \
140
- CALL_V1_LAUNCHER(T, CACHE_T, BLOCK_SIZE, IS_FP8_KV_CACHE, false); \
141
- }
142
-
143
- // NOTE(woosuk): To reduce the compilation time, we omitted block sizes
144
- // 1, 2, 4, 64, 128, 256.
145
- #define CALL_V1_LAUNCHER_BLOCK_SIZE(T, CACHE_T, KV_DTYPE) \
146
- switch (block_size) { \
147
- case 8: \
148
- CALL_V1_LAUNCHER_SPARSITY(T, CACHE_T, 8, KV_DTYPE); \
149
- break; \
150
- case 16: \
151
- CALL_V1_LAUNCHER_SPARSITY(T, CACHE_T, 16, KV_DTYPE); \
152
- break; \
153
- case 32: \
154
- CALL_V1_LAUNCHER_SPARSITY(T, CACHE_T, 32, KV_DTYPE); \
155
- break; \
156
- default: \
157
- TORCH_CHECK(false, "Unsupported block size: ", block_size); \
158
- break; \
159
- }
160
-
161
- void paged_attention_v1(
162
- torch::Tensor& out, // [num_seqs, num_heads, head_size]
163
- torch::Tensor& query, // [num_seqs, num_heads, head_size]
164
- torch::Tensor&
165
- key_cache, // [num_blocks, num_heads, head_size/x, block_size, x]
166
- torch::Tensor&
167
- value_cache, // [num_blocks, num_heads, head_size, block_size]
168
- int64_t num_kv_heads, // [num_heads]
169
- double scale,
170
- torch::Tensor& block_tables, // [num_seqs, max_num_blocks_per_seq]
171
- torch::Tensor& seq_lens, // [num_seqs]
172
- int64_t block_size, int64_t max_seq_len,
173
- const std::optional<torch::Tensor>& alibi_slopes,
174
- const std::string& kv_cache_dtype, torch::Tensor& k_scale,
175
- torch::Tensor& v_scale, const int64_t tp_rank,
176
- const int64_t blocksparse_local_blocks,
177
- const int64_t blocksparse_vert_stride, const int64_t blocksparse_block_size,
178
- const int64_t blocksparse_head_sliding_step) {
179
- const bool is_block_sparse = (blocksparse_vert_stride > 1);
180
-
181
- DISPATCH_BY_KV_CACHE_DTYPE(query.dtype(), kv_cache_dtype,
182
- CALL_V1_LAUNCHER_BLOCK_SIZE)
183
- }
184
-
185
- #undef MAX
186
- #undef MIN
187
- #undef DIVIDE_ROUND_UP
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
paged-attention/attention/paged_attention_v2.cu DELETED
@@ -1,197 +0,0 @@
1
- /*
2
- * Adapted from
3
- * https://github.com/NVIDIA/FasterTransformer/blob/release/v5.3_tag/src/fastertransformer/kernels/decoder_masked_multihead_attention/decoder_masked_multihead_attention_template.hpp
4
- * Copyright (c) 2023, The vLLM team.
5
- * Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved.
6
- *
7
- * Licensed under the Apache License, Version 2.0 (the "License");
8
- * you may not use this file except in compliance with the License.
9
- * You may obtain a copy of the License at
10
- *
11
- * http://www.apache.org/licenses/LICENSE-2.0
12
- *
13
- * Unless required by applicable law or agreed to in writing, software
14
- * distributed under the License is distributed on an "AS IS" BASIS,
15
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
- * See the License for the specific language governing permissions and
17
- * limitations under the License.
18
- */
19
-
20
- #include "attention_kernels.cuh"
21
- #include "cuda_compat.h"
22
-
23
- #define MAX(a, b) ((a) > (b) ? (a) : (b))
24
- #define MIN(a, b) ((a) < (b) ? (a) : (b))
25
- #define DIVIDE_ROUND_UP(a, b) (((a) + (b) - 1) / (b))
26
-
27
- #define LAUNCH_PAGED_ATTENTION_V2(HEAD_SIZE) \
28
- vllm::paged_attention_v2_kernel<T, CACHE_T, HEAD_SIZE, BLOCK_SIZE, \
29
- NUM_THREADS, KV_DTYPE, IS_BLOCK_SPARSE, \
30
- PARTITION_SIZE> \
31
- <<<grid, block, shared_mem_size, stream>>>( \
32
- exp_sums_ptr, max_logits_ptr, tmp_out_ptr, query_ptr, key_cache_ptr, \
33
- value_cache_ptr, num_kv_heads, scale, block_tables_ptr, \
34
- seq_lens_ptr, max_num_blocks_per_seq, alibi_slopes_ptr, q_stride, \
35
- kv_block_stride, kv_head_stride, k_scale_ptr, v_scale_ptr, tp_rank, \
36
- blocksparse_local_blocks, blocksparse_vert_stride, \
37
- blocksparse_block_size, blocksparse_head_sliding_step); \
38
- vllm::paged_attention_v2_reduce_kernel<T, HEAD_SIZE, NUM_THREADS, \
39
- PARTITION_SIZE> \
40
- <<<reduce_grid, block, reduce_shared_mem_size, stream>>>( \
41
- out_ptr, exp_sums_ptr, max_logits_ptr, tmp_out_ptr, seq_lens_ptr, \
42
- max_num_partitions);
43
-
44
- template <typename T, typename CACHE_T, int BLOCK_SIZE,
45
- vllm::Fp8KVCacheDataType KV_DTYPE, bool IS_BLOCK_SPARSE,
46
- int NUM_THREADS = 128, int PARTITION_SIZE = 512>
47
- void paged_attention_v2_launcher(
48
- torch::Tensor& out, torch::Tensor& exp_sums, torch::Tensor& max_logits,
49
- torch::Tensor& tmp_out, torch::Tensor& query, torch::Tensor& key_cache,
50
- torch::Tensor& value_cache, int num_kv_heads, float scale,
51
- torch::Tensor& block_tables, torch::Tensor& seq_lens, int max_seq_len,
52
- const std::optional<torch::Tensor>& alibi_slopes, torch::Tensor& k_scale,
53
- torch::Tensor& v_scale, const int tp_rank,
54
- const int blocksparse_local_blocks, const int blocksparse_vert_stride,
55
- const int blocksparse_block_size, const int blocksparse_head_sliding_step) {
56
- int num_seqs = query.size(0);
57
- int num_heads = query.size(1);
58
- int head_size = query.size(2);
59
- int max_num_blocks_per_seq = block_tables.size(1);
60
- int q_stride = query.stride(0);
61
- int kv_block_stride = key_cache.stride(0);
62
- int kv_head_stride = key_cache.stride(1);
63
-
64
- // NOTE: alibi_slopes is optional.
65
- const float* alibi_slopes_ptr =
66
- alibi_slopes
67
- ? reinterpret_cast<const float*>(alibi_slopes.value().data_ptr())
68
- : nullptr;
69
-
70
- T* out_ptr = reinterpret_cast<T*>(out.data_ptr());
71
- float* exp_sums_ptr = reinterpret_cast<float*>(exp_sums.data_ptr());
72
- float* max_logits_ptr = reinterpret_cast<float*>(max_logits.data_ptr());
73
- T* tmp_out_ptr = reinterpret_cast<T*>(tmp_out.data_ptr());
74
- T* query_ptr = reinterpret_cast<T*>(query.data_ptr());
75
- CACHE_T* key_cache_ptr = reinterpret_cast<CACHE_T*>(key_cache.data_ptr());
76
- CACHE_T* value_cache_ptr = reinterpret_cast<CACHE_T*>(value_cache.data_ptr());
77
- int* block_tables_ptr = block_tables.data_ptr<int>();
78
- int* seq_lens_ptr = seq_lens.data_ptr<int>();
79
- const float* k_scale_ptr = reinterpret_cast<const float*>(k_scale.data_ptr());
80
- const float* v_scale_ptr = reinterpret_cast<const float*>(v_scale.data_ptr());
81
-
82
- constexpr int NUM_WARPS = NUM_THREADS / WARP_SIZE;
83
- int max_num_partitions = DIVIDE_ROUND_UP(max_seq_len, PARTITION_SIZE);
84
- int logits_size = PARTITION_SIZE * sizeof(float);
85
- int outputs_size = (NUM_WARPS / 2) * head_size * sizeof(float);
86
-
87
- // For paged attention v2 kernel.
88
- dim3 grid(num_heads, num_seqs, max_num_partitions);
89
- int shared_mem_size = std::max(logits_size, outputs_size);
90
- // For paged attention v2 reduce kernel.
91
- dim3 reduce_grid(num_heads, num_seqs);
92
- int reduce_shared_mem_size = 2 * max_num_partitions * sizeof(float);
93
-
94
- dim3 block(NUM_THREADS);
95
- const at::cuda::OptionalCUDAGuard device_guard(device_of(query));
96
- const cudaStream_t stream = at::cuda::getCurrentCUDAStream();
97
- switch (head_size) {
98
- // NOTE(woosuk): To reduce the compilation time, we only compile for the
99
- // head sizes that we use in the model. However, we can easily extend this
100
- // to support any head size which is a multiple of 16.
101
- case 32:
102
- LAUNCH_PAGED_ATTENTION_V2(32);
103
- break;
104
- case 64:
105
- LAUNCH_PAGED_ATTENTION_V2(64);
106
- break;
107
- case 80:
108
- LAUNCH_PAGED_ATTENTION_V2(80);
109
- break;
110
- case 96:
111
- LAUNCH_PAGED_ATTENTION_V2(96);
112
- break;
113
- case 112:
114
- LAUNCH_PAGED_ATTENTION_V2(112);
115
- break;
116
- case 120:
117
- LAUNCH_PAGED_ATTENTION_V2(120);
118
- break;
119
- case 128:
120
- LAUNCH_PAGED_ATTENTION_V2(128);
121
- break;
122
- case 192:
123
- LAUNCH_PAGED_ATTENTION_V2(192);
124
- break;
125
- case 256:
126
- LAUNCH_PAGED_ATTENTION_V2(256);
127
- break;
128
- default:
129
- TORCH_CHECK(false, "Unsupported head size: ", head_size);
130
- break;
131
- }
132
- }
133
-
134
- #define CALL_V2_LAUNCHER(T, CACHE_T, BLOCK_SIZE, KV_DTYPE, IS_BLOCK_SPARSE) \
135
- paged_attention_v2_launcher<T, CACHE_T, BLOCK_SIZE, KV_DTYPE, \
136
- IS_BLOCK_SPARSE>( \
137
- out, exp_sums, max_logits, tmp_out, query, key_cache, value_cache, \
138
- num_kv_heads, scale, block_tables, seq_lens, max_seq_len, alibi_slopes, \
139
- k_scale, v_scale, tp_rank, blocksparse_local_blocks, \
140
- blocksparse_vert_stride, blocksparse_block_size, \
141
- blocksparse_head_sliding_step);
142
-
143
- #define CALL_V2_LAUNCHER_SPARSITY(T, CACHE_T, BLOCK_SIZE, IS_FP8_KV_CACHE) \
144
- if (is_block_sparse) { \
145
- CALL_V2_LAUNCHER(T, CACHE_T, BLOCK_SIZE, IS_FP8_KV_CACHE, true); \
146
- } else { \
147
- CALL_V2_LAUNCHER(T, CACHE_T, BLOCK_SIZE, IS_FP8_KV_CACHE, false); \
148
- }
149
-
150
- // NOTE(woosuk): To reduce the compilation time, we omitted block sizes
151
- // 1, 2, 4, 64, 128, 256.
152
- #define CALL_V2_LAUNCHER_BLOCK_SIZE(T, CACHE_T, KV_DTYPE) \
153
- switch (block_size) { \
154
- case 8: \
155
- CALL_V2_LAUNCHER_SPARSITY(T, CACHE_T, 8, KV_DTYPE); \
156
- break; \
157
- case 16: \
158
- CALL_V2_LAUNCHER_SPARSITY(T, CACHE_T, 16, KV_DTYPE); \
159
- break; \
160
- case 32: \
161
- CALL_V2_LAUNCHER_SPARSITY(T, CACHE_T, 32, KV_DTYPE); \
162
- break; \
163
- default: \
164
- TORCH_CHECK(false, "Unsupported block size: ", block_size); \
165
- break; \
166
- }
167
-
168
- void paged_attention_v2(
169
- torch::Tensor& out, // [num_seqs, num_heads, head_size]
170
- torch::Tensor& exp_sums, // [num_seqs, num_heads, max_num_partitions]
171
- torch::Tensor& max_logits, // [num_seqs, num_heads, max_num_partitions]
172
- torch::Tensor&
173
- tmp_out, // [num_seqs, num_heads, max_num_partitions, head_size]
174
- torch::Tensor& query, // [num_seqs, num_heads, head_size]
175
- torch::Tensor&
176
- key_cache, // [num_blocks, num_heads, head_size/x, block_size, x]
177
- torch::Tensor&
178
- value_cache, // [num_blocks, num_heads, head_size, block_size]
179
- int64_t num_kv_heads, // [num_heads]
180
- double scale,
181
- torch::Tensor& block_tables, // [num_seqs, max_num_blocks_per_seq]
182
- torch::Tensor& seq_lens, // [num_seqs]
183
- int64_t block_size, int64_t max_seq_len,
184
- const std::optional<torch::Tensor>& alibi_slopes,
185
- const std::string& kv_cache_dtype, torch::Tensor& k_scale,
186
- torch::Tensor& v_scale, const int64_t tp_rank,
187
- const int64_t blocksparse_local_blocks,
188
- const int64_t blocksparse_vert_stride, const int64_t blocksparse_block_size,
189
- const int64_t blocksparse_head_sliding_step) {
190
- const bool is_block_sparse = (blocksparse_vert_stride > 1);
191
- DISPATCH_BY_KV_CACHE_DTYPE(query.dtype(), kv_cache_dtype,
192
- CALL_V2_LAUNCHER_BLOCK_SIZE)
193
- }
194
-
195
- #undef MAX
196
- #undef MIN
197
- #undef DIVIDE_ROUND_UP
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
paged-attention/cache_kernels.cu DELETED
@@ -1,734 +0,0 @@
1
- #include <torch/all.h>
2
- #include <ATen/cuda/CUDAContext.h>
3
- #include <c10/cuda/CUDAGuard.h>
4
-
5
- #include "cuda_utils.h"
6
- #include "cuda_compat.h"
7
- #include "dispatch_utils.h"
8
-
9
- #ifdef USE_ROCM
10
- #include "quantization/fp8/amd/quant_utils.cuh"
11
- #else
12
- #include "quantization/fp8/nvidia/quant_utils.cuh"
13
- #endif
14
-
15
- #include <algorithm>
16
- #include <cassert>
17
- #include <map>
18
- #include <vector>
19
-
20
- #ifdef USE_ROCM
21
- #include <hip/hip_bf16.h>
22
- typedef __hip_bfloat16 __nv_bfloat16;
23
- #endif
24
-
25
- void swap_blocks(torch::Tensor& src, torch::Tensor& dst,
26
- const torch::Tensor& block_mapping) {
27
- torch::Device src_device = src.device();
28
- torch::Device dst_device = dst.device();
29
- cudaMemcpyKind memcpy_type;
30
- if (src_device.is_cuda() && dst_device.is_cuda()) {
31
- TORCH_CHECK(src_device.index() == dst_device.index(),
32
- "src and dst must be on the same GPU");
33
- memcpy_type = cudaMemcpyDeviceToDevice;
34
- } else if (src_device.is_cuda() && dst_device.is_cpu()) {
35
- memcpy_type = cudaMemcpyDeviceToHost;
36
- } else if (src_device.is_cpu() && dst_device.is_cuda()) {
37
- memcpy_type = cudaMemcpyHostToDevice;
38
- } else {
39
- TORCH_CHECK(false, "Invalid device combination");
40
- }
41
-
42
- // NOTE(youkaichao): keep in mind that `block_mapping` should be
43
- // a cpu tensor, otherwise every `item` call will require a gpu-cpu
44
- // synchronization.
45
- TORCH_CHECK(block_mapping.device().is_cpu(), "block_mapping must be on CPU");
46
-
47
- char* src_ptr = static_cast<char*>(src.data_ptr());
48
- char* dst_ptr = static_cast<char*>(dst.data_ptr());
49
-
50
- // We use the stride instead of numel in case the cache is padded for memory
51
- // alignment reasons, we assume the blocks data (inclusive of any padding)
52
- // is contiguous in memory
53
- const int64_t block_size_in_bytes = src.element_size() * src.stride(0);
54
- const at::cuda::OptionalCUDAGuard device_guard(
55
- src_device.is_cuda() ? src_device : dst_device);
56
- const cudaStream_t stream = at::cuda::getCurrentCUDAStream();
57
- // NOTE(woosuk): This can be slow if the number of blocks is large.
58
- const int64_t num_blocks = block_mapping.size(0);
59
- for (size_t i = 0; i < num_blocks; i++) {
60
- int64_t src_block_number = block_mapping[i][0].item<int64_t>();
61
- int64_t dst_block_number = block_mapping[i][1].item<int64_t>();
62
- int64_t src_offset = src_block_number * block_size_in_bytes;
63
- int64_t dst_offset = dst_block_number * block_size_in_bytes;
64
- cudaMemcpyAsync(dst_ptr + dst_offset, src_ptr + src_offset,
65
- block_size_in_bytes, memcpy_type, stream);
66
- }
67
- }
68
-
69
- namespace vllm {
70
-
71
- // Grid: (num_layers, num_pairs)
72
- template <typename scalar_t>
73
- __global__ void copy_blocks_kernel(int64_t* key_cache_ptrs,
74
- int64_t* value_cache_ptrs,
75
- const int64_t* __restrict__ block_mapping,
76
- const int numel_per_block) {
77
- const int layer_idx = blockIdx.x;
78
- const int pair_idx = blockIdx.y;
79
-
80
- scalar_t* key_cache = reinterpret_cast<scalar_t*>(key_cache_ptrs[layer_idx]);
81
- scalar_t* value_cache =
82
- reinterpret_cast<scalar_t*>(value_cache_ptrs[layer_idx]);
83
- int64_t src_block_number = block_mapping[2 * pair_idx];
84
- int64_t dst_block_number = block_mapping[2 * pair_idx + 1];
85
-
86
- const int64_t src_block_offset = src_block_number * numel_per_block;
87
- const int64_t dst_block_offset = dst_block_number * numel_per_block;
88
- for (int i = threadIdx.x; i < numel_per_block; i += blockDim.x) {
89
- int64_t src_offset = src_block_offset + i;
90
- int64_t dst_offset = dst_block_offset + i;
91
- key_cache[dst_offset] = key_cache[src_offset];
92
- }
93
- for (int i = threadIdx.x; i < numel_per_block; i += blockDim.x) {
94
- int64_t src_offset = src_block_offset + i;
95
- int64_t dst_offset = dst_block_offset + i;
96
- value_cache[dst_offset] = value_cache[src_offset];
97
- }
98
- }
99
-
100
- // Kernel for MLA, which works on a single joint kv_cache
101
- // Grid: (num_layers, num_pairs)
102
- template <typename scalar_t>
103
- __global__ void copy_blocks_mla_kernel(
104
- int64_t* cache_ptrs, const int64_t* __restrict__ block_mapping,
105
- const int mem_footprint_per_block) {
106
- const int layer_idx = blockIdx.x;
107
- const int pair_idx = blockIdx.y;
108
- scalar_t* cache = reinterpret_cast<scalar_t*>(cache_ptrs[layer_idx]);
109
- int64_t src_block = block_mapping[2 * pair_idx];
110
- int64_t dst_block = block_mapping[2 * pair_idx + 1];
111
- int64_t src_offset = src_block * mem_footprint_per_block;
112
- int64_t dst_offset = dst_block * mem_footprint_per_block;
113
- for (int i = threadIdx.x; i < mem_footprint_per_block; i += blockDim.x) {
114
- cache[dst_offset + i] = cache[src_offset + i];
115
- }
116
- }
117
-
118
- } // namespace vllm
119
-
120
- // Note: the key_caches and value_caches vectors are constant but
121
- // not the Tensors they contain. The vectors need to be const refs
122
- // in order to satisfy pytorch's C++ operator registration code.
123
- void copy_blocks(std::vector<torch::Tensor> const& key_caches,
124
- std::vector<torch::Tensor> const& value_caches,
125
- const torch::Tensor& block_mapping) {
126
- int num_layers = key_caches.size();
127
- TORCH_CHECK(num_layers == value_caches.size());
128
- if (num_layers == 0) {
129
- return;
130
- }
131
- torch::Device cache_device = key_caches[0].device();
132
- TORCH_CHECK(cache_device.is_cuda());
133
-
134
- // Create data structures for the kernel.
135
- // Create an array of pointers to the key and value caches.
136
- int64_t key_cache_ptrs[num_layers];
137
- int64_t value_cache_ptrs[num_layers];
138
- for (int layer_idx = 0; layer_idx < num_layers; ++layer_idx) {
139
- key_cache_ptrs[layer_idx] =
140
- reinterpret_cast<int64_t>(key_caches[layer_idx].data_ptr());
141
- value_cache_ptrs[layer_idx] =
142
- reinterpret_cast<int64_t>(value_caches[layer_idx].data_ptr());
143
- }
144
-
145
- // block_mapping is a 2D tensor with shape (num_pairs, 2).
146
- int num_pairs = block_mapping.size(0);
147
-
148
- // Move the data structures to the GPU.
149
- // NOTE: This synchronizes the CPU and GPU.
150
- torch::Tensor key_cache_ptrs_tensor =
151
- torch::from_blob(key_cache_ptrs, {num_layers}, torch::kInt64)
152
- .to(cache_device);
153
- torch::Tensor value_cache_ptrs_tensor =
154
- torch::from_blob(value_cache_ptrs, {num_layers}, torch::kInt64)
155
- .to(cache_device);
156
-
157
- // Launch the kernel.
158
- const int numel_per_block = key_caches[0][0].numel();
159
- dim3 grid(num_layers, num_pairs);
160
- dim3 block(std::min(1024, numel_per_block));
161
- const at::cuda::OptionalCUDAGuard device_guard(cache_device);
162
- const cudaStream_t stream = at::cuda::getCurrentCUDAStream();
163
- VLLM_DISPATCH_FLOATING_AND_BYTE_TYPES(
164
- key_caches[0].scalar_type(), "copy_blocks_kernel", ([&] {
165
- vllm::copy_blocks_kernel<scalar_t><<<grid, block, 0, stream>>>(
166
- key_cache_ptrs_tensor.data_ptr<int64_t>(),
167
- value_cache_ptrs_tensor.data_ptr<int64_t>(),
168
- block_mapping.data_ptr<int64_t>(), numel_per_block);
169
- }));
170
- }
171
-
172
- // copy blocks kernel for MLA (assumes a joint KV-cache)
173
- void copy_blocks_mla(std::vector<torch::Tensor> const& kv_caches,
174
- const torch::Tensor& block_mapping) {
175
- int num_layers = kv_caches.size();
176
- if (num_layers == 0) {
177
- return;
178
- }
179
- torch::Device cache_device = kv_caches[0].device();
180
- TORCH_CHECK(cache_device.is_cuda(), "kv_cache must be on CUDA");
181
-
182
- std::vector<int64_t> cache_ptrs(num_layers);
183
- for (int layer_idx = 0; layer_idx < num_layers; ++layer_idx) {
184
- cache_ptrs[layer_idx] =
185
- reinterpret_cast<int64_t>(kv_caches[layer_idx].data_ptr());
186
- }
187
- torch::Tensor cache_ptrs_tensor =
188
- torch::from_blob(cache_ptrs.data(), {num_layers}, torch::kInt64)
189
- .to(cache_device);
190
-
191
- int num_pairs = block_mapping.size(0);
192
- // We use the stride instead of numel in case the cache is padded for memory
193
- // alignment reasons, we assume the blocks data (inclusive of any padding)
194
- // is contiguous in memory
195
- int mem_footprint_per_block = kv_caches[0].stride(0);
196
- dim3 grid(num_layers, num_pairs);
197
- dim3 block(std::min(1024, mem_footprint_per_block));
198
- const at::cuda::OptionalCUDAGuard device_guard(cache_device);
199
- const cudaStream_t stream = at::cuda::getCurrentCUDAStream();
200
- VLLM_DISPATCH_FLOATING_AND_BYTE_TYPES(
201
- kv_caches[0].scalar_type(), "copy_blocks_mla_kernel", ([&] {
202
- vllm::copy_blocks_mla_kernel<scalar_t><<<grid, block, 0, stream>>>(
203
- cache_ptrs_tensor.data_ptr<int64_t>(),
204
- block_mapping.data_ptr<int64_t>(), mem_footprint_per_block);
205
- }));
206
- }
207
-
208
- namespace vllm {
209
-
210
- template <typename scalar_t, typename cache_t, Fp8KVCacheDataType kv_dt>
211
- __global__ void reshape_and_cache_kernel(
212
- const scalar_t* __restrict__ key, // [num_tokens, num_heads, head_size]
213
- const scalar_t* __restrict__ value, // [num_tokens, num_heads, head_size]
214
- cache_t* __restrict__ key_cache, // [num_blocks, num_heads, head_size/x,
215
- // block_size, x]
216
- cache_t* __restrict__ value_cache, // [num_blocks, num_heads, head_size,
217
- // block_size]
218
- const int64_t* __restrict__ slot_mapping, // [num_tokens]
219
- const int key_stride, const int value_stride, const int num_heads,
220
- const int head_size, const int block_size, const int x,
221
- const float* k_scale, const float* v_scale) {
222
- const int64_t token_idx = blockIdx.x;
223
- const int64_t slot_idx = slot_mapping[token_idx];
224
- if (slot_idx < 0) {
225
- // Padding token that should be ignored.
226
- return;
227
- }
228
-
229
- const int64_t block_idx = slot_idx / block_size;
230
- const int64_t block_offset = slot_idx % block_size;
231
-
232
- const int n = num_heads * head_size;
233
- for (int i = threadIdx.x; i < n; i += blockDim.x) {
234
- const int64_t src_key_idx = token_idx * key_stride + i;
235
- const int64_t src_value_idx = token_idx * value_stride + i;
236
-
237
- const int head_idx = i / head_size;
238
- const int head_offset = i % head_size;
239
- const int x_idx = head_offset / x;
240
- const int x_offset = head_offset % x;
241
-
242
- const int64_t tgt_key_idx =
243
- block_idx * num_heads * (head_size / x) * block_size * x +
244
- head_idx * (head_size / x) * block_size * x + x_idx * block_size * x +
245
- block_offset * x + x_offset;
246
- const int64_t tgt_value_idx =
247
- block_idx * num_heads * head_size * block_size +
248
- head_idx * head_size * block_size + head_offset * block_size +
249
- block_offset;
250
- scalar_t tgt_key = key[src_key_idx];
251
- scalar_t tgt_value = value[src_value_idx];
252
- if constexpr (kv_dt == Fp8KVCacheDataType::kAuto) {
253
- key_cache[tgt_key_idx] = tgt_key;
254
- value_cache[tgt_value_idx] = tgt_value;
255
- } else {
256
- key_cache[tgt_key_idx] =
257
- fp8::scaled_convert<cache_t, scalar_t, kv_dt>(tgt_key, *k_scale);
258
- value_cache[tgt_value_idx] =
259
- fp8::scaled_convert<cache_t, scalar_t, kv_dt>(tgt_value, *v_scale);
260
- }
261
- }
262
- }
263
-
264
- template <typename scalar_t, typename cache_t, Fp8KVCacheDataType kv_dt>
265
- __global__ void reshape_and_cache_flash_kernel(
266
- const scalar_t* __restrict__ key, // [num_tokens, num_heads, head_size]
267
- const scalar_t* __restrict__ value, // [num_tokens, num_heads, head_size]
268
- cache_t* __restrict__ key_cache, // [num_blocks, block_size, num_heads,
269
- // head_size]
270
- cache_t* __restrict__ value_cache, // [num_blocks, block_size, num_heads,
271
- // head_size]
272
- const int64_t* __restrict__ slot_mapping, // [num_tokens]
273
- const int64_t block_stride, const int64_t page_stride,
274
- const int64_t head_stride, const int64_t key_stride,
275
- const int64_t value_stride, const int num_heads, const int head_size,
276
- const int block_size, const float* k_scale, const float* v_scale) {
277
- const int64_t token_idx = blockIdx.x;
278
- const int64_t slot_idx = slot_mapping[token_idx];
279
- // NOTE: slot_idx can be -1 if the token is padded
280
- if (slot_idx < 0) {
281
- return;
282
- }
283
- const int64_t block_idx = slot_idx / block_size;
284
- const int64_t block_offset = slot_idx % block_size;
285
- const int n = num_heads * head_size;
286
- for (int i = threadIdx.x; i < n; i += blockDim.x) {
287
- const int64_t src_key_idx = token_idx * key_stride + i;
288
- const int64_t src_value_idx = token_idx * value_stride + i;
289
- const int head_idx = i / head_size;
290
- const int head_offset = i % head_size;
291
- const int64_t tgt_key_value_idx = block_idx * block_stride +
292
- block_offset * page_stride +
293
- head_idx * head_stride + head_offset;
294
- scalar_t tgt_key = key[src_key_idx];
295
- scalar_t tgt_value = value[src_value_idx];
296
- if constexpr (kv_dt == Fp8KVCacheDataType::kAuto) {
297
- key_cache[tgt_key_value_idx] = tgt_key;
298
- value_cache[tgt_key_value_idx] = tgt_value;
299
- } else {
300
- key_cache[tgt_key_value_idx] =
301
- fp8::scaled_convert<cache_t, scalar_t, kv_dt>(tgt_key, *k_scale);
302
- value_cache[tgt_key_value_idx] =
303
- fp8::scaled_convert<cache_t, scalar_t, kv_dt>(tgt_value, *v_scale);
304
- }
305
- }
306
- }
307
-
308
- template <typename scalar_t, typename cache_t, Fp8KVCacheDataType kv_dt>
309
- __global__ void concat_and_cache_mla_kernel(
310
- const scalar_t* __restrict__ kv_c, // [num_tokens, kv_lora_rank]
311
- const scalar_t* __restrict__ k_pe, // [num_tokens, pe_dim]
312
- cache_t* __restrict__ kv_cache, // [num_blocks, block_size, (kv_lora_rank
313
- // + pe_dim)]
314
- const int64_t* __restrict__ slot_mapping, // [num_tokens]
315
- const int block_stride, //
316
- const int entry_stride, //
317
- const int kv_c_stride, //
318
- const int k_pe_stride, //
319
- const int kv_lora_rank, //
320
- const int pe_dim, //
321
- const int block_size, //
322
- const float* scale //
323
- ) {
324
- const int64_t token_idx = blockIdx.x;
325
- const int64_t slot_idx = slot_mapping[token_idx];
326
- // NOTE: slot_idx can be -1 if the token is padded
327
- if (slot_idx < 0) {
328
- return;
329
- }
330
- const int64_t block_idx = slot_idx / block_size;
331
- const int64_t block_offset = slot_idx % block_size;
332
-
333
- auto copy = [&](const scalar_t* __restrict__ src, cache_t* __restrict__ dst,
334
- int src_stride, int dst_stride, int size, int offset) {
335
- for (int i = threadIdx.x; i < size; i += blockDim.x) {
336
- const int64_t src_idx = token_idx * src_stride + i;
337
- const int64_t dst_idx =
338
- block_idx * block_stride + block_offset * entry_stride + i + offset;
339
- if constexpr (kv_dt == Fp8KVCacheDataType::kAuto) {
340
- dst[dst_idx] = src[src_idx];
341
- } else {
342
- dst[dst_idx] =
343
- fp8::scaled_convert<cache_t, scalar_t, kv_dt>(src[src_idx], *scale);
344
- }
345
- }
346
- };
347
-
348
- copy(kv_c, kv_cache, kv_c_stride, block_stride, kv_lora_rank, 0);
349
- copy(k_pe, kv_cache, k_pe_stride, block_stride, pe_dim, kv_lora_rank);
350
- }
351
-
352
- } // namespace vllm
353
-
354
- // KV_T is the data type of key and value tensors.
355
- // CACHE_T is the stored data type of kv-cache.
356
- // KV_DTYPE is the real data type of kv-cache.
357
- #define CALL_RESHAPE_AND_CACHE(KV_T, CACHE_T, KV_DTYPE) \
358
- vllm::reshape_and_cache_kernel<KV_T, CACHE_T, KV_DTYPE> \
359
- <<<grid, block, 0, stream>>>( \
360
- reinterpret_cast<KV_T*>(key.data_ptr()), \
361
- reinterpret_cast<KV_T*>(value.data_ptr()), \
362
- reinterpret_cast<CACHE_T*>(key_cache.data_ptr()), \
363
- reinterpret_cast<CACHE_T*>(value_cache.data_ptr()), \
364
- slot_mapping.data_ptr<int64_t>(), key_stride, value_stride, \
365
- num_heads, head_size, block_size, x, \
366
- reinterpret_cast<const float*>(k_scale.data_ptr()), \
367
- reinterpret_cast<const float*>(v_scale.data_ptr()));
368
-
369
- void reshape_and_cache(
370
- torch::Tensor& key, // [num_tokens, num_heads, head_size]
371
- torch::Tensor& value, // [num_tokens, num_heads, head_size]
372
- torch::Tensor&
373
- key_cache, // [num_blocks, num_heads, head_size/x, block_size, x]
374
- torch::Tensor&
375
- value_cache, // [num_blocks, num_heads, head_size, block_size]
376
- torch::Tensor& slot_mapping, // [num_tokens]
377
- const std::string& kv_cache_dtype, torch::Tensor& k_scale,
378
- torch::Tensor& v_scale) {
379
- int num_tokens = slot_mapping.size(0);
380
- int num_heads = key.size(1);
381
- int head_size = key.size(2);
382
- int block_size = key_cache.size(3);
383
- int x = key_cache.size(4);
384
-
385
- int key_stride = key.stride(0);
386
- int value_stride = value.stride(0);
387
-
388
- dim3 grid(num_tokens);
389
- dim3 block(std::min(num_heads * head_size, 512));
390
- const at::cuda::OptionalCUDAGuard device_guard(device_of(key));
391
- const cudaStream_t stream = at::cuda::getCurrentCUDAStream();
392
-
393
- DISPATCH_BY_KV_CACHE_DTYPE(key.dtype(), kv_cache_dtype,
394
- CALL_RESHAPE_AND_CACHE)
395
- }
396
-
397
- // KV_T is the data type of key and value tensors.
398
- // CACHE_T is the stored data type of kv-cache.
399
- // KV_DTYPE is the real data type of kv-cache.
400
- #define CALL_RESHAPE_AND_CACHE_FLASH(KV_T, CACHE_T, KV_DTYPE) \
401
- vllm::reshape_and_cache_flash_kernel<KV_T, CACHE_T, KV_DTYPE> \
402
- <<<grid, block, 0, stream>>>( \
403
- reinterpret_cast<KV_T*>(key.data_ptr()), \
404
- reinterpret_cast<KV_T*>(value.data_ptr()), \
405
- reinterpret_cast<CACHE_T*>(key_cache.data_ptr()), \
406
- reinterpret_cast<CACHE_T*>(value_cache.data_ptr()), \
407
- slot_mapping.data_ptr<int64_t>(), block_stride, page_stride, \
408
- head_stride, key_stride, value_stride, num_heads, head_size, \
409
- block_size, reinterpret_cast<const float*>(k_scale.data_ptr()), \
410
- reinterpret_cast<const float*>(v_scale.data_ptr()));
411
-
412
- void reshape_and_cache_flash(
413
- torch::Tensor& key, // [num_tokens, num_heads, head_size]
414
- torch::Tensor& value, // [num_tokens, num_heads, head_size]
415
- torch::Tensor& key_cache, // [num_blocks, block_size, num_heads, head_size]
416
- torch::Tensor&
417
- value_cache, // [num_blocks, block_size, num_heads, head_size]
418
- torch::Tensor& slot_mapping, // [num_tokens] or [num_actual_tokens]
419
- const std::string& kv_cache_dtype, torch::Tensor& k_scale,
420
- torch::Tensor& v_scale) {
421
- // NOTE(woosuk): In vLLM V1, key.size(0) can be different from
422
- // slot_mapping.size(0) because of padding for CUDA graphs.
423
- // In vLLM V0, key.size(0) is always equal to slot_mapping.size(0) because
424
- // both include padding.
425
- // In vLLM V1, however, key.size(0) can be larger than slot_mapping.size(0)
426
- // since key includes padding for CUDA graphs, while slot_mapping does not.
427
- // In this case, slot_mapping.size(0) represents the actual number of tokens
428
- // before padding.
429
- // For compatibility with both cases, we use slot_mapping.size(0) as the
430
- // number of tokens.
431
- int num_tokens = slot_mapping.size(0);
432
- int num_heads = key.size(1);
433
- int head_size = key.size(2);
434
- int block_size = key_cache.size(1);
435
-
436
- int64_t key_stride = key.stride(0);
437
- int64_t value_stride = value.stride(0);
438
- int64_t block_stride = key_cache.stride(0);
439
- int64_t page_stride = key_cache.stride(1);
440
- int64_t head_stride = key_cache.stride(2);
441
- TORCH_CHECK(key_cache.stride(0) == value_cache.stride(0));
442
-
443
- dim3 grid(num_tokens);
444
- dim3 block(std::min(num_heads * head_size, 512));
445
- const at::cuda::OptionalCUDAGuard device_guard(device_of(key));
446
- const cudaStream_t stream = at::cuda::getCurrentCUDAStream();
447
-
448
- DISPATCH_BY_KV_CACHE_DTYPE(key.dtype(), kv_cache_dtype,
449
- CALL_RESHAPE_AND_CACHE_FLASH);
450
- }
451
-
452
- // KV_T is the data type of key and value tensors.
453
- // CACHE_T is the stored data type of kv-cache.
454
- // KV_DTYPE is the real data type of kv-cache.
455
- #define CALL_CONCAT_AND_CACHE_MLA(KV_T, CACHE_T, KV_DTYPE) \
456
- vllm::concat_and_cache_mla_kernel<KV_T, CACHE_T, KV_DTYPE> \
457
- <<<grid, block, 0, stream>>>( \
458
- reinterpret_cast<KV_T*>(kv_c.data_ptr()), \
459
- reinterpret_cast<KV_T*>(k_pe.data_ptr()), \
460
- reinterpret_cast<CACHE_T*>(kv_cache.data_ptr()), \
461
- slot_mapping.data_ptr<int64_t>(), block_stride, entry_stride, \
462
- kv_c_stride, k_pe_stride, kv_lora_rank, pe_dim, block_size, \
463
- reinterpret_cast<const float*>(scale.data_ptr()));
464
-
465
- void concat_and_cache_mla(
466
- torch::Tensor& kv_c, // [num_tokens, kv_lora_rank]
467
- torch::Tensor& k_pe, // [num_tokens, pe_dim]
468
- torch::Tensor& kv_cache, // [num_blocks, block_size, (kv_lora_rank +
469
- // pe_dim)]
470
- torch::Tensor& slot_mapping, // [num_tokens] or [num_actual_tokens]
471
- const std::string& kv_cache_dtype, torch::Tensor& scale) {
472
- // NOTE(woosuk): In vLLM V1, key.size(0) can be different from
473
- // slot_mapping.size(0) because of padding for CUDA graphs.
474
- // In vLLM V0, key.size(0) is always equal to slot_mapping.size(0) because
475
- // both include padding.
476
- // In vLLM V1, however, key.size(0) can be larger than slot_mapping.size(0)
477
- // since key includes padding for CUDA graphs, while slot_mapping does not.
478
- // In this case, slot_mapping.size(0) represents the actual number of tokens
479
- // before padding.
480
- // For compatibility with both cases, we use slot_mapping.size(0) as the
481
- // number of tokens.
482
- int num_tokens = slot_mapping.size(0);
483
- int kv_lora_rank = kv_c.size(1);
484
- int pe_dim = k_pe.size(1);
485
- int block_size = kv_cache.size(1);
486
-
487
- TORCH_CHECK(kv_cache.size(2) == kv_lora_rank + pe_dim);
488
-
489
- int kv_c_stride = kv_c.stride(0);
490
- int k_pe_stride = k_pe.stride(0);
491
- int block_stride = kv_cache.stride(0);
492
- int entry_stride = kv_cache.stride(1);
493
-
494
- dim3 grid(num_tokens);
495
- dim3 block(std::min(kv_lora_rank, 512));
496
- const at::cuda::OptionalCUDAGuard device_guard(device_of(kv_c));
497
- const cudaStream_t stream = at::cuda::getCurrentCUDAStream();
498
-
499
- DISPATCH_BY_KV_CACHE_DTYPE(kv_c.dtype(), kv_cache_dtype,
500
- CALL_CONCAT_AND_CACHE_MLA);
501
- }
502
-
503
- namespace vllm {
504
-
505
- template <typename Tout, typename Tin, Fp8KVCacheDataType kv_dt>
506
- __global__ void convert_fp8_kernel(const Tin* __restrict__ src_cache,
507
- Tout* __restrict__ dst_cache,
508
- const float scale,
509
- const int64_t block_stride) {
510
- const int64_t block_idx = blockIdx.x;
511
- for (int i = threadIdx.x; i < block_stride; i += blockDim.x) {
512
- int64_t idx = block_idx * block_stride + i;
513
- dst_cache[idx] =
514
- fp8::scaled_convert<Tout, Tin, kv_dt>(src_cache[idx], scale);
515
- }
516
- }
517
-
518
- } // namespace vllm
519
-
520
- #define CALL_CONVERT_FP8(Tout, Tin, KV_DTYPE) \
521
- vllm::convert_fp8_kernel<Tout, Tin, KV_DTYPE><<<grid, block, 0, stream>>>( \
522
- reinterpret_cast<Tin*>(src_cache.data_ptr()), \
523
- reinterpret_cast<Tout*>(dst_cache.data_ptr()), scale, block_stride);
524
-
525
- // Only for testing.
526
- void convert_fp8(torch::Tensor& dst_cache, torch::Tensor& src_cache,
527
- const double scale, const std::string& kv_cache_dtype) {
528
- torch::Device src_device = src_cache.device();
529
- torch::Device dst_device = dst_cache.device();
530
- TORCH_CHECK(src_device.is_cuda(), "src must be on a GPU")
531
- TORCH_CHECK(dst_device.is_cuda(), "dst must be on a GPU")
532
- TORCH_CHECK(src_device.index() == dst_device.index(),
533
- "src and dst must be on the same GPU");
534
- at::cuda::OptionalCUDAGuard device_guard(src_device);
535
-
536
- int64_t num_blocks = src_cache.size(0);
537
- int64_t block_stride = src_cache.stride(0);
538
-
539
- dim3 grid(num_blocks);
540
- dim3 block(std::min(block_stride, int64_t(512)));
541
- const cudaStream_t stream = at::cuda::getCurrentCUDAStream();
542
-
543
- if (kv_cache_dtype == "auto") {
544
- if (src_cache.dtype() == at::ScalarType::Float) {
545
- CALL_CONVERT_FP8(uint8_t, float, vllm::Fp8KVCacheDataType::kAuto);
546
- } else if (src_cache.dtype() == at::ScalarType::Half) {
547
- CALL_CONVERT_FP8(uint8_t, uint16_t, vllm::Fp8KVCacheDataType::kAuto);
548
- } else if (src_cache.dtype() == at::ScalarType::BFloat16) {
549
- CALL_CONVERT_FP8(uint8_t, __nv_bfloat16, vllm::Fp8KVCacheDataType::kAuto);
550
- } else if (dst_cache.dtype() == at::ScalarType::Float) {
551
- CALL_CONVERT_FP8(float, uint8_t, vllm::Fp8KVCacheDataType::kAuto);
552
- } else if (dst_cache.dtype() == at::ScalarType::Half) {
553
- CALL_CONVERT_FP8(uint16_t, uint8_t, vllm::Fp8KVCacheDataType::kAuto);
554
- } else if (dst_cache.dtype() == at::ScalarType::BFloat16) {
555
- CALL_CONVERT_FP8(__nv_bfloat16, uint8_t, vllm::Fp8KVCacheDataType::kAuto);
556
- }
557
- } else if (kv_cache_dtype == "fp8" || kv_cache_dtype == "fp8_e4m3") {
558
- if (src_cache.dtype() == at::ScalarType::Float) {
559
- CALL_CONVERT_FP8(uint8_t, float, vllm::Fp8KVCacheDataType::kFp8E4M3);
560
- } else if (src_cache.dtype() == at::ScalarType::Half) {
561
- CALL_CONVERT_FP8(uint8_t, uint16_t, vllm::Fp8KVCacheDataType::kFp8E4M3);
562
- } else if (src_cache.dtype() == at::ScalarType::BFloat16) {
563
- CALL_CONVERT_FP8(uint8_t, __nv_bfloat16,
564
- vllm::Fp8KVCacheDataType::kFp8E4M3);
565
- } else if (dst_cache.dtype() == at::ScalarType::Float) {
566
- CALL_CONVERT_FP8(float, uint8_t, vllm::Fp8KVCacheDataType::kFp8E4M3);
567
- } else if (dst_cache.dtype() == at::ScalarType::Half) {
568
- CALL_CONVERT_FP8(uint16_t, uint8_t, vllm::Fp8KVCacheDataType::kFp8E4M3);
569
- } else if (dst_cache.dtype() == at::ScalarType::BFloat16) {
570
- CALL_CONVERT_FP8(__nv_bfloat16, uint8_t,
571
- vllm::Fp8KVCacheDataType::kFp8E4M3);
572
- }
573
- } else {
574
- TORCH_CHECK(false, "Unsupported data type: ", kv_cache_dtype);
575
- }
576
- }
577
-
578
- namespace vllm {
579
-
580
- // grid is launched with dimensions (batch, num_splits)
581
- template <typename scalar_t>
582
- __global__ void gather_cache(
583
- const scalar_t* __restrict__ src_cache, // [NUM_BLOCKS, BLOCK_SIZE,
584
- // ENTRIES...]
585
- scalar_t* __restrict__ dst, // [TOT_TOKENS, ENTRIES...]
586
- const int32_t* __restrict__ block_table, // [BATCH, BLOCK_INDICES]
587
- const int32_t* __restrict__ cu_seq_lens, // [BATCH+1]
588
- const int32_t block_size, const int32_t entry_size,
589
- const int64_t block_table_stride, const int64_t cache_block_stride,
590
- const int64_t cache_entry_stride, const int64_t dst_entry_stride,
591
- const int32_t* __restrict__ seq_starts) { // Optional: starting offsets per
592
- // batch
593
-
594
- const int64_t bid = blockIdx.x; // Batch ID
595
- const int32_t num_splits = gridDim.y;
596
- const int32_t split = blockIdx.y;
597
- const int32_t seq_start = cu_seq_lens[bid];
598
- const int32_t seq_end = cu_seq_lens[bid + 1];
599
- const int32_t seq_len = seq_end - seq_start;
600
- const int32_t tot_blocks = cuda_utils::ceil_div(seq_len, block_size);
601
- const int32_t split_blocks = cuda_utils::ceil_div(tot_blocks, num_splits);
602
-
603
- const int32_t split_start = split * split_blocks;
604
- const int32_t split_end = min((split + 1) * split_blocks, tot_blocks);
605
-
606
- const bool is_active_split = (split_start < tot_blocks);
607
- const bool is_last_split = (split_end == tot_blocks);
608
-
609
- if (!is_active_split) return;
610
-
611
- int32_t full_blocks_end = split_end;
612
- int32_t partial_block_size = 0;
613
-
614
- // Adjust the pointer for the block_table for this batch.
615
- // If seq_starts is provided, compute an offset based on (seq_starts[bid] /
616
- // page_size)
617
- const int32_t batch_offset = bid * block_table_stride;
618
- int32_t offset = 0;
619
- if (seq_starts != nullptr) {
620
- offset = seq_starts[bid] / block_size;
621
- }
622
- const int32_t* batch_block_table = block_table + batch_offset + offset;
623
-
624
- // Adjust dst pointer based on the cumulative sequence lengths.
625
- dst += seq_start * dst_entry_stride;
626
-
627
- if (is_last_split) {
628
- partial_block_size = seq_len % block_size;
629
- if (partial_block_size) full_blocks_end -= 1;
630
- }
631
-
632
- auto copy_entry = [&](const scalar_t* __restrict__ _src,
633
- scalar_t* __restrict__ _dst) {
634
- for (int i = threadIdx.x; i < entry_size; i += blockDim.x)
635
- _dst[i] = _src[i];
636
- };
637
-
638
- for (int pid = split_start; pid < full_blocks_end; ++pid) {
639
- auto block_id = batch_block_table[pid];
640
- auto block_start_ptr = src_cache + block_id * cache_block_stride;
641
- auto block_dst_ptr = dst + pid * block_size * dst_entry_stride;
642
- for (int eid = 0; eid < block_size; ++eid) {
643
- copy_entry(block_start_ptr + eid * cache_entry_stride,
644
- block_dst_ptr + eid * dst_entry_stride);
645
- }
646
- }
647
-
648
- if (partial_block_size) {
649
- auto block_id = batch_block_table[full_blocks_end];
650
- auto block_start_ptr = src_cache + block_id * cache_block_stride;
651
- auto block_dst_ptr = dst + full_blocks_end * block_size * dst_entry_stride;
652
- for (int eid = 0; eid < partial_block_size; ++eid) {
653
- copy_entry(block_start_ptr + eid * cache_entry_stride,
654
- block_dst_ptr + eid * dst_entry_stride);
655
- }
656
- }
657
- }
658
-
659
- } // namespace vllm
660
-
661
- // Macro to dispatch the kernel based on the data type.
662
- #define CALL_GATHER_CACHE(CPY_DTYPE) \
663
- vllm::gather_cache<CPY_DTYPE><<<grid, block, 0, stream>>>( \
664
- reinterpret_cast<CPY_DTYPE*>(src_cache.data_ptr()), \
665
- reinterpret_cast<CPY_DTYPE*>(dst.data_ptr()), \
666
- block_table.data_ptr<int32_t>(), cu_seq_lens.data_ptr<int32_t>(), \
667
- block_size, entry_size, block_table_stride, cache_block_stride, \
668
- cache_entry_stride, dst_entry_stride, seq_starts_ptr);
669
-
670
- // Gather sequences from the cache into the destination tensor.
671
- // - cu_seq_lens contains the cumulative sequence lengths for each batch
672
- // - block_table contains the cache block indices for each sequence
673
- // - Optionally, seq_starts (if provided) offsets the starting block index by
674
- // (seq_starts[bid] / page_size)
675
- void gather_cache(
676
- torch::Tensor const& src_cache, // [NUM_BLOCKS, BLOCK_SIZE, ENTRIES...]
677
- torch::Tensor const& dst, // [TOT_TOKENS, ENTRIES...]
678
- torch::Tensor const& block_table, // [BATCH, BLOCK_INDICES]
679
- torch::Tensor const& cu_seq_lens, // [BATCH+1]
680
- int64_t batch_size,
681
- std::optional<torch::Tensor> seq_starts = std::nullopt) {
682
- at::cuda::OptionalCUDAGuard device_guard(src_cache.device());
683
- const cudaStream_t stream = at::cuda::getCurrentCUDAStream();
684
-
685
- int32_t block_size = src_cache.size(1);
686
- int32_t entry_size = src_cache.flatten(2, -1).size(2);
687
-
688
- TORCH_CHECK(block_table.dtype() == torch::kInt32,
689
- "block_table must be int32");
690
- TORCH_CHECK(cu_seq_lens.dtype() == torch::kInt32,
691
- "cu_seq_lens must be int32");
692
- if (seq_starts.has_value()) {
693
- TORCH_CHECK(seq_starts.value().dtype() == torch::kInt32,
694
- "seq_starts must be int32");
695
- }
696
-
697
- TORCH_CHECK(src_cache.device() == dst.device(),
698
- "src_cache and dst must be on the same device");
699
- TORCH_CHECK(src_cache.device() == block_table.device(),
700
- "src_cache and block_table must be on the same device");
701
- TORCH_CHECK(src_cache.device() == cu_seq_lens.device(),
702
- "src_cache and cu_seq_lens must be on the same device");
703
- if (seq_starts.has_value()) {
704
- TORCH_CHECK(src_cache.device() == seq_starts.value().device(),
705
- "src_cache and seq_starts must be on the same device");
706
- }
707
-
708
- int64_t block_table_stride = block_table.stride(0);
709
- int64_t cache_block_stride = src_cache.stride(0);
710
- int64_t cache_entry_stride = src_cache.stride(1);
711
- int64_t dst_entry_stride = dst.stride(0);
712
-
713
- // Decide on the number of splits based on the batch size.
714
- int num_splits = batch_size > 128 ? 2 : batch_size > 64 ? 4 : 16;
715
- dim3 grid(batch_size, num_splits);
716
- dim3 block(1024);
717
-
718
- TORCH_CHECK(src_cache.dtype() == dst.dtype(),
719
- "src_cache and dst must have the same dtype");
720
-
721
- const int dtype_bits = src_cache.element_size() * 8;
722
- const int32_t* seq_starts_ptr =
723
- seq_starts.has_value() ? seq_starts.value().data_ptr<int32_t>() : nullptr;
724
-
725
- if (dtype_bits == 32) {
726
- CALL_GATHER_CACHE(uint32_t);
727
- } else if (dtype_bits == 16) {
728
- CALL_GATHER_CACHE(uint16_t);
729
- } else if (dtype_bits == 8) {
730
- CALL_GATHER_CACHE(uint8_t);
731
- } else {
732
- TORCH_CHECK(false, "Unsupported data type width: ", dtype_bits);
733
- }
734
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
paged-attention/cuda_compat.h DELETED
@@ -1,49 +0,0 @@
1
- #pragma once
2
-
3
- #ifdef USE_ROCM
4
- #include <hip/hip_runtime.h>
5
- #endif
6
-
7
- #if defined(USE_ROCM) && defined(__GFX9__)
8
- #define WARP_SIZE 64
9
- #else
10
- #define WARP_SIZE 32
11
- #endif
12
-
13
- #ifndef USE_ROCM
14
- #define VLLM_LDG(arg) __ldg(arg)
15
- #else
16
- #define VLLM_LDG(arg) *(arg)
17
- #endif
18
-
19
- #ifndef USE_ROCM
20
- #define VLLM_SHFL_XOR_SYNC(var, lane_mask) \
21
- __shfl_xor_sync(uint32_t(-1), var, lane_mask)
22
- #define VLLM_SHFL_XOR_SYNC_WIDTH(var, lane_mask, width) \
23
- __shfl_xor_sync(uint32_t(-1), var, lane_mask, width)
24
- #else
25
- #define VLLM_SHFL_XOR_SYNC(var, lane_mask) __shfl_xor(var, lane_mask)
26
- #define VLLM_SHFL_XOR_SYNC_WIDTH(var, lane_mask, width) \
27
- __shfl_xor(var, lane_mask, width)
28
- #endif
29
-
30
- #ifndef USE_ROCM
31
- #define VLLM_SHFL_SYNC(var, src_lane) __shfl_sync(uint32_t(-1), var, src_lane)
32
- #else
33
- #define VLLM_SHFL_SYNC(var, src_lane) __shfl(var, src_lane)
34
- #endif
35
-
36
- #ifndef USE_ROCM
37
- #define VLLM_SHFL_DOWN_SYNC(var, lane_delta) \
38
- __shfl_down_sync(uint32_t(-1), var, lane_delta)
39
- #else
40
- #define VLLM_SHFL_DOWN_SYNC(var, lane_delta) __shfl_down(var, lane_delta)
41
- #endif
42
-
43
- #ifndef USE_ROCM
44
- #define VLLM_DevFuncAttribute_SET_MaxDynamicSharedMemorySize(FUNC, VAL) \
45
- cudaFuncSetAttribute(FUNC, cudaFuncAttributeMaxDynamicSharedMemorySize, VAL)
46
- #else
47
- #define VLLM_DevFuncAttribute_SET_MaxDynamicSharedMemorySize(FUNC, VAL) \
48
- hipFuncSetAttribute(FUNC, hipFuncAttributeMaxDynamicSharedMemorySize, VAL)
49
- #endif
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
paged-attention/dispatch_utils.h DELETED
@@ -1,83 +0,0 @@
1
- /*
2
- * Adapted from
3
- * https://github.com/pytorch/pytorch/blob/v2.0.1/aten/src/ATen/Dispatch.h
4
- */
5
- #pragma once
6
-
7
- #include <torch/all.h>
8
-
9
- // Need a special dispatch case macro since we will nest the FP8 dispatch.
10
- // Instead of the usual 'scalar_t', this names the dispatched type 'fp8_t'.
11
- #define AT_DISPATCH_FP8_CASE(enum_type, ...) \
12
- AT_PRIVATE_CASE_TYPE_USING_HINT(enum_type, fp8_t, __VA_ARGS__)
13
-
14
- #define VLLM_DISPATCH_CASE_FLOATING_TYPES(...) \
15
- AT_DISPATCH_CASE(at::ScalarType::Float, __VA_ARGS__) \
16
- AT_DISPATCH_CASE(at::ScalarType::Half, __VA_ARGS__) \
17
- AT_DISPATCH_CASE(at::ScalarType::BFloat16, __VA_ARGS__)
18
-
19
- #define VLLM_DISPATCH_FLOATING_TYPES(TYPE, NAME, ...) \
20
- AT_DISPATCH_SWITCH(TYPE, NAME, VLLM_DISPATCH_CASE_FLOATING_TYPES(__VA_ARGS__))
21
-
22
- // ROCm devices might use either fn or fnuz, so set up dispatch table for both.
23
- // A host-based check at runtime will create a preferred FP8 type for ROCm
24
- // such that the correct kernel is dispatched.
25
- #ifdef USE_ROCM
26
- #define VLLM_DISPATCH_CASE_FP8_TYPES(...) \
27
- AT_DISPATCH_FP8_CASE(at::ScalarType::Float8_e4m3fn, __VA_ARGS__) \
28
- AT_DISPATCH_FP8_CASE(at::ScalarType::Float8_e4m3fnuz, __VA_ARGS__)
29
-
30
- #define VLLM_DISPATCH_CASE_QUANT_TYPES(...) \
31
- AT_DISPATCH_CASE(at::ScalarType::Float8_e4m3fn, __VA_ARGS__) \
32
- AT_DISPATCH_CASE(at::ScalarType::Float8_e4m3fnuz, __VA_ARGS__) \
33
- AT_DISPATCH_CASE(at::ScalarType::Char, __VA_ARGS__)
34
- #else
35
- #define VLLM_DISPATCH_CASE_FP8_TYPES(...) \
36
- AT_DISPATCH_FP8_CASE(at::ScalarType::Float8_e4m3fn, __VA_ARGS__)
37
-
38
- #define VLLM_DISPATCH_CASE_QUANT_TYPES(...) \
39
- AT_DISPATCH_CASE(at::ScalarType::Float8_e4m3fn, __VA_ARGS__) \
40
- AT_DISPATCH_CASE(at::ScalarType::Char, __VA_ARGS__)
41
- #endif
42
-
43
- // When using this dispatch macro, the type is 'fp8_t' not 'scalar_t'.
44
- // See AT_DISPATCH_FP8_CASE above.
45
- #define VLLM_DISPATCH_FP8_TYPES(TYPE, NAME, ...) \
46
- AT_DISPATCH_SWITCH(TYPE, NAME, VLLM_DISPATCH_CASE_FP8_TYPES(__VA_ARGS__))
47
-
48
- #define VLLM_DISPATCH_QUANT_TYPES(TYPE, NAME, ...) \
49
- AT_DISPATCH_SWITCH(TYPE, NAME, VLLM_DISPATCH_CASE_QUANT_TYPES(__VA_ARGS__))
50
-
51
- #define VLLM_DISPATCH_CASE_FLOATING_AND_BYTE_TYPES(...) \
52
- AT_DISPATCH_CASE(at::ScalarType::Float, __VA_ARGS__) \
53
- AT_DISPATCH_CASE(at::ScalarType::Half, __VA_ARGS__) \
54
- AT_DISPATCH_CASE(at::ScalarType::BFloat16, __VA_ARGS__) \
55
- AT_DISPATCH_CASE(at::ScalarType::Byte, __VA_ARGS__)
56
-
57
- #define VLLM_DISPATCH_FLOATING_AND_BYTE_TYPES(TYPE, NAME, ...) \
58
- AT_DISPATCH_SWITCH(TYPE, NAME, \
59
- VLLM_DISPATCH_CASE_FLOATING_AND_BYTE_TYPES(__VA_ARGS__))
60
-
61
- #define VLLM_DISPATCH_CASE_INTEGRAL_TYPES(...) \
62
- AT_DISPATCH_CASE(at::ScalarType::Byte, __VA_ARGS__) \
63
- AT_DISPATCH_CASE(at::ScalarType::Char, __VA_ARGS__) \
64
- AT_DISPATCH_CASE(at::ScalarType::Short, __VA_ARGS__) \
65
- AT_DISPATCH_CASE(at::ScalarType::Int, __VA_ARGS__) \
66
- AT_DISPATCH_CASE(at::ScalarType::Long, __VA_ARGS__)
67
-
68
- #define VLLM_DISPATCH_CASE_INTEGRAL_AND_UNSIGNED_TYPES(...) \
69
- AT_DISPATCH_CASE(at::ScalarType::Byte, __VA_ARGS__) \
70
- AT_DISPATCH_CASE(at::ScalarType::Char, __VA_ARGS__) \
71
- AT_DISPATCH_CASE(at::ScalarType::Short, __VA_ARGS__) \
72
- AT_DISPATCH_CASE(at::ScalarType::Int, __VA_ARGS__) \
73
- AT_DISPATCH_CASE(at::ScalarType::Long, __VA_ARGS__) \
74
- AT_DISPATCH_CASE(at::ScalarType::UInt16, __VA_ARGS__) \
75
- AT_DISPATCH_CASE(at::ScalarType::UInt32, __VA_ARGS__) \
76
- AT_DISPATCH_CASE(at::ScalarType::UInt64, __VA_ARGS__)
77
-
78
- #define VLLM_DISPATCH_INTEGRAL_TYPES(TYPE, NAME, ...) \
79
- AT_DISPATCH_SWITCH(TYPE, NAME, VLLM_DISPATCH_CASE_INTEGRAL_TYPES(__VA_ARGS__))
80
-
81
- #define VLLM_DISPATCH_INTEGRAL_AND_UNSIGNED_TYPES(TYPE, NAME, ...) \
82
- AT_DISPATCH_SWITCH( \
83
- TYPE, NAME, VLLM_DISPATCH_CASE_INTEGRAL_AND_UNSIGNED_TYPES(__VA_ARGS__))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
paged-attention/quantization/fp8/amd/quant_utils.cuh DELETED
@@ -1,671 +0,0 @@
1
- #pragma once
2
- #include <hip/hip_fp8.h>
3
-
4
- #include <hip/hip_fp16.h>
5
- #include <hip/hip_bf16.h>
6
- #include <hip/hip_bfloat16.h>
7
-
8
- #include "../../../attention/attention_dtypes.h"
9
-
10
- namespace vllm {
11
- #ifdef USE_ROCM
12
-
13
- namespace fp8 {
14
- #ifdef ENABLE_FP8
15
-
16
- // Use hardware cvt instruction for fp8 on rocm
17
- template <typename fp8_type>
18
- __device__ __forceinline__ fp8_type cvt_c10(float const r) {
19
- return {};
20
- }
21
-
22
- // __hip_fp8_e4m3 only exists starting in ROCm 6.3. The macro
23
- // HIP_FP8_TYPE_OCP comes from the hip_fp8.h header and also makes
24
- // its first appearance in ROCm 6.3. Since VLLM_DISPATCH_FP8_TYPES
25
- // on ROCm instantiates both OCP and FNUZ kernels, we need to replace
26
- // the new HW cvt with something reasonable that doesn't rely on the
27
- // ROCm 6.3 feature. This allows compiling on ROCm 6.2 or newer.
28
- template <>
29
- __device__ __forceinline__ c10::Float8_e4m3fn cvt_c10(float const r) {
30
- #if HIP_FP8_TYPE_OCP
31
- return c10::Float8_e4m3fn(
32
- __hip_cvt_float_to_fp8(r, __hip_fp8_e4m3::__default_saturation,
33
- __hip_fp8_e4m3::__default_interpret),
34
- c10::Float8_e4m3fn::from_bits());
35
- #else
36
- // Cast implemented by pytorch. Uses bit manipulation instead of HW cvt.
37
- // HW cvt above is faster when it is available (ROCm 6.3 or newer).
38
- return static_cast<c10::Float8_e4m3fn>(r);
39
- #endif
40
- }
41
-
42
- template <>
43
- __device__ __forceinline__ c10::Float8_e4m3fnuz cvt_c10(float const r) {
44
- return c10::Float8_e4m3fnuz(
45
- __hip_cvt_float_to_fp8(r, __hip_fp8_e4m3_fnuz::__default_saturation,
46
- __hip_fp8_e4m3_fnuz::__default_interpret),
47
- c10::Float8_e4m3fnuz::from_bits());
48
- }
49
-
50
- template <typename Tout, typename Tin>
51
- __inline__ __device__ Tout vec_conversion(const Tin& x) {
52
- return x;
53
- }
54
-
55
- template <typename Tout, typename Tin>
56
- __inline__ __device__ Tout scaled_vec_conversion(const Tin& x,
57
- const float scale) {
58
- return x;
59
- }
60
-
61
- #if HIP_FP8_TYPE_OCP
62
- using fp8_type = __hip_fp8_e4m3;
63
- using fp8x2_type = __hip_fp8x2_e4m3;
64
- #else
65
- using fp8_type = __hip_fp8_e4m3_fnuz;
66
- using fp8x2_type = __hip_fp8x2_e4m3_fnuz;
67
- #endif
68
-
69
- // fp8 -> half
70
- template <>
71
- __inline__ __device__ uint16_t
72
- vec_conversion<uint16_t, uint8_t>(const uint8_t& a) {
73
- return __hip_cvt_fp8_to_halfraw(a, fp8_type::__default_interpret).x;
74
- }
75
-
76
- // fp8x2 -> half2
77
- template <>
78
- __inline__ __device__ uint32_t
79
- vec_conversion<uint32_t, uint16_t>(const uint16_t& a) {
80
- union {
81
- __half2_raw h2r;
82
- uint32_t ui32;
83
- } tmp;
84
- tmp.h2r = __hip_cvt_fp8x2_to_halfraw2(a, fp8_type::__default_interpret);
85
- return tmp.ui32;
86
- }
87
-
88
- // fp8x4 -> half2x2
89
- template <>
90
- __inline__ __device__ uint2 vec_conversion<uint2, uint32_t>(const uint32_t& a) {
91
- union {
92
- uint2 u32x2;
93
- uint32_t u32[2];
94
- } tmp;
95
- tmp.u32[0] = vec_conversion<uint32_t, uint16_t>((uint16_t)a);
96
- tmp.u32[1] = vec_conversion<uint32_t, uint16_t>((uint16_t)(a >> 16U));
97
- return tmp.u32x2;
98
- }
99
-
100
- // fp8x8 -> half2x4
101
- template <>
102
- __inline__ __device__ uint4 vec_conversion<uint4, uint2>(const uint2& a) {
103
- union {
104
- uint4 u64x2;
105
- uint2 u64[2];
106
- } tmp;
107
- tmp.u64[0] = vec_conversion<uint2, uint32_t>(a.x);
108
- tmp.u64[1] = vec_conversion<uint2, uint32_t>(a.y);
109
- return tmp.u64x2;
110
- }
111
-
112
- using __nv_bfloat16 = __hip_bfloat16;
113
-
114
- // fp8 -> __nv_bfloat16
115
- template <>
116
- __inline__ __device__ __nv_bfloat16
117
- vec_conversion<__nv_bfloat16, uint8_t>(const uint8_t& a) {
118
- fp8_type f8;
119
- f8.__x = a;
120
- return __float2bfloat16(static_cast<float>(f8));
121
- }
122
-
123
- using __nv_bfloat162 = __hip_bfloat162;
124
-
125
- // fp8x2 -> __nv_bfloat162
126
- template <>
127
- __inline__ __device__ __nv_bfloat162
128
- vec_conversion<__nv_bfloat162, uint16_t>(const uint16_t& a) {
129
- __nv_bfloat162 res;
130
- res.x = vec_conversion<__nv_bfloat16, uint8_t>((uint8_t)a);
131
- res.y = vec_conversion<__nv_bfloat16, uint8_t>((uint8_t)(a >> 8U));
132
- return res;
133
- }
134
-
135
- // fp8x4 -> bf16_4_t
136
- template <>
137
- __inline__ __device__ bf16_4_t
138
- vec_conversion<bf16_4_t, uint32_t>(const uint32_t& a) {
139
- bf16_4_t res;
140
- res.x = vec_conversion<__nv_bfloat162, uint16_t>((uint16_t)a);
141
- res.y = vec_conversion<__nv_bfloat162, uint16_t>((uint16_t)(a >> 16U));
142
- return res;
143
- }
144
-
145
- // fp8x8 -> bf16_8_t
146
- template <>
147
- __inline__ __device__ bf16_8_t vec_conversion<bf16_8_t, uint2>(const uint2& a) {
148
- bf16_4_t tmp1, tmp2;
149
- tmp1 = vec_conversion<bf16_4_t, uint32_t>(a.x);
150
- tmp2 = vec_conversion<bf16_4_t, uint32_t>(a.y);
151
- bf16_8_t res;
152
- res.x = tmp1.x;
153
- res.y = tmp1.y;
154
- res.z = tmp2.x;
155
- res.w = tmp2.y;
156
- return res;
157
- }
158
-
159
- // fp8 -> float
160
- template <>
161
- __inline__ __device__ float vec_conversion<float, uint8_t>(const uint8_t& a) {
162
- fp8_type f8;
163
- f8.__x = a;
164
- return static_cast<float>(f8);
165
- }
166
-
167
- // fp8x2 -> float2
168
- template <>
169
- __inline__ __device__ float2
170
- vec_conversion<float2, uint16_t>(const uint16_t& a) {
171
- fp8x2_type f8x2;
172
- f8x2.__x = a;
173
- return static_cast<float2>(f8x2);
174
- }
175
-
176
- // fp8x4 -> float4
177
- template <>
178
- __inline__ __device__ Float4_
179
- vec_conversion<Float4_, uint32_t>(const uint32_t& a) {
180
- Float4_ res;
181
- res.x = vec_conversion<float2, uint16_t>((uint16_t)a);
182
- res.y = vec_conversion<float2, uint16_t>((uint16_t)(a >> 16U));
183
- return res;
184
- }
185
-
186
- // fp8x4 -> float4
187
- template <>
188
- __inline__ __device__ float4
189
- vec_conversion<float4, uint32_t>(const uint32_t& a) {
190
- Float4_ tmp = vec_conversion<Float4_, uint32_t>(a);
191
- float4 res = make_float4(tmp.x.x, tmp.x.y, tmp.y.x, tmp.y.y);
192
- return res;
193
- }
194
-
195
- // fp8x8 -> float8
196
- template <>
197
- __inline__ __device__ Float8_ vec_conversion<Float8_, uint2>(const uint2& a) {
198
- Float4_ tmp1, tmp2;
199
- tmp1 = vec_conversion<Float4_, uint32_t>(a.x);
200
- tmp2 = vec_conversion<Float4_, uint32_t>(a.y);
201
- Float8_ res;
202
- res.x = tmp1.x;
203
- res.y = tmp1.y;
204
- res.z = tmp2.x;
205
- res.w = tmp2.y;
206
- return res;
207
- }
208
-
209
- // half -> fp8
210
- template <>
211
- __inline__ __device__ uint8_t
212
- vec_conversion<uint8_t, uint16_t>(const uint16_t& a) {
213
- __half_raw tmp;
214
- tmp.x = a;
215
- return __hip_cvt_halfraw_to_fp8(tmp, fp8_type::__default_saturation,
216
- fp8_type::__default_interpret);
217
- }
218
-
219
- template <>
220
- __inline__ __device__ uint16_t
221
- vec_conversion<uint16_t, uint32_t>(const uint32_t& a) {
222
- union {
223
- uint32_t ui32;
224
- __half2_raw h2r;
225
- } tmp;
226
- tmp.ui32 = a;
227
- return __hip_cvt_halfraw2_to_fp8x2(tmp.h2r, fp8_type::__default_saturation,
228
- fp8_type::__default_interpret);
229
- }
230
-
231
- // bf16 -> fp8
232
- template <>
233
- __inline__ __device__ uint8_t
234
- vec_conversion<uint8_t, __nv_bfloat16>(const __nv_bfloat16& a) {
235
- return __hip_cvt_float_to_fp8(__bfloat162float(a),
236
- fp8_type::__default_saturation,
237
- fp8_type::__default_interpret);
238
- }
239
-
240
- // float -> fp8
241
- template <>
242
- __inline__ __device__ uint8_t vec_conversion<uint8_t, float>(const float& a) {
243
- return __hip_cvt_float_to_fp8(a, fp8_type::__default_saturation,
244
- fp8_type::__default_interpret);
245
- }
246
-
247
- // float2 -> half2
248
- template <>
249
- __inline__ __device__ uint32_t
250
- vec_conversion<uint32_t, float2>(const float2& a) {
251
- union {
252
- half2 float16;
253
- uint32_t uint32;
254
- };
255
-
256
- float16 = __float22half2_rn(a);
257
- return uint32;
258
- }
259
-
260
- // Float4 -> half2x2
261
- template <>
262
- __inline__ __device__ uint2 vec_conversion<uint2, Float4_>(const Float4_& a) {
263
- uint2 b;
264
- float2 val;
265
- val.x = a.x.x;
266
- val.y = a.x.y;
267
- b.x = vec_conversion<uint32_t, float2>(val);
268
-
269
- val.x = a.y.x;
270
- val.y = a.y.y;
271
- b.y = vec_conversion<uint32_t, float2>(val);
272
- return b;
273
- }
274
-
275
- // Float4 -> float4
276
- template <>
277
- __inline__ __device__ float4 vec_conversion<float4, Float4_>(const Float4_& a) {
278
- float4 b;
279
- b.x = a.x.x;
280
- b.y = a.x.y;
281
- b.z = a.y.x;
282
- b.w = a.y.y;
283
- return b;
284
- }
285
-
286
- // Float8 -> half2x4
287
- template <>
288
- __inline__ __device__ uint4 vec_conversion<uint4, Float8_>(const Float8_& a) {
289
- uint4 b;
290
- b.x = vec_conversion<uint32_t, float2>(a.x);
291
- b.y = vec_conversion<uint32_t, float2>(a.y);
292
- b.z = vec_conversion<uint32_t, float2>(a.z);
293
- b.w = vec_conversion<uint32_t, float2>(a.w);
294
- return b;
295
- }
296
-
297
- // float2 -> bfloat162
298
- template <>
299
- __inline__ __device__ __nv_bfloat162
300
- vec_conversion<__nv_bfloat162, float2>(const float2& a) {
301
- __nv_bfloat162 b = __float22bfloat162_rn(a);
302
- return b;
303
- }
304
-
305
- // Float4 -> bfloat162x2
306
- template <>
307
- __inline__ __device__ bf16_4_t
308
- vec_conversion<bf16_4_t, Float4_>(const Float4_& a) {
309
- bf16_4_t b;
310
- b.x = __float22bfloat162_rn(a.x);
311
- b.y = __float22bfloat162_rn(a.y);
312
- return b;
313
- }
314
-
315
- // Float8 -> bfloat162x4
316
- template <>
317
- __inline__ __device__ bf16_8_t
318
- vec_conversion<bf16_8_t, Float8_>(const Float8_& a) {
319
- bf16_8_t b;
320
- b.x = __float22bfloat162_rn(a.x);
321
- b.y = __float22bfloat162_rn(a.y);
322
- b.z = __float22bfloat162_rn(a.z);
323
- b.w = __float22bfloat162_rn(a.w);
324
- return b;
325
- }
326
-
327
- /* Scaled and vectorized conversions, for data exchange between high and low
328
- precision domains
329
-
330
- Convention of the scale in API, e.g: FP8_data = Quantization(
331
- High_Precision_data / scale ) s.t. Quantize(HP / scale) => FP8 Dequant(FP8) *
332
- scale => HP
333
-
334
- */
335
-
336
- using __nv_bfloat16 = __hip_bfloat16;
337
-
338
- // fp8 -> __nv_bfloat16
339
- template <>
340
- __inline__ __device__ __nv_bfloat16
341
- scaled_vec_conversion<__nv_bfloat16, uint8_t>(const uint8_t& a, float scale) {
342
- fp8_type f8;
343
- f8.__x = a;
344
- return __float2bfloat16(static_cast<float>(f8) * scale);
345
- }
346
-
347
- // fp8x2 -> __nv_bfloat162
348
- template <>
349
- __inline__ __device__ __nv_bfloat162
350
- scaled_vec_conversion<__nv_bfloat162, uint16_t>(const uint16_t& a,
351
- float scale) {
352
- __nv_bfloat162 res;
353
- res.x = scaled_vec_conversion<__nv_bfloat16, uint8_t>((uint8_t)a, scale);
354
- res.y =
355
- scaled_vec_conversion<__nv_bfloat16, uint8_t>((uint8_t)(a >> 8U), scale);
356
- return res;
357
- }
358
-
359
- // fp8x4 -> bf16_4_t
360
- template <>
361
- __inline__ __device__ bf16_4_t
362
- scaled_vec_conversion<bf16_4_t, uint32_t>(const uint32_t& a, float scale) {
363
- bf16_4_t res;
364
- res.x = scaled_vec_conversion<__nv_bfloat162, uint16_t>((uint16_t)a, scale);
365
- res.y = scaled_vec_conversion<__nv_bfloat162, uint16_t>((uint16_t)(a >> 16U),
366
- scale);
367
- return res;
368
- }
369
-
370
- // fp8x8 -> bf16_8_t
371
- template <>
372
- __inline__ __device__ bf16_8_t
373
- scaled_vec_conversion<bf16_8_t, uint2>(const uint2& a, float scale) {
374
- bf16_4_t tmp1, tmp2;
375
- tmp1 = scaled_vec_conversion<bf16_4_t, uint32_t>(a.x, scale);
376
- tmp2 = scaled_vec_conversion<bf16_4_t, uint32_t>(a.y, scale);
377
- bf16_8_t res;
378
- res.x = tmp1.x;
379
- res.y = tmp1.y;
380
- res.z = tmp2.x;
381
- res.w = tmp2.y;
382
- return res;
383
- }
384
-
385
- // fp8 -> float
386
- template <>
387
- __inline__ __device__ float scaled_vec_conversion<float, uint8_t>(
388
- const uint8_t& a, float scale) {
389
- fp8_type f8;
390
- f8.__x = a;
391
- return static_cast<float>(f8) * scale;
392
- }
393
-
394
- // fp8x2 -> float2
395
- template <>
396
- __inline__ __device__ float2
397
- scaled_vec_conversion<float2, uint16_t>(const uint16_t& a, float scale) {
398
- fp8x2_type f8x2;
399
- f8x2.__x = a;
400
- return static_cast<float2>(f8x2) * scale;
401
- }
402
-
403
- // fp8x4 -> float4
404
- template <>
405
- __inline__ __device__ Float4_
406
- scaled_vec_conversion<Float4_, uint32_t>(const uint32_t& a, const float scale) {
407
- Float4_ res;
408
- res.x = scaled_vec_conversion<float2, uint16_t>((uint16_t)a, scale);
409
- res.y = scaled_vec_conversion<float2, uint16_t>((uint16_t)(a >> 16U), scale);
410
- return res;
411
- }
412
-
413
- // fp8x4 -> float4
414
- template <>
415
- __inline__ __device__ float4
416
- scaled_vec_conversion<float4, uint32_t>(const uint32_t& a, float scale) {
417
- Float4_ res = scaled_vec_conversion<Float4_, uint32_t>(a, scale);
418
- return {res.x.x, res.x.y, res.y.x, res.y.y};
419
- }
420
-
421
- // fp8x8 -> float8
422
- template <>
423
- __inline__ __device__ Float8_
424
- scaled_vec_conversion<Float8_, uint2>(const uint2& a, float scale) {
425
- Float4_ tmp1, tmp2;
426
- tmp1 = scaled_vec_conversion<Float4_, uint32_t>(a.x, scale);
427
- tmp2 = scaled_vec_conversion<Float4_, uint32_t>(a.y, scale);
428
- Float8_ res;
429
- res.x = tmp1.x;
430
- res.y = tmp1.y;
431
- res.z = tmp2.x;
432
- res.w = tmp2.y;
433
- return res;
434
- }
435
-
436
- // fp8 -> half
437
- template <>
438
- __inline__ __device__ uint16_t
439
- scaled_vec_conversion<uint16_t, uint8_t>(const uint8_t& a, float scale) {
440
- __half_raw res;
441
- res.data = scaled_vec_conversion<float, uint8_t>(a, scale);
442
- return res.x;
443
- }
444
-
445
- // fp8x2 -> half2
446
- template <>
447
- __inline__ __device__ uint32_t
448
- scaled_vec_conversion<uint32_t, uint16_t>(const uint16_t& a, float scale) {
449
- union {
450
- __half2_raw h2r;
451
- uint32_t ui32;
452
- } tmp;
453
- tmp.h2r = __hip_cvt_fp8x2_to_halfraw2(a, fp8_type::__default_interpret);
454
- tmp.h2r.x.data *= scale;
455
- tmp.h2r.y.data *= scale;
456
- return tmp.ui32;
457
- }
458
-
459
- // fp8x4 -> half2x2
460
- template <>
461
- __inline__ __device__ uint2
462
- scaled_vec_conversion<uint2, uint32_t>(const uint32_t& a, float scale) {
463
- union {
464
- uint2 u32x2;
465
- uint32_t u32[2];
466
- } tmp;
467
- tmp.u32[0] = scaled_vec_conversion<uint32_t, uint16_t>((uint16_t)a, scale);
468
- tmp.u32[1] =
469
- scaled_vec_conversion<uint32_t, uint16_t>((uint16_t)(a >> 16U), scale);
470
- return tmp.u32x2;
471
- }
472
-
473
- // fp8x8 -> half2x4
474
- template <>
475
- __inline__ __device__ uint4 scaled_vec_conversion<uint4, uint2>(const uint2& a,
476
- float scale) {
477
- union {
478
- uint4 u64x2;
479
- uint2 u64[2];
480
- } tmp;
481
- tmp.u64[0] = scaled_vec_conversion<uint2, uint32_t>(a.x, scale);
482
- tmp.u64[1] = scaled_vec_conversion<uint2, uint32_t>(a.y, scale);
483
- return tmp.u64x2;
484
- }
485
-
486
- // half -> fp8
487
- template <>
488
- __inline__ __device__ uint8_t
489
- scaled_vec_conversion<uint8_t, uint16_t>(const uint16_t& a, float scale) {
490
- __half_raw tmp;
491
- tmp.x = a;
492
- tmp.data /= scale;
493
- return __hip_cvt_halfraw_to_fp8(tmp, fp8_type::__default_saturation,
494
- fp8_type::__default_interpret);
495
- }
496
-
497
- // halfx2 -> fp8x2
498
- template <>
499
- __inline__ __device__ uint16_t
500
- scaled_vec_conversion<uint16_t, uint32_t>(const uint32_t& a, float scale) {
501
- union {
502
- uint32_t ui32;
503
- __half2_raw h2r;
504
- } tmp;
505
- tmp.ui32 = a;
506
- tmp.h2r.x.data /= scale;
507
- tmp.h2r.y.data /= scale;
508
- return __hip_cvt_halfraw2_to_fp8x2(tmp.h2r, fp8_type::__default_saturation,
509
- fp8_type::__default_interpret);
510
- }
511
-
512
- // half2x2 -> fp8x4
513
- template <>
514
- __inline__ __device__ uint32_t
515
- scaled_vec_conversion<uint32_t, uint2>(const uint2& a, float scale) {
516
- union {
517
- uint16_t ui16[2];
518
- uint32_t ui32;
519
- } tmp;
520
- tmp.ui16[0] = scaled_vec_conversion<uint16_t, uint32_t>(a.x, scale);
521
- tmp.ui16[1] = scaled_vec_conversion<uint16_t, uint32_t>(a.y, scale);
522
- return tmp.ui32;
523
- }
524
-
525
- // half2x4 -> fp8x8
526
- template <>
527
- __inline__ __device__ uint2 scaled_vec_conversion<uint2, uint4>(const uint4& a,
528
- float scale) {
529
- union {
530
- uint2 ui2[2];
531
- uint4 ui4;
532
- } tmp;
533
- tmp.ui4 = a;
534
- uint2 res;
535
- res.x = scaled_vec_conversion<uint32_t, uint2>(tmp.ui2[0], scale);
536
- res.y = scaled_vec_conversion<uint32_t, uint2>(tmp.ui2[1], scale);
537
- return res;
538
- }
539
-
540
- // bf16 -> fp8
541
- template <>
542
- __inline__ __device__ uint8_t scaled_vec_conversion<uint8_t, __nv_bfloat16>(
543
- const __nv_bfloat16& a, float scale) {
544
- return __hip_cvt_float_to_fp8(__bfloat162float(a) / scale,
545
- fp8_type::__default_saturation,
546
- fp8_type::__default_interpret);
547
- }
548
-
549
- // bf16x2 -> fp8x2
550
- template <>
551
- __inline__ __device__ uint16_t scaled_vec_conversion<uint16_t, __nv_bfloat162>(
552
- const __nv_bfloat162& a, float scale) {
553
- union {
554
- uint8_t ui8[2];
555
- uint16_t ui16;
556
- } tmp;
557
- tmp.ui8[0] = scaled_vec_conversion<uint8_t, __nv_bfloat16>(a.x, scale);
558
- tmp.ui8[1] = scaled_vec_conversion<uint8_t, __nv_bfloat16>(a.y, scale);
559
- return tmp.ui16;
560
- }
561
-
562
- // bf16x4 -> fp8x4
563
- template <>
564
- __inline__ __device__ uint32_t
565
- scaled_vec_conversion<uint32_t, bf16_4_t>(const bf16_4_t& a, float scale) {
566
- union {
567
- uint16_t ui16[2];
568
- uint32_t ui32;
569
- } tmp;
570
- tmp.ui16[0] = scaled_vec_conversion<uint16_t, __nv_bfloat162>(a.x, scale);
571
- tmp.ui16[1] = scaled_vec_conversion<uint16_t, __nv_bfloat162>(a.y, scale);
572
- return tmp.ui32;
573
- }
574
-
575
- // bf16x8 -> fp8x8
576
- template <>
577
- __inline__ __device__ uint2
578
- scaled_vec_conversion<uint2, bf16_8_t>(const bf16_8_t& a, float scale) {
579
- uint2 res;
580
- res.x = scaled_vec_conversion<uint32_t, bf16_4_t>({a.x, a.y}, scale);
581
- res.y = scaled_vec_conversion<uint32_t, bf16_4_t>({a.z, a.w}, scale);
582
- return res;
583
- }
584
-
585
- // float -> fp8
586
- template <>
587
- __inline__ __device__ uint8_t
588
- scaled_vec_conversion<uint8_t, float>(const float& a, float scale) {
589
- return __hip_cvt_float_to_fp8(a / scale, fp8_type::__default_saturation,
590
- fp8_type::__default_interpret);
591
- }
592
-
593
- // floatx2 -> fp8x2
594
- template <>
595
- __inline__ __device__ uint16_t
596
- scaled_vec_conversion<uint16_t, float2>(const float2& a, float scale) {
597
- return __hip_cvt_float2_to_fp8x2(a / scale, fp8_type::__default_saturation,
598
- fp8_type::__default_interpret);
599
- }
600
-
601
- // floatx4 -> fp8x4
602
- template <>
603
- __inline__ __device__ uint32_t
604
- scaled_vec_conversion<uint32_t, float4>(const float4& a, float scale) {
605
- union {
606
- uint16_t ui16[2];
607
- uint32_t ui32;
608
- } tmp;
609
- tmp.ui16[0] = scaled_vec_conversion<uint16_t, float2>({a.x, a.y}, scale);
610
- tmp.ui16[1] = scaled_vec_conversion<uint16_t, float2>({a.z, a.w}, scale);
611
- return tmp.ui32;
612
- }
613
- #endif // ENABLE_FP8
614
-
615
- template <typename Tout, typename Tin, Fp8KVCacheDataType kv_dt>
616
- __inline__ __device__ Tout convert(const Tin& x) {
617
- #ifdef ENABLE_FP8
618
- if constexpr (kv_dt == Fp8KVCacheDataType::kFp8E4M3) {
619
- return vec_conversion<Tout, Tin>(x);
620
- }
621
- #endif
622
- assert(false);
623
- return {}; // Squash missing return statement warning
624
- }
625
-
626
- template <typename Tout, typename Tin, Fp8KVCacheDataType kv_dt>
627
- __inline__ __device__ Tout scaled_convert(const Tin& x, const float scale) {
628
- #ifdef ENABLE_FP8
629
- if constexpr (kv_dt == Fp8KVCacheDataType::kFp8E4M3) {
630
- return scaled_vec_conversion<Tout, Tin>(x, scale);
631
- }
632
- #endif
633
- assert(false);
634
- return {}; // Squash missing return statement warning
635
- }
636
-
637
- // The following macro is used to dispatch the conversion function based on
638
- // the data type of the key and value cache. The FN is a macro that calls a
639
- // function with template<typename scalar_t, typename cache_t,
640
- // Fp8KVCacheDataType kv_dt>.
641
- #define DISPATCH_BY_KV_CACHE_DTYPE(SRC_DTYPE, KV_DTYPE, FN) \
642
- if (KV_DTYPE == "auto") { \
643
- if (SRC_DTYPE == at::ScalarType::Float) { \
644
- FN(float, float, vllm::Fp8KVCacheDataType::kAuto); \
645
- } else if (SRC_DTYPE == at::ScalarType::Half) { \
646
- FN(uint16_t, uint16_t, vllm::Fp8KVCacheDataType::kAuto); \
647
- } else if (SRC_DTYPE == at::ScalarType::BFloat16) { \
648
- FN(__nv_bfloat16, __nv_bfloat16, vllm::Fp8KVCacheDataType::kAuto); \
649
- } else { \
650
- TORCH_CHECK(false, "Unsupported input type of kv cache: ", SRC_DTYPE); \
651
- } \
652
- } else { \
653
- if (KV_DTYPE == "fp8" || KV_DTYPE == "fp8_e4m3") { \
654
- if (SRC_DTYPE == at::ScalarType::Float) { \
655
- FN(float, uint8_t, vllm::Fp8KVCacheDataType::kFp8E4M3); \
656
- } else if (SRC_DTYPE == at::ScalarType::Half) { \
657
- FN(uint16_t, uint8_t, vllm::Fp8KVCacheDataType::kFp8E4M3); \
658
- } else if (SRC_DTYPE == at::ScalarType::BFloat16) { \
659
- FN(__nv_bfloat16, uint8_t, vllm::Fp8KVCacheDataType::kFp8E4M3); \
660
- } else { \
661
- TORCH_CHECK(false, \
662
- "Unsupported input type of kv cache: ", SRC_DTYPE); \
663
- } \
664
- } else { \
665
- TORCH_CHECK(false, "Unsupported data type of kv cache: ", KV_DTYPE); \
666
- } \
667
- }
668
-
669
- } // namespace fp8
670
- #endif // USE_ROCM
671
- } // namespace vllm
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
paged-attention/quantization/fp8/nvidia/quant_utils.cuh DELETED
@@ -1,573 +0,0 @@
1
- #pragma once
2
-
3
- #include "../../../attention/attention_dtypes.h"
4
- #include <assert.h>
5
- #include <float.h>
6
- #include <stdint.h>
7
- #include <type_traits>
8
-
9
- namespace vllm {
10
- #ifndef USE_ROCM
11
-
12
- namespace fp8 {
13
- #ifdef ENABLE_FP8
14
-
15
- #if 0 // Disable the following code to reduce the binary size.
16
- template <typename Tout, typename Tin>
17
- __inline__ __device__ Tout
18
- vec_conversion(const Tin &x, const __nv_fp8_interpretation_t fp8_type) {
19
- return x;
20
- }
21
-
22
- // fp8 -> half
23
- template <>
24
- __inline__ __device__ uint16_t vec_conversion<uint16_t, uint8_t>(
25
- const uint8_t &a, const __nv_fp8_interpretation_t fp8_type) {
26
- __half_raw res = __nv_cvt_fp8_to_halfraw(a, fp8_type);
27
- return res.x;
28
- }
29
-
30
- // fp8x2 -> half2
31
- template <>
32
- __inline__ __device__ uint32_t vec_conversion<uint32_t, uint16_t>(
33
- const uint16_t &a, const __nv_fp8_interpretation_t fp8_type) {
34
- union {
35
- uint16_t u16[2];
36
- uint32_t u32;
37
- } tmp;
38
- __half2_raw res = __nv_cvt_fp8x2_to_halfraw2(a, fp8_type);
39
- tmp.u16[0] = res.x;
40
- tmp.u16[1] = res.y;
41
- return tmp.u32;
42
- }
43
-
44
- // fp8x4 -> half2x2
45
- template <>
46
- __inline__ __device__ uint2 vec_conversion<uint2, uint32_t>(
47
- const uint32_t &a, const __nv_fp8_interpretation_t fp8_type) {
48
- union {
49
- uint2 u32x2;
50
- uint32_t u32[2];
51
- } tmp;
52
- tmp.u32[0] = vec_conversion<uint32_t, uint16_t>((uint16_t)a, fp8_type);
53
- tmp.u32[1] =
54
- vec_conversion<uint32_t, uint16_t>((uint16_t)(a >> 16U), fp8_type);
55
- return tmp.u32x2;
56
- }
57
-
58
- // fp8x8 -> half2x4
59
- template <>
60
- __inline__ __device__ uint4 vec_conversion<uint4, uint2>(
61
- const uint2 &a, const __nv_fp8_interpretation_t fp8_type) {
62
- union {
63
- uint4 u64x2;
64
- uint2 u64[2];
65
- } tmp;
66
- tmp.u64[0] = vec_conversion<uint2, uint32_t>(a.x, fp8_type);
67
- tmp.u64[1] = vec_conversion<uint2, uint32_t>(a.y, fp8_type);
68
- return tmp.u64x2;
69
- }
70
-
71
- // fp8 -> __nv_bfloat16
72
- template <>
73
- __inline__ __device__ __nv_bfloat16 vec_conversion<__nv_bfloat16, uint8_t>(
74
- const uint8_t &a, const __nv_fp8_interpretation_t fp8_type) {
75
- // Note there is no direct convert function from fp8 to bf16.
76
- // fp8 -> half
77
- __half_raw res = __nv_cvt_fp8_to_halfraw(a, fp8_type);
78
- // half -> float -> bf16
79
- float tmp = half_to_float(res.x);
80
- return __float2bfloat16(tmp);
81
- }
82
-
83
- // fp8x2 -> __nv_bfloat162
84
- template <>
85
- __inline__ __device__ __nv_bfloat162 vec_conversion<__nv_bfloat162, uint16_t>(
86
- const uint16_t &a, const __nv_fp8_interpretation_t fp8_type) {
87
- __nv_bfloat162 res;
88
- res.x = vec_conversion<__nv_bfloat16, uint8_t>((uint8_t)a, fp8_type);
89
- res.y = vec_conversion<__nv_bfloat16, uint8_t>((uint8_t)(a >> 8U), fp8_type);
90
- return res;
91
- }
92
-
93
- // fp8x4 -> bf16_4_t
94
- template <>
95
- __inline__ __device__ bf16_4_t vec_conversion<bf16_4_t, uint32_t>(
96
- const uint32_t &a, const __nv_fp8_interpretation_t fp8_type) {
97
- bf16_4_t res;
98
- res.x = vec_conversion<__nv_bfloat162, uint16_t>((uint16_t)a, fp8_type);
99
- res.y =
100
- vec_conversion<__nv_bfloat162, uint16_t>((uint16_t)(a >> 16U), fp8_type);
101
- return res;
102
- }
103
-
104
- // fp8x8 -> bf16_8_t
105
- template <>
106
- __inline__ __device__ bf16_8_t vec_conversion<bf16_8_t, uint2>(
107
- const uint2 &a, const __nv_fp8_interpretation_t fp8_type) {
108
- bf16_4_t tmp1, tmp2;
109
- tmp1 = vec_conversion<bf16_4_t, uint32_t>(a.x, fp8_type);
110
- tmp2 = vec_conversion<bf16_4_t, uint32_t>(a.y, fp8_type);
111
- bf16_8_t res;
112
- res.x = tmp1.x;
113
- res.y = tmp1.y;
114
- res.z = tmp2.x;
115
- res.w = tmp2.y;
116
- return res;
117
- }
118
-
119
- // fp8 -> float
120
- template <>
121
- __inline__ __device__ float
122
- vec_conversion<float, uint8_t>(const uint8_t &a,
123
- const __nv_fp8_interpretation_t fp8_type) {
124
- // fp8 -> half
125
- uint16_t tmp = vec_conversion<uint16_t, uint8_t>(a, fp8_type);
126
- // half -> float
127
- return half_to_float(tmp);
128
- }
129
-
130
- // fp8x2 -> float2
131
- template <>
132
- __inline__ __device__ float2 vec_conversion<float2, uint16_t>(
133
- const uint16_t &a, const __nv_fp8_interpretation_t fp8_type) {
134
- // fp8x2 -> half2
135
- uint32_t tmp = vec_conversion<uint32_t, uint16_t>(a, fp8_type);
136
- // half2 -> float2
137
- return half2_to_float2(tmp);
138
- }
139
-
140
- // fp8x4 -> float4
141
- template <>
142
- __inline__ __device__ Float4_ vec_conversion<Float4_, uint32_t>(
143
- const uint32_t &a, const __nv_fp8_interpretation_t fp8_type) {
144
- Float4_ res;
145
- res.x = vec_conversion<float2, uint16_t>((uint16_t)a, fp8_type);
146
- res.y = vec_conversion<float2, uint16_t>((uint16_t)(a >> 16U), fp8_type);
147
- return res;
148
- }
149
-
150
- // fp8x8 -> float8
151
- template <>
152
- __inline__ __device__ Float8_ vec_conversion<Float8_, uint2>(
153
- const uint2 &a, const __nv_fp8_interpretation_t fp8_type) {
154
- Float4_ tmp1, tmp2;
155
- tmp1 = vec_conversion<Float4_, uint32_t>(a.x, fp8_type);
156
- tmp2 = vec_conversion<Float4_, uint32_t>(a.y, fp8_type);
157
- Float8_ res;
158
- res.x = tmp1.x;
159
- res.y = tmp1.y;
160
- res.z = tmp2.x;
161
- res.w = tmp2.y;
162
- return res;
163
- }
164
-
165
- // half -> fp8
166
- template <>
167
- __inline__ __device__ uint8_t vec_conversion<uint8_t, uint16_t>(
168
- const uint16_t &a, const __nv_fp8_interpretation_t fp8_type) {
169
- __half_raw tmp;
170
- tmp.x = a;
171
- __nv_fp8_storage_t res =
172
- __nv_cvt_halfraw_to_fp8(tmp, __NV_SATFINITE, fp8_type);
173
- return (uint8_t)res;
174
- }
175
-
176
- // bf16 -> fp8
177
- template <>
178
- __inline__ __device__ uint8_t vec_conversion<uint8_t, __nv_bfloat16>(
179
- const __nv_bfloat16 &a, const __nv_fp8_interpretation_t fp8_type) {
180
- #if defined(__CUDA_ARCH__) && __CUDA_ARCH__ < 800
181
- assert(false);
182
- #else
183
- __nv_fp8_storage_t res = __nv_cvt_bfloat16raw_to_fp8(
184
- __nv_bfloat16_raw(a), __NV_SATFINITE, fp8_type);
185
- return (uint8_t)res;
186
- #endif
187
- }
188
-
189
- // float -> fp8
190
- template <>
191
- __inline__ __device__ uint8_t vec_conversion<uint8_t, float>(
192
- const float &a, const __nv_fp8_interpretation_t fp8_type) {
193
- __nv_fp8_storage_t res = __nv_cvt_float_to_fp8(a, __NV_SATFINITE, fp8_type);
194
- return (uint8_t)res;
195
- }
196
-
197
- // fp8x4 -> float4
198
- template <>
199
- __inline__ __device__ float4 vec_conversion<float4, uint32_t>(
200
- const uint32_t &a, const __nv_fp8_interpretation_t fp8_type) {
201
- Float4_ tmp = vec_conversion<Float4_, uint32_t>(a, fp8_type);
202
- float4 res = make_float4(tmp.x.x, tmp.x.y, tmp.y.x, tmp.y.y);
203
- return res;
204
- }
205
-
206
- template <>
207
- __inline__ __device__ uint32_t vec_conversion<uint32_t, float2>(
208
- const float2 &a, const __nv_fp8_interpretation_t fp8_type) {
209
- union {
210
- half2 float16;
211
- uint32_t uint32;
212
- };
213
-
214
- float16 = __float22half2_rn(a);
215
- return uint32;
216
- }
217
-
218
- template <>
219
- __inline__ __device__ uint2 vec_conversion<uint2, Float4_>(
220
- const Float4_ &a, const __nv_fp8_interpretation_t fp8_type) {
221
- uint2 b;
222
- float2 val;
223
- val.x = a.x.x;
224
- val.y = a.x.y;
225
- b.x = vec_conversion<uint32_t, float2>(val, fp8_type);
226
-
227
- val.x = a.y.x;
228
- val.y = a.y.y;
229
- b.y = vec_conversion<uint32_t, float2>(val, fp8_type);
230
-
231
- return b;
232
- }
233
-
234
- template <>
235
- __inline__ __device__ float4 vec_conversion<float4, Float4_>(
236
- const Float4_ &a, const __nv_fp8_interpretation_t fp8_type) {
237
- float4 b;
238
- b.x = a.x.x;
239
- b.y = a.x.y;
240
- b.z = a.y.x;
241
- b.w = a.y.y;
242
- return b;
243
- }
244
-
245
- template <>
246
- __inline__ __device__ uint4 vec_conversion<uint4, Float8_>(
247
- const Float8_ &a, const __nv_fp8_interpretation_t fp8_type) {
248
- uint4 b;
249
- b.x = vec_conversion<uint32_t, float2>(a.x, fp8_type);
250
- b.y = vec_conversion<uint32_t, float2>(a.y, fp8_type);
251
- b.z = vec_conversion<uint32_t, float2>(a.z, fp8_type);
252
- b.w = vec_conversion<uint32_t, float2>(a.w, fp8_type);
253
- return b;
254
- }
255
-
256
- template <>
257
- __inline__ __device__ __nv_bfloat162 vec_conversion<__nv_bfloat162, float2>(
258
- const float2 &a, const __nv_fp8_interpretation_t fp8_type) {
259
- __nv_bfloat162 b;
260
- from_float(b, a);
261
- return b;
262
- }
263
-
264
- template <>
265
- __inline__ __device__ bf16_4_t vec_conversion<bf16_4_t, Float4_>(
266
- const Float4_ &a, const __nv_fp8_interpretation_t fp8_type) {
267
- bf16_4_t b;
268
- from_float(b, a);
269
- return b;
270
- }
271
-
272
- template <>
273
- __inline__ __device__ bf16_8_t vec_conversion<bf16_8_t, Float8_>(
274
- const Float8_ &a, const __nv_fp8_interpretation_t fp8_type) {
275
- bf16_8_t b;
276
- from_float(b, a);
277
- return b;
278
- }
279
- #endif
280
-
281
- /* Scaled and vectorized conversions, for data exchange between high and low
282
- precision domains Convention of the scale in API, e.g: FP8_data =
283
- Quantization( High_Precision_data / scale ) s.t. Quantize(HP / scale) => FP8
284
- Dequant(FP8) * scale => HP
285
- */
286
-
287
- template <typename Tout, typename Tin>
288
- __inline__ __device__ Tout scaled_vec_conversion(
289
- const Tin& x, const float scale, const __nv_fp8_interpretation_t fp8_type) {
290
- return x;
291
- }
292
-
293
- // fp8 -> half
294
- template <>
295
- __inline__ __device__ uint16_t scaled_vec_conversion<uint16_t, uint8_t>(
296
- const uint8_t& a, const float scale,
297
- const __nv_fp8_interpretation_t fp8_type) {
298
- __half_raw tmp = __nv_cvt_fp8_to_halfraw(a, fp8_type);
299
- return float_to_half(half_to_float(tmp.x) * scale);
300
- }
301
-
302
- // fp8x2 -> half2
303
- template <>
304
- __inline__ __device__ uint32_t scaled_vec_conversion<uint32_t, uint16_t>(
305
- const uint16_t& a, const float scale,
306
- const __nv_fp8_interpretation_t fp8_type) {
307
- union {
308
- uint16_t u16[2];
309
- uint32_t u32;
310
- } tmp;
311
- __half2_raw res = __nv_cvt_fp8x2_to_halfraw2(a, fp8_type);
312
- tmp.u16[0] = float_to_half(half_to_float(res.x) * scale);
313
- tmp.u16[1] = float_to_half(half_to_float(res.y) * scale);
314
- return tmp.u32;
315
- }
316
-
317
- // fp8x4 -> half2x2
318
- template <>
319
- __inline__ __device__ uint2 scaled_vec_conversion<uint2, uint32_t>(
320
- const uint32_t& a, const float scale,
321
- const __nv_fp8_interpretation_t fp8_type) {
322
- union {
323
- uint2 u32x2;
324
- uint32_t u32[2];
325
- } tmp;
326
- tmp.u32[0] =
327
- scaled_vec_conversion<uint32_t, uint16_t>((uint16_t)a, scale, fp8_type);
328
- tmp.u32[1] = scaled_vec_conversion<uint32_t, uint16_t>((uint16_t)(a >> 16U),
329
- scale, fp8_type);
330
- return tmp.u32x2;
331
- }
332
-
333
- // fp8x8 -> half2x4
334
- template <>
335
- __inline__ __device__ uint4
336
- scaled_vec_conversion<uint4, uint2>(const uint2& a, const float scale,
337
- const __nv_fp8_interpretation_t fp8_type) {
338
- union {
339
- uint4 u64x2;
340
- uint2 u64[2];
341
- } tmp;
342
- tmp.u64[0] = scaled_vec_conversion<uint2, uint32_t>(a.x, scale, fp8_type);
343
- tmp.u64[1] = scaled_vec_conversion<uint2, uint32_t>(a.y, scale, fp8_type);
344
- return tmp.u64x2;
345
- }
346
-
347
- // fp8 -> __nv_bfloat16
348
- template <>
349
- __inline__ __device__ __nv_bfloat16
350
- scaled_vec_conversion<__nv_bfloat16, uint8_t>(
351
- const uint8_t& a, const float scale,
352
- const __nv_fp8_interpretation_t fp8_type) {
353
- // Note there is no direct convert function from fp8 to bf16.
354
- // fp8 -> half
355
- __half_raw res = __nv_cvt_fp8_to_halfraw(a, fp8_type);
356
- // half -> float -> bf16
357
- float tmp = half_to_float(res.x);
358
- return __float2bfloat16(tmp * scale);
359
- }
360
-
361
- // fp8x2 -> __nv_bfloat162
362
- template <>
363
- __inline__ __device__ __nv_bfloat162
364
- scaled_vec_conversion<__nv_bfloat162, uint16_t>(
365
- const uint16_t& a, const float scale,
366
- const __nv_fp8_interpretation_t fp8_type) {
367
- __nv_bfloat162 res;
368
- res.x = scaled_vec_conversion<__nv_bfloat16, uint8_t>((uint8_t)a, scale,
369
- fp8_type);
370
- res.y = scaled_vec_conversion<__nv_bfloat16, uint8_t>((uint8_t)(a >> 8U),
371
- scale, fp8_type);
372
- return res;
373
- }
374
-
375
- // fp8x4 -> bf16_4_t
376
- template <>
377
- __inline__ __device__ bf16_4_t scaled_vec_conversion<bf16_4_t, uint32_t>(
378
- const uint32_t& a, const float scale,
379
- const __nv_fp8_interpretation_t fp8_type) {
380
- bf16_4_t res;
381
- res.x = scaled_vec_conversion<__nv_bfloat162, uint16_t>((uint16_t)a, scale,
382
- fp8_type);
383
- res.y = scaled_vec_conversion<__nv_bfloat162, uint16_t>((uint16_t)(a >> 16U),
384
- scale, fp8_type);
385
- return res;
386
- }
387
-
388
- // fp8x8 -> bf16_8_t
389
- template <>
390
- __inline__ __device__ bf16_8_t scaled_vec_conversion<bf16_8_t, uint2>(
391
- const uint2& a, const float scale,
392
- const __nv_fp8_interpretation_t fp8_type) {
393
- bf16_4_t tmp1, tmp2;
394
- tmp1 = scaled_vec_conversion<bf16_4_t, uint32_t>(a.x, scale, fp8_type);
395
- tmp2 = scaled_vec_conversion<bf16_4_t, uint32_t>(a.y, scale, fp8_type);
396
- bf16_8_t res;
397
- res.x = tmp1.x;
398
- res.y = tmp1.y;
399
- res.z = tmp2.x;
400
- res.w = tmp2.y;
401
- return res;
402
- }
403
-
404
- // fp8 -> float
405
- template <>
406
- __inline__ __device__ float scaled_vec_conversion<float, uint8_t>(
407
- const uint8_t& a, const float scale,
408
- const __nv_fp8_interpretation_t fp8_type) {
409
- // fp8 -> half
410
- __half_raw res = __nv_cvt_fp8_to_halfraw(a, fp8_type);
411
- uint16_t tmp = res.x;
412
-
413
- // half -> float
414
- return half_to_float(tmp) * scale;
415
- }
416
-
417
- // fp8x2 -> float2
418
- template <>
419
- __inline__ __device__ float2 scaled_vec_conversion<float2, uint16_t>(
420
- const uint16_t& a, const float scale,
421
- const __nv_fp8_interpretation_t fp8_type) {
422
- // fp8x2 -> half2
423
- uint32_t tmp = scaled_vec_conversion<uint32_t, uint16_t>(a, scale, fp8_type);
424
- // half2 -> float2
425
- return half2_to_float2(tmp);
426
- }
427
-
428
- // fp8x4 -> float4
429
- template <>
430
- __inline__ __device__ Float4_ scaled_vec_conversion<Float4_, uint32_t>(
431
- const uint32_t& a, const float scale,
432
- const __nv_fp8_interpretation_t fp8_type) {
433
- Float4_ res;
434
- res.x = scaled_vec_conversion<float2, uint16_t>((uint16_t)a, scale, fp8_type);
435
- res.y = scaled_vec_conversion<float2, uint16_t>((uint16_t)(a >> 16U), scale,
436
- fp8_type);
437
- return res;
438
- }
439
-
440
- // fp8x8 -> float8
441
- template <>
442
- __inline__ __device__ Float8_ scaled_vec_conversion<Float8_, uint2>(
443
- const uint2& a, const float scale,
444
- const __nv_fp8_interpretation_t fp8_type) {
445
- Float4_ tmp1, tmp2;
446
- tmp1 = scaled_vec_conversion<Float4_, uint32_t>(a.x, scale, fp8_type);
447
- tmp2 = scaled_vec_conversion<Float4_, uint32_t>(a.y, scale, fp8_type);
448
- Float8_ res;
449
- res.x = tmp1.x;
450
- res.y = tmp1.y;
451
- res.z = tmp2.x;
452
- res.w = tmp2.y;
453
- return res;
454
- }
455
-
456
- // half -> fp8
457
- template <>
458
- __inline__ __device__ uint8_t scaled_vec_conversion<uint8_t, uint16_t>(
459
- const uint16_t& a, const float scale,
460
- const __nv_fp8_interpretation_t fp8_type) {
461
- __nv_fp8_storage_t res =
462
- __nv_cvt_float_to_fp8(half_to_float(a) / scale, __NV_SATFINITE, fp8_type);
463
- return (uint8_t)res;
464
- }
465
-
466
- // bf16 -> fp8
467
- template <>
468
- __inline__ __device__ uint8_t scaled_vec_conversion<uint8_t, __nv_bfloat16>(
469
- const __nv_bfloat16& a, const float scale,
470
- const __nv_fp8_interpretation_t fp8_type) {
471
- #if defined(__CUDA_ARCH__) && __CUDA_ARCH__ < 800
472
- assert(false);
473
- #else
474
- __nv_fp8_storage_t res = __nv_cvt_float_to_fp8(__bfloat162float(a) / scale,
475
- __NV_SATFINITE, fp8_type);
476
- return (uint8_t)res;
477
- #endif
478
- __builtin_unreachable(); // Suppress missing return statement warning
479
- }
480
-
481
- // float -> fp8
482
- template <>
483
- __inline__ __device__ uint8_t scaled_vec_conversion<uint8_t, float>(
484
- const float& a, const float scale,
485
- const __nv_fp8_interpretation_t fp8_type) {
486
- __nv_fp8_storage_t res =
487
- __nv_cvt_float_to_fp8(a / scale, __NV_SATFINITE, fp8_type);
488
- return (uint8_t)res;
489
- }
490
-
491
- // fp8x4 -> float4
492
- template <>
493
- __inline__ __device__ float4 scaled_vec_conversion<float4, uint32_t>(
494
- const uint32_t& a, const float scale,
495
- const __nv_fp8_interpretation_t fp8_type) {
496
- Float4_ tmp = scaled_vec_conversion<Float4_, uint32_t>(a, scale, fp8_type);
497
- float4 res = make_float4(tmp.x.x, tmp.x.y, tmp.y.x, tmp.y.y);
498
- return res;
499
- }
500
- #endif // ENABLE_FP8
501
-
502
- template <typename Tout, typename Tin, Fp8KVCacheDataType kv_dt>
503
- __inline__ __device__ Tout convert(const Tin& x) {
504
- #if 0 // Disable the following code to reduce the binary size.
505
- if constexpr (kv_dt == Fp8KVCacheDataType::kFp8E4M3) {
506
- return vec_conversion<Tout, Tin>(x, __NV_E4M3);
507
- } else if constexpr (kv_dt == Fp8KVCacheDataType::kFp8E5M2) {
508
- return vec_conversion<Tout, Tin>(x, __NV_E5M2);
509
- }
510
- #endif
511
- assert(false);
512
- __builtin_unreachable(); // Suppress missing return statement warning
513
- }
514
-
515
- template <typename Tout, typename Tin, Fp8KVCacheDataType kv_dt>
516
- __inline__ __device__ Tout scaled_convert(const Tin& x, const float scale) {
517
- #ifdef ENABLE_FP8
518
- if constexpr (kv_dt == Fp8KVCacheDataType::kFp8E4M3) {
519
- return scaled_vec_conversion<Tout, Tin>(x, scale, __NV_E4M3);
520
- } else if constexpr (kv_dt == Fp8KVCacheDataType::kFp8E5M2) {
521
- return scaled_vec_conversion<Tout, Tin>(x, scale, __NV_E5M2);
522
- }
523
- #endif
524
- assert(false);
525
- __builtin_unreachable(); // Suppress missing return statement warning
526
- }
527
-
528
- // The following macro is used to dispatch the conversion function based on
529
- // the data type of the key and value cache. The FN is a macro that calls a
530
- // function with template<typename scalar_t, typename cache_t,
531
- // Fp8KVCacheDataType kv_dt>.
532
- #define DISPATCH_BY_KV_CACHE_DTYPE(SRC_DTYPE, KV_DTYPE, FN) \
533
- if (KV_DTYPE == "auto") { \
534
- if (SRC_DTYPE == at::ScalarType::Float) { \
535
- FN(float, float, vllm::Fp8KVCacheDataType::kAuto); \
536
- } else if (SRC_DTYPE == at::ScalarType::Half) { \
537
- FN(uint16_t, uint16_t, vllm::Fp8KVCacheDataType::kAuto); \
538
- } else if (SRC_DTYPE == at::ScalarType::BFloat16) { \
539
- FN(__nv_bfloat16, __nv_bfloat16, vllm::Fp8KVCacheDataType::kAuto); \
540
- } else { \
541
- TORCH_CHECK(false, "Unsupported input type of kv cache: ", SRC_DTYPE); \
542
- } \
543
- } else { \
544
- if (KV_DTYPE == "fp8" || KV_DTYPE == "fp8_e4m3") { \
545
- if (SRC_DTYPE == at::ScalarType::Float) { \
546
- FN(float, uint8_t, vllm::Fp8KVCacheDataType::kFp8E4M3); \
547
- } else if (SRC_DTYPE == at::ScalarType::Half) { \
548
- FN(uint16_t, uint8_t, vllm::Fp8KVCacheDataType::kFp8E4M3); \
549
- } else if (SRC_DTYPE == at::ScalarType::BFloat16) { \
550
- FN(__nv_bfloat16, uint8_t, vllm::Fp8KVCacheDataType::kFp8E4M3); \
551
- } else { \
552
- TORCH_CHECK(false, \
553
- "Unsupported input type of kv cache: ", SRC_DTYPE); \
554
- } \
555
- } else if (KV_DTYPE == "fp8_e5m2") { \
556
- if (SRC_DTYPE == at::ScalarType::Float) { \
557
- FN(float, uint8_t, vllm::Fp8KVCacheDataType::kFp8E5M2); \
558
- } else if (SRC_DTYPE == at::ScalarType::Half) { \
559
- FN(uint16_t, uint8_t, vllm::Fp8KVCacheDataType::kFp8E5M2); \
560
- } else if (SRC_DTYPE == at::ScalarType::BFloat16) { \
561
- FN(__nv_bfloat16, uint8_t, vllm::Fp8KVCacheDataType::kFp8E5M2); \
562
- } else { \
563
- TORCH_CHECK(false, \
564
- "Unsupported input type of kv cache: ", SRC_DTYPE); \
565
- } \
566
- } else { \
567
- TORCH_CHECK(false, "Unsupported data type of kv cache: ", KV_DTYPE); \
568
- } \
569
- }
570
-
571
- } // namespace fp8
572
- #endif // not USE_ROCM
573
- } // namespace vllm
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
tests/kernels/__init__.py DELETED
File without changes
tests/kernels/allclose_default.py DELETED
@@ -1,14 +0,0 @@
1
- import torch
2
-
3
- # Reference default values of atol and rtol are from
4
- # https://github.com/pytorch/pytorch/blob/6d96beb6bec24d73ee3f080bac54d2104068f675/test/test_transformers.py#L67
5
- default_atol = {torch.float16: 1e-3, torch.bfloat16: 1e-3, torch.float: 1e-5}
6
- default_rtol = {torch.float16: 1e-3, torch.bfloat16: 1.6e-2, torch.float: 1.3e-6}
7
-
8
-
9
- def get_default_atol(output) -> float:
10
- return default_atol[output.dtype]
11
-
12
-
13
- def get_default_rtol(output) -> float:
14
- return default_rtol[output.dtype]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
tests/kernels/conftest.py DELETED
@@ -1,157 +0,0 @@
1
- from typing import List, Optional, Tuple, Union
2
-
3
- import paged_attention as ops
4
- import pytest
5
- import torch
6
-
7
-
8
- @pytest.fixture()
9
- def kv_cache_factory():
10
- return create_kv_caches_with_random
11
-
12
-
13
- @pytest.fixture()
14
- def kv_cache_factory_flashinfer():
15
- return create_kv_caches_with_random_flash
16
-
17
-
18
- STR_DTYPE_TO_TORCH_DTYPE = {
19
- "half": torch.half,
20
- "bfloat16": torch.bfloat16,
21
- "float": torch.float,
22
- "fp8": torch.uint8,
23
- "fp8_e4m3": torch.uint8,
24
- "fp8_e5m2": torch.uint8,
25
- }
26
-
27
-
28
- def create_kv_caches_with_random(
29
- num_blocks: int,
30
- block_size: int,
31
- num_layers: int,
32
- num_heads: int,
33
- head_size: int,
34
- cache_dtype: Optional[Union[str, torch.dtype]],
35
- model_dtype: Optional[Union[str, torch.dtype]] = None,
36
- seed: int = 0,
37
- device: Optional[str] = "cuda",
38
- ) -> Tuple[List[torch.Tensor], List[torch.Tensor]]:
39
- if cache_dtype == "fp8" and head_size % 16:
40
- raise ValueError(
41
- f"Does not support key cache of type fp8 with head_size {head_size}"
42
- )
43
- from paged_attention.platforms import current_platform
44
-
45
- current_platform.seed_everything(seed)
46
-
47
- torch_dtype = get_kv_cache_torch_dtype(cache_dtype, model_dtype)
48
-
49
- scale = head_size**-0.5
50
- x = 16 // torch.tensor([], dtype=torch_dtype).element_size()
51
- key_cache_shape = (num_blocks, num_heads, head_size // x, block_size, x)
52
- key_caches: List[torch.Tensor] = []
53
- for _ in range(num_layers):
54
- key_cache = torch.empty(size=key_cache_shape, dtype=torch_dtype, device=device)
55
- if cache_dtype in ["auto", "half", "bfloat16", "float"]:
56
- key_cache.uniform_(-scale, scale)
57
- elif cache_dtype == "fp8":
58
- _generate_random_fp8(key_cache, -scale, scale)
59
- else:
60
- raise ValueError(f"Does not support key cache of type {cache_dtype}")
61
- key_caches.append(key_cache)
62
-
63
- value_cache_shape = (num_blocks, num_heads, head_size, block_size)
64
- value_caches: List[torch.Tensor] = []
65
- for _ in range(num_layers):
66
- value_cache = torch.empty(
67
- size=value_cache_shape, dtype=torch_dtype, device=device
68
- )
69
- if cache_dtype in ["auto", "half", "bfloat16", "float"]:
70
- value_cache.uniform_(-scale, scale)
71
- elif cache_dtype == "fp8":
72
- _generate_random_fp8(value_cache, -scale, scale)
73
- else:
74
- raise ValueError(f"Does not support value cache of type {cache_dtype}")
75
- value_caches.append(value_cache)
76
- return key_caches, value_caches
77
-
78
-
79
- def create_kv_caches_with_random_flash(
80
- num_blocks: int,
81
- block_size: int,
82
- num_layers: int,
83
- num_heads: int,
84
- head_size: int,
85
- cache_dtype: Optional[Union[str, torch.dtype]],
86
- model_dtype: Optional[Union[str, torch.dtype]] = None,
87
- seed: int = 0,
88
- device: Optional[str] = "cuda",
89
- ) -> Tuple[List[torch.Tensor], List[torch.Tensor]]:
90
- from paged_attention.platforms import current_platform
91
-
92
- current_platform.seed_everything(seed)
93
-
94
- torch_dtype = get_kv_cache_torch_dtype(cache_dtype, model_dtype)
95
- key_value_cache_shape = (num_blocks, 2, block_size, num_heads, head_size)
96
- scale = head_size**-0.5
97
-
98
- key_caches: List[torch.Tensor] = []
99
- value_caches: List[torch.Tensor] = []
100
-
101
- for _ in range(num_layers):
102
- key_value_cache = torch.empty(
103
- size=key_value_cache_shape, dtype=torch_dtype, device=device
104
- )
105
- if cache_dtype in ["auto", "half", "bfloat16", "float"]:
106
- key_value_cache.uniform_(-scale, scale)
107
- elif cache_dtype == "fp8":
108
- _generate_random_fp8(key_value_cache, -scale, scale)
109
- else:
110
- raise ValueError(f"Does not support key cache of type {cache_dtype}")
111
- key_caches.append(key_value_cache[:, 0])
112
- value_caches.append(key_value_cache[:, 1])
113
- return key_caches, value_caches
114
-
115
-
116
- def get_kv_cache_torch_dtype(
117
- cache_dtype: Optional[Union[str, torch.dtype]],
118
- model_dtype: Optional[Union[str, torch.dtype]] = None,
119
- ) -> torch.dtype:
120
- if isinstance(cache_dtype, str):
121
- if cache_dtype == "auto":
122
- if isinstance(model_dtype, str):
123
- torch_dtype = STR_DTYPE_TO_TORCH_DTYPE[model_dtype]
124
- elif isinstance(model_dtype, torch.dtype):
125
- torch_dtype = model_dtype
126
- else:
127
- raise ValueError(f"Invalid model dtype: {model_dtype}")
128
- elif cache_dtype in ["half", "bfloat16", "float"]:
129
- torch_dtype = STR_DTYPE_TO_TORCH_DTYPE[cache_dtype]
130
- elif cache_dtype == "fp8":
131
- torch_dtype = torch.uint8
132
- else:
133
- raise ValueError(f"Invalid kv cache dtype: {cache_dtype}")
134
- elif isinstance(cache_dtype, torch.dtype):
135
- torch_dtype = cache_dtype
136
- else:
137
- raise ValueError(f"Invalid kv cache dtype: {cache_dtype}")
138
- return torch_dtype
139
-
140
-
141
- def _generate_random_fp8(
142
- tensor: torch.Tensor,
143
- low: float,
144
- high: float,
145
- ) -> None:
146
- # NOTE(zhaoyang): Due to NaN and Inf representation for fp8 data type,
147
- # it may occur Inf or NaN if we directly use torch.randint
148
- # to generate random data for fp8 data.
149
- # For example, s.11111.00 in fp8e5m2 format represents Inf.
150
- # | E4M3 | E5M2
151
- # -----|-------------|-------------------
152
- # Inf | N/A | s.11111.00
153
- # NaN | s.1111.111 | s.11111.{01,10,11}
154
- tensor_tmp = torch.empty_like(tensor, dtype=torch.float16)
155
- tensor_tmp.uniform_(low, high)
156
- ops.convert_fp8(tensor, tensor_tmp)
157
- del tensor_tmp
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
tests/kernels/test_attention.py DELETED
@@ -1,439 +0,0 @@
1
- import random
2
- from typing import List, Optional, Tuple
3
-
4
- import paged_attention as ops
5
- import pytest
6
- import torch
7
- from paged_attention.platforms import current_platform
8
-
9
- from .allclose_default import get_default_atol, get_default_rtol
10
- from .utils import get_max_shared_memory_bytes, opcheck
11
-
12
- FLOAT32_BYTES = torch.finfo(torch.float).bits // 8
13
- # This will change depending on the compute capability.
14
- # - 512 as a buffer
15
- MAX_SEQ_LEN = get_max_shared_memory_bytes() // FLOAT32_BYTES - 512
16
- # There may not be enough gpu memory due to large NUM_BLOCKS.
17
- # Reduce NUM_BLOCKS when it happens.
18
- NUM_BLOCKS = 4321 # Arbitrary values for testing
19
- PARTITION_SIZE = 512
20
- # flshattF and tritonflashattF supported: {torch.float16, torch.bfloat16}
21
- DTYPES = (
22
- [torch.half, torch.bfloat16, torch.float]
23
- if not current_platform.is_rocm()
24
- else [torch.half, torch.bfloat16]
25
- )
26
- NUM_GEN_SEQS = [7] # Arbitrary values for testing
27
- NUM_PREFILL_SEQS = [3] # Arbitrary values for testing
28
- NUM_HEADS = [(40, 40), (64, 8)] # Arbitrary values for testing
29
-
30
- # This should be sync with get_supported_head_sizes() in
31
- # vllm.attention.ops.paged_attn.PagedAttention
32
- HEAD_SIZES = [32, 64, 80, 96, 112, 120, 128, 192, 256]
33
-
34
- BLOCK_SIZES = [16, 32]
35
- USE_ALIBI = [False, True]
36
- if current_platform.is_mps():
37
- KV_CACHE_DTYPE = ["auto", "fp8"]
38
- else:
39
- KV_CACHE_DTYPE = ["auto", "fp8"]
40
- SEEDS = [0]
41
- if current_platform.is_mps():
42
- DEVICES = ["mps:0"]
43
- else:
44
- DEVICES = [f"cuda:{i}" for i in range(1 if torch.cuda.device_count() == 1 else 2)]
45
-
46
-
47
- def ref_masked_attention(
48
- query: torch.Tensor,
49
- key: torch.Tensor,
50
- value: torch.Tensor,
51
- scale: float,
52
- attn_mask: Optional[torch.Tensor] = None,
53
- ) -> torch.Tensor:
54
- attn_weights = scale * torch.einsum("qhd,khd->hqk", query, key).float()
55
- if attn_mask is not None:
56
- attn_weights = attn_weights + attn_mask.float()
57
- attn_weights = torch.softmax(attn_weights, dim=-1).to(value.dtype)
58
- out = torch.einsum("hqk,khd->qhd", attn_weights, value)
59
- return out
60
-
61
-
62
- def ref_single_query_cached_kv_attention(
63
- output: torch.Tensor,
64
- query: torch.Tensor,
65
- num_queries_per_kv: int,
66
- key_cache: torch.Tensor,
67
- value_cache: torch.Tensor,
68
- block_tables: torch.Tensor,
69
- seq_lens: torch.Tensor,
70
- scale: float,
71
- alibi_slopes: Optional[torch.Tensor],
72
- ) -> None:
73
- num_query_heads = query.shape[1]
74
- num_kv_heads = value_cache.shape[1]
75
- head_size = value_cache.shape[2]
76
- block_size = value_cache.shape[3]
77
- num_seqs = query.shape[0]
78
-
79
- block_tables_lst = block_tables.cpu().tolist()
80
- seq_lens_lst = seq_lens.cpu().tolist()
81
- for i in range(num_seqs):
82
- q = query[i].unsqueeze(0)
83
- block_table = block_tables_lst[i]
84
- seq_len = int(seq_lens_lst[i])
85
-
86
- keys_lst: List[torch.Tensor] = []
87
- values_lst: List[torch.Tensor] = []
88
- for j in range(seq_len):
89
- block_number = int(block_table[j // block_size])
90
- block_offset = j % block_size
91
-
92
- k = key_cache[block_number, :, :, block_offset, :]
93
- k = k.reshape(num_kv_heads, head_size)
94
- keys_lst.append(k)
95
-
96
- v = value_cache[block_number, :, :, block_offset]
97
- values_lst.append(v)
98
- keys = torch.stack(keys_lst, dim=0)
99
- values = torch.stack(values_lst, dim=0)
100
- if num_queries_per_kv > 1:
101
- # Handle MQA and GQA
102
- keys = torch.repeat_interleave(keys, num_queries_per_kv, dim=1)
103
- values = torch.repeat_interleave(values, num_queries_per_kv, dim=1)
104
-
105
- alibi_bias = None
106
- if alibi_slopes is not None:
107
- # Create the ALiBi bias used in the paged attention kernel.
108
- position_ids = torch.arange(seq_len).int()
109
- alibi_bias = (position_ids - seq_len + 1).float()
110
- alibi_bias = alibi_slopes.view(-1, 1, 1) * alibi_bias.view(1, 1, -1)
111
-
112
- out = ref_masked_attention(q, keys, values, scale, alibi_bias)
113
- out = out.view(num_query_heads, head_size)
114
- output[i].copy_(out, non_blocking=True)
115
-
116
-
117
- @pytest.mark.parametrize(
118
- "version", ["v1", "v2"] if not current_platform.is_rocm() else ["v1", "v2", "rocm"]
119
- )
120
- @pytest.mark.parametrize("num_seqs", NUM_GEN_SEQS)
121
- @pytest.mark.parametrize("num_heads", NUM_HEADS)
122
- @pytest.mark.parametrize("head_size", HEAD_SIZES)
123
- @pytest.mark.parametrize("use_alibi", USE_ALIBI)
124
- @pytest.mark.parametrize("block_size", BLOCK_SIZES)
125
- @pytest.mark.parametrize("dtype", DTYPES)
126
- @pytest.mark.parametrize("kv_cache_dtype", KV_CACHE_DTYPE)
127
- @pytest.mark.parametrize("seed", SEEDS)
128
- @pytest.mark.parametrize("device", DEVICES)
129
- def test_paged_attention(
130
- kv_cache_factory,
131
- version: str,
132
- num_seqs: int,
133
- num_heads: Tuple[int, int],
134
- head_size: int,
135
- use_alibi: bool,
136
- block_size: int,
137
- dtype: torch.dtype,
138
- kv_cache_dtype: str,
139
- seed: int,
140
- device: str,
141
- ) -> None:
142
- if (kv_cache_dtype == "fp8" and head_size % 16) or (
143
- version == "rocm" and head_size not in (64, 128)
144
- ):
145
- pytest.skip()
146
-
147
- current_platform.seed_everything(seed)
148
- torch.set_default_device(device)
149
- scale = float(1.0 / (head_size**0.5))
150
- num_query_heads, num_kv_heads = num_heads
151
- query = torch.empty(num_seqs, num_query_heads, head_size, dtype=dtype)
152
- query.uniform_(-scale, scale)
153
-
154
- assert num_query_heads % num_kv_heads == 0
155
- num_queries_per_kv = num_query_heads // num_kv_heads
156
- alibi_slopes = None
157
- if use_alibi:
158
- alibi_slopes = torch.randn(num_query_heads, dtype=torch.float)
159
-
160
- seq_lens = [random.randint(1, MAX_SEQ_LEN) for _ in range(num_seqs)]
161
- seq_lens[-1] = MAX_SEQ_LEN
162
- max_seq_len = max(seq_lens)
163
- seq_lens = torch.tensor(seq_lens, dtype=torch.int)
164
-
165
- # Create the block tables.
166
- max_num_blocks_per_seq = (max_seq_len + block_size - 1) // block_size
167
- block_tables_lst: List[List[int]] = []
168
- for _ in range(num_seqs):
169
- block_table = [
170
- random.randint(0, NUM_BLOCKS - 1) for _ in range(max_num_blocks_per_seq)
171
- ]
172
- block_tables_lst.append(block_table)
173
-
174
- block_tables = torch.tensor(block_tables_lst, dtype=torch.int)
175
-
176
- # Create the KV caches.
177
- key_caches, value_caches = kv_cache_factory(
178
- NUM_BLOCKS,
179
- block_size,
180
- 1,
181
- num_kv_heads,
182
- head_size,
183
- kv_cache_dtype,
184
- dtype,
185
- seed,
186
- device,
187
- )
188
- key_cache, value_cache = key_caches[0], value_caches[0]
189
-
190
- # Using default kv_scale
191
- k_scale = v_scale = torch.tensor(1.0, dtype=torch.float32, device=device)
192
-
193
- # Call the paged attention kernel.
194
- output = torch.empty_like(query)
195
- if version == "v1":
196
- ops.paged_attention_v1(
197
- output,
198
- query,
199
- key_cache,
200
- value_cache,
201
- num_kv_heads,
202
- scale,
203
- block_tables,
204
- seq_lens,
205
- block_size,
206
- max_seq_len,
207
- alibi_slopes,
208
- kv_cache_dtype,
209
- k_scale,
210
- v_scale,
211
- )
212
-
213
- opcheck(
214
- ops.ops.paged_attention_v1,
215
- (
216
- output,
217
- query,
218
- key_cache,
219
- value_cache,
220
- num_kv_heads,
221
- scale,
222
- block_tables,
223
- seq_lens,
224
- block_size,
225
- max_seq_len,
226
- alibi_slopes,
227
- kv_cache_dtype,
228
- k_scale,
229
- v_scale,
230
- 0,
231
- 0,
232
- 0,
233
- 64,
234
- 0,
235
- ),
236
- cond=(
237
- head_size == HEAD_SIZES[0]
238
- and block_size == BLOCK_SIZES[0]
239
- and not device.startswith("mps")
240
- ),
241
- )
242
-
243
- elif version in ("v2", "rocm"):
244
- num_partitions = (max_seq_len + PARTITION_SIZE - 1) // PARTITION_SIZE
245
- assert PARTITION_SIZE % block_size == 0
246
- num_seqs, num_heads, head_size = output.shape
247
- tmp_output = torch.empty(
248
- size=(num_seqs, num_heads, num_partitions, head_size),
249
- dtype=output.dtype,
250
- )
251
- exp_sums = torch.empty(
252
- size=(num_seqs, num_heads, num_partitions),
253
- dtype=torch.float32,
254
- )
255
- max_logits = torch.empty_like(exp_sums)
256
- if version == "v2":
257
- ops.paged_attention_v2(
258
- output,
259
- exp_sums,
260
- max_logits,
261
- tmp_output,
262
- query,
263
- key_cache,
264
- value_cache,
265
- num_kv_heads,
266
- scale,
267
- block_tables,
268
- seq_lens,
269
- block_size,
270
- max_seq_len,
271
- alibi_slopes,
272
- kv_cache_dtype,
273
- k_scale,
274
- v_scale,
275
- )
276
-
277
- opcheck(
278
- ops.ops.paged_attention_v2,
279
- (
280
- output,
281
- exp_sums,
282
- max_logits,
283
- tmp_output,
284
- query,
285
- key_cache,
286
- value_cache,
287
- num_kv_heads,
288
- scale,
289
- block_tables,
290
- seq_lens,
291
- block_size,
292
- max_seq_len,
293
- alibi_slopes,
294
- kv_cache_dtype,
295
- k_scale,
296
- v_scale,
297
- 0,
298
- 0,
299
- 0,
300
- 64,
301
- 0,
302
- ),
303
- cond=(
304
- head_size == HEAD_SIZES[0]
305
- and block_size == BLOCK_SIZES[0]
306
- and not device.startswith("mps")
307
- ),
308
- )
309
-
310
- else:
311
- ops.paged_attention_rocm(
312
- output,
313
- exp_sums,
314
- max_logits,
315
- tmp_output,
316
- query,
317
- key_cache,
318
- value_cache,
319
- num_kv_heads,
320
- scale,
321
- block_tables,
322
- seq_lens,
323
- block_size,
324
- max_seq_len,
325
- alibi_slopes,
326
- kv_cache_dtype,
327
- k_scale,
328
- v_scale,
329
- )
330
-
331
- opcheck(
332
- torch.ops._rocm_C.paged_attention,
333
- (
334
- output,
335
- exp_sums,
336
- max_logits,
337
- tmp_output,
338
- query,
339
- key_cache,
340
- value_cache,
341
- num_kv_heads,
342
- scale,
343
- block_tables,
344
- seq_lens,
345
- block_size,
346
- max_seq_len,
347
- alibi_slopes,
348
- kv_cache_dtype,
349
- k_scale,
350
- v_scale,
351
- ),
352
- cond=(
353
- head_size == HEAD_SIZES[0]
354
- and block_size == BLOCK_SIZES[0]
355
- and not device.startswith("mps")
356
- ),
357
- )
358
-
359
- else:
360
- raise AssertionError(f"Unknown version: {version}")
361
-
362
- # Run the reference implementation.
363
- if kv_cache_dtype == "fp8":
364
- # Convert cache data back to dtype.
365
- x = 16 // torch.tensor([], dtype=dtype).element_size()
366
- key_cache_shape = (NUM_BLOCKS, num_kv_heads, head_size // x, block_size, x)
367
- dequantized_key_cache = torch.empty(
368
- size=key_cache_shape, dtype=dtype, device=device
369
- )
370
- ops.convert_fp8(dequantized_key_cache, key_cache)
371
- key_cache = dequantized_key_cache
372
-
373
- value_cache_shape = value_cache.shape
374
- dequantized_value_cache = torch.empty(
375
- size=value_cache_shape, dtype=dtype, device=device
376
- )
377
- ops.convert_fp8(dequantized_value_cache, value_cache)
378
- value_cache = dequantized_value_cache
379
-
380
- ref_output = torch.empty_like(query)
381
- ref_single_query_cached_kv_attention(
382
- ref_output,
383
- query,
384
- num_queries_per_kv,
385
- key_cache,
386
- value_cache,
387
- block_tables,
388
- seq_lens,
389
- scale,
390
- alibi_slopes,
391
- )
392
-
393
- # NOTE(woosuk): Due to the kernel-level differences in the two
394
- # implementations, there is a small numerical difference in the two
395
- # outputs. Thus, we use a relaxed tolerance for the test.
396
- atol = get_default_atol(output) if current_platform.is_rocm() else 1e-3
397
- rtol = get_default_rtol(output) if current_platform.is_rocm() else 1e-5
398
-
399
- # NOTE(zhaoyang): FP8 KV Cache will introduce quantization error,
400
- # so we use a relaxed tolerance for the test.
401
- atol, rtol = 1e-3, 1e-5
402
- if kv_cache_dtype == "fp8":
403
- atol, rtol = 1e-2, 1e-5
404
- # NOTE: bfloat16 with ALiBi can have slightly higher precision differences
405
- elif dtype == torch.bfloat16 and use_alibi:
406
- atol, rtol = 2e-3, 1e-5
407
- torch.testing.assert_close(output, ref_output, atol=atol, rtol=rtol)
408
-
409
-
410
- def ref_multi_query_kv_attention(
411
- cu_seq_lens: List[int],
412
- query: torch.Tensor,
413
- key: torch.Tensor,
414
- value: torch.Tensor,
415
- scale: float,
416
- dtype: torch.dtype,
417
- ) -> torch.Tensor:
418
- num_seqs = len(cu_seq_lens) - 1
419
- ref_outputs: List[torch.Tensor] = []
420
- for i in range(num_seqs):
421
- start_idx = cu_seq_lens[i]
422
- end_idx = cu_seq_lens[i + 1]
423
- seq_len = end_idx - start_idx
424
-
425
- # Create attention mask.
426
- attn_mask = torch.triu(torch.ones(seq_len, seq_len, dtype=dtype), diagonal=1)
427
- attn_mask = attn_mask * torch.finfo(dtype).min
428
- attn_mask = attn_mask.to(dtype=dtype)
429
-
430
- ref_output = ref_masked_attention(
431
- query[start_idx:end_idx],
432
- key[start_idx:end_idx],
433
- value[start_idx:end_idx],
434
- scale,
435
- attn_mask=attn_mask,
436
- )
437
- ref_outputs.append(ref_output)
438
-
439
- return torch.cat(ref_outputs, dim=0)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
tests/kernels/test_cache.py DELETED
@@ -1,500 +0,0 @@
1
- import random
2
- from typing import List, Tuple
3
-
4
- import paged_attention as ops
5
- import pytest
6
- import torch
7
- from paged_attention.platforms import current_platform
8
-
9
- from .utils import DEFAULT_OPCHECK_TEST_UTILS, opcheck
10
-
11
- COPYING_DIRECTION = [("gpu", "cpu"), ("gpu", "gpu"), ("cpu", "gpu")]
12
- DTYPES = [torch.half, torch.bfloat16, torch.float]
13
- NUM_TOKENS = [42] # Arbitrary values for testing
14
- NUM_LAYERS = [1] # Arbitrary values for testing
15
- NUM_HEADS = [8] # Arbitrary values for testing
16
- HEAD_SIZES = [64, 80, 120, 256]
17
- BLOCK_SIZES = [8, 16, 32]
18
-
19
- # Arbitrary values for testing
20
- # don't make it too large. e.g. [1024, 36000] will OOM
21
- NUM_BLOCKS = [1024, 10000]
22
-
23
- NUM_MAPPINGS = [256] # Arbitrary values for testing
24
- SEEDS = [0]
25
- if current_platform.is_mps():
26
- DEVICES = ["mps:0"]
27
- else:
28
- DEVICES = [f"cuda:{i}" for i in range(1 if torch.cuda.device_count() == 1 else 2)]
29
-
30
- if current_platform.is_mps():
31
- KV_CACHE_DTYPE = ["auto", "fp8"]
32
- else:
33
- KV_CACHE_DTYPE = ["auto", "fp8"]
34
-
35
-
36
- @pytest.mark.parametrize("num_mappings", NUM_MAPPINGS)
37
- @pytest.mark.parametrize("num_layers", NUM_LAYERS)
38
- @pytest.mark.parametrize("num_heads", NUM_HEADS)
39
- @pytest.mark.parametrize("head_size", HEAD_SIZES)
40
- @pytest.mark.parametrize("block_size", BLOCK_SIZES)
41
- @pytest.mark.parametrize("num_blocks", NUM_BLOCKS)
42
- @pytest.mark.parametrize("dtype", DTYPES)
43
- @pytest.mark.parametrize("seed", SEEDS)
44
- @pytest.mark.parametrize("device", DEVICES)
45
- @pytest.mark.parametrize("kv_cache_dtype", KV_CACHE_DTYPE)
46
- @torch.inference_mode()
47
- def test_copy_blocks(
48
- kv_cache_factory,
49
- num_mappings: int,
50
- num_layers: int,
51
- num_heads: int,
52
- head_size: int,
53
- block_size: int,
54
- num_blocks: int,
55
- dtype: torch.dtype,
56
- seed: int,
57
- kv_cache_dtype: str,
58
- device: str,
59
- ) -> None:
60
- if kv_cache_dtype == "fp8" and head_size % 16:
61
- pytest.skip()
62
- current_platform.seed_everything(seed)
63
- # Don't set MPS as default device to avoid placeholder storage error
64
- if not device.startswith("mps"):
65
- torch.set_default_device(device)
66
- # Generate random block mappings where each source block is mapped to two
67
- # destination blocks.
68
- assert 2 * num_mappings <= num_blocks
69
- src_blocks = random.sample(range(num_blocks), num_mappings)
70
- remainig_blocks = list(set(range(num_blocks)) - set(src_blocks))
71
- dst_blocks = random.sample(remainig_blocks, 2 * num_mappings)
72
- block_mapping: List[Tuple[int, int]] = []
73
- for i in range(num_mappings):
74
- src = src_blocks[i]
75
- dst1 = dst_blocks[2 * i]
76
- dst2 = dst_blocks[2 * i + 1]
77
- block_mapping.append((src, dst1))
78
- block_mapping.append((src, dst2))
79
-
80
- # Create the KV caches.
81
- key_caches, value_caches = kv_cache_factory(
82
- num_blocks,
83
- block_size,
84
- num_layers,
85
- num_heads,
86
- head_size,
87
- kv_cache_dtype,
88
- dtype,
89
- seed,
90
- device,
91
- )
92
-
93
- # Clone the KV caches.
94
- cloned_key_caches = [key_cache.clone() for key_cache in key_caches]
95
- cloned_value_caches = [value_cache.clone() for value_cache in value_caches]
96
-
97
- # Call the copy blocks kernel.
98
- block_mapping_tensor = torch.tensor(
99
- block_mapping, dtype=torch.int64, device=device
100
- ).view(-1, 2)
101
-
102
- opcheck(
103
- ops.ops.copy_blocks,
104
- (key_caches, value_caches, block_mapping_tensor),
105
- test_utils=DEFAULT_OPCHECK_TEST_UTILS,
106
- cond=(head_size == HEAD_SIZES[0]),
107
- )
108
- ops.copy_blocks(key_caches, value_caches, block_mapping_tensor)
109
-
110
- # Run the reference implementation.
111
- for src, dst in block_mapping:
112
- for cloned_key_cache in cloned_key_caches:
113
- cloned_key_cache[dst].copy_(cloned_key_cache[src])
114
- for cloned_value_cache in cloned_value_caches:
115
- cloned_value_cache[dst].copy_(cloned_value_cache[src])
116
-
117
- # Compare the results.
118
- for key_cache, cloned_key_cache in zip(key_caches, cloned_key_caches):
119
- torch.testing.assert_close(key_cache, cloned_key_cache)
120
- for value_cache, cloned_value_cache in zip(value_caches, cloned_value_caches):
121
- torch.testing.assert_close(value_cache, cloned_value_cache)
122
-
123
-
124
- @pytest.mark.parametrize("num_tokens", NUM_TOKENS)
125
- @pytest.mark.parametrize("num_heads", NUM_HEADS)
126
- @pytest.mark.parametrize("head_size", HEAD_SIZES)
127
- @pytest.mark.parametrize("block_size", BLOCK_SIZES)
128
- @pytest.mark.parametrize("num_blocks", NUM_BLOCKS)
129
- @pytest.mark.parametrize("dtype", DTYPES)
130
- @pytest.mark.parametrize("seed", SEEDS)
131
- @pytest.mark.parametrize("device", DEVICES)
132
- @pytest.mark.parametrize("kv_cache_dtype", KV_CACHE_DTYPE)
133
- @torch.inference_mode()
134
- def test_reshape_and_cache(
135
- kv_cache_factory,
136
- num_tokens: int,
137
- num_heads: int,
138
- head_size: int,
139
- block_size: int,
140
- num_blocks: int,
141
- dtype: torch.dtype,
142
- seed: int,
143
- device: str,
144
- kv_cache_dtype: str,
145
- ) -> None:
146
- if kv_cache_dtype == "fp8" and head_size % 16:
147
- pytest.skip()
148
- current_platform.seed_everything(seed)
149
- # Don't set MPS as default device to avoid placeholder storage error
150
- if not device.startswith("mps"):
151
- torch.set_default_device(device)
152
- # Create a random slot mapping.
153
- num_slots = block_size * num_blocks
154
- slot_mapping_lst = random.sample(range(num_slots), num_tokens)
155
- slot_mapping = torch.tensor(slot_mapping_lst, dtype=torch.long, device=device)
156
-
157
- qkv = torch.randn(num_tokens, 3, num_heads, head_size, dtype=dtype, device=device)
158
- _, key, value = qkv.unbind(dim=1)
159
-
160
- # Create the KV caches.
161
- key_caches, value_caches = kv_cache_factory(
162
- num_blocks,
163
- block_size,
164
- 1,
165
- num_heads,
166
- head_size,
167
- kv_cache_dtype,
168
- dtype,
169
- seed,
170
- device,
171
- )
172
- key_cache, value_cache = key_caches[0], value_caches[0]
173
-
174
- # Clone the KV caches.
175
- if kv_cache_dtype == "fp8":
176
- cloned_key_cache = torch.empty_like(key_cache, dtype=torch.float16)
177
- ops.convert_fp8(cloned_key_cache, key_cache)
178
- cloned_value_cache = torch.empty_like(value_cache, dtype=torch.float16)
179
- ops.convert_fp8(cloned_value_cache, value_cache)
180
- else:
181
- cloned_key_cache = key_cache.clone()
182
- cloned_value_cache = value_cache.clone()
183
-
184
- # Using default kv_scale
185
- k_scale = v_scale = torch.tensor(1.0, dtype=torch.float32, device=device)
186
-
187
- # Call the reshape_and_cache kernel.
188
- opcheck(
189
- ops.ops.reshape_and_cache,
190
- (
191
- key,
192
- value,
193
- key_cache,
194
- value_cache,
195
- slot_mapping,
196
- kv_cache_dtype,
197
- k_scale,
198
- v_scale,
199
- ),
200
- cond=(head_size == HEAD_SIZES[0]),
201
- )
202
- ops.reshape_and_cache(
203
- key,
204
- value,
205
- key_cache,
206
- value_cache,
207
- slot_mapping,
208
- kv_cache_dtype,
209
- k_scale,
210
- v_scale,
211
- )
212
-
213
- if kv_cache_dtype == "fp8":
214
- result_key_cache = torch.empty_like(key_cache, dtype=torch.float16)
215
- ops.convert_fp8(result_key_cache, key_cache)
216
- result_value_cache = torch.empty_like(value_cache, dtype=torch.float16)
217
- ops.convert_fp8(result_value_cache, value_cache)
218
-
219
- # Run the reference implementation.
220
- reshaped_key = key.reshape(num_tokens, *key_cache[0, :, :, 0, :].shape)
221
- block_indicies = torch.div(slot_mapping, block_size, rounding_mode="floor")
222
- block_indicies_lst = block_indicies.cpu().tolist()
223
- block_offsets = slot_mapping % block_size
224
- block_offsets_lst = block_offsets.cpu().tolist()
225
- for i in range(num_tokens):
226
- block_idx = block_indicies_lst[i]
227
- block_offset = block_offsets_lst[i]
228
- cloned_key_cache[block_idx, :, :, block_offset, :] = reshaped_key[i]
229
- cloned_value_cache[block_idx, :, :, block_offset] = value[i]
230
-
231
- if kv_cache_dtype == "fp8":
232
- torch.testing.assert_close(
233
- result_key_cache, cloned_key_cache, atol=0.02, rtol=0.2
234
- )
235
- torch.testing.assert_close(
236
- result_value_cache, cloned_value_cache, atol=0.02, rtol=0.2
237
- )
238
- else:
239
- torch.testing.assert_close(key_cache, cloned_key_cache)
240
- torch.testing.assert_close(value_cache, cloned_value_cache)
241
-
242
-
243
- @pytest.mark.parametrize("num_tokens", NUM_TOKENS)
244
- @pytest.mark.parametrize("num_heads", NUM_HEADS)
245
- @pytest.mark.parametrize("head_size", HEAD_SIZES)
246
- @pytest.mark.parametrize("block_size", BLOCK_SIZES)
247
- @pytest.mark.parametrize("num_blocks", NUM_BLOCKS)
248
- @pytest.mark.parametrize("dtype", DTYPES)
249
- @pytest.mark.parametrize("seed", SEEDS)
250
- @pytest.mark.parametrize("device", DEVICES)
251
- @pytest.mark.parametrize("kv_cache_dtype", KV_CACHE_DTYPE)
252
- @torch.inference_mode()
253
- def test_reshape_and_cache_flash(
254
- kv_cache_factory_flashinfer,
255
- num_tokens: int,
256
- num_heads: int,
257
- head_size: int,
258
- block_size: int,
259
- num_blocks: int,
260
- dtype: torch.dtype,
261
- seed: int,
262
- device: str,
263
- kv_cache_dtype: str,
264
- ) -> None:
265
- # Flash variant doesn't support FP8 on MPS devices yet
266
- if current_platform.is_mps() and kv_cache_dtype == "fp8":
267
- pytest.skip("reshape_and_cache_flash doesn't support FP8 on MPS")
268
- current_platform.seed_everything(seed)
269
- # Don't set MPS as default device to avoid placeholder storage error
270
- if not device.startswith("mps"):
271
- torch.set_default_device(device)
272
-
273
- # Create a random slot mapping.
274
- num_slots = block_size * num_blocks
275
- slot_mapping_lst = random.sample(range(num_slots), num_tokens)
276
- slot_mapping = torch.tensor(slot_mapping_lst, dtype=torch.long, device=device)
277
-
278
- qkv = torch.randn(num_tokens, 3, num_heads, head_size, dtype=dtype, device=device)
279
- _, key, value = qkv.unbind(dim=1)
280
-
281
- # Create the KV caches.
282
- key_caches, value_caches = kv_cache_factory_flashinfer(
283
- num_blocks,
284
- block_size,
285
- 1,
286
- num_heads,
287
- head_size,
288
- kv_cache_dtype,
289
- dtype,
290
- device=device,
291
- )
292
- key_cache, value_cache = key_caches[0].contiguous(), value_caches[0].contiguous()
293
- del key_caches
294
- del value_caches
295
-
296
- k_scale = (key.amax() / 256.0).to(torch.float32)
297
- v_scale = (value.amax() / 256.0).to(torch.float32)
298
-
299
- # Clone the KV caches.
300
- if kv_cache_dtype == "fp8":
301
- cloned_key_cache = torch.empty_like(key_cache, dtype=torch.float16)
302
- ops.convert_fp8(cloned_key_cache, key_cache, k_scale, kv_cache_dtype)
303
- cloned_value_cache = torch.empty_like(value_cache, dtype=torch.float16)
304
- ops.convert_fp8(cloned_value_cache, value_cache, v_scale, kv_cache_dtype)
305
- else:
306
- cloned_key_cache = key_cache.clone()
307
- cloned_value_cache = value_cache.clone()
308
-
309
- # Call the reshape_and_cache kernel.
310
- opcheck(
311
- ops.ops.reshape_and_cache_flash,
312
- (
313
- key,
314
- value,
315
- key_cache,
316
- value_cache,
317
- slot_mapping,
318
- kv_cache_dtype,
319
- k_scale,
320
- v_scale,
321
- ),
322
- cond=(head_size == HEAD_SIZES[0]),
323
- )
324
- ops.reshape_and_cache_flash(
325
- key,
326
- value,
327
- key_cache,
328
- value_cache,
329
- slot_mapping,
330
- kv_cache_dtype,
331
- k_scale,
332
- v_scale,
333
- )
334
-
335
- if kv_cache_dtype == "fp8":
336
- result_key_cache = torch.empty_like(key_cache, dtype=torch.float16)
337
- ops.convert_fp8(
338
- result_key_cache, key_cache, k_scale.item(), kv_dtype=kv_cache_dtype
339
- )
340
- result_value_cache = torch.empty_like(value_cache, dtype=torch.float16)
341
- ops.convert_fp8(
342
- result_value_cache, value_cache, v_scale.item(), kv_dtype=kv_cache_dtype
343
- )
344
-
345
- # Run the reference implementation.
346
- block_indicies = torch.div(slot_mapping, block_size, rounding_mode="floor")
347
- block_indicies_lst = block_indicies.cpu().tolist()
348
- block_offsets = slot_mapping % block_size
349
- block_offsets_lst = block_offsets.cpu().tolist()
350
- for i in range(num_tokens):
351
- block_idx = block_indicies_lst[i]
352
- block_offset = block_offsets_lst[i]
353
- cloned_key_cache[block_idx, block_offset, :, :] = key[i]
354
- cloned_value_cache[block_idx, block_offset, :, :] = value[i]
355
-
356
- if kv_cache_dtype == "fp8":
357
- torch.testing.assert_close(
358
- result_key_cache, cloned_key_cache, atol=0.02, rtol=0.2
359
- )
360
- torch.testing.assert_close(
361
- result_value_cache, cloned_value_cache, atol=0.02, rtol=0.2
362
- )
363
- else:
364
- torch.testing.assert_close(key_cache, cloned_key_cache)
365
- torch.testing.assert_close(value_cache, cloned_value_cache)
366
-
367
-
368
- @pytest.mark.parametrize("direction", COPYING_DIRECTION)
369
- @pytest.mark.parametrize("num_mappings", NUM_MAPPINGS)
370
- @pytest.mark.parametrize("num_heads", NUM_HEADS)
371
- @pytest.mark.parametrize("head_size", HEAD_SIZES)
372
- @pytest.mark.parametrize("block_size", BLOCK_SIZES)
373
- @pytest.mark.parametrize("num_blocks", NUM_BLOCKS)
374
- @pytest.mark.parametrize("dtype", DTYPES)
375
- @pytest.mark.parametrize("seed", SEEDS)
376
- @pytest.mark.parametrize("device", DEVICES)
377
- @pytest.mark.parametrize("kv_cache_dtype", KV_CACHE_DTYPE)
378
- @torch.inference_mode()
379
- def test_swap_blocks(
380
- kv_cache_factory,
381
- direction: Tuple[str, str],
382
- num_mappings: int,
383
- num_heads: int,
384
- head_size: int,
385
- block_size: int,
386
- num_blocks: int,
387
- dtype: torch.dtype,
388
- seed: int,
389
- device: str,
390
- kv_cache_dtype: str,
391
- ) -> None:
392
- if kv_cache_dtype == "fp8" and "cpu" in direction:
393
- pytest.skip()
394
- if kv_cache_dtype == "fp8" and head_size % 16:
395
- pytest.skip()
396
-
397
- current_platform.seed_everything(seed)
398
-
399
- src_device = device if direction[0] == "gpu" else "cpu"
400
- dst_device = device if direction[1] == "gpu" else "cpu"
401
-
402
- src_blocks = random.sample(range(num_blocks), num_mappings)
403
- # For the same device, mapping must not overlap
404
- if src_device == dst_device:
405
- remaining_blocks = list(set(range(num_blocks)) - set(src_blocks))
406
- dst_blocks = random.sample(remaining_blocks, num_mappings)
407
- else:
408
- dst_blocks = random.sample(range(num_blocks), num_mappings)
409
-
410
- block_mapping = list(zip(src_blocks, dst_blocks))
411
- block_mapping_tensor = torch.tensor(
412
- block_mapping, dtype=torch.int64, device="cpu"
413
- ).view(-1, 2)
414
-
415
- # Create the KV caches on the first device.
416
- src_key_caches, src_value_caches = kv_cache_factory(
417
- num_blocks,
418
- block_size,
419
- 1,
420
- num_heads,
421
- head_size,
422
- kv_cache_dtype,
423
- dtype,
424
- seed,
425
- src_device,
426
- )
427
-
428
- # Create the KV caches on the second device.
429
- dist_key_caches, dist_value_caches = kv_cache_factory(
430
- num_blocks,
431
- block_size,
432
- 1,
433
- num_heads,
434
- head_size,
435
- kv_cache_dtype,
436
- dtype,
437
- seed,
438
- dst_device,
439
- )
440
-
441
- src_key_caches_clone = src_key_caches[0].clone()
442
- src_value_caches_clone = src_value_caches[0].clone()
443
-
444
- # Call the swap_blocks kernel.
445
- do_opcheck = head_size == HEAD_SIZES[0]
446
- opcheck(
447
- ops.ops.swap_blocks,
448
- (src_key_caches[0], dist_key_caches[0], block_mapping_tensor),
449
- cond=do_opcheck,
450
- )
451
- opcheck(
452
- ops.ops.swap_blocks,
453
- (src_value_caches[0], dist_value_caches[0], block_mapping_tensor),
454
- cond=do_opcheck,
455
- )
456
-
457
- ops.swap_blocks(src_key_caches[0], dist_key_caches[0], block_mapping_tensor)
458
- ops.swap_blocks(src_value_caches[0], dist_value_caches[0], block_mapping_tensor)
459
-
460
- for src, dst in block_mapping:
461
- torch.testing.assert_close(
462
- src_key_caches_clone[src].cpu(), dist_key_caches[0][dst].cpu()
463
- )
464
- torch.testing.assert_close(
465
- src_value_caches_clone[src].cpu(), dist_value_caches[0][dst].cpu()
466
- )
467
-
468
-
469
- @pytest.mark.parametrize("num_heads", NUM_HEADS)
470
- @pytest.mark.parametrize("head_size", HEAD_SIZES)
471
- @pytest.mark.parametrize("block_size", BLOCK_SIZES)
472
- @pytest.mark.parametrize("num_blocks", NUM_BLOCKS)
473
- @pytest.mark.parametrize("dtype", DTYPES)
474
- @pytest.mark.parametrize("seed", SEEDS)
475
- @pytest.mark.parametrize("device", DEVICES)
476
- @torch.inference_mode()
477
- def test_fp8_e4m3_conversion(
478
- num_heads: int,
479
- head_size: int,
480
- block_size: int,
481
- num_blocks: int,
482
- dtype: torch.dtype,
483
- seed: int,
484
- device: str,
485
- ) -> None:
486
- current_platform.seed_everything(seed)
487
-
488
- low = -224.0
489
- high = 224.0
490
- shape = (num_blocks, num_heads, head_size, block_size)
491
- cache = torch.empty(shape, dtype=dtype, device=device)
492
- cache.uniform_(low, high)
493
-
494
- cache_fp8 = torch.empty_like(cache, dtype=torch.uint8)
495
- ops.convert_fp8(cache_fp8, cache)
496
-
497
- converted_cache = torch.empty_like(cache)
498
- ops.convert_fp8(converted_cache, cache_fp8)
499
-
500
- torch.testing.assert_close(cache, converted_cache, atol=0.02, rtol=0.2)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
tests/kernels/utils.py DELETED
@@ -1,99 +0,0 @@
1
- """Kernel test utils"""
2
-
3
- import itertools
4
- import random
5
- import unittest
6
- from functools import lru_cache
7
- from numbers import Number
8
- from typing import Any, Dict, List, NamedTuple, Optional, Sequence, Tuple, Union
9
-
10
- import pytest
11
- import torch
12
- from torch._prims_common import TensorLikeType
13
-
14
- # For now, disable "test_aot_dispatch_dynamic" since there are some
15
- # bugs related to this test in PyTorch 2.4.
16
- DEFAULT_OPCHECK_TEST_UTILS: Tuple[str, ...] = (
17
- "test_schema",
18
- "test_autograd_registration",
19
- "test_faketensor",
20
- )
21
-
22
- ALL_OPCHECK_TEST_UTILS: Tuple[str, ...] = (
23
- "test_schema",
24
- "test_autograd_registration",
25
- "test_faketensor",
26
- "test_aot_dispatch_dynamic",
27
- )
28
-
29
-
30
- # Copied/modified from torch._refs.__init__.py
31
- def fp8_allclose(
32
- a: TensorLikeType,
33
- b: TensorLikeType,
34
- rtol: float = 1e-05,
35
- atol: float = 1e-08,
36
- equal_nan: bool = False,
37
- ) -> bool:
38
- """
39
- Reference implementation of torch.allclose
40
- """
41
- torch._refs._check_close_args(name="torch.allclose", a=a, b=b, rtol=rtol, atol=atol)
42
-
43
- # MPS doesn't support float64, so use float32 for comparison
44
- if a.device.type == "mps" or b.device.type == "mps":
45
- a_cmp = a.float()
46
- b_cmp = b.float()
47
- else:
48
- a_cmp = a.double()
49
- b_cmp = b.double()
50
-
51
- return bool(
52
- torch.all(
53
- torch.isclose(
54
- a_cmp, b_cmp, rtol=rtol, atol=atol, equal_nan=equal_nan
55
- )
56
- ).item()
57
- )
58
-
59
-
60
- def compute_max_diff(output, output_ref):
61
- return torch.mean(torch.abs(output - output_ref)) / torch.mean(
62
- torch.abs(output_ref)
63
- )
64
-
65
-
66
- # A special version of op check that has a restricted default set of test_utils
67
- # and a patched version of allclose that supports fp8 types.
68
- def opcheck(
69
- op: Union[
70
- torch._ops.OpOverload,
71
- torch._ops.OpOverloadPacket,
72
- torch._library.custom_ops.CustomOpDef,
73
- ],
74
- args: Tuple[Any, ...],
75
- kwargs: Optional[Dict[str, Any]] = None,
76
- *,
77
- test_utils: Union[str, Sequence[str]] = ALL_OPCHECK_TEST_UTILS,
78
- raise_exception: bool = True,
79
- cond: bool = True,
80
- ) -> Dict[str, str]:
81
- with unittest.mock.patch("torch.allclose", new=fp8_allclose):
82
- if not cond:
83
- return {}
84
-
85
- return torch.library.opcheck(
86
- op, args, kwargs, test_utils=test_utils, raise_exception=raise_exception
87
- )
88
-
89
-
90
- @lru_cache(maxsize=None)
91
- def get_max_shared_memory_bytes(gpu: int = 0) -> int:
92
- """Returns the maximum shared memory per thread block in bytes."""
93
- from paged_attention import ops
94
-
95
- max_shared_mem = ops.get_max_shared_memory_per_block_device_attribute(gpu)
96
- # value 0 will cause MAX_SEQ_LEN become negative and test_attention.py
97
- # will fail
98
- assert max_shared_mem > 0, "max_shared_mem can not be zero"
99
- return int(max_shared_mem)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
torch-ext/paged_attention/__init__.py DELETED
@@ -1,21 +0,0 @@
1
- from ._custom_ops import (
2
- convert_fp8,
3
- copy_blocks,
4
- paged_attention_v1,
5
- paged_attention_v2,
6
- reshape_and_cache,
7
- reshape_and_cache_flash,
8
- swap_blocks,
9
- )
10
- from ._ops import ops
11
-
12
- __all__ = [
13
- "convert_fp8",
14
- "copy_blocks",
15
- "ops",
16
- "paged_attention_v1",
17
- "paged_attention_v2",
18
- "reshape_and_cache",
19
- "reshape_and_cache_flash",
20
- "swap_blocks",
21
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
torch-ext/paged_attention/_custom_ops.py DELETED
@@ -1,173 +0,0 @@
1
- from typing import List, Optional
2
-
3
- import torch
4
-
5
- from ._ops import ops
6
-
7
-
8
- # page attention ops
9
- def paged_attention_v1(
10
- out: torch.Tensor,
11
- query: torch.Tensor,
12
- key_cache: torch.Tensor,
13
- value_cache: torch.Tensor,
14
- num_kv_heads: int,
15
- scale: float,
16
- block_tables: torch.Tensor,
17
- seq_lens: torch.Tensor,
18
- block_size: int,
19
- max_seq_len: int,
20
- alibi_slopes: Optional[torch.Tensor],
21
- kv_cache_dtype: str,
22
- k_scale: float,
23
- v_scale: float,
24
- tp_rank: int = 0,
25
- blocksparse_local_blocks: int = 0,
26
- blocksparse_vert_stride: int = 0,
27
- blocksparse_block_size: int = 64,
28
- blocksparse_head_sliding_step: int = 0,
29
- ) -> None:
30
- ops.paged_attention_v1(
31
- out,
32
- query,
33
- key_cache,
34
- value_cache,
35
- num_kv_heads,
36
- scale,
37
- block_tables,
38
- seq_lens,
39
- block_size,
40
- max_seq_len,
41
- alibi_slopes,
42
- kv_cache_dtype,
43
- k_scale,
44
- v_scale,
45
- tp_rank,
46
- blocksparse_local_blocks,
47
- blocksparse_vert_stride,
48
- blocksparse_block_size,
49
- blocksparse_head_sliding_step,
50
- )
51
-
52
-
53
- def paged_attention_v2(
54
- out: torch.Tensor,
55
- exp_sum: torch.Tensor,
56
- max_logits: torch.Tensor,
57
- tmp_out: torch.Tensor,
58
- query: torch.Tensor,
59
- key_cache: torch.Tensor,
60
- value_cache: torch.Tensor,
61
- num_kv_heads: int,
62
- scale: float,
63
- block_tables: torch.Tensor,
64
- seq_lens: torch.Tensor,
65
- block_size: int,
66
- max_seq_len: int,
67
- alibi_slopes: Optional[torch.Tensor],
68
- kv_cache_dtype: str,
69
- k_scale: float,
70
- v_scale: float,
71
- tp_rank: int = 0,
72
- blocksparse_local_blocks: int = 0,
73
- blocksparse_vert_stride: int = 0,
74
- blocksparse_block_size: int = 64,
75
- blocksparse_head_sliding_step: int = 0,
76
- ) -> None:
77
- ops.paged_attention_v2(
78
- out,
79
- exp_sum,
80
- max_logits,
81
- tmp_out,
82
- query,
83
- key_cache,
84
- value_cache,
85
- num_kv_heads,
86
- scale,
87
- block_tables,
88
- seq_lens,
89
- block_size,
90
- max_seq_len,
91
- alibi_slopes,
92
- kv_cache_dtype,
93
- k_scale,
94
- v_scale,
95
- tp_rank,
96
- blocksparse_local_blocks,
97
- blocksparse_vert_stride,
98
- blocksparse_block_size,
99
- blocksparse_head_sliding_step,
100
- )
101
-
102
-
103
- def reshape_and_cache(
104
- key: torch.Tensor,
105
- value: torch.Tensor,
106
- key_cache: torch.Tensor,
107
- value_cache: torch.Tensor,
108
- slot_mapping: torch.Tensor,
109
- kv_cache_dtype: str,
110
- k_scale: float,
111
- v_scale: float,
112
- ) -> None:
113
- ops.reshape_and_cache(
114
- key,
115
- value,
116
- key_cache,
117
- value_cache,
118
- slot_mapping,
119
- kv_cache_dtype,
120
- k_scale,
121
- v_scale,
122
- )
123
-
124
-
125
- def reshape_and_cache_flash(
126
- key: torch.Tensor,
127
- value: torch.Tensor,
128
- key_cache: torch.Tensor,
129
- value_cache: torch.Tensor,
130
- slot_mapping: torch.Tensor,
131
- kv_cache_dtype: str,
132
- k_scale: torch.Tensor,
133
- v_scale: torch.Tensor,
134
- ) -> None:
135
- ops.reshape_and_cache_flash(
136
- key,
137
- value,
138
- key_cache,
139
- value_cache,
140
- slot_mapping,
141
- kv_cache_dtype,
142
- k_scale,
143
- v_scale,
144
- )
145
-
146
-
147
- def copy_blocks(
148
- key_caches: List[torch.Tensor],
149
- value_caches: List[torch.Tensor],
150
- block_mapping: torch.Tensor,
151
- ) -> None:
152
- ops.copy_blocks(key_caches, value_caches, block_mapping)
153
-
154
-
155
- def swap_blocks(
156
- src: torch.Tensor, dst: torch.Tensor, block_mapping: torch.Tensor
157
- ) -> None:
158
- ops.swap_blocks(src, dst, block_mapping)
159
-
160
-
161
- def convert_fp8(
162
- output: torch.Tensor, input: torch.Tensor, scale: float = 1.0, kv_dtype: str = "fp8"
163
- ) -> None:
164
- ops.convert_fp8(output, input, scale, kv_dtype)
165
-
166
-
167
- __all__ = [
168
- "convert_fp8",
169
- "paged_attention_v1",
170
- "paged_attention_v2",
171
- "reshape_and_cache",
172
- "copy_blocks",
173
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
torch-ext/paged_attention/platforms.py DELETED
@@ -1,92 +0,0 @@
1
- import os
2
- import random
3
- from abc import ABC, abstractmethod
4
- from functools import lru_cache, wraps
5
- from typing import Callable, ParamSpec, TypeVar
6
-
7
- import numpy as np
8
- import torch
9
-
10
- IS_ROCM = torch.version.hip is not None
11
- IS_MPS = torch.backends.mps.is_available()
12
-
13
-
14
- class Platform(ABC):
15
- @classmethod
16
- def seed_everything(cls, seed: int) -> None:
17
- """
18
- Set the seed of each random module.
19
- `torch.manual_seed` will set seed on all devices.
20
-
21
- Loosely based on: https://github.com/Lightning-AI/pytorch-lightning/blob/2.4.0/src/lightning/fabric/utilities/seed.py#L20
22
- """
23
- random.seed(seed)
24
- np.random.seed(seed)
25
- torch.manual_seed(seed)
26
-
27
- @abstractmethod
28
- def get_device_name(self, device_id: int = 0) -> str: ...
29
-
30
- @abstractmethod
31
- def is_cuda(self) -> bool: ...
32
-
33
- @abstractmethod
34
- def is_rocm(self) -> bool: ...
35
-
36
- @abstractmethod
37
- def is_mps(self) -> bool: ...
38
-
39
-
40
- class CudaPlatform(Platform):
41
- @classmethod
42
- @lru_cache(maxsize=8)
43
- def get_device_name(cls, device_id: int = 0) -> str:
44
- return torch.cuda.get_device_name(0)
45
-
46
- def is_cuda(self) -> bool:
47
- return True
48
-
49
- def is_rocm(self) -> bool:
50
- return False
51
-
52
- def is_mps(self) -> bool:
53
- return False
54
-
55
-
56
- class RocmPlatform(Platform):
57
- @classmethod
58
- @lru_cache(maxsize=8)
59
- def get_device_name(cls, device_id: int = 0) -> str:
60
- return torch.cuda.get_device_name(device_id)
61
-
62
- def is_cuda(self) -> bool:
63
- return False
64
-
65
- def is_rocm(self) -> bool:
66
- return True
67
-
68
- def is_mps(self) -> bool:
69
- return False
70
-
71
-
72
- class MpsPlatform(Platform):
73
- @classmethod
74
- @lru_cache(maxsize=8)
75
- def get_device_name(cls, device_id: int = 0) -> str:
76
- return torch.cuda.get_device_name(device_id)
77
-
78
- def is_cuda(self) -> bool:
79
- return False
80
-
81
- def is_rocm(self) -> bool:
82
- return False
83
-
84
- def is_mps(self) -> bool:
85
- return True
86
-
87
- current_platform = (
88
- RocmPlatform() if IS_ROCM else
89
- MpsPlatform() if IS_MPS else
90
- CudaPlatform() if torch.cuda.is_available() else
91
- None
92
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
torch-ext/torch_binding.cpp DELETED
@@ -1,122 +0,0 @@
1
- #include <torch/library.h>
2
-
3
- #include "registration.h"
4
-
5
- #include "torch_binding.h"
6
-
7
- // Note on op signatures:
8
- // The X_meta signatures are for the meta functions corresponding to op X.
9
- // They must be kept in sync with the signature for X. Generally, only
10
- // functions that return Tensors require a meta function.
11
- //
12
- // See the following links for detailed docs on op registration and function
13
- // schemas.
14
- // https://docs.google.com/document/d/1_W62p8WJOQQUzPsJYa7s701JXt0qf2OfLub2sbkHOaU/edit#heading=h.ptttacy8y1u9
15
- // https://github.com/pytorch/pytorch/blob/main/aten/src/ATen/native/README.md#annotations
16
-
17
- TORCH_LIBRARY_EXPAND(TORCH_EXTENSION_NAME, ops) {
18
- // Attention ops
19
- // Compute the attention between an input query and the cached
20
- // keys/values using PagedAttention.
21
- ops.def(
22
- "paged_attention_v1("
23
- " Tensor! out, Tensor query, Tensor key_cache,"
24
- " Tensor value_cache, int num_kv_heads, float scale,"
25
- " Tensor block_tables, Tensor seq_lens, int block_size,"
26
- " int max_seq_len, Tensor? alibi_slopes,"
27
- " str kv_cache_dtype, Tensor k_scale, Tensor v_scale,"
28
- " int tp_rank, int blocksparse_local_blocks,"
29
- " int blocksparse_vert_stride, int blocksparse_block_size,"
30
- " int blocksparse_head_sliding_step) -> ()");
31
- #if defined(CUDA_KERNEL) || defined(ROCM_KERNEL)
32
- ops.impl("paged_attention_v1", torch::kCUDA, &paged_attention_v1);
33
- #elif defined(METAL_KERNEL)
34
- ops.impl("paged_attention_v1", torch::kMPS, paged_attention_v1);
35
- #endif
36
-
37
- // PagedAttention V2.
38
- ops.def(
39
- "paged_attention_v2("
40
- " Tensor! out, Tensor! exp_sums, Tensor! max_logits,"
41
- " Tensor! tmp_out, Tensor query, Tensor key_cache,"
42
- " Tensor value_cache, int num_kv_heads, float scale,"
43
- " Tensor block_tables, Tensor seq_lens, int block_size,"
44
- " int max_seq_len, Tensor? alibi_slopes,"
45
- " str kv_cache_dtype, Tensor k_scale, Tensor v_scale,"
46
- " int tp_rank, int blocksparse_local_blocks,"
47
- " int blocksparse_vert_stride, int blocksparse_block_size,"
48
- " int blocksparse_head_sliding_step) -> ()");
49
- #if defined(CUDA_KERNEL) || defined(ROCM_KERNEL)
50
- ops.impl("paged_attention_v2", torch::kCUDA, &paged_attention_v2);
51
- #elif defined(METAL_KERNEL)
52
- ops.impl("paged_attention_v2", torch::kMPS, paged_attention_v2);
53
- #endif
54
-
55
- // Swap in (out) the cache blocks from src to dst.
56
- ops.def(
57
- "swap_blocks(Tensor src, Tensor! dst, Tensor block_mapping) -> ()");
58
- #if defined(CUDA_KERNEL) || defined(ROCM_KERNEL)
59
- ops.impl("swap_blocks", torch::kCUDA, &swap_blocks);
60
- #elif defined(METAL_KERNEL)
61
- ops.impl("swap_blocks", torch::kMPS, swap_blocks);
62
- #endif
63
-
64
- // Copy the cache blocks from src to dst.
65
- ops.def(
66
- "copy_blocks(Tensor(a!)[] key_caches, Tensor[](b!) value_caches, "
67
- "Tensor block_mapping) -> ()");
68
- #if defined(CUDA_KERNEL) || defined(ROCM_KERNEL)
69
- ops.impl("copy_blocks", torch::kCUDA, &copy_blocks);
70
- #elif defined(METAL_KERNEL)
71
- ops.impl("copy_blocks", torch::kMPS, copy_blocks);
72
- #endif
73
-
74
- // Reshape the key and value tensors and cache them.
75
- ops.def(
76
- "reshape_and_cache(Tensor key, Tensor value,"
77
- " Tensor! key_cache, Tensor! value_cache,"
78
- " Tensor slot_mapping,"
79
- " str kv_cache_dtype,"
80
- " Tensor k_scale, Tensor v_scale) -> ()");
81
- #if defined(CUDA_KERNEL) || defined(ROCM_KERNEL)
82
- ops.impl("reshape_and_cache", torch::kCUDA, &reshape_and_cache);
83
- #elif defined(METAL_KERNEL)
84
- ops.impl("reshape_and_cache", torch::kMPS, reshape_and_cache);
85
- #endif
86
-
87
- // Reshape the key and value tensors and cache them.
88
- ops.def(
89
- "reshape_and_cache_flash(Tensor key, Tensor value,"
90
- " Tensor! key_cache,"
91
- " Tensor! value_cache,"
92
- " Tensor slot_mapping,"
93
- " str kv_cache_dtype,"
94
- " Tensor k_scale, Tensor v_scale) -> ()");
95
- #if defined(CUDA_KERNEL) || defined(ROCM_KERNEL)
96
- ops.impl("reshape_and_cache_flash", torch::kCUDA, &reshape_and_cache_flash);
97
- #elif defined(METAL_KERNEL)
98
- ops.impl("reshape_and_cache_flash", torch::kMPS, reshape_and_cache_flash);
99
- #endif
100
-
101
- // Gets the specified device attribute.
102
- ops.def("get_device_attribute(int attribute, int device_id) -> int");
103
- ops.impl("get_device_attribute", &get_device_attribute);
104
-
105
- // Gets the maximum shared memory per block device attribute.
106
- ops.def(
107
- "get_max_shared_memory_per_block_device_attribute(int device_id) -> int");
108
- ops.impl("get_max_shared_memory_per_block_device_attribute",
109
- &get_max_shared_memory_per_block_device_attribute);
110
-
111
- // Convert the key and value cache to fp8 data type.
112
- ops.def(
113
- "convert_fp8(Tensor! dst_cache, Tensor src_cache, float scale, "
114
- "str kv_cache_dtype) -> ()");
115
- #if defined(CUDA_KERNEL) || defined(ROCM_KERNEL)
116
- ops.impl("convert_fp8", torch::kCUDA, &convert_fp8);
117
- #elif defined(METAL_KERNEL)
118
- ops.impl("convert_fp8", torch::kMPS, convert_fp8);
119
- #endif
120
- }
121
-
122
- REGISTER_EXTENSION(TORCH_EXTENSION_NAME)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
torch-ext/torch_binding.h DELETED
@@ -1,56 +0,0 @@
1
- #pragma once
2
-
3
- #include <torch/torch.h>
4
-
5
- void paged_attention_v1(
6
- torch::Tensor& out, torch::Tensor& query, torch::Tensor& key_cache,
7
- torch::Tensor& value_cache, int64_t num_kv_heads, double scale,
8
- torch::Tensor& block_tables, torch::Tensor& seq_lens, int64_t block_size,
9
- int64_t max_seq_len, const std::optional<torch::Tensor>& alibi_slopes,
10
- const std::string& kv_cache_dtype, torch::Tensor& k_scale,
11
- torch::Tensor& v_scale, const int64_t tp_rank,
12
- const int64_t blocksparse_local_blocks,
13
- const int64_t blocksparse_vert_stride, const int64_t blocksparse_block_size,
14
- const int64_t blocksparse_head_sliding_step);
15
-
16
- void paged_attention_v2(
17
- torch::Tensor& out, torch::Tensor& exp_sums, torch::Tensor& max_logits,
18
- torch::Tensor& tmp_out, torch::Tensor& query, torch::Tensor& key_cache,
19
- torch::Tensor& value_cache, int64_t num_kv_heads, double scale,
20
- torch::Tensor& block_tables, torch::Tensor& seq_lens, int64_t block_size,
21
- int64_t max_seq_len, const std::optional<torch::Tensor>& alibi_slopes,
22
- const std::string& kv_cache_dtype, torch::Tensor& k_scale,
23
- torch::Tensor& v_scale, const int64_t tp_rank,
24
- const int64_t blocksparse_local_blocks,
25
- const int64_t blocksparse_vert_stride, const int64_t blocksparse_block_size,
26
- const int64_t blocksparse_head_sliding_step);
27
-
28
- void swap_blocks(torch::Tensor& src, torch::Tensor& dst,
29
- const torch::Tensor& block_mapping);
30
-
31
- // Note: the key_caches and value_caches vectors are constant but
32
- // not the Tensors they contain. The vectors need to be const refs
33
- // in order to satisfy pytorch's C++ operator registration code.
34
- void copy_blocks(std::vector<torch::Tensor> const& key_caches,
35
- std::vector<torch::Tensor> const& value_caches,
36
- const torch::Tensor& block_mapping);
37
-
38
- void reshape_and_cache(torch::Tensor& key, torch::Tensor& value,
39
- torch::Tensor& key_cache, torch::Tensor& value_cache,
40
- torch::Tensor& slot_mapping,
41
- const std::string& kv_cache_dtype,
42
- torch::Tensor& k_scale, torch::Tensor& v_scale);
43
-
44
- void reshape_and_cache_flash(torch::Tensor& key, torch::Tensor& value,
45
- torch::Tensor& key_cache,
46
- torch::Tensor& value_cache,
47
- torch::Tensor& slot_mapping,
48
- const std::string& kv_cache_dtype,
49
- torch::Tensor& k_scale, torch::Tensor& v_scale);
50
-
51
- int64_t get_device_attribute(int64_t attribute, int64_t device_id);
52
-
53
- int64_t get_max_shared_memory_per_block_device_attribute(int64_t device_id);
54
-
55
- void convert_fp8(torch::Tensor& dst_cache, torch::Tensor& src_cache,
56
- const double scale, const std::string& kv_cache_dtype);