File size: 11,475 Bytes
6e69dae f4ba638 2035283 f4ba638 7e39151 f4ba638 4d362db 2035283 4d362db 2035283 62ec8be 4d362db 35adf41 f4ba638 35adf41 f4ba638 4d362db f4ba638 2035283 e00ae54 f4ba638 35adf41 f4ba638 4d362db f4ba638 4d362db cfddf8b f4ba638 35adf41 f4ba638 4d362db f4ba638 4d362db cfddf8b f4ba638 aa3f650 35adf41 62ec8be aa3f650 62ec8be aa3f650 4d362db aa3f650 4d362db cfddf8b aa3f650 2035283 cfddf8b f4ba638 35adf41 f4ba638 4d362db f4ba638 35adf41 4d362db 35adf41 f4ba638 35adf41 4d362db f4ba638 35adf41 f4ba638 35adf41 aa3f650 62ec8be 35adf41 e00ae54 cfddf8b 2035283 f4ba638 aa3f650 f4ba638 e00ae54 cfddf8b 2035283 f4ba638 62ec8be e00ae54 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 | #include <torch/library.h>
#include <ATen/cuda/CUDAContext.h>
#include <c10/cuda/CUDAGuard.h>
#include <algorithm>
#include <vector>
#include "../mak_cuda/mak_kernels.h"
#include "registration.h"
#include "torch_binding.h"
namespace {
void check_prog(const at::Tensor& prog) {
TORCH_CHECK(prog.is_cuda(), "prog must be CUDA");
TORCH_CHECK(prog.scalar_type() == at::kLong, "prog must be int64");
TORCH_CHECK(prog.dim() == 2 && prog.size(1) == 16 && prog.is_contiguous(),
"prog must be a contiguous [n_phases, 16] tensor");
}
void check_barrier(const at::Tensor& barrier) {
TORCH_CHECK(barrier.is_cuda() && barrier.scalar_type() == at::kInt &&
barrier.numel() >= 34,
"barrier must be a CUDA int32 tensor with >= 2 elements");
}
struct LaunchShape {
int smem;
int ring_stages;
};
// Dynamic shared = bf16 staging + the cp.async weight ring (one stage is
// 8 warps x 32 lanes x 16B = 4KB); the ring depth adapts to the device.
// Batched decode fully stages [B][K] for the small-K projections, so the
// cap admits panels up to ~96KB; whether that many blocks stay co-resident
// is settled by the occupancy query, not this bound.
LaunchShape shape_for(int64_t stage_elems) {
TORCH_CHECK(stage_elems > 0 && stage_elems <= 49152,
"staging size out of range");
const int stage_bytes = (int)(stage_elems * 2);
const int rs = mak_ring_stages_impl(stage_bytes);
return {stage_bytes + rs * 4096, rs};
}
} // namespace
int64_t mak_num_blocks(at::Tensor prog, int64_t max_k) {
const c10::cuda::CUDAGuard guard(prog.device());
return (int64_t)mak_grid_blocks_impl(shape_for(max_k).smem);
}
int64_t mak_batch_maxb() { return (int64_t)mak_batch_maxb_impl(); }
double mak_bw_per_sm(at::Tensor probe) {
const c10::cuda::CUDAGuard guard(probe.device());
int dev = 0;
cudaGetDevice(&dev);
int clk_khz = 0, bus_bits = 0, sms = 1;
cudaDeviceGetAttribute(&clk_khz, cudaDevAttrMemoryClockRate, dev);
cudaDeviceGetAttribute(&bus_bits, cudaDevAttrGlobalMemoryBusWidth, dev);
cudaDeviceGetAttribute(&sms, cudaDevAttrMultiProcessorCount, dev);
if (sms < 1) sms = 1;
const double gbps =
2.0 * (double)clk_khz * 1e3 * ((double)bus_bits / 8.0) / 1e9;
return gbps / (double)sms;
}
void mak_run(at::Tensor prog, at::Tensor barrier, int64_t pos,
int64_t step_slot, int64_t max_k) {
check_prog(prog);
check_barrier(barrier);
const c10::cuda::CUDAGuard guard(prog.device());
auto stream = at::cuda::getCurrentCUDAStream();
int* bar = barrier.data_ptr<int>();
const LaunchShape sh = shape_for(max_k);
const cudaError_t err = mak_launch_mega(
reinterpret_cast<const long long*>(prog.data_ptr<int64_t>()),
(int)prog.size(0), (int)pos, 1, (int)step_slot, -1, 1, sh.ring_stages,
bar, mak_grid_blocks_impl(sh.smem), sh.smem, stream.stream(), nullptr);
TORCH_CHECK(err == cudaSuccess, "mak_run launch failed: ",
cudaGetErrorString(err));
}
void mak_run_steps(at::Tensor prog, at::Tensor barrier, int64_t pos0,
int64_t steps, int64_t slot0, int64_t max_k) {
check_prog(prog);
check_barrier(barrier);
const c10::cuda::CUDAGuard guard(prog.device());
auto stream = at::cuda::getCurrentCUDAStream();
int* bar = barrier.data_ptr<int>();
const long long* pp =
reinterpret_cast<const long long*>(prog.data_ptr<int64_t>());
const int n = (int)prog.size(0);
const LaunchShape sh = shape_for(max_k);
const int grid = mak_grid_blocks_impl(sh.smem);
for (int64_t i = 0; i < steps; ++i) {
const cudaError_t err =
mak_launch_mega(pp, n, (int)(pos0 + i), 1, (int)(slot0 + i), 0, 1,
sh.ring_stages, bar, grid, sh.smem, stream.stream(),
nullptr);
TORCH_CHECK(err == cudaSuccess, "mak_run_steps launch failed: ",
cudaGetErrorString(err));
}
}
void mak_run_seq(at::Tensor prog, at::Tensor barrier, int64_t pos0,
int64_t steps, int64_t slot0, int64_t prompt_len,
int64_t max_k, int64_t chunk_m) {
check_prog(prog);
check_barrier(barrier);
TORCH_CHECK(steps >= 1, "steps must be >= 1");
TORCH_CHECK(chunk_m >= 1 && chunk_m <= 8, "chunk_m must be in [1, 8]");
const c10::cuda::CUDAGuard guard(prog.device());
auto stream = at::cuda::getCurrentCUDAStream();
int* bar = barrier.data_ptr<int>();
const LaunchShape sh = shape_for(max_k * chunk_m);
const cudaError_t err = mak_launch_mega(
reinterpret_cast<const long long*>(prog.data_ptr<int64_t>()),
(int)prog.size(0), (int)pos0, (int)steps, (int)slot0, (int)prompt_len,
(int)chunk_m, sh.ring_stages, bar, mak_grid_blocks_impl(sh.smem),
sh.smem, stream.stream(), nullptr);
TORCH_CHECK(err == cudaSuccess, "mak_run_seq launch failed: ",
cudaGetErrorString(err));
}
// B <= 8 runs the fused shared-staging kernel (input staged [B][K]); larger
// B runs the global-scratch kernel, which does not stage the input in
// shared and so is bounded only by the register accumulators.
// stage_elems is the largest shared panel the batch program stages: for the
// fused path max_k*B, for the wide path the max B*K over the projections
// that stay fused (the rest transform into global scratch and stage nothing).
void mak_run_batch(at::Tensor prog, at::Tensor barrier, at::Tensor pos_b,
int64_t steps, int64_t slot0, int64_t max_k,
int64_t kv_bstride, int64_t stage_elems) {
check_prog(prog);
check_barrier(barrier);
const int maxb = mak_batch_maxb();
TORCH_CHECK(pos_b.is_cuda() && pos_b.scalar_type() == at::kInt &&
pos_b.is_contiguous() && pos_b.numel() >= 1 &&
pos_b.numel() <= maxb,
"pos_b must be a contiguous CUDA int32 tensor with 1..",
maxb, " elements");
TORCH_CHECK(steps >= 1, "steps must be >= 1");
const int B = (int)pos_b.numel();
const c10::cuda::CUDAGuard guard(prog.device());
auto stream = at::cuda::getCurrentCUDAStream();
int* bar = barrier.data_ptr<int>();
const long long* pp =
reinterpret_cast<const long long*>(prog.data_ptr<int64_t>());
// The fully-fused path holds [B][K] for every projection and up to 8
// accumulators, so it serves B up to min(8, 16384 / max_k); wider batches
// take the acc[NB] kernel, where large-K projections spill to scratch.
const int64_t fused_cap = std::min<int64_t>(8, 16384 / max_k);
const LaunchShape sh = shape_for(stage_elems);
cudaError_t err;
if ((int64_t)B <= fused_cap) {
err = mak_launch_mega_batch(
pp, (int)prog.size(0), (int)steps, (int)slot0, sh.ring_stages, bar,
mak_grid_blocks_impl(sh.smem), sh.smem, pos_b.data_ptr<int>(), B,
(long long)kv_bstride, stream.stream());
} else {
err = mak_launch_mega_batch_big(
pp, (int)prog.size(0), (int)steps, (int)slot0, sh.ring_stages, bar,
mak_grid_blocks_big_impl(sh.smem), sh.smem, pos_b.data_ptr<int>(), B,
(long long)kv_bstride, stream.stream());
}
TORCH_CHECK(err == cudaSuccess, "mak_run_batch launch failed: ",
cudaGetErrorString(err));
}
void mak_run_ts(at::Tensor prog, at::Tensor barrier, int64_t pos,
int64_t step_slot, int64_t max_k, at::Tensor ts) {
check_prog(prog);
check_barrier(barrier);
TORCH_CHECK(ts.is_cuda() && ts.scalar_type() == at::kLong &&
ts.numel() >= prog.size(0) + 1,
"ts must be a CUDA int64 tensor with n_phases+1 elements");
const c10::cuda::CUDAGuard guard(prog.device());
auto stream = at::cuda::getCurrentCUDAStream();
int* bar = barrier.data_ptr<int>();
const LaunchShape sh = shape_for(max_k);
const cudaError_t err = mak_launch_mega(
reinterpret_cast<const long long*>(prog.data_ptr<int64_t>()),
(int)prog.size(0), (int)pos, 1, (int)step_slot, -1, 1, sh.ring_stages,
bar, mak_grid_blocks_impl(sh.smem), sh.smem, stream.stream(),
reinterpret_cast<long long*>(ts.data_ptr<int64_t>()));
TORCH_CHECK(err == cudaSuccess, "mak_run_ts launch failed: ",
cudaGetErrorString(err));
}
at::Tensor mak_run_phased(at::Tensor prog, int64_t pos, int64_t step_slot,
bool timed, int64_t max_k) {
check_prog(prog);
const c10::cuda::CUDAGuard guard(prog.device());
auto stream = at::cuda::getCurrentCUDAStream();
const long long* pp =
reinterpret_cast<const long long*>(prog.data_ptr<int64_t>());
const int n = (int)prog.size(0);
const LaunchShape sh = shape_for(max_k);
const int grid = mak_grid_blocks_impl(sh.smem);
if (timed) {
std::vector<float> ms((size_t)n, 0.f);
cudaEvent_t a, b;
cudaEventCreate(&a);
cudaEventCreate(&b);
for (int p = 0; p < n; ++p) {
cudaEventRecord(a, stream.stream());
const cudaError_t err = mak_launch_phase(pp, p, (int)pos,
(int)step_slot,
sh.ring_stages, grid, sh.smem,
stream.stream());
TORCH_CHECK(err == cudaSuccess, "mak_run_phased launch failed: ",
cudaGetErrorString(err));
cudaEventRecord(b, stream.stream());
cudaEventSynchronize(b);
cudaEventElapsedTime(&ms[p], a, b);
}
cudaEventDestroy(a);
cudaEventDestroy(b);
return torch::tensor(ms, torch::dtype(torch::kFloat32));
}
for (int p = 0; p < n; ++p) {
const cudaError_t err = mak_launch_phase(pp, p, (int)pos, (int)step_slot,
sh.ring_stages, grid, sh.smem,
stream.stream());
TORCH_CHECK(err == cudaSuccess, "mak_run_phased launch failed: ",
cudaGetErrorString(err));
}
return torch::empty({0}, torch::dtype(torch::kFloat32));
}
TORCH_LIBRARY_EXPAND(TORCH_EXTENSION_NAME, ops) {
ops.def("mak_run(Tensor prog, Tensor barrier, int pos, int step_slot, "
"int max_k) -> ()");
ops.def("mak_run_steps(Tensor prog, Tensor barrier, int pos0, int steps, "
"int slot0, int max_k) -> ()");
ops.def("mak_run_seq(Tensor prog, Tensor barrier, int pos0, int steps, "
"int slot0, int prompt_len, int max_k, int chunk_m) -> ()");
ops.def("mak_run_phased(Tensor prog, int pos, int step_slot, bool timed, "
"int max_k) -> Tensor");
ops.def("mak_num_blocks(Tensor prog, int max_k) -> int");
ops.def("mak_bw_per_sm(Tensor probe) -> float");
ops.def("mak_run_ts(Tensor prog, Tensor barrier, int pos, int step_slot, "
"int max_k, Tensor ts) -> ()");
ops.def("mak_run_batch(Tensor prog, Tensor barrier, Tensor pos_b, "
"int steps, int slot0, int max_k, int kv_bstride, "
"int stage_elems) -> ()");
// no tensor argument, so register a device-agnostic implementation
ops.def("mak_batch_maxb() -> int", &mak_batch_maxb);
#if defined(CUDA_KERNEL) || defined(ROCM_KERNEL)
ops.impl("mak_run", torch::kCUDA, &mak_run);
ops.impl("mak_run_steps", torch::kCUDA, &mak_run_steps);
ops.impl("mak_run_seq", torch::kCUDA, &mak_run_seq);
ops.impl("mak_run_phased", torch::kCUDA, &mak_run_phased);
ops.impl("mak_num_blocks", torch::kCUDA, &mak_num_blocks);
ops.impl("mak_bw_per_sm", torch::kCUDA, &mak_bw_per_sm);
ops.impl("mak_run_ts", torch::kCUDA, &mak_run_ts);
ops.impl("mak_run_batch", torch::kCUDA, &mak_run_batch);
#endif
}
REGISTER_EXTENSION(TORCH_EXTENSION_NAME)
|