Kernels:
Trusted publisher
Remove spurious files
Browse files- build.toml +0 -110
- cuda-utils/cuda_utils.h +0 -41
- cuda-utils/cuda_utils_kernels.cu +0 -35
- flake.lock +0 -168
- flake.nix +0 -17
- paged-attention-metal/attention/paged_attention.metal +0 -1401
- paged-attention-metal/cache.mm +0 -562
- paged-attention-metal/cache/copy_blocks.metal +0 -51
- paged-attention-metal/cache/reshape_and_cache.metal +0 -193
- paged-attention-metal/convert_fp8.metal +0 -77
- paged-attention-metal/convert_fp8.mm +0 -138
- paged-attention-metal/device.mm +0 -17
- paged-attention-metal/float8.metal +0 -122
- paged-attention-metal/paged_attention.mm +0 -693
- paged-attention-metal/utils.metal +0 -246
- paged-attention/attention/attention_dtypes.h +0 -7
- paged-attention/attention/attention_generic.cuh +0 -65
- paged-attention/attention/attention_kernels.cuh +0 -670
- paged-attention/attention/attention_utils.cuh +0 -57
- paged-attention/attention/dtype_bfloat16.cuh +0 -463
- paged-attention/attention/dtype_float16.cuh +0 -504
- paged-attention/attention/dtype_float32.cuh +0 -251
- paged-attention/attention/dtype_fp8.cuh +0 -41
- paged-attention/attention/paged_attention_v1.cu +0 -187
- paged-attention/attention/paged_attention_v2.cu +0 -197
- paged-attention/cache_kernels.cu +0 -734
- paged-attention/cuda_compat.h +0 -49
- paged-attention/dispatch_utils.h +0 -83
- paged-attention/quantization/fp8/amd/quant_utils.cuh +0 -671
- paged-attention/quantization/fp8/nvidia/quant_utils.cuh +0 -573
- tests/kernels/__init__.py +0 -0
- tests/kernels/allclose_default.py +0 -14
- tests/kernels/conftest.py +0 -157
- tests/kernels/test_attention.py +0 -439
- tests/kernels/test_cache.py +0 -500
- tests/kernels/utils.py +0 -99
- torch-ext/paged_attention/__init__.py +0 -21
- torch-ext/paged_attention/_custom_ops.py +0 -173
- torch-ext/paged_attention/platforms.py +0 -92
- torch-ext/torch_binding.cpp +0 -122
- 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, ©_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);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|