Squashed from work: - asset-ai: FastH3 4-step fast video backend; clip keyframes on the wire - asset-ui: loop video chains — text→image→video that ends where it began - h3: safetensors -> pruned-Q4_K GGUF quantizer for the 24GB DiT tiers - h3_quant_gguf verify: row-error gates calibrated to the measured Q4_K floor - asset-ai realtime: the feedback loop — the source anchors, the drifted frame inits - asset-ai realtime: a feedback loop survives a resize and travels by default - asset-ai realtime: the feedback loop frees itself from the feed handshake and pauses for its listener - asset-ai realtime: the outbound encode leaves the loop's critical path - asset-ai ocr: the ocr domain — Chandra 2 at page resolution, and the tower goes planner-owned - llm slots: a lane can hold an image span — embedding prefill and a rope cursor of its own - vision tower on CUDA: the encode leg gets its two missing kernels - llm/ocr: one M-RoPE grid encoder for both image paths, and a livelock made an error - vision tower on CUDA: the f16 GEMM keeps the precision it was throwing away - live: a feed that moves box takes its trip with it — one seed image - vision tower on CUDA: the tiled attention becomes bit-exact, and tensor cores go - llm prefill on CUDA: the MMA attention kernel gets the tile a 4-to-1 model needs - asset-ai ocr: the CUDA encode lane joins the integration — vision-parity sits beside run's three arms, and the kernels - Merge branch 'ocr-perf-integration' into work - asset-ai: the live anchor can follow the trip, and text leaves the 5090 - asset-ai: the camera moves the world, and the world starts still - asset-import: the EA strategy classics, in the one 2D contract - rtsmap: one seeded generator for tiled strategy maps - asset-ui: one card for the strategy classics, with a pack dropdown - asset-ai: music3 reference-audio path, ocr/h3 backends, registry - asset: mp4 sample index for range-streaming, chat tools, import profiles - cnc: tiberium is twelve growth frames, not twelve empty variants - platform: native file and save dialogs, in-house on all three desktops - chat: the scan holds out for a lane home - chat: a full home queues you — take the free lane - chat: the preload has a percentage, and the boundless cap stops showing - llm cuda: the 32x2 attention tile — even GQA ratios stay on MMA - sa3 gets a bake path: the sfx model's tables precomputed by a diffusion-side bin - sqlite_query: anti-join regression test - td import: HARV's second frame block is its harvesting cycle, not a turret - asset-ui: sprite enhancement runs on the 32B dev DiT — distillation, not the prompt, was the ceiling - ai-hub: makepad-asset-ai becomes makepad-ai-hub at libs/ai/hub, the chat pane becomes makepad-chat-ui, the service bin - asset-ui: test health fixtures grow the realtime field they were born without - ai-hub: one home at ~/.makepad — weights/ run/ cache/ logs/, the service cache migrates from ai_content by a single re - ai-hub: subprocess workers die with the node — process groups everywhere, PDEATHSIG on linux, one KILL_ON_JOB_CLOSE Jo - ai-hub: the hub object — AiHub::in_process, pipes vocabulary, and the local LLM engine generalized out of mpfiles (aic - strict-json: the dependency-free JSON module gets its own crate; asset-client re-exports it so nothing downstream move - ai-hub: the machine layer — node entries, the 0600 machine token, and the residency election that IS the lock (aicore - ai-hub: MPHUB1 — the fabric beacon only dedicated nodes can send (aicore §4) - ai-hub: job leases — work lives only while it is renewed (aicore §8) - asset-creator: the pipeline library is born — specs, the deps gate, and the derived-state law (aicore §9) - ai-hub: RAM residency facts — the CPU-side twin of residency.rs (aicore §3) - ai-hub: ETA placement primitives — relative GPU throughput, the four-term estimate, and an observable breakdown (aicor - ai-hub: leases go live on the wire — origin fields on submit, /job/<id>/keepalive, /bye, and the reaper that cancels w - ai-hub: the chat providers move in — fleet qwen, openai, grok, claude/codex/grok CLIs, the responses driver, and the w - asset-creator: the engine — one pipeline run against the hub, deps-gated, spliced, cancellable, resumable-by-construct - ai-hub: the machine node mode — --machine binds loopback, registers in ~/.makepad/run, and exits on its own once idle - asset-creator: makepad-creator-run — the detached client for runs that must outlive a window (aicore §9) - ai-hub: a native Claude Messages-API provider — API-key or Claude Code OAuth, bounded SSE streaming, injected tools (a - route + converse: off makepad_ai — the Agent seam moves to converse, route's cloud dispatcher rides the hub's Claude p - asset-creator: the preset tables move in — fifteen chain-policy constants shared by every creator app (aicore §9 / P6) - makepad_ai is deleted — every backend is a hub pipe, the agent seam lives with its consumers (aicore §14, decided 2026 - ai-hub: loads hold the machine residency election — set_model_state claims on Loaded and publishes the service port (a - ai-hub: chats run the machine election — route to a serving holder, wait on a loading one, claim and publish when open - ai-hub: pick_for_domain_eta — ETA-ranked placement over the shared hard-filter core (aicore §6 / P4) - asset-creator: the engine picks a provider per stage at dispatch time — a chain's later stages see fresh fleet state ( - ai-hub: the fabric secret gates the service HTTP surface — bearer on everything but /health and the ticketed peer path - vj: DREAM runs execute in the app — pipelines.rs becomes the run it used to watch (aicore §9 / F1) - asset-creator: the runner — generate one thing and put it in the catalog, one implementation for every surface (aicore - chat-ui: the session runs in the app — no broker anywhere on the chat path (aicore P8 / F5) - asset-store: assets.query is a first-class query endpoint — the bounded SQL surface outlives the broker (aicore P8 / F - asset-creator: CreatorTools — the chat tool pack for a store that only stores (aicore §9 / P8) - asset-store: the shrink — the store stores (aicore P7) - importer + asset-server host: the coordination era ends (aicore P7) - store config purge + asset-ui goes fleet-direct; the derive protocol gets its route proof (aicore P7) - client + chat dispatcher: the dead wire comes out (aicore P7/P8) - ai-hub: 0.3.0 — the health version says which era a node runs - ai-hub: the default fleet is 'gen' — apps hear the LAN without env plumbing - ai-hub: the preload note percents the prefill, not the job bar - ai-hub: conversations keep their KV — the wire mirror, the lane identity, the in-turn dynamic context (aicore §7) - ai-hub: an open-think model is thinking from its first token - libs: the zero-warning sweep — stitch casts say what they mean, xatlas keeps upstream's surface quietly - zero-warning sweep, round two — the first full-workspace pass - zero-warning sweep, round three — the model lanes and the deep examples - zero-warning sweep, round four — the last stragglers - zero-warning sweep, round five — vj and chat-ui - zero-warning sweep, round six — three cascades Co-authored-by: Claude <info@makepad.nl>
4739 lines
201 KiB
Text
4739 lines
201 KiB
Text
// SPDX-License-Identifier: MIT
|
|
// Copyright (c) 2023-2026 The ggml authors
|
|
//
|
|
// Substantial portions derived from ggml / llama.cpp
|
|
// (https://github.com/ggml-org/llama.cpp), MIT licensed.
|
|
// The original copyright notice and permission notice are retained.
|
|
// See libs/ai/NOTICE and, where present, LICENSE in this directory.
|
|
//
|
|
// Native CUDA kernels for llama + gen-AI graphs. Compiled once by
|
|
// libs/ggml/build.rs from this file (not by makepad-llama).
|
|
//
|
|
// Semantics contracts:
|
|
// - K-quant dequantization is a transcription of the CPU references in
|
|
// libs/ggml/src/quant.rs (dequantize_q4_k / dequantize_row_q5_k /
|
|
// dequantize_q6_k), which are themselves bit-exact vs upstream ggml.
|
|
// - rope IMROPE/MROPE is a transcription of kernel_rope_multi in
|
|
// libs/ggml/src/backend/metal/ggml/ggml-metal.metal (the Metal oracle).
|
|
// - All strides are BYTE strides unless the name says elems.
|
|
// - Every launcher takes the stream last and returns cudaGetLastError().
|
|
|
|
#include <cuda_runtime.h>
|
|
#include <cuda_fp16.h>
|
|
#include <cuda_bf16.h>
|
|
#include <mma.h>
|
|
#include <math.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#define QK_K 256
|
|
|
|
static __device__ __forceinline__ void mkllm_scale_min_k4(
|
|
int is, const uint8_t * scales, uint8_t * sc, uint8_t * m) {
|
|
if (is < 4) {
|
|
*sc = scales[is] & 63;
|
|
*m = scales[is + 4] & 63;
|
|
} else {
|
|
*sc = (scales[is + 4] & 0x0F) | ((scales[is - 4] >> 6) << 4);
|
|
*m = (scales[is + 4] >> 4) | ((scales[is] >> 6) << 4);
|
|
}
|
|
}
|
|
|
|
// Dequantize one 256-value super-block position `l` (0..256) for each kind.
|
|
static __device__ __forceinline__ float mkllm_deq_q4k_at(const uint8_t * b, int l) {
|
|
const float d = __half2float(((const __half *) b)[0]);
|
|
const float dmin = __half2float(((const __half *) b)[1]);
|
|
const uint8_t * scales = b + 4;
|
|
const uint8_t * qs = b + 16;
|
|
const int j64 = l >> 6; // 64-value group
|
|
const int rem = l & 63;
|
|
const int is = 2 * j64 + (rem >> 5);
|
|
uint8_t sc, m;
|
|
mkllm_scale_min_k4(is, scales, &sc, &m);
|
|
const uint8_t q = qs[32 * j64 + (rem & 31)];
|
|
const int nib = (rem < 32) ? (q & 0x0F) : (q >> 4);
|
|
return d * (float) sc * (float) nib - dmin * (float) m;
|
|
}
|
|
|
|
static __device__ __forceinline__ float mkllm_deq_q5k_at(const uint8_t * b, int l) {
|
|
const float d = __half2float(((const __half *) b)[0]);
|
|
const float dmin = __half2float(((const __half *) b)[1]);
|
|
const uint8_t * scales = b + 4;
|
|
const uint8_t * qh = b + 16;
|
|
const uint8_t * qs = b + 48;
|
|
const int j64 = l >> 6;
|
|
const int rem = l & 63;
|
|
const int is = 2 * j64 + (rem >> 5);
|
|
uint8_t sc, m;
|
|
mkllm_scale_min_k4(is, scales, &sc, &m);
|
|
const uint8_t q = qs[32 * j64 + (rem & 31)];
|
|
const int nib = (rem < 32) ? (q & 0x0F) : (q >> 4);
|
|
const uint8_t u = (uint8_t) (1u << is);
|
|
const float hi = (qh[rem & 31] & u) ? 16.0f : 0.0f;
|
|
return d * (float) sc * ((float) nib + hi) - dmin * (float) m;
|
|
}
|
|
|
|
static __device__ __forceinline__ float mkllm_deq_q6k_at(const uint8_t * b, int l) {
|
|
const float d = __half2float(*(const __half *) (b + 208));
|
|
const int n = l >> 7; // 128-value half
|
|
const int r = l & 127; // position within half
|
|
const uint8_t * ql = b + n * 64;
|
|
const uint8_t * qh = b + 128 + n * 32;
|
|
const int8_t * sc = (const int8_t *) (b + 192 + n * 8);
|
|
const int group = r >> 5; // 0..3 (l, l+32, l+64, l+96 pattern)
|
|
const int lo = r & 31;
|
|
const int is = lo / 16;
|
|
int q;
|
|
switch (group) {
|
|
case 0: q = (int) ((int8_t) ((ql[lo] & 0x0F) | ((qh[lo] & 3) << 4))) - 32; break;
|
|
case 1: q = (int) ((int8_t) ((ql[lo + 32] & 0x0F) | (((qh[lo] >> 2) & 3) << 4))) - 32; break;
|
|
case 2: q = (int) ((int8_t) ((ql[lo] >> 4) | (((qh[lo] >> 4) & 3) << 4))) - 32; break;
|
|
default: q = (int) ((int8_t) ((ql[lo + 32] >> 4) | (((qh[lo] >> 6) & 3) << 4))) - 32; break;
|
|
}
|
|
return d * (float) sc[is + 2 * group] * (float) q;
|
|
}
|
|
|
|
// `kind` values crossing the Rust FFI. 0..3 are the "legacy" kinds that the
|
|
// hand-written kernels in this file template over; 4..7 are served only by the
|
|
// vendored official llama.cpp templates (mmq.cuh / mmvq.cuh) plus the row
|
|
// dequant in iq_convert.cuh. Keep in sync with QUANT_* in
|
|
// libs/ai/cuda/src/llm_ops.rs and `quant_kind` in
|
|
// libs/ai/llm/src/cuda_exec/real.rs.
|
|
#define MKLLM_QUANT_Q4K 0
|
|
#define MKLLM_QUANT_Q5K 1
|
|
#define MKLLM_QUANT_Q6K 2
|
|
// q8_0 uses 32-value/34-byte blocks; the executor addresses quants in
|
|
// 256-value units, so treat 8 packed q8_0 blocks (272 bytes) as one unit.
|
|
#define MKLLM_QUANT_Q80 3
|
|
#define MKLLM_QUANT_LEGACY_LAST MKLLM_QUANT_Q80
|
|
// unsloth Dynamic (UD-) GGUFs mix these into otherwise-K-quant files.
|
|
#define MKLLM_QUANT_Q3K 4
|
|
#define MKLLM_QUANT_IQ4XS 5
|
|
// iq4_nl is a 32-value/18-byte block; like q8_0 above, eight of them are
|
|
// addressed as one 256-value unit (144 bytes).
|
|
#define MKLLM_QUANT_IQ4NL 6
|
|
#define MKLLM_QUANT_IQ3S 7
|
|
#define MKLLM_QUANT_COUNT 8
|
|
|
|
static __device__ __forceinline__ float mkllm_deq_q80_at(const uint8_t * b, int l) {
|
|
const uint8_t * blk = b + (l >> 5) * 34;
|
|
const float d = __half2float(*(const __half *) blk);
|
|
return d * (float) ((const int8_t *) (blk + 2))[l & 31];
|
|
}
|
|
|
|
// Legacy-kind selectors. These are reached with a compile-time KIND from the
|
|
// hand-written kernels only, so an unknown kind is a programming error, not a
|
|
// runtime input: return a NaN / zero-size sentinel rather than silently
|
|
// decoding the bytes as some other type. (House rule from the reclaimed-
|
|
// readback fix: a contract violation must be loud, never plausible.)
|
|
static __device__ __forceinline__ float mkllm_deq_at(int kind, const uint8_t * b, int l) {
|
|
switch (kind) {
|
|
case MKLLM_QUANT_Q4K: return mkllm_deq_q4k_at(b, l);
|
|
case MKLLM_QUANT_Q5K: return mkllm_deq_q5k_at(b, l);
|
|
case MKLLM_QUANT_Q6K: return mkllm_deq_q6k_at(b, l);
|
|
case MKLLM_QUANT_Q80: return mkllm_deq_q80_at(b, l);
|
|
default: return __int_as_float(0x7fffffff); // NaN
|
|
}
|
|
}
|
|
|
|
static __device__ __forceinline__ int mkllm_quant_block_bytes_dev(int kind) {
|
|
switch (kind) {
|
|
case MKLLM_QUANT_Q4K: return 144;
|
|
case MKLLM_QUANT_Q5K: return 176;
|
|
case MKLLM_QUANT_Q6K: return 210;
|
|
case MKLLM_QUANT_Q80: return 272;
|
|
default: return 0;
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Device info
|
|
// ---------------------------------------------------------------------------
|
|
|
|
extern "C" cudaError_t mkllm_device_info(
|
|
int device, char * name, int name_cap,
|
|
int * cc_major, int * cc_minor, size_t * total_mem, int * sm_count) {
|
|
cudaDeviceProp prop;
|
|
cudaError_t err = cudaGetDeviceProperties(&prop, device);
|
|
if (err != cudaSuccess) {
|
|
return err;
|
|
}
|
|
if (name != nullptr && name_cap > 0) {
|
|
int n = (int) strlen(prop.name);
|
|
if (n >= name_cap) n = name_cap - 1;
|
|
memcpy(name, prop.name, n);
|
|
name[n] = 0;
|
|
}
|
|
*cc_major = prop.major;
|
|
*cc_minor = prop.minor;
|
|
*total_mem = prop.totalGlobalMem;
|
|
*sm_count = prop.multiProcessorCount;
|
|
return cudaSuccess;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Quantized / float mat-vec: dst[N, M] = src0[K, N]^T . src1[K, M], M small.
|
|
// One warp per (row, col-batch) output; f32 accumulation; reads the raw
|
|
// GGUF block stream directly (no dequant materialization).
|
|
// ---------------------------------------------------------------------------
|
|
|
|
static __device__ __forceinline__ float4 mkllm_ld_f4(const float * p) {
|
|
return *(const float4 *) p;
|
|
}
|
|
|
|
// Warp-per-row quantized mat-vec, bandwidth-shaped: the 128-byte nibble/int8
|
|
// payload of each super-block is read as coalesced u32 words (one per lane),
|
|
// sub-block scales are decoded once into registers, and for M > 1 the decoded
|
|
// weights are reused across activation columns (M <= 8 held in registers).
|
|
//
|
|
// q4_K lane slice: lane l owns 4 payload bytes of 64-value group g = l/8 —
|
|
// 4 low-nibble values at g*64 + (l%8)*4 .. +3 and 4 high-nibble values at
|
|
// +32; the two touched sub-blocks are 2g (low) and 2g+1 (high).
|
|
//
|
|
// Lossless vs the scalar dequant oracles: same lane ownership, same
|
|
// `d*sc*q - dmin*m` then FMA into acc, same shfl_down reduction. Q6 is
|
|
// branchless (group is not warp-uniform) and uses 2-byte payload loads
|
|
// because Q6_K blocks are 210 bytes (2-aligned, not 4).
|
|
template <int KIND, int M_MAX>
|
|
static __global__ void mkllm_mmv_qk_kernel(
|
|
const uint8_t * __restrict__ src0, const float * __restrict__ src1,
|
|
float * __restrict__ dst,
|
|
int K, int N, int M,
|
|
size_t src0_row_bytes, size_t src1_col_elems, size_t dst_col_elems) {
|
|
const int row = blockIdx.x * (blockDim.y) + threadIdx.y;
|
|
if (row >= N) return;
|
|
const int lane = threadIdx.x; // 32 lanes
|
|
const uint8_t * row_bytes = src0 + (size_t) row * src0_row_bytes;
|
|
const int blocks = K / QK_K;
|
|
|
|
float acc[M_MAX];
|
|
#pragma unroll
|
|
for (int c = 0; c < M_MAX; c++) {
|
|
acc[c] = 0.0f;
|
|
}
|
|
|
|
for (int b = 0; b < blocks; b++) {
|
|
const uint8_t * blk = row_bytes + (size_t) b * mkllm_quant_block_bytes_dev(KIND);
|
|
const int xbase = b * QK_K;
|
|
|
|
if (KIND == MKLLM_QUANT_Q4K || KIND == MKLLM_QUANT_Q5K) {
|
|
const float d = __half2float(((const __half *) blk)[0]);
|
|
const float dmin = __half2float(((const __half *) blk)[1]);
|
|
const uint8_t * scales = blk + 4;
|
|
const uint8_t * qs = blk + (KIND == MKLLM_QUANT_Q4K ? 16 : 48);
|
|
const int group = lane >> 3; // 64-value group 0..3
|
|
const int byte4 = (lane & 7) * 4; // byte offset in group
|
|
const uint32_t q = *(const uint32_t *) (qs + group * 32 + byte4);
|
|
const int sb_lo = 2 * group;
|
|
uint8_t sc_lo, m_lo, sc_hi, m_hi;
|
|
mkllm_scale_min_k4(sb_lo, scales, &sc_lo, &m_lo);
|
|
mkllm_scale_min_k4(sb_lo + 1, scales, &sc_hi, &m_hi);
|
|
float w[8];
|
|
#pragma unroll
|
|
for (int i = 0; i < 4; i++) {
|
|
const uint32_t byte = (q >> (8 * i)) & 0xFF;
|
|
float lo = (float) (byte & 0x0F);
|
|
float hi = (float) (byte >> 4);
|
|
if (KIND == MKLLM_QUANT_Q5K) {
|
|
const uint8_t qh = blk[16 + byte4 + i];
|
|
lo += (qh & (1u << sb_lo)) ? 16.0f : 0.0f;
|
|
hi += (qh & (2u << sb_lo)) ? 16.0f : 0.0f;
|
|
}
|
|
w[i] = d * (float) sc_lo * lo - dmin * (float) m_lo;
|
|
w[4 + i] = d * (float) sc_hi * hi - dmin * (float) m_hi;
|
|
}
|
|
const int lo_at = xbase + group * 64 + byte4;
|
|
#pragma unroll
|
|
for (int c = 0; c < M_MAX; c++) {
|
|
if (c >= M) break;
|
|
const float * x = src1 + (size_t) c * src1_col_elems;
|
|
#pragma unroll
|
|
for (int i = 0; i < 4; i++) {
|
|
acc[c] += w[i] * x[lo_at + i] + w[4 + i] * x[lo_at + 32 + i];
|
|
}
|
|
}
|
|
} else if (KIND == MKLLM_QUANT_Q6K) {
|
|
// 8 consecutive values at l0 = lane*8. Group is not warp-uniform,
|
|
// so extract with bit math (no switch). Q6_K block is 210 bytes.
|
|
const float d = __half2float(*(const __half *) (blk + 208));
|
|
const int l0 = lane * 8;
|
|
const int n = l0 >> 7;
|
|
const int r = l0 & 127;
|
|
const int group = r >> 5;
|
|
const int lo0 = r & 31;
|
|
const int is = lo0 >> 4;
|
|
const int ql_off = (group & 1) * 32;
|
|
const int nibble = (group >> 1) & 1;
|
|
const int qh_shift = group * 2;
|
|
const uint8_t * ql = blk + n * 64 + ql_off + lo0;
|
|
const uint8_t * qh = blk + 128 + n * 32 + lo0;
|
|
const int8_t * sc = (const int8_t *) (blk + 192 + n * 8);
|
|
const float dsc = d * (float) sc[is + 2 * group];
|
|
const uint16_t * ql16 = (const uint16_t *) ql;
|
|
const uint16_t * qh16 = (const uint16_t *) qh;
|
|
const uint16_t ql_w[4] = { ql16[0], ql16[1], ql16[2], ql16[3] };
|
|
const uint16_t qh_w[4] = { qh16[0], qh16[1], qh16[2], qh16[3] };
|
|
float wv[8];
|
|
#pragma unroll
|
|
for (int i = 0; i < 8; i++) {
|
|
const int qlb = (int) ((ql_w[i >> 1] >> ((i & 1) * 8)) & 0xFF);
|
|
const int qhb = (int) ((qh_w[i >> 1] >> ((i & 1) * 8)) & 0xFF);
|
|
const int raw = ((qlb >> (4 * nibble)) & 0x0F)
|
|
| (((qhb >> qh_shift) & 3) << 4);
|
|
const int q = (int) ((int8_t) raw) - 32;
|
|
wv[i] = dsc * (float) q;
|
|
}
|
|
#pragma unroll
|
|
for (int c = 0; c < M_MAX; c++) {
|
|
if (c >= M) break;
|
|
const float * x = src1 + (size_t) c * src1_col_elems + xbase + l0;
|
|
const float4 x0 = mkllm_ld_f4(x);
|
|
const float4 x1 = mkllm_ld_f4(x + 4);
|
|
acc[c] += wv[0] * x0.x;
|
|
acc[c] += wv[1] * x0.y;
|
|
acc[c] += wv[2] * x0.z;
|
|
acc[c] += wv[3] * x0.w;
|
|
acc[c] += wv[4] * x1.x;
|
|
acc[c] += wv[5] * x1.y;
|
|
acc[c] += wv[6] * x1.z;
|
|
acc[c] += wv[7] * x1.w;
|
|
}
|
|
} else { // Q8_0 packed: 8 sub-blocks of 32 int8 + f16 d
|
|
const int sub = lane >> 2; // 0..7
|
|
const int b4 = (lane & 3) * 8; // 8 values per lane
|
|
const uint8_t * q8 = blk + sub * 34;
|
|
const float d = __half2float(*(const __half *) q8);
|
|
const int8_t * qv = (const int8_t *) (q8 + 2);
|
|
const int at = xbase + sub * 32 + b4;
|
|
#pragma unroll
|
|
for (int c = 0; c < M_MAX; c++) {
|
|
if (c >= M) break;
|
|
const float * x = src1 + (size_t) c * src1_col_elems;
|
|
float sub_acc = 0.0f;
|
|
#pragma unroll
|
|
for (int i = 0; i < 8; i++) {
|
|
sub_acc += (float) qv[b4 + i] * x[at + i];
|
|
}
|
|
acc[c] += d * sub_acc;
|
|
}
|
|
}
|
|
}
|
|
|
|
#pragma unroll
|
|
for (int c = 0; c < M_MAX; c++) {
|
|
if (c >= M) break;
|
|
float total = acc[c];
|
|
for (int off = 16; off > 0; off >>= 1) {
|
|
total += __shfl_down_sync(0xffffffff, total, off);
|
|
}
|
|
if (lane == 0) {
|
|
dst[(size_t) c * dst_col_elems + row] = total;
|
|
}
|
|
}
|
|
}
|
|
|
|
template <int KIND>
|
|
static void mkllm_launch_mmv_quant(
|
|
const void * src0, const float * src1, float * dst,
|
|
int K, int N, int M,
|
|
size_t src0_row_bytes, size_t src1_col_elems, size_t dst_col_elems,
|
|
cudaStream_t stream) {
|
|
dim3 block(32, 4);
|
|
dim3 grid((N + 3) / 4);
|
|
if (M == 1) {
|
|
mkllm_mmv_qk_kernel<KIND, 1><<<grid, block, 0, stream>>>(
|
|
(const uint8_t *) src0, src1, dst, K, N, M, src0_row_bytes, src1_col_elems, dst_col_elems);
|
|
} else if (M <= 2) {
|
|
mkllm_mmv_qk_kernel<KIND, 2><<<grid, block, 0, stream>>>(
|
|
(const uint8_t *) src0, src1, dst, K, N, M, src0_row_bytes, src1_col_elems, dst_col_elems);
|
|
} else if (M <= 4) {
|
|
mkllm_mmv_qk_kernel<KIND, 4><<<grid, block, 0, stream>>>(
|
|
(const uint8_t *) src0, src1, dst, K, N, M, src0_row_bytes, src1_col_elems, dst_col_elems);
|
|
} else {
|
|
mkllm_mmv_qk_kernel<KIND, 8><<<grid, block, 0, stream>>>(
|
|
(const uint8_t *) src0, src1, dst, K, N, M, src0_row_bytes, src1_col_elems, dst_col_elems);
|
|
}
|
|
}
|
|
|
|
extern "C" cudaError_t mkllm_mmv_quant(
|
|
int kind,
|
|
const void * src0, const float * src1, float * dst,
|
|
int K, int N, int M,
|
|
size_t src0_row_bytes, size_t src1_col_elems, size_t dst_col_elems,
|
|
cudaStream_t stream) {
|
|
switch (kind) {
|
|
case MKLLM_QUANT_Q4K:
|
|
mkllm_launch_mmv_quant<MKLLM_QUANT_Q4K>(
|
|
src0, src1, dst, K, N, M, src0_row_bytes, src1_col_elems, dst_col_elems, stream);
|
|
break;
|
|
case MKLLM_QUANT_Q5K:
|
|
mkllm_launch_mmv_quant<MKLLM_QUANT_Q5K>(
|
|
src0, src1, dst, K, N, M, src0_row_bytes, src1_col_elems, dst_col_elems, stream);
|
|
break;
|
|
case MKLLM_QUANT_Q80:
|
|
mkllm_launch_mmv_quant<MKLLM_QUANT_Q80>(
|
|
src0, src1, dst, K, N, M, src0_row_bytes, src1_col_elems, dst_col_elems, stream);
|
|
break;
|
|
case MKLLM_QUANT_Q6K:
|
|
mkllm_launch_mmv_quant<MKLLM_QUANT_Q6K>(
|
|
src0, src1, dst, K, N, M, src0_row_bytes, src1_col_elems, dst_col_elems, stream);
|
|
break;
|
|
default:
|
|
// This hand-written mat-vec only templates over the legacy kinds.
|
|
// Falling back to Q6_K here would silently decode e.g. iq4_xs
|
|
// bytes as q6_K and return plausible garbage.
|
|
return cudaErrorInvalidValue;
|
|
}
|
|
return cudaGetLastError();
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// M=1 packed K-quant x dynamic Q8_1 activation MMV.
|
|
//
|
|
// Quantizer and vec-dot structure follow llama.cpp b10430
|
|
// (commit 4c1a0af40d88c7fbb3b15c85bf2e8016d1d5b64c):
|
|
// ggml-cuda/quantize.cu, mmvq.{cu,cuh}, vecdotq.cuh
|
|
// Copyright (c) ggml authors. MIT license. Retained because this is a
|
|
// substantial port of the warp Q8_1 quant and packed Q4/Q5/Q6 dp4a dots.
|
|
//
|
|
// Warp-parallel Q8_1: one lane per value, warp max + sum, d = amax/127,
|
|
// q = roundf(xi/d). Q4_K/Q5_K min terms use d8 * sum(q8) via dp4a with
|
|
// 0x01010101. Q6_K uses signed (ql|qh)-32. Decode launch is llama.cpp
|
|
// GENERIC MMVQ: dim3(32, 4) on 3090/4090/5090 (ncols_dst=1).
|
|
// Fail closed unless M==1, K%256==0, and kind is Q4_K/Q5_K/Q6_K/Q8_0.
|
|
// The float MMV kernel above remains the fallback.
|
|
// ---------------------------------------------------------------------------
|
|
|
|
#define MKLLM_Q81_GS 32
|
|
#define MKLLM_QK8_1 32
|
|
#define MKLLM_QI8_1 8
|
|
#define MKLLM_QR4_K 2
|
|
#define MKLLM_QI4_K (QK_K / (4 * MKLLM_QR4_K))
|
|
#define MKLLM_QR5_K 2
|
|
#define MKLLM_QI5_K (QK_K / (4 * MKLLM_QR5_K))
|
|
#define MKLLM_QR6_K 2
|
|
#define MKLLM_QI6_K (QK_K / (4 * MKLLM_QR6_K))
|
|
#define MKLLM_VDR_Q4_K_MMVQ 2
|
|
#define MKLLM_VDR_Q5_K_MMVQ 2
|
|
#define MKLLM_VDR_Q6_K_MMVQ 1
|
|
#define MKLLM_MMVQ_NWARPS 4
|
|
|
|
struct __align__(4) mkllm_block_q8_1 {
|
|
half2 ds;
|
|
int8_t qs[MKLLM_QK8_1];
|
|
};
|
|
static_assert(sizeof(mkllm_block_q8_1) == 36, "block_q8_1 size");
|
|
|
|
struct mkllm_block_q4_K {
|
|
half2 dm;
|
|
uint8_t scales[12];
|
|
uint8_t qs[QK_K / 2];
|
|
};
|
|
static_assert(sizeof(mkllm_block_q4_K) == 144, "block_q4_K size");
|
|
|
|
struct mkllm_block_q5_K {
|
|
half2 dm;
|
|
uint8_t scales[12];
|
|
uint8_t qh[QK_K / 8];
|
|
uint8_t qs[QK_K / 2];
|
|
};
|
|
static_assert(sizeof(mkllm_block_q5_K) == 176, "block_q5_K size");
|
|
|
|
struct mkllm_block_q6_K {
|
|
uint8_t ql[QK_K / 2];
|
|
uint8_t qh[QK_K / 4];
|
|
int8_t scales[QK_K / 16];
|
|
half d;
|
|
};
|
|
static_assert(sizeof(mkllm_block_q6_K) == 210, "block_q6_K size");
|
|
|
|
static __device__ __forceinline__ int mkllm_get_int_b2(const void * x, int i32) {
|
|
const uint16_t * x16 = (const uint16_t *) x;
|
|
return (int) x16[2 * i32] | ((int) x16[2 * i32 + 1] << 16);
|
|
}
|
|
|
|
static __device__ __forceinline__ int mkllm_get_int_b4(const void * x, int i32) {
|
|
return ((const int *) x)[i32];
|
|
}
|
|
|
|
static __device__ __forceinline__ float mkllm_warp_reduce_max32(float v) {
|
|
#pragma unroll
|
|
for (int off = 16; off > 0; off >>= 1) {
|
|
v = fmaxf(v, __shfl_xor_sync(0xffffffff, v, off));
|
|
}
|
|
return v;
|
|
}
|
|
|
|
static __device__ __forceinline__ float mkllm_warp_reduce_sum32(float v) {
|
|
#pragma unroll
|
|
for (int off = 16; off > 0; off >>= 1) {
|
|
v += __shfl_xor_sync(0xffffffff, v, off);
|
|
}
|
|
return v;
|
|
}
|
|
|
|
// llama.cpp quantize.cu:5-48 + :282-285. CUDA_QUANTIZE_BLOCK_SIZE=256,
|
|
// one thread per value, warp_reduce<QK8_1> inside each 32-lane group.
|
|
#define MKLLM_QUANTIZE_BLOCK_SIZE 256
|
|
static __global__ void __launch_bounds__(MKLLM_QUANTIZE_BLOCK_SIZE, 1)
|
|
mkllm_quantize_q81_kernel(
|
|
const float * __restrict__ x, mkllm_block_q8_1 * __restrict__ y, int ne0) {
|
|
const int i0 = (int) blockDim.x * (int) blockIdx.x + (int) threadIdx.x;
|
|
if (i0 >= ne0) {
|
|
return;
|
|
}
|
|
const int ib = i0 / MKLLM_QK8_1;
|
|
const int iqs = i0 % MKLLM_QK8_1;
|
|
const float xi = x[i0];
|
|
float amax = fabsf(xi);
|
|
float sum = xi;
|
|
amax = mkllm_warp_reduce_max32(amax);
|
|
sum = mkllm_warp_reduce_sum32(sum);
|
|
const float d = amax / 127.0f;
|
|
const int8_t q = amax == 0.0f ? 0 : (int8_t) roundf(xi / d);
|
|
y[ib].qs[iqs] = q;
|
|
if (iqs > 0) {
|
|
return;
|
|
}
|
|
y[ib].ds = make_half2(d, sum);
|
|
}
|
|
|
|
extern "C" cudaError_t mkllm_quantize_q81(
|
|
const float * x, void * y, int k, cudaStream_t stream) {
|
|
if (k <= 0 || (k % MKLLM_Q81_GS) != 0) {
|
|
return cudaErrorInvalidValue;
|
|
}
|
|
const int block_num = (k + MKLLM_QUANTIZE_BLOCK_SIZE - 1) / MKLLM_QUANTIZE_BLOCK_SIZE;
|
|
mkllm_quantize_q81_kernel<<<block_num, MKLLM_QUANTIZE_BLOCK_SIZE, 0, stream>>>(
|
|
x, (mkllm_block_q8_1 *) y, k);
|
|
return cudaGetLastError();
|
|
}
|
|
|
|
#include "fattn/mmvq.cuh"
|
|
#include "fattn/mmq.cuh"
|
|
#include "fattn/norm.cuh"
|
|
// Needs ggml-common.h's block structs + codebook tables, which arrive with
|
|
// mmvq.cuh above.
|
|
#include "iq_convert.cuh"
|
|
|
|
// llama.cpp ggml-cuda.cu:4000 ggml_cuda_op_rms_norm_fused /
|
|
// ggml-cuda.cu:3994 ggml_cuda_op_rms_norm_fused_add. Strides are BYTES.
|
|
extern "C" cudaError_t mkllm_rms_norm_mul(
|
|
const void * x, const void * mul, const void * add, void * dst,
|
|
int ncols, int nrows, int nchannels, int nsamples, float eps,
|
|
size_t x_nb1, size_t x_nb2, size_t x_nb3,
|
|
size_t d_nb1, size_t d_nb2, size_t d_nb3,
|
|
size_t mul_nb1, size_t mul_nb2, size_t mul_nb3,
|
|
int mul_ne0, int mul_ne1, int mul_ne2, int mul_ne3,
|
|
size_t add_nb1, size_t add_nb2, size_t add_nb3,
|
|
int add_ne0, int add_ne1, int add_ne2, int add_ne3,
|
|
cudaStream_t stream) {
|
|
if (ncols <= 0 || nrows <= 0 || nchannels <= 0 || nsamples <= 0 || mul == nullptr) {
|
|
return cudaErrorInvalidValue;
|
|
}
|
|
rms_norm_mul_f32_cuda(
|
|
(const float *) x, (const float *) mul, (const float *) add, (float *) dst,
|
|
ncols, nrows, nchannels, nsamples,
|
|
(int64_t) (x_nb1 / sizeof(float)), (int64_t) (x_nb2 / sizeof(float)),
|
|
(int64_t) (x_nb3 / sizeof(float)),
|
|
(int64_t) (d_nb1 / sizeof(float)), (int64_t) (d_nb2 / sizeof(float)),
|
|
(int64_t) (d_nb3 / sizeof(float)),
|
|
(int64_t) (mul_nb1 / sizeof(float)), (int64_t) (mul_nb2 / sizeof(float)),
|
|
(int64_t) (mul_nb3 / sizeof(float)),
|
|
(uint32_t) mul_ne0, (uint32_t) mul_ne1, (uint32_t) mul_ne2, (uint32_t) mul_ne3,
|
|
(int64_t) (add_nb1 / sizeof(float)), (int64_t) (add_nb2 / sizeof(float)),
|
|
(int64_t) (add_nb3 / sizeof(float)),
|
|
(uint32_t) add_ne0, (uint32_t) add_ne1, (uint32_t) add_ne2, (uint32_t) add_ne3,
|
|
eps, stream);
|
|
return cudaGetLastError();
|
|
}
|
|
|
|
// kind -> ggml_type for the vendored official templates. GGML_TYPE_COUNT is
|
|
// the "no such kind" sentinel; every caller must reject it rather than
|
|
// defaulting, or a new kind added to only some of these switches would decode
|
|
// as Q4_K and return plausible-but-wrong numbers.
|
|
static ggml_type mkllm_kind_to_ggml(int kind) {
|
|
switch (kind) {
|
|
case MKLLM_QUANT_Q4K: return GGML_TYPE_Q4_K;
|
|
case MKLLM_QUANT_Q5K: return GGML_TYPE_Q5_K;
|
|
case MKLLM_QUANT_Q6K: return GGML_TYPE_Q6_K;
|
|
case MKLLM_QUANT_Q80: return GGML_TYPE_Q8_0;
|
|
case MKLLM_QUANT_Q3K: return GGML_TYPE_Q3_K;
|
|
case MKLLM_QUANT_IQ4XS: return GGML_TYPE_IQ4_XS;
|
|
case MKLLM_QUANT_IQ4NL: return GGML_TYPE_IQ4_NL;
|
|
case MKLLM_QUANT_IQ3S: return GGML_TYPE_IQ3_S;
|
|
default: return GGML_TYPE_COUNT;
|
|
}
|
|
}
|
|
|
|
// Bytes per ggml storage block (NOT per 256 values: iq4_nl and q8_0 are
|
|
// 32-value blocks). Returns 0 for an unknown kind; callers must reject it.
|
|
static int mkllm_kind_block_bytes(int kind) {
|
|
switch (kind) {
|
|
case MKLLM_QUANT_Q4K: return (int) sizeof(block_q4_K);
|
|
case MKLLM_QUANT_Q5K: return (int) sizeof(block_q5_K);
|
|
case MKLLM_QUANT_Q6K: return (int) sizeof(block_q6_K);
|
|
case MKLLM_QUANT_Q80: return (int) sizeof(block_q8_0);
|
|
case MKLLM_QUANT_Q3K: return (int) sizeof(block_q3_K);
|
|
case MKLLM_QUANT_IQ4XS: return (int) sizeof(block_iq4_xs);
|
|
case MKLLM_QUANT_IQ4NL: return (int) sizeof(block_iq4_nl);
|
|
case MKLLM_QUANT_IQ3S: return (int) sizeof(block_iq3_s);
|
|
default: return 0;
|
|
}
|
|
}
|
|
|
|
// The MMQ path quantizes activations to block_q8_1_mmq in one of two scale
|
|
// layouts; picking the wrong one silently corrupts the result. Mirrors
|
|
// mmq_get_q8_1_ds_layout() in fattn/mmq.cuh. Returns -1 for an unknown kind.
|
|
static int mkllm_kind_mmq_ds4(int kind) {
|
|
switch (kind) {
|
|
case MKLLM_QUANT_Q4K:
|
|
case MKLLM_QUANT_Q5K: return 1; // MMQ_Q8_1_DS_LAYOUT_DS4
|
|
case MKLLM_QUANT_Q6K:
|
|
case MKLLM_QUANT_Q3K:
|
|
case MKLLM_QUANT_IQ4XS:
|
|
case MKLLM_QUANT_IQ4NL:
|
|
case MKLLM_QUANT_IQ3S: return 0; // MMQ_Q8_1_DS_LAYOUT_D4
|
|
default: return -1;
|
|
}
|
|
}
|
|
|
|
// Bytes the FFI caller must reserve per (row-of-256-values) unit when it
|
|
// addresses a weight row in 256-value units. Returns 0 for an unknown kind.
|
|
static int mkllm_kind_bytes_per_256(int kind) {
|
|
const int blk = mkllm_kind_block_bytes(kind);
|
|
if (blk == 0) {
|
|
return 0;
|
|
}
|
|
switch (kind) {
|
|
case MKLLM_QUANT_Q80:
|
|
case MKLLM_QUANT_IQ4NL: return blk * 8; // 32-value blocks
|
|
default: return blk; // 256-value super-blocks
|
|
}
|
|
}
|
|
|
|
// Which quantized-matmul routes a kind is VERIFIED on, bit 0 = official MMVQ
|
|
// (decode), bit 1 = official J=128 MMQ (prefill). A kind may always fall back
|
|
// to `mkllm_dequant_rows_bf16` + cuBLAS, which is checked separately and is
|
|
// exact up to bf16 rounding, so clearing a bit costs speed, never support.
|
|
//
|
|
// IQ3_S is cleared on both: `llama-cuda-canary opcheck` shows its tiles
|
|
// disagreeing with the dequant by ~1e-2 of the summed term magnitude on
|
|
// sm_120, where every other kind lands near 3e-5 — while `getrows_iq3s`
|
|
// proves the dequant itself is bit-exact against the scalar reference (which
|
|
// is in turn pinned to llama.cpp's gguf-py dequantizers). The vendored
|
|
// vec_dot_iq3_s_q8_1 / load_tiles_iq3_s are byte-identical to upstream and the
|
|
// iq3s_grid table matches, so this is an open upstream-kernel question, not a
|
|
// porting slip — parked with the evidence rather than shipped with a route
|
|
// that returns plausible-but-wrong numbers. IQ3_S is 4 tensors of 866 in
|
|
// Qwen3.8-27B-UD-Q4_K_M, so the cost of the dequant fallback is noise.
|
|
// Re-enable by flipping a bit here and re-running the canary.
|
|
#define MKLLM_ROUTE_MMVQ 1
|
|
#define MKLLM_ROUTE_MMQ 2
|
|
|
|
static int mkllm_kind_route_mask(int kind) {
|
|
switch (kind) {
|
|
case MKLLM_QUANT_Q4K:
|
|
case MKLLM_QUANT_Q5K:
|
|
case MKLLM_QUANT_Q6K:
|
|
case MKLLM_QUANT_Q3K:
|
|
case MKLLM_QUANT_IQ4NL:
|
|
case MKLLM_QUANT_IQ4XS: return MKLLM_ROUTE_MMVQ | MKLLM_ROUTE_MMQ;
|
|
case MKLLM_QUANT_IQ3S: return 0;
|
|
// q8_0 decode is the hand-written mmv_quant kernel, not official MMVQ.
|
|
case MKLLM_QUANT_Q80: return 0;
|
|
default: return -1;
|
|
}
|
|
}
|
|
|
|
extern "C" int mkllm_quant_kind_routes(int kind) {
|
|
return mkllm_kind_route_mask(kind);
|
|
}
|
|
|
|
extern "C" int mkllm_quant_kind_block_bytes(int kind) {
|
|
return mkllm_kind_block_bytes(kind);
|
|
}
|
|
|
|
// Exposed so the Rust dispatcher picks the activation layout from the same
|
|
// table the kernels use, instead of keeping a second copy that can drift.
|
|
extern "C" int mkllm_quant_kind_mmq_ds4(int kind) {
|
|
return mkllm_kind_mmq_ds4(kind);
|
|
}
|
|
|
|
extern "C" int mkllm_quant_kind_bytes_per_256(int kind) {
|
|
return mkllm_kind_bytes_per_256(kind);
|
|
}
|
|
|
|
// llama.cpp mmvq.cu ggml_cuda_mul_mat_vec_q: ONE weight read serves up to
|
|
// MMVQ_MAX_BATCH_SIZE destination columns. Pinning this at ncols_dst=1 sent
|
|
// every 2..8-token batch to the generic float mat-vec, which costs ~2/3 of a
|
|
// full forward PER EXTRA TOKEN — exactly what stops speculative decoding from
|
|
// paying. The kernel template already spans the whole range; only the
|
|
// launcher had to learn it.
|
|
template <ggml_type type, int ncols_dst, bool has_fusion>
|
|
static void mkllm_launch_mmvq_official_ncols(
|
|
const void * vx, const void * vgate, const void * vy, float * dst,
|
|
int k, int n, int stride_row_x, cudaStream_t stream) {
|
|
ggml_cuda_mm_fusion_args_device fusion{};
|
|
if constexpr (has_fusion) {
|
|
fusion.gate = vgate;
|
|
fusion.glu_op = GGML_GLU_OP_SWIGLU;
|
|
}
|
|
// mmvq.cu:729-731 + :807-814: ids=null, small_k=false. nchannels_y_fd is
|
|
// zero when ids is null; channel/sample ratios are fastdiv(1) and their
|
|
// strides 1 (ggml_cuda_op_mul_mat_vec_q).
|
|
const uint3 nchannels_y = make_uint3(0, 0, 0);
|
|
const uint3 channel_ratio = init_fastdiv_values(1);
|
|
const uint3 sample_ratio = init_fastdiv_values(1);
|
|
const int nwarps = calc_nwarps(type, ncols_dst, MMVQ_PARAMETERS_GENERIC);
|
|
const int rows_per_block =
|
|
calc_rows_per_block(ncols_dst, MMVQ_PARAMETERS_GENERIC, false, nwarps);
|
|
const dim3 block_dims(32, (unsigned) nwarps, 1);
|
|
const dim3 block_nums((unsigned) ((n + rows_per_block - 1) / rows_per_block), 1, 1);
|
|
const uint32_t stride_col_y = (uint32_t) (k / QK8_1);
|
|
static int trace_mode = -1;
|
|
static int traces = 0;
|
|
if (trace_mode < 0) {
|
|
trace_mode = getenv("MAKEPAD_LLAMA_MMVQ_TRACE") ? 1 : 0;
|
|
}
|
|
if (trace_mode && traces < 16) {
|
|
fprintf(stderr,
|
|
"mmvq.launch: official=mmvq.cu:389 type=%d ncols_dst=%d fuse=%d nwarps=%d "
|
|
"rows_per_block=%d grid=(%u,1,1) k=%d n=%d stride_row_x=%d stride_col_y=%u\n",
|
|
(int) type, ncols_dst, (int) has_fusion, nwarps, rows_per_block,
|
|
block_nums.x, k, n, stride_row_x, stride_col_y);
|
|
++traces;
|
|
}
|
|
mul_mat_vec_q<type, ncols_dst, has_fusion, false><<<block_nums, block_dims, 0, stream>>>(
|
|
vx, vy, nullptr, fusion, dst,
|
|
(uint32_t) k, nchannels_y, (uint32_t) stride_row_x, stride_col_y,
|
|
(uint32_t) n, channel_ratio, 1u, 1u, 1u,
|
|
sample_ratio, 1u, 1u, 1u, 0u);
|
|
}
|
|
|
|
template <ggml_type type, bool has_fusion>
|
|
static cudaError_t mkllm_launch_mmvq_official(
|
|
const void * vx, const void * vgate, const void * vy, float * dst,
|
|
int k, int n, int m, int stride_row_x, cudaStream_t stream) {
|
|
#define MKLLM_MMVQ_CASE(NC) \
|
|
case NC: mkllm_launch_mmvq_official_ncols<type, NC, has_fusion>( \
|
|
vx, vgate, vy, dst, k, n, stride_row_x, stream); break;
|
|
switch (m) {
|
|
MKLLM_MMVQ_CASE(1)
|
|
MKLLM_MMVQ_CASE(2)
|
|
MKLLM_MMVQ_CASE(3)
|
|
MKLLM_MMVQ_CASE(4)
|
|
MKLLM_MMVQ_CASE(5)
|
|
MKLLM_MMVQ_CASE(6)
|
|
MKLLM_MMVQ_CASE(7)
|
|
MKLLM_MMVQ_CASE(8)
|
|
default: return cudaErrorInvalidValue;
|
|
}
|
|
#undef MKLLM_MMVQ_CASE
|
|
return cudaSuccess;
|
|
}
|
|
|
|
// llama.cpp mmq.cuh:3960 launch_mul_mat_q (shared size, stream-k nsm, fixup).
|
|
// Host ggml_backend_cuda_context trimmed; kernel is the official template.
|
|
template <ggml_type type, bool need_check>
|
|
static cudaError_t mkllm_launch_mul_mat_q_checked(
|
|
const void * x, const void * y, float * dst,
|
|
int k, int n, int m, int stride_row_x, int stride_col_dst,
|
|
int nsm, float * tmp_fixup, cudaStream_t stream) {
|
|
constexpr int mmq_x = 128;
|
|
static int mmq_prints = 0;
|
|
if (mmq_prints < 8 && (m % 128 != 0)) {
|
|
fprintf(stderr, "mmq.launch: type=%d m=%d n=%d k=%d mmq_x=%d need_check=%d nsm=%d\n",
|
|
(int) type, m, n, k, mmq_x, (int) need_check, nsm);
|
|
++mmq_prints;
|
|
}
|
|
const int cc = 890;
|
|
const int warp_size = 32;
|
|
const int nwarps = mmq_get_nwarps_host(cc, warp_size);
|
|
const int mmq_y = get_mmq_y_host(cc);
|
|
const int nbytes_shared = (int) mmq_get_nbytes_shared<type>(
|
|
mmq_x, mmq_y, cc, warp_size, nwarps);
|
|
const dim3 block_dims((unsigned) warp_size, (unsigned) nwarps, 1);
|
|
const int nty = (n + mmq_y - 1) / mmq_y;
|
|
const int ntx = (m + mmq_x - 1) / mmq_x;
|
|
cudaError_t err = cudaFuncSetAttribute(
|
|
mul_mat_q<type, mmq_x, need_check>,
|
|
cudaFuncAttributeMaxDynamicSharedMemorySize, nbytes_shared);
|
|
if (err != cudaSuccess) {
|
|
return err;
|
|
}
|
|
const bool use_stream_k = nsm > 0 && tmp_fixup != nullptr;
|
|
if (!use_stream_k) {
|
|
dim3 grid((unsigned) ntx * (unsigned) nty, 1, 1);
|
|
mul_mat_q<type, mmq_x, need_check><<<grid, block_dims, nbytes_shared, stream>>>(
|
|
(const char *) x, (const int *) y, nullptr, nullptr, dst, nullptr,
|
|
k, n, m, stride_row_x, m, stride_col_dst,
|
|
1, 1, 0, 0, 0,
|
|
1, 1, 0, 0, 0,
|
|
m);
|
|
return cudaGetLastError();
|
|
}
|
|
dim3 grid_sk((unsigned) nsm, 1, 1);
|
|
const bool fixup_needed = ((int64_t) ntx * nty) % nsm != 0;
|
|
mul_mat_q<type, mmq_x, need_check><<<grid_sk, block_dims, nbytes_shared, stream>>>(
|
|
(const char *) x, (const int *) y, nullptr, nullptr, dst, tmp_fixup,
|
|
k, n, m, stride_row_x, m, stride_col_dst,
|
|
1, 1, 0, 0, 0,
|
|
1, 1, 0, 0, 0,
|
|
m);
|
|
err = cudaGetLastError();
|
|
if (err != cudaSuccess) {
|
|
return err;
|
|
}
|
|
if (!fixup_needed) {
|
|
return cudaSuccess;
|
|
}
|
|
mul_mat_q_stream_k_fixup<type, mmq_x, need_check><<<grid_sk, block_dims, 0, stream>>>(
|
|
nullptr, nullptr, dst, tmp_fixup,
|
|
k, n, m, (size_t) stride_col_dst,
|
|
1, 0, 1, 0, m);
|
|
return cudaGetLastError();
|
|
}
|
|
|
|
template <ggml_type type>
|
|
static cudaError_t mkllm_launch_mul_mat_q(
|
|
const void * x, const void * y, float * dst,
|
|
int k, int n, int m, int stride_row_x, int stride_col_dst,
|
|
int nsm, float * tmp_fixup, cudaStream_t stream) {
|
|
const int mmq_y = get_mmq_y_host(890);
|
|
if (n % mmq_y == 0) {
|
|
return mkllm_launch_mul_mat_q_checked<type, false>(
|
|
x, y, dst, k, n, m, stride_row_x, stride_col_dst, nsm, tmp_fixup, stream);
|
|
}
|
|
return mkllm_launch_mul_mat_q_checked<type, true>(
|
|
x, y, dst, k, n, m, stride_row_x, stride_col_dst, nsm, tmp_fixup, stream);
|
|
}
|
|
|
|
#if 0
|
|
// Homemade MMVQ inner loop retired: official vecdotq.cuh + mul_mat_vec_q.
|
|
static __device__ __forceinline__ int mkllm_ld_i32_b2(const void * p) {
|
|
const uint16_t * x = (const uint16_t *) p;
|
|
return (int) x[0] | ((int) x[1] << 16);
|
|
}
|
|
|
|
static __device__ __forceinline__ int mkllm_dp4a(int a, int b, int c) {
|
|
#if defined(__CUDA_ARCH__) && __CUDA_ARCH__ >= 610
|
|
return __dp4a(a, b, c);
|
|
#else
|
|
const signed char * aa = (const signed char *) &a;
|
|
const signed char * bb = (const signed char *) &b;
|
|
return c + (int) aa[0] * bb[0] + (int) aa[1] * bb[1]
|
|
+ (int) aa[2] * bb[2] + (int) aa[3] * bb[3];
|
|
#endif
|
|
}
|
|
|
|
// llama.cpp vecdotq.cuh:502-524 vec_dot_q4_K_q8_1_impl_vmmq
|
|
static __device__ __forceinline__ float mkllm_vec_dot_q4_K_q8_1_impl_vmmq(
|
|
const int * __restrict__ v, const int * __restrict__ u, const uint8_t * __restrict__ sc,
|
|
const uint8_t * __restrict__ m, const half2 & dm4, const float * __restrict__ d8) {
|
|
float sumf_d = 0.0f;
|
|
float sumf_m = 0.0f;
|
|
#pragma unroll
|
|
for (int i = 0; i < MKLLM_QR4_K; ++i) {
|
|
const int v0i = (v[0] >> (4 * i)) & 0x0F0F0F0F;
|
|
const int v1i = (v[1] >> (4 * i)) & 0x0F0F0F0F;
|
|
const int dot1 = mkllm_dp4a(v1i, u[2 * i + 1], mkllm_dp4a(v0i, u[2 * i + 0], 0));
|
|
const int dot2 = mkllm_dp4a(0x01010101, u[2 * i + 1], mkllm_dp4a(0x01010101, u[2 * i + 0], 0));
|
|
sumf_d += d8[i] * (dot1 * sc[i]);
|
|
sumf_m += d8[i] * (dot2 * m[i]);
|
|
}
|
|
const float2 dm4f = __half22float2(dm4);
|
|
return dm4f.x * sumf_d - dm4f.y * sumf_m;
|
|
}
|
|
|
|
// llama.cpp vecdotq.cuh:816-859 vec_dot_q4_K_q8_1
|
|
static __device__ __forceinline__ float mkllm_vec_dot_q4_K_q8_1(
|
|
const void * vbq, const mkllm_block_q8_1 * bq8_1, int kbx, int iqs) {
|
|
const mkllm_block_q4_K * bq4_K = (const mkllm_block_q4_K *) vbq + kbx;
|
|
int v[2];
|
|
int u[2 * MKLLM_QR4_K];
|
|
float d8[MKLLM_QR4_K];
|
|
const int bq8_offset = MKLLM_QR4_K * ((iqs / 2) / (MKLLM_QI8_1 / 2));
|
|
const int * q4 = (const int *) (bq4_K->qs + 16 * bq8_offset + 4 * ((iqs / 2) % 4));
|
|
v[0] = q4[0];
|
|
v[1] = q4[4];
|
|
const uint16_t * scales = (const uint16_t *) bq4_K->scales;
|
|
uint16_t aux[2];
|
|
const int j = bq8_offset / 2;
|
|
if (j < 2) {
|
|
aux[0] = scales[j + 0] & 0x3f3f;
|
|
aux[1] = scales[j + 2] & 0x3f3f;
|
|
} else {
|
|
aux[0] = ((scales[j + 2] >> 0) & 0x0f0f) | ((scales[j - 2] & 0xc0c0) >> 2);
|
|
aux[1] = ((scales[j + 2] >> 4) & 0x0f0f) | ((scales[j - 0] & 0xc0c0) >> 2);
|
|
}
|
|
const uint8_t * sc = (const uint8_t *) aux;
|
|
const uint8_t * m = sc + 2;
|
|
#pragma unroll
|
|
for (int i = 0; i < MKLLM_QR4_K; ++i) {
|
|
const mkllm_block_q8_1 * bq8i = bq8_1 + bq8_offset + i;
|
|
d8[i] = __low2float(bq8i->ds);
|
|
const int * q8 = (const int *) bq8i->qs + ((iqs / 2) % 4);
|
|
u[2 * i + 0] = q8[0];
|
|
u[2 * i + 1] = q8[4];
|
|
}
|
|
return mkllm_vec_dot_q4_K_q8_1_impl_vmmq(v, u, sc, m, bq4_K->dm, d8);
|
|
}
|
|
|
|
static __device__ __forceinline__ float mkllm_vec_dot_q5_K_q8_1(
|
|
const void * vbq, const mkllm_block_q8_1 * bq8_1, int kbx, int iqs) {
|
|
const mkllm_block_q5_K * bq5_K = (const mkllm_block_q5_K *) vbq + kbx;
|
|
int vl[2];
|
|
int vh[2];
|
|
int u[2 * MKLLM_QR5_K];
|
|
float d8[MKLLM_QR5_K];
|
|
const int bq8_offset = MKLLM_QR5_K * ((iqs / 2) / (MKLLM_QI8_1 / 2));
|
|
const int * ql = (const int *) (bq5_K->qs + 16 * bq8_offset + 4 * ((iqs / 2) % 4));
|
|
const int * qh = (const int *) (bq5_K->qh + 4 * ((iqs / 2) % 4));
|
|
vl[0] = ql[0];
|
|
vl[1] = ql[4];
|
|
vh[0] = qh[0] >> bq8_offset;
|
|
vh[1] = qh[4] >> bq8_offset;
|
|
const uint16_t * scales = (const uint16_t *) bq5_K->scales;
|
|
uint16_t aux[2];
|
|
const int j = bq8_offset / 2;
|
|
if (j < 2) {
|
|
aux[0] = scales[j + 0] & 0x3f3f;
|
|
aux[1] = scales[j + 2] & 0x3f3f;
|
|
} else {
|
|
aux[0] = ((scales[j + 2] >> 0) & 0x0f0f) | ((scales[j - 2] & 0xc0c0) >> 2);
|
|
aux[1] = ((scales[j + 2] >> 4) & 0x0f0f) | ((scales[j - 0] & 0xc0c0) >> 2);
|
|
}
|
|
const uint8_t * sc = (const uint8_t *) aux;
|
|
const uint8_t * m = sc + 2;
|
|
#pragma unroll
|
|
for (int i = 0; i < MKLLM_QR5_K; ++i) {
|
|
const mkllm_block_q8_1 * bq8i = bq8_1 + bq8_offset + i;
|
|
d8[i] = __low2float(bq8i->ds);
|
|
const int * q8 = (const int *) bq8i->qs + ((iqs / 2) % 4);
|
|
u[2 * i + 0] = q8[0];
|
|
u[2 * i + 1] = q8[4];
|
|
}
|
|
float sumf_d = 0.0f;
|
|
float sumf_m = 0.0f;
|
|
#pragma unroll
|
|
for (int i = 0; i < MKLLM_QR5_K; ++i) {
|
|
const int vl0i = (vl[0] >> (4 * i)) & 0x0F0F0F0F;
|
|
const int vl1i = (vl[1] >> (4 * i)) & 0x0F0F0F0F;
|
|
const int vh0i = ((vh[0] >> i) << 4) & 0x10101010;
|
|
const int vh1i = ((vh[1] >> i) << 4) & 0x10101010;
|
|
const int v0i = vl0i | vh0i;
|
|
const int v1i = vl1i | vh1i;
|
|
const int dot1 = mkllm_dp4a(v0i, u[2 * i + 0], mkllm_dp4a(v1i, u[2 * i + 1], 0));
|
|
const int dot2 = mkllm_dp4a(0x01010101, u[2 * i + 0], mkllm_dp4a(0x01010101, u[2 * i + 1], 0));
|
|
sumf_d += d8[i] * (dot1 * sc[i]);
|
|
sumf_m += d8[i] * (dot2 * m[i]);
|
|
}
|
|
const float2 dm5f = __half22float2(bq5_K->dm);
|
|
return dm5f.x * sumf_d - dm5f.y * sumf_m;
|
|
}
|
|
|
|
static __device__ __forceinline__ float mkllm_vec_dot_q6_K_q8_1(
|
|
const void * vbq, const mkllm_block_q8_1 * bq8_1, int kbx, int iqs) {
|
|
const mkllm_block_q6_K * bq6_K = (const mkllm_block_q6_K *) vbq + kbx;
|
|
const int bq8_offset = 2 * MKLLM_QR6_K * (iqs / (MKLLM_QI6_K / 2))
|
|
+ (iqs % (MKLLM_QI6_K / 2)) / (MKLLM_QI6_K / 4);
|
|
const int scale_offset = (MKLLM_QI6_K / 4) * (iqs / (MKLLM_QI6_K / 2))
|
|
+ (iqs % (MKLLM_QI6_K / 2)) / (MKLLM_QI6_K / 8);
|
|
const int vh_shift = 2 * ((iqs % (MKLLM_QI6_K / 2)) / (MKLLM_QI6_K / 4));
|
|
const int vl = mkllm_get_int_b2(bq6_K->ql, iqs);
|
|
const int vh = mkllm_get_int_b2(
|
|
bq6_K->qh, (MKLLM_QI6_K / 4) * (iqs / (MKLLM_QI6_K / 2)) + iqs % (MKLLM_QI6_K / 4)) >> vh_shift;
|
|
const int8_t * scales = bq6_K->scales + scale_offset;
|
|
int u[MKLLM_QR6_K];
|
|
float d8[MKLLM_QR6_K];
|
|
#pragma unroll
|
|
for (int i = 0; i < MKLLM_QR6_K; ++i) {
|
|
u[i] = mkllm_get_int_b4(bq8_1[bq8_offset + 2 * i].qs, iqs % MKLLM_QI8_1);
|
|
d8[i] = __low2float(bq8_1[bq8_offset + 2 * i].ds);
|
|
}
|
|
float sumf = 0.0f;
|
|
#pragma unroll
|
|
for (int i = 0; i < MKLLM_QR6_K; ++i) {
|
|
const int sc = scales[4 * i];
|
|
const int vil = (vl >> (4 * i)) & 0x0F0F0F0F;
|
|
const int vih = ((vh >> (4 * i)) << 4) & 0x30303030;
|
|
#if defined(__CUDA_ARCH__) && __CUDA_ARCH__ >= 300
|
|
const int vi = __vsubss4(vil | vih, 0x20202020);
|
|
#else
|
|
const int vi = (vil | vih) - 0x20202020;
|
|
#endif
|
|
sumf += d8[i] * (mkllm_dp4a(vi, u[i], 0) * sc);
|
|
}
|
|
return __half2float(bq6_K->d) * sumf;
|
|
}
|
|
|
|
template <int KIND>
|
|
static __device__ __forceinline__ float mkllm_vec_dot_q_templ(
|
|
const void * vx, const mkllm_block_q8_1 * y, int kbx, int iqs) {
|
|
if constexpr (KIND == MKLLM_QUANT_Q5K) {
|
|
return mkllm_vec_dot_q5_K_q8_1(vx, y, kbx, iqs);
|
|
} else if constexpr (KIND == MKLLM_QUANT_Q6K) {
|
|
return mkllm_vec_dot_q6_K_q8_1(vx, y, kbx, iqs);
|
|
} else {
|
|
return mkllm_vec_dot_q4_K_q8_1(vx, y, kbx, iqs);
|
|
}
|
|
}
|
|
|
|
// llama.cpp mmvq.cu:389-589 mul_mat_vec_q specialized to
|
|
// ncols_dst=1, rows_per_cuda_block=1, GENERIC nwarps=4, no ids.
|
|
// tmp[ncols][rpb], warp_reduce_sum<warp_size>, write threadIdx.x < rpb.
|
|
template <int KIND, int FUSE>
|
|
static __global__ void __launch_bounds__(MKLLM_MMVQ_NWARPS * 32, 1) mkllm_mmvq_q81_kernel(
|
|
const void * __restrict__ vx,
|
|
const void * __restrict__ vgate,
|
|
const mkllm_block_q8_1 * __restrict__ vy,
|
|
float * __restrict__ dst,
|
|
int k, int n, size_t src0_row_bytes, size_t gate_row_bytes) {
|
|
constexpr int ncols_dst = 1;
|
|
constexpr int rows_per_cuda_block = 1;
|
|
constexpr int nwarps = MKLLM_MMVQ_NWARPS;
|
|
constexpr int warp_size = 32;
|
|
constexpr int vdr = (KIND == MKLLM_QUANT_Q6K) ? MKLLM_VDR_Q6_K_MMVQ : MKLLM_VDR_Q4_K_MMVQ;
|
|
constexpr int qi = (KIND == MKLLM_QUANT_Q6K) ? MKLLM_QI6_K : MKLLM_QI4_K;
|
|
constexpr int qk = QK_K;
|
|
constexpr int blk_bytes = KIND == MKLLM_QUANT_Q6K ? (int) sizeof(mkllm_block_q6_K)
|
|
: KIND == MKLLM_QUANT_Q5K ? (int) sizeof(mkllm_block_q5_K)
|
|
: (int) sizeof(mkllm_block_q4_K);
|
|
const int tid = warp_size * (int) threadIdx.y + (int) threadIdx.x;
|
|
const int row0 = rows_per_cuda_block * (int) blockIdx.x;
|
|
const int blocks_per_row_x = k / qk;
|
|
constexpr int blocks_per_iter = vdr * nwarps * warp_size / qi;
|
|
const int stride_row_x = (int) (src0_row_bytes / (size_t) blk_bytes);
|
|
const int kbx_offset = row0 * stride_row_x;
|
|
const mkllm_block_q8_1 * y = vy;
|
|
float tmp[ncols_dst][rows_per_cuda_block] = {{0.0f}};
|
|
float tmp_gate[ncols_dst][rows_per_cuda_block] = {{0.0f}};
|
|
for (int kbx = tid / (qi / vdr); kbx < blocks_per_row_x; kbx += blocks_per_iter) {
|
|
const int kby = kbx * (qk / MKLLM_QK8_1);
|
|
const int kqs = vdr * (tid % (qi / vdr));
|
|
#pragma unroll
|
|
for (int j = 0; j < ncols_dst; ++j) {
|
|
#pragma unroll
|
|
for (int i = 0; i < rows_per_cuda_block; ++i) {
|
|
tmp[j][i] += mkllm_vec_dot_q_templ<KIND>(
|
|
vx, &y[j + kby], kbx_offset + i * stride_row_x + kbx, kqs);
|
|
if constexpr (FUSE) {
|
|
const int stride_gate = (int) (gate_row_bytes / (size_t) blk_bytes);
|
|
tmp_gate[j][i] += mkllm_vec_dot_q_templ<KIND>(
|
|
vgate, &y[j + kby], row0 * stride_gate + i * stride_gate + kbx, kqs);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
__shared__ float tmp_shared[nwarps - 1 > 0 ? nwarps - 1 : 1][ncols_dst][rows_per_cuda_block][warp_size];
|
|
__shared__ float tmp_shared_gate[(FUSE && (nwarps - 1 > 0)) ? nwarps - 1 : 1][ncols_dst][rows_per_cuda_block][warp_size];
|
|
if (threadIdx.y > 0) {
|
|
#pragma unroll
|
|
for (int j = 0; j < ncols_dst; ++j) {
|
|
#pragma unroll
|
|
for (int i = 0; i < rows_per_cuda_block; ++i) {
|
|
tmp_shared[threadIdx.y - 1][j][i][threadIdx.x] = tmp[j][i];
|
|
if constexpr (FUSE) {
|
|
tmp_shared_gate[threadIdx.y - 1][j][i][threadIdx.x] = tmp_gate[j][i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
__syncthreads();
|
|
if (threadIdx.y > 0) {
|
|
return;
|
|
}
|
|
dst += row0;
|
|
#pragma unroll
|
|
for (int j = 0; j < ncols_dst; ++j) {
|
|
#pragma unroll
|
|
for (int i = 0; i < rows_per_cuda_block; ++i) {
|
|
#pragma unroll
|
|
for (int l = 0; l < nwarps - 1; ++l) {
|
|
tmp[j][i] += tmp_shared[l][j][i][threadIdx.x];
|
|
if constexpr (FUSE) {
|
|
tmp_gate[j][i] += tmp_shared_gate[l][j][i][threadIdx.x];
|
|
}
|
|
}
|
|
tmp[j][i] = mkllm_warp_reduce_sum32(tmp[j][i]);
|
|
if constexpr (FUSE) {
|
|
tmp_gate[j][i] = mkllm_warp_reduce_sum32(tmp_gate[j][i]);
|
|
}
|
|
}
|
|
// mmvq.cu:554: threadIdx.x < rows_per_cuda_block
|
|
if (threadIdx.x < rows_per_cuda_block
|
|
&& (rows_per_cuda_block == 1 || uint32_t(row0 + threadIdx.x) < (uint32_t) n)) {
|
|
float result = tmp[j][threadIdx.x];
|
|
if constexpr (FUSE) {
|
|
const float gate_value = tmp_gate[j][threadIdx.x];
|
|
result *= gate_value / (1.0f + expf(-gate_value));
|
|
}
|
|
dst[j + threadIdx.x] = result;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static cudaError_t mkllm_launch_mmvq_q81(
|
|
int kind, int fuse,
|
|
const void * src0, const void * gate, const void * y, float * dst,
|
|
int K, int N, int M, size_t src0_row_bytes, size_t gate_row_bytes,
|
|
cudaStream_t stream) {
|
|
if (K <= 0 || N <= 0 || (K % QK_K) != 0 || M < 1 || M > MMVQ_MAX_BATCH_SIZE) {
|
|
return cudaErrorInvalidValue;
|
|
}
|
|
if (fuse && (gate == nullptr || gate_row_bytes == 0)) {
|
|
return cudaErrorInvalidValue;
|
|
}
|
|
// Bytes per ggml block (34 for q8_0, 18 for iq4_nl, 256-value super-block
|
|
// otherwise) — mul_mat_vec_q's stride_row_x counts BLOCKS of src0's type.
|
|
const int blk_bytes = mkllm_kind_block_bytes(kind);
|
|
const int per_256 = mkllm_kind_bytes_per_256(kind);
|
|
if (blk_bytes == 0 || per_256 == 0) {
|
|
return cudaErrorInvalidValue;
|
|
}
|
|
if ((mkllm_kind_route_mask(kind) & MKLLM_ROUTE_MMVQ) == 0) {
|
|
return cudaErrorInvalidValue;
|
|
}
|
|
if (src0_row_bytes < (size_t) (K / QK_K) * (size_t) per_256
|
|
|| (src0_row_bytes % (size_t) blk_bytes) != 0) {
|
|
return cudaErrorInvalidValue;
|
|
}
|
|
if (fuse && gate_row_bytes != src0_row_bytes) {
|
|
return cudaErrorInvalidValue;
|
|
}
|
|
const int stride_row_x = (int) (src0_row_bytes / (size_t) blk_bytes);
|
|
cudaError_t launch_err = cudaSuccess;
|
|
// One arm per kind, no permissive default: a kind added here but not to
|
|
// the other switches must fail loudly instead of decoding as Q4_K.
|
|
#define MKLLM_MMVQ_KIND(KIND, TYPE) \
|
|
case KIND: \
|
|
launch_err = fuse \
|
|
? mkllm_launch_mmvq_official<TYPE, true>( \
|
|
src0, gate, y, dst, K, N, M, stride_row_x, stream) \
|
|
: mkllm_launch_mmvq_official<TYPE, false>( \
|
|
src0, nullptr, y, dst, K, N, M, stride_row_x, stream); \
|
|
break;
|
|
switch (kind) {
|
|
MKLLM_MMVQ_KIND(MKLLM_QUANT_Q4K, GGML_TYPE_Q4_K)
|
|
MKLLM_MMVQ_KIND(MKLLM_QUANT_Q5K, GGML_TYPE_Q5_K)
|
|
MKLLM_MMVQ_KIND(MKLLM_QUANT_Q6K, GGML_TYPE_Q6_K)
|
|
MKLLM_MMVQ_KIND(MKLLM_QUANT_Q80, GGML_TYPE_Q8_0)
|
|
MKLLM_MMVQ_KIND(MKLLM_QUANT_Q3K, GGML_TYPE_Q3_K)
|
|
MKLLM_MMVQ_KIND(MKLLM_QUANT_IQ4XS, GGML_TYPE_IQ4_XS)
|
|
MKLLM_MMVQ_KIND(MKLLM_QUANT_IQ4NL, GGML_TYPE_IQ4_NL)
|
|
MKLLM_MMVQ_KIND(MKLLM_QUANT_IQ3S, GGML_TYPE_IQ3_S)
|
|
default: return cudaErrorInvalidValue;
|
|
}
|
|
#undef MKLLM_MMVQ_KIND
|
|
if (launch_err != cudaSuccess) {
|
|
return launch_err;
|
|
}
|
|
return cudaGetLastError();
|
|
}
|
|
|
|
extern "C" cudaError_t mkllm_mmv_quant_q81(
|
|
int kind,
|
|
const void * src0, const void * y, float * dst,
|
|
int K, int N, int M,
|
|
size_t src0_row_bytes, size_t dst_col_elems,
|
|
cudaStream_t stream) {
|
|
(void) dst_col_elems;
|
|
return mkllm_launch_mmvq_q81(
|
|
kind, 0, src0, nullptr, y, dst, K, N, M, src0_row_bytes, 0, stream);
|
|
}
|
|
|
|
extern "C" cudaError_t mkllm_mmv_quant_q81_swiglu(
|
|
int kind,
|
|
const void * up, const void * gate, const void * y, float * dst,
|
|
int K, int N, int M,
|
|
size_t up_row_bytes, size_t gate_row_bytes,
|
|
cudaStream_t stream) {
|
|
return mkllm_launch_mmvq_q81(
|
|
kind, 1, up, gate, y, dst, K, N, M, up_row_bytes, gate_row_bytes, stream);
|
|
}
|
|
|
|
#if 0
|
|
// Retired unfaithful Q8_1 MMV (f32 scale, 2 warps) starts here.
|
|
template <int KIND>
|
|
static __device__ __forceinline__ float mkllm_q81_dot_sb(
|
|
const uint8_t * __restrict__ blk,
|
|
const int8_t * __restrict__ q8,
|
|
const float * __restrict__ d8,
|
|
int lane) {
|
|
float acc = 0.0f;
|
|
if (KIND == MKLLM_QUANT_Q4K || KIND == MKLLM_QUANT_Q5K) {
|
|
const float d = __half2float(((const __half *) blk)[0]);
|
|
const float dmin = __half2float(((const __half *) blk)[1]);
|
|
const uint8_t * scales = blk + 4;
|
|
const uint8_t * qs = blk + (KIND == MKLLM_QUANT_Q4K ? 16 : 48);
|
|
const int group = lane >> 3;
|
|
const int byte4 = (lane & 7) * 4;
|
|
const uint32_t packed = *(const uint32_t *) (qs + group * 32 + byte4);
|
|
const int sb_lo = 2 * group;
|
|
uint8_t sc_lo, m_lo, sc_hi, m_hi;
|
|
mkllm_scale_min_k4(sb_lo, scales, &sc_lo, &m_lo);
|
|
mkllm_scale_min_k4(sb_lo + 1, scales, &sc_hi, &m_hi);
|
|
int q_lo = (int) (packed & 0x0F0F0F0Fu);
|
|
int q_hi = (int) ((packed >> 4) & 0x0F0F0F0Fu);
|
|
if (KIND == MKLLM_QUANT_Q5K) {
|
|
int hi_lo = 0;
|
|
int hi_hi = 0;
|
|
#pragma unroll
|
|
for (int i = 0; i < 4; i++) {
|
|
const uint8_t qh = blk[16 + byte4 + i];
|
|
if (qh & (1u << sb_lo)) hi_lo |= 16 << (8 * i);
|
|
if (qh & (2u << sb_lo)) hi_hi |= 16 << (8 * i);
|
|
}
|
|
q_lo |= hi_lo;
|
|
q_hi |= hi_hi;
|
|
}
|
|
const int lo_at = group * 64 + byte4;
|
|
const int u_lo = *(const int *) (q8 + lo_at);
|
|
const int u_hi = *(const int *) (q8 + lo_at + 32);
|
|
const float ds_lo = d8[group * 2];
|
|
const float ds_hi = d8[group * 2 + 1];
|
|
const int dot_lo = mkllm_dp4a(q_lo, u_lo, 0);
|
|
const int dot_hi = mkllm_dp4a(q_hi, u_hi, 0);
|
|
const int sum_lo = mkllm_dp4a(0x01010101, u_lo, 0);
|
|
const int sum_hi = mkllm_dp4a(0x01010101, u_hi, 0);
|
|
acc += d * (float) sc_lo * ds_lo * (float) dot_lo
|
|
- dmin * (float) m_lo * ds_lo * (float) sum_lo;
|
|
acc += d * (float) sc_hi * ds_hi * (float) dot_hi
|
|
- dmin * (float) m_hi * ds_hi * (float) sum_hi;
|
|
} else if (KIND == MKLLM_QUANT_Q6K) {
|
|
const float d = __half2float(*(const __half *) (blk + 208));
|
|
const int l0 = lane * 8;
|
|
const int n = l0 >> 7;
|
|
const int r = l0 & 127;
|
|
const int group = r >> 5;
|
|
const int lo0 = r & 31;
|
|
const int is = lo0 / 16;
|
|
const uint8_t * ql = blk + n * 64;
|
|
const uint8_t * qh = blk + 128 + n * 32;
|
|
const int8_t * sc = (const int8_t *) (blk + 192) + n * 8;
|
|
int8_t qv[8];
|
|
#pragma unroll
|
|
for (int i = 0; i < 8; i++) {
|
|
const int lo = lo0 + i;
|
|
int q;
|
|
switch (group) {
|
|
case 0:
|
|
q = (int) ((ql[lo] & 0x0F) | ((qh[lo] & 3) << 4));
|
|
break;
|
|
case 1:
|
|
q = (int) ((ql[lo + 32] & 0x0F) | (((qh[lo] >> 2) & 3) << 4));
|
|
break;
|
|
case 2:
|
|
q = (int) ((ql[lo] >> 4) | (((qh[lo] >> 4) & 3) << 4));
|
|
break;
|
|
default:
|
|
q = (int) ((ql[lo + 32] >> 4) | (((qh[lo] >> 6) & 3) << 4));
|
|
break;
|
|
}
|
|
qv[i] = (int8_t) (q - 32);
|
|
}
|
|
const int u0 = *(const int *) (q8 + l0);
|
|
const int u1 = *(const int *) (q8 + l0 + 4);
|
|
const int v0 = *(const int *) qv;
|
|
const int v1 = *(const int *) (qv + 4);
|
|
const int dot = mkllm_dp4a(v0, u0, mkllm_dp4a(v1, u1, 0));
|
|
acc += d * (float) sc[is + 2 * group] * d8[l0 / MKLLM_Q81_GS] * (float) dot;
|
|
} else {
|
|
const int sub = lane >> 2;
|
|
const int b4 = (lane & 3) * 8;
|
|
const uint8_t * q8w = blk + sub * 34;
|
|
const float dw = __half2float(*(const __half *) q8w);
|
|
const int at = sub * 32 + b4;
|
|
const int w0 = mkllm_ld_i32_b2(q8w + 2 + b4);
|
|
const int w1 = mkllm_ld_i32_b2(q8w + 2 + b4 + 4);
|
|
const int u0 = *(const int *) (q8 + at);
|
|
const int u1 = *(const int *) (q8 + at + 4);
|
|
const int dot = mkllm_dp4a(w0, u0, mkllm_dp4a(w1, u1, 0));
|
|
acc += dw * d8[at / MKLLM_Q81_GS] * (float) dot;
|
|
}
|
|
return acc;
|
|
}
|
|
|
|
template <int KIND>
|
|
static __global__ void mkllm_mmv_qk_q81_kernel(
|
|
const uint8_t * __restrict__ src0,
|
|
const int8_t * __restrict__ q8,
|
|
const float * __restrict__ d8,
|
|
float * __restrict__ dst,
|
|
int K, int N,
|
|
size_t src0_row_bytes, size_t dst_col_elems) {
|
|
// Ada decode: dim3(32, 2) — two warps cooperate on one output row.
|
|
const int row = (int) blockIdx.x;
|
|
if (row >= N) return;
|
|
const int lane = (int) threadIdx.x;
|
|
const int warp = (int) threadIdx.y;
|
|
const uint8_t * row_bytes = src0 + (size_t) row * src0_row_bytes;
|
|
const int blocks = K / QK_K;
|
|
float acc = 0.0f;
|
|
|
|
for (int b = warp; b < blocks; b += MKLLM_MMVQ_NWARPS) {
|
|
const uint8_t * blk = row_bytes + (size_t) b * mkllm_quant_block_bytes_dev(KIND);
|
|
const int xbase = b * QK_K;
|
|
acc += mkllm_q81_dot_sb<KIND>(blk, q8 + xbase, d8 + xbase / MKLLM_Q81_GS, lane);
|
|
}
|
|
|
|
for (int off = 16; off > 0; off >>= 1) {
|
|
acc += __shfl_down_sync(0xffffffff, acc, off);
|
|
}
|
|
__shared__ float warp_sum[MKLLM_MMVQ_NWARPS];
|
|
if (lane == 0) {
|
|
warp_sum[warp] = acc;
|
|
}
|
|
__syncthreads();
|
|
if (warp == 0 && lane == 0) {
|
|
float total = 0.0f;
|
|
#pragma unroll
|
|
for (int w = 0; w < MKLLM_MMVQ_NWARPS; w++) {
|
|
total += warp_sum[w];
|
|
}
|
|
dst[row] = total;
|
|
}
|
|
(void) dst_col_elems;
|
|
}
|
|
|
|
extern "C" cudaError_t mkllm_mmv_quant_q81(
|
|
int kind,
|
|
const void * src0, const int8_t * q8, const float * d8, float * dst,
|
|
int K, int N,
|
|
size_t src0_row_bytes, size_t dst_col_elems,
|
|
cudaStream_t stream) {
|
|
if (K <= 0 || N <= 0 || (K % QK_K) != 0) {
|
|
return cudaErrorInvalidValue;
|
|
}
|
|
if (kind < MKLLM_QUANT_Q4K || kind > MKLLM_QUANT_Q80) {
|
|
return cudaErrorInvalidValue;
|
|
}
|
|
dim3 block(32, MKLLM_MMVQ_NWARPS);
|
|
dim3 grid(N);
|
|
switch (kind) {
|
|
case MKLLM_QUANT_Q4K:
|
|
mkllm_mmv_qk_q81_kernel<MKLLM_QUANT_Q4K><<<grid, block, 0, stream>>>(
|
|
(const uint8_t *) src0, q8, d8, dst, K, N, src0_row_bytes, dst_col_elems);
|
|
break;
|
|
case MKLLM_QUANT_Q5K:
|
|
mkllm_mmv_qk_q81_kernel<MKLLM_QUANT_Q5K><<<grid, block, 0, stream>>>(
|
|
(const uint8_t *) src0, q8, d8, dst, K, N, src0_row_bytes, dst_col_elems);
|
|
break;
|
|
case MKLLM_QUANT_Q80:
|
|
mkllm_mmv_qk_q81_kernel<MKLLM_QUANT_Q80><<<grid, block, 0, stream>>>(
|
|
(const uint8_t *) src0, q8, d8, dst, K, N, src0_row_bytes, dst_col_elems);
|
|
break;
|
|
case MKLLM_QUANT_Q6K:
|
|
mkllm_mmv_qk_q81_kernel<MKLLM_QUANT_Q6K><<<grid, block, 0, stream>>>(
|
|
(const uint8_t *) src0, q8, d8, dst, K, N, src0_row_bytes, dst_col_elems);
|
|
break;
|
|
default:
|
|
return cudaErrorInvalidValue;
|
|
}
|
|
return cudaGetLastError();
|
|
}
|
|
#endif
|
|
|
|
// Batched Q8_1 quant: one warp per (column, 32-value block). q is packed [M, K].
|
|
static __global__ void mkllm_quantize_q81_batched_kernel(
|
|
const float * __restrict__ x, int8_t * __restrict__ q,
|
|
float * __restrict__ d, int nblk, size_t col_stride) {
|
|
const int blk = (int) blockIdx.x;
|
|
const int col = (int) blockIdx.y;
|
|
const int lane = (int) threadIdx.x;
|
|
if (blk >= nblk || lane >= MKLLM_Q81_GS) {
|
|
return;
|
|
}
|
|
const float xi = x[(size_t) col * col_stride + (size_t) blk * MKLLM_Q81_GS + lane];
|
|
const float amax = mkllm_warp_reduce_max32(fabsf(xi));
|
|
const float d_blk = amax / 127.0f;
|
|
const int8_t qi = (amax == 0.0f) ? 0 : (int8_t) roundf(xi / d_blk);
|
|
q[((size_t) col * (size_t) nblk + (size_t) blk) * MKLLM_Q81_GS + lane] = qi;
|
|
if (lane == 0) {
|
|
d[(size_t) col * (size_t) nblk + (size_t) blk] = d_blk;
|
|
}
|
|
}
|
|
|
|
extern "C" cudaError_t mkllm_quantize_q81_batched(
|
|
const float * x, int8_t * q, float * d, int k, int m,
|
|
size_t col_stride, cudaStream_t stream) {
|
|
if (k <= 0 || m <= 0 || (k % MKLLM_Q81_GS) != 0) {
|
|
return cudaErrorInvalidValue;
|
|
}
|
|
const int nblk = k / MKLLM_Q81_GS;
|
|
dim3 grid(nblk, m);
|
|
mkllm_quantize_q81_batched_kernel<<<grid, MKLLM_Q81_GS, 0, stream>>>(
|
|
x, q, d, nblk, col_stride);
|
|
return cudaGetLastError();
|
|
}
|
|
|
|
// Unused compiled-only helper for the disabled Q8_1 prefill MMQ path.
|
|
template <int KIND>
|
|
static __device__ __forceinline__ float mkllm_q81_dot_sb(
|
|
const uint8_t * __restrict__ blk,
|
|
const int8_t * __restrict__ q8,
|
|
const float * __restrict__ d8,
|
|
int lane) {
|
|
(void) blk;
|
|
(void) q8;
|
|
(void) d8;
|
|
(void) lane;
|
|
return 0.0f;
|
|
}
|
|
|
|
// Prefill packed MMQ: Q4/Q5/Q6/Q8_0 x Q8_1, no BF16 expansion.
|
|
// One warp per output row, BM=8 act columns sharing a super-block in smem.
|
|
#define MKLLM_MMQ_Q81_BM 8
|
|
#define MKLLM_MMQ_Q81_ROWS 8
|
|
|
|
template <int KIND>
|
|
static __global__ void mkllm_mmq_q81_kernel(
|
|
const uint8_t * __restrict__ src0,
|
|
const int8_t * __restrict__ q8,
|
|
const float * __restrict__ d8,
|
|
float * __restrict__ dst,
|
|
int K, int N, int M,
|
|
size_t src0_row_bytes, size_t dst_col_elems) {
|
|
const int row = (int) blockIdx.x * MKLLM_MMQ_Q81_ROWS + (int) threadIdx.y;
|
|
const int col0 = (int) blockIdx.y * MKLLM_MMQ_Q81_BM;
|
|
const int lane = (int) threadIdx.x;
|
|
const int tid = (int) threadIdx.y * 32 + lane;
|
|
const int nblk = K / MKLLM_Q81_GS;
|
|
const int blocks = K / QK_K;
|
|
|
|
__shared__ int8_t smem_q[MKLLM_MMQ_Q81_BM * QK_K];
|
|
__shared__ float smem_d[MKLLM_MMQ_Q81_BM * (QK_K / MKLLM_Q81_GS)];
|
|
|
|
float acc[MKLLM_MMQ_Q81_BM];
|
|
#pragma unroll
|
|
for (int c = 0; c < MKLLM_MMQ_Q81_BM; c++) {
|
|
acc[c] = 0.0f;
|
|
}
|
|
|
|
const uint8_t * row_bytes = (row < N) ? src0 + (size_t) row * src0_row_bytes : src0;
|
|
for (int b = 0; b < blocks; b++) {
|
|
const int xbase = b * QK_K;
|
|
const int dbase = xbase / MKLLM_Q81_GS;
|
|
// 256 threads load 8*256 Q8 bytes (8 iters) + 8*8 scales.
|
|
for (int p = tid; p < MKLLM_MMQ_Q81_BM * QK_K; p += MKLLM_MMQ_Q81_ROWS * 32) {
|
|
const int c = p / QK_K;
|
|
const int kk = p - c * QK_K;
|
|
const int col = col0 + c;
|
|
int8_t v = 0;
|
|
if (col < M) {
|
|
v = q8[(size_t) col * (size_t) K + (size_t) xbase + kk];
|
|
}
|
|
smem_q[p] = v;
|
|
}
|
|
if (tid < MKLLM_MMQ_Q81_BM * (QK_K / MKLLM_Q81_GS)) {
|
|
const int c = tid / (QK_K / MKLLM_Q81_GS);
|
|
const int db = tid - c * (QK_K / MKLLM_Q81_GS);
|
|
const int col = col0 + c;
|
|
float dv = 0.0f;
|
|
if (col < M) {
|
|
dv = d8[(size_t) col * (size_t) nblk + (size_t) dbase + db];
|
|
}
|
|
smem_d[tid] = dv;
|
|
}
|
|
__syncthreads();
|
|
|
|
if (row < N) {
|
|
const uint8_t * blk = row_bytes + (size_t) b * mkllm_quant_block_bytes_dev(KIND);
|
|
#pragma unroll
|
|
for (int c = 0; c < MKLLM_MMQ_Q81_BM; c++) {
|
|
acc[c] += mkllm_q81_dot_sb<KIND>(
|
|
blk,
|
|
smem_q + c * QK_K,
|
|
smem_d + c * (QK_K / MKLLM_Q81_GS),
|
|
lane);
|
|
}
|
|
}
|
|
__syncthreads();
|
|
}
|
|
|
|
if (row >= N) {
|
|
return;
|
|
}
|
|
#pragma unroll
|
|
for (int c = 0; c < MKLLM_MMQ_Q81_BM; c++) {
|
|
const int col = col0 + c;
|
|
if (col >= M) {
|
|
break;
|
|
}
|
|
float total = acc[c];
|
|
for (int off = 16; off > 0; off >>= 1) {
|
|
total += __shfl_down_sync(0xffffffff, total, off);
|
|
}
|
|
if (lane == 0) {
|
|
dst[(size_t) col * dst_col_elems + row] = total;
|
|
}
|
|
}
|
|
}
|
|
|
|
extern "C" cudaError_t mkllm_mmq_quant_q81(
|
|
int kind,
|
|
const void * src0, const int8_t * q8, const float * d8, float * dst,
|
|
int K, int N, int M,
|
|
size_t src0_row_bytes, size_t dst_col_elems,
|
|
cudaStream_t stream) {
|
|
if (K <= 0 || N <= 0 || M <= 0 || (K % QK_K) != 0) {
|
|
return cudaErrorInvalidValue;
|
|
}
|
|
if (kind < MKLLM_QUANT_Q4K || kind > MKLLM_QUANT_Q80) {
|
|
return cudaErrorInvalidValue;
|
|
}
|
|
dim3 block(32, MKLLM_MMQ_Q81_ROWS);
|
|
dim3 grid((N + MKLLM_MMQ_Q81_ROWS - 1) / MKLLM_MMQ_Q81_ROWS,
|
|
(M + MKLLM_MMQ_Q81_BM - 1) / MKLLM_MMQ_Q81_BM);
|
|
switch (kind) {
|
|
case MKLLM_QUANT_Q4K:
|
|
mkllm_mmq_q81_kernel<MKLLM_QUANT_Q4K><<<grid, block, 0, stream>>>(
|
|
(const uint8_t *) src0, q8, d8, dst, K, N, M, src0_row_bytes, dst_col_elems);
|
|
break;
|
|
case MKLLM_QUANT_Q5K:
|
|
mkllm_mmq_q81_kernel<MKLLM_QUANT_Q5K><<<grid, block, 0, stream>>>(
|
|
(const uint8_t *) src0, q8, d8, dst, K, N, M, src0_row_bytes, dst_col_elems);
|
|
break;
|
|
case MKLLM_QUANT_Q80:
|
|
mkllm_mmq_q81_kernel<MKLLM_QUANT_Q80><<<grid, block, 0, stream>>>(
|
|
(const uint8_t *) src0, q8, d8, dst, K, N, M, src0_row_bytes, dst_col_elems);
|
|
break;
|
|
case MKLLM_QUANT_Q6K:
|
|
mkllm_mmq_q81_kernel<MKLLM_QUANT_Q6K><<<grid, block, 0, stream>>>(
|
|
(const uint8_t *) src0, q8, d8, dst, K, N, M, src0_row_bytes, dst_col_elems);
|
|
break;
|
|
default:
|
|
return cudaErrorInvalidValue;
|
|
}
|
|
return cudaGetLastError();
|
|
}
|
|
|
|
// f32/f16 weight mat-vec twin (small M), same output convention.
|
|
template <typename W>
|
|
static __global__ void mkllm_mmv_float_kernel(
|
|
const W * __restrict__ src0, const float * __restrict__ src1,
|
|
float * __restrict__ dst,
|
|
int K, int N, int M,
|
|
size_t src0_row_elems, size_t src1_col_elems, size_t dst_col_elems) {
|
|
const int row = blockIdx.x * blockDim.y + threadIdx.y;
|
|
if (row >= N) return;
|
|
const int lane = threadIdx.x;
|
|
const W * w = src0 + (size_t) row * src0_row_elems;
|
|
for (int col = 0; col < M; col++) {
|
|
const float * x = src1 + (size_t) col * src1_col_elems;
|
|
float acc = 0.0f;
|
|
for (int l = lane; l < K; l += 32) {
|
|
acc += (float) w[l] * x[l];
|
|
}
|
|
for (int off = 16; off > 0; off >>= 1) {
|
|
acc += __shfl_down_sync(0xffffffff, acc, off);
|
|
}
|
|
if (lane == 0) {
|
|
dst[(size_t) col * dst_col_elems + row] = acc;
|
|
}
|
|
}
|
|
}
|
|
|
|
// llama.cpp mmvf.cu:123-154 + :301-369, F32 ncols_dst=1, no fusion/ids.
|
|
// One block per output row; float2 loads; block_size picked to minimize
|
|
// (K/2)/block_size iterations (mmvf.cu:426-438). Decode alpha/beta are
|
|
// N=48 K=5120 F32 — the naive warp-per-row kernel above is ~33us each.
|
|
template <int BLOCK>
|
|
static __global__ void mkllm_mmvf_f32_m1(
|
|
const float * __restrict__ x, const float * __restrict__ y,
|
|
float * __restrict__ dst, int ncols2, int nrows, size_t stride_row) {
|
|
const int row = blockIdx.x;
|
|
if (row >= nrows) {
|
|
return;
|
|
}
|
|
const int tid = threadIdx.x;
|
|
const float2 * x2 = (const float2 *) (x + (size_t) row * stride_row);
|
|
const float2 * y2 = (const float2 *) y;
|
|
__shared__ float buf_iw[32];
|
|
float sumf = 0.0f;
|
|
for (int col2 = tid; col2 < ncols2; col2 += BLOCK) {
|
|
const float2 tmpx = x2[col2];
|
|
const float2 tmpy = y2[col2];
|
|
sumf += tmpx.x * tmpy.x;
|
|
sumf += tmpx.y * tmpy.y;
|
|
}
|
|
sumf = mkllm_warp_reduce_sum32(sumf);
|
|
if (BLOCK > 32) {
|
|
if (tid < 32) {
|
|
buf_iw[tid] = 0.0f;
|
|
}
|
|
__syncthreads();
|
|
buf_iw[tid / 32] = sumf;
|
|
__syncthreads();
|
|
if (tid < 32) {
|
|
sumf = mkllm_warp_reduce_sum32(buf_iw[tid]);
|
|
}
|
|
}
|
|
if (tid == 0) {
|
|
dst[row] = sumf;
|
|
}
|
|
}
|
|
|
|
extern "C" cudaError_t mkllm_mmv_f32(
|
|
const float * src0, const float * src1, float * dst,
|
|
int K, int N, int M,
|
|
size_t src0_row_elems, size_t src1_col_elems, size_t dst_col_elems,
|
|
cudaStream_t stream) {
|
|
// mmvf.cu:414: ncols % 2 == 0. M=1 decode path only; M>1 keeps the
|
|
// existing warp-per-row kernel (opcheck mmv_f32 uses M=3).
|
|
if (M == 1 && (K % 2) == 0 && src0_row_elems % 2 == 0
|
|
&& src1_col_elems == (size_t) K) {
|
|
int block_size_best = 32;
|
|
int niter_best = (K + 2 * 32 - 1) / (2 * 32);
|
|
for (int block_size = 64; block_size <= 256; block_size += 32) {
|
|
const int niter = (K + 2 * block_size - 1) / (2 * block_size);
|
|
if (niter < niter_best) {
|
|
niter_best = niter;
|
|
block_size_best = block_size;
|
|
}
|
|
}
|
|
const int ncols2 = K / 2;
|
|
const dim3 grid((unsigned) N);
|
|
switch (block_size_best) {
|
|
case 32:
|
|
mkllm_mmvf_f32_m1<32><<<grid, 32, 0, stream>>>(
|
|
src0, src1, dst, ncols2, N, src0_row_elems);
|
|
break;
|
|
case 64:
|
|
mkllm_mmvf_f32_m1<64><<<grid, 64, 0, stream>>>(
|
|
src0, src1, dst, ncols2, N, src0_row_elems);
|
|
break;
|
|
case 96:
|
|
mkllm_mmvf_f32_m1<96><<<grid, 96, 0, stream>>>(
|
|
src0, src1, dst, ncols2, N, src0_row_elems);
|
|
break;
|
|
case 128:
|
|
mkllm_mmvf_f32_m1<128><<<grid, 128, 0, stream>>>(
|
|
src0, src1, dst, ncols2, N, src0_row_elems);
|
|
break;
|
|
case 160:
|
|
mkllm_mmvf_f32_m1<160><<<grid, 160, 0, stream>>>(
|
|
src0, src1, dst, ncols2, N, src0_row_elems);
|
|
break;
|
|
case 192:
|
|
mkllm_mmvf_f32_m1<192><<<grid, 192, 0, stream>>>(
|
|
src0, src1, dst, ncols2, N, src0_row_elems);
|
|
break;
|
|
case 224:
|
|
mkllm_mmvf_f32_m1<224><<<grid, 224, 0, stream>>>(
|
|
src0, src1, dst, ncols2, N, src0_row_elems);
|
|
break;
|
|
default:
|
|
mkllm_mmvf_f32_m1<256><<<grid, 256, 0, stream>>>(
|
|
src0, src1, dst, ncols2, N, src0_row_elems);
|
|
break;
|
|
}
|
|
return cudaGetLastError();
|
|
}
|
|
dim3 block(32, 4);
|
|
dim3 grid((N + 3) / 4);
|
|
mkllm_mmv_float_kernel<float><<<grid, block, 0, stream>>>(
|
|
src0, src1, dst, K, N, M, src0_row_elems, src1_col_elems, dst_col_elems);
|
|
return cudaGetLastError();
|
|
}
|
|
|
|
extern "C" cudaError_t mkllm_mmv_f16(
|
|
const void * src0, const float * src1, float * dst,
|
|
int K, int N, int M,
|
|
size_t src0_row_elems, size_t src1_col_elems, size_t dst_col_elems,
|
|
cudaStream_t stream) {
|
|
dim3 block(32, 4);
|
|
dim3 grid((N + 3) / 4);
|
|
mkllm_mmv_float_kernel<__half><<<grid, block, 0, stream>>>(
|
|
(const __half *) src0, src1, dst, K, N, M, src0_row_elems, src1_col_elems, dst_col_elems);
|
|
return cudaGetLastError();
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Slab dequantization to bf16 (prefill GEMM path; transient slabs only).
|
|
//
|
|
// K-quant payload bytes encode several output values. Decode those values in
|
|
// one thread so scale metadata and payload loads are shared rather than doing
|
|
// one full block decode per scalar. Keep __float2bfloat16 here: the executor's
|
|
// parity contract uses CUDA's round-to-nearest-even BF16 conversion.
|
|
// ---------------------------------------------------------------------------
|
|
|
|
static __global__ void mkllm_dequant_q4k_rows_bf16_kernel(
|
|
const uint8_t * __restrict__ src, __nv_bfloat16 * __restrict__ dst,
|
|
int rows, int K, size_t src_row_bytes) {
|
|
const int row = blockIdx.y;
|
|
const int sb = blockIdx.x;
|
|
const int t = threadIdx.x; // one thread per packed q byte
|
|
if (row >= rows || t >= 128) return;
|
|
const int group = t >> 5; // 64-value group, 0..3
|
|
const int l = t & 31;
|
|
const uint8_t * blk = src + (size_t) row * src_row_bytes + (size_t) sb * 144;
|
|
const float d = __half2float(((const __half *) blk)[0]);
|
|
const float dmin = __half2float(((const __half *) blk)[1]);
|
|
const uint8_t * scales = blk + 4;
|
|
const uint8_t q = blk[16 + 32 * group + l];
|
|
uint8_t sc0, m0, sc1, m1;
|
|
mkllm_scale_min_k4(2 * group, scales, &sc0, &m0);
|
|
mkllm_scale_min_k4(2 * group + 1, scales, &sc1, &m1);
|
|
__nv_bfloat16 * out = dst + (size_t) row * K + (size_t) sb * QK_K + group * 64 + l;
|
|
out[0] = __float2bfloat16(
|
|
d * (float) sc0 * (float) (q & 0x0F) - dmin * (float) m0);
|
|
out[32] = __float2bfloat16(
|
|
d * (float) sc1 * (float) (q >> 4) - dmin * (float) m1);
|
|
}
|
|
|
|
static __global__ void mkllm_dequant_q5k_rows_bf16_kernel(
|
|
const uint8_t * __restrict__ src, __nv_bfloat16 * __restrict__ dst,
|
|
int rows, int K, size_t src_row_bytes) {
|
|
const int row = blockIdx.y;
|
|
const int sb = blockIdx.x;
|
|
const int t = threadIdx.x;
|
|
if (row >= rows || t >= 128) return;
|
|
const int group = t >> 5;
|
|
const int l = t & 31;
|
|
const int is0 = 2 * group;
|
|
const int is1 = is0 + 1;
|
|
const uint8_t * blk = src + (size_t) row * src_row_bytes + (size_t) sb * 176;
|
|
const float d = __half2float(((const __half *) blk)[0]);
|
|
const float dmin = __half2float(((const __half *) blk)[1]);
|
|
const uint8_t * scales = blk + 4;
|
|
const uint8_t * qh = blk + 16;
|
|
const uint8_t q = blk[48 + 32 * group + l];
|
|
uint8_t sc0, m0, sc1, m1;
|
|
mkllm_scale_min_k4(is0, scales, &sc0, &m0);
|
|
mkllm_scale_min_k4(is1, scales, &sc1, &m1);
|
|
const float q0 = (float) (q & 0x0F) + ((qh[l] & (1u << is0)) ? 16.0f : 0.0f);
|
|
const float q1 = (float) (q >> 4) + ((qh[l] & (1u << is1)) ? 16.0f : 0.0f);
|
|
__nv_bfloat16 * out = dst + (size_t) row * K + (size_t) sb * QK_K + group * 64 + l;
|
|
out[0] = __float2bfloat16(d * (float) sc0 * q0 - dmin * (float) m0);
|
|
out[32] = __float2bfloat16(d * (float) sc1 * q1 - dmin * (float) m1);
|
|
}
|
|
|
|
static __global__ void mkllm_dequant_q6k_rows_bf16_kernel(
|
|
const uint8_t * __restrict__ src, __nv_bfloat16 * __restrict__ dst,
|
|
int rows, int K, size_t src_row_bytes) {
|
|
const int row = blockIdx.y;
|
|
const int sb = blockIdx.x;
|
|
const int t = threadIdx.x; // one thread produces four values
|
|
if (row >= rows || t >= 64) return;
|
|
const int half = t >> 5;
|
|
const int l = t & 31;
|
|
const int is = l >> 4;
|
|
const uint8_t * blk = src + (size_t) row * src_row_bytes + (size_t) sb * 210;
|
|
const uint8_t * ql = blk + half * 64;
|
|
const uint8_t * qh = blk + 128 + half * 32;
|
|
const int8_t * sc = (const int8_t *) (blk + 192) + half * 8;
|
|
const float d = __half2float(*(const __half *) (blk + 208));
|
|
const int q0 = (int) ((int8_t) ((ql[l] & 0x0F) | ((qh[l] & 3) << 4))) - 32;
|
|
const int q1 = (int) ((int8_t) ((ql[l + 32] & 0x0F) | (((qh[l] >> 2) & 3) << 4))) - 32;
|
|
const int q2 = (int) ((int8_t) ((ql[l] >> 4) | (((qh[l] >> 4) & 3) << 4))) - 32;
|
|
const int q3 = (int) ((int8_t) ((ql[l + 32] >> 4) | (((qh[l] >> 6) & 3) << 4))) - 32;
|
|
__nv_bfloat16 * out = dst + (size_t) row * K + (size_t) sb * QK_K + half * 128 + l;
|
|
out[0] = __float2bfloat16(d * (float) sc[is] * (float) q0);
|
|
out[32] = __float2bfloat16(d * (float) sc[is + 2] * (float) q1);
|
|
out[64] = __float2bfloat16(d * (float) sc[is + 4] * (float) q2);
|
|
out[96] = __float2bfloat16(d * (float) sc[is + 6] * (float) q3);
|
|
}
|
|
|
|
static __global__ void mkllm_dequant_q80_rows_bf16_kernel(
|
|
const uint8_t * __restrict__ src, __nv_bfloat16 * __restrict__ dst,
|
|
int rows, int K, size_t src_row_bytes) {
|
|
const int row = blockIdx.y;
|
|
const int idx = blockIdx.x * blockDim.x + threadIdx.x;
|
|
if (row >= rows || idx >= K) return;
|
|
const uint8_t * row_bytes = src + (size_t) row * src_row_bytes;
|
|
const uint8_t * blk = row_bytes + (size_t) (idx / QK_K) * 272;
|
|
dst[(size_t) row * K + idx] = __float2bfloat16(mkllm_deq_q80_at(blk, idx % QK_K));
|
|
}
|
|
|
|
extern "C" cudaError_t mkllm_dequant_rows_bf16(
|
|
int kind, const void * src, void * dst, int rows, int K,
|
|
size_t src_row_bytes, cudaStream_t stream) {
|
|
dim3 grid(K / QK_K, rows);
|
|
switch (kind) {
|
|
case MKLLM_QUANT_Q4K:
|
|
mkllm_dequant_q4k_rows_bf16_kernel<<<grid, 128, 0, stream>>>(
|
|
(const uint8_t *) src, (__nv_bfloat16 *) dst, rows, K, src_row_bytes);
|
|
break;
|
|
case MKLLM_QUANT_Q5K:
|
|
mkllm_dequant_q5k_rows_bf16_kernel<<<grid, 128, 0, stream>>>(
|
|
(const uint8_t *) src, (__nv_bfloat16 *) dst, rows, K, src_row_bytes);
|
|
break;
|
|
case MKLLM_QUANT_Q80:
|
|
mkllm_dequant_q80_rows_bf16_kernel<<<grid, 256, 0, stream>>>(
|
|
(const uint8_t *) src, (__nv_bfloat16 *) dst, rows, K, src_row_bytes);
|
|
break;
|
|
case MKLLM_QUANT_Q6K:
|
|
mkllm_dequant_q6k_rows_bf16_kernel<<<grid, 64, 0, stream>>>(
|
|
(const uint8_t *) src, (__nv_bfloat16 *) dst, rows, K, src_row_bytes);
|
|
break;
|
|
// Q3_K and the IQ codebook kinds: the ported llama.cpp convert.cu
|
|
// kernels in iq_convert.cuh, adapted to this row-strided layout.
|
|
case MKLLM_QUANT_Q3K:
|
|
case MKLLM_QUANT_IQ4XS:
|
|
case MKLLM_QUANT_IQ4NL:
|
|
case MKLLM_QUANT_IQ3S:
|
|
return mkllm_dequant_iq_rows_bf16(
|
|
kind, src, dst, rows, K, src_row_bytes, stream);
|
|
default:
|
|
return cudaErrorInvalidValue;
|
|
}
|
|
return cudaGetLastError();
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Fused packed-quant MMQ: dst[N, M] = W[K, N]^T . X[K, M] without writing a
|
|
// global dequant slab. Super-blocks are decoded with the same Q4_K/Q5_K/Q6_K
|
|
// /Q8_0 helpers as the CPU reference, then rounded with __float2bfloat16
|
|
// (RN-even) so the observable GEMM path matches the previous slab+cublas
|
|
// numerics. Activations are rounded the same way in registers. Accumulation
|
|
// is f32 on BF16 tensor cores (sm_80+).
|
|
//
|
|
// Tile: BN=64 weight rows, BM=32 activation columns, BK=256 (one super-block).
|
|
// Eight warps: 4 along N x 2 along M, each owning a 16x16 WMMA fragment.
|
|
// ---------------------------------------------------------------------------
|
|
|
|
#define MKLLM_MMQ_BN 64
|
|
#define MKLLM_MMQ_BM 32
|
|
#define MKLLM_MMQ_BK 256
|
|
#define MKLLM_MMQ_SLD 256
|
|
|
|
template <int KIND>
|
|
static __global__ void mkllm_mmq_qk_kernel(
|
|
const uint8_t * __restrict__ src0, const float * __restrict__ src1,
|
|
float * __restrict__ dst,
|
|
int K, int N, int M,
|
|
size_t src0_row_bytes, size_t src1_col_elems, size_t dst_col_elems) {
|
|
const int n0 = (int) blockIdx.x * MKLLM_MMQ_BN;
|
|
const int m0 = (int) blockIdx.y * MKLLM_MMQ_BM;
|
|
const int tid = (int) threadIdx.x;
|
|
const int block_bytes = mkllm_quant_block_bytes_dev(KIND);
|
|
const int rows = min(MKLLM_MMQ_BN, N - n0);
|
|
const int cols = min(MKLLM_MMQ_BM, M - m0);
|
|
|
|
extern __shared__ char smem_raw[];
|
|
__nv_bfloat16 * smem_w = (__nv_bfloat16 *) smem_raw;
|
|
__nv_bfloat16 * smem_x = smem_w + MKLLM_MMQ_BN * MKLLM_MMQ_SLD;
|
|
|
|
#if defined(__CUDA_ARCH__) && __CUDA_ARCH__ >= 800
|
|
using namespace nvcuda;
|
|
wmma::fragment<wmma::matrix_a, 16, 16, 16, __nv_bfloat16, wmma::row_major> a_frag;
|
|
wmma::fragment<wmma::matrix_b, 16, 16, 16, __nv_bfloat16, wmma::col_major> b_frag;
|
|
wmma::fragment<wmma::accumulator, 16, 16, 16, float> c_frag;
|
|
wmma::fill_fragment(c_frag, 0.0f);
|
|
const int warp = tid >> 5;
|
|
const int warp_n = warp >> 1;
|
|
const int warp_m = warp & 1;
|
|
#else
|
|
float acc[8];
|
|
#pragma unroll
|
|
for (int i = 0; i < 8; i++) acc[i] = 0.0f;
|
|
const int lane = tid & 31;
|
|
const int warp = tid >> 5;
|
|
const int warp_n = warp >> 1;
|
|
const int warp_m = warp & 1;
|
|
#endif
|
|
|
|
const int superblocks = K / QK_K;
|
|
for (int sb = 0; sb < superblocks; sb++) {
|
|
const int k0 = sb * QK_K;
|
|
// Packed decode into SMEM (same formula as slab dequant, not per-l scalar).
|
|
if (KIND == MKLLM_QUANT_Q4K || KIND == MKLLM_QUANT_Q5K) {
|
|
for (int p = tid; p < MKLLM_MMQ_BN * 128; p += 256) {
|
|
const int row = p / 128;
|
|
const int t = p - row * 128;
|
|
const int group = t >> 5;
|
|
const int l = t & 31;
|
|
__nv_bfloat16 v0 = __float2bfloat16(0.0f);
|
|
__nv_bfloat16 v1 = __float2bfloat16(0.0f);
|
|
if (row < rows) {
|
|
const uint8_t * blk = src0 + (size_t) (n0 + row) * src0_row_bytes
|
|
+ (size_t) sb * block_bytes;
|
|
const float d = __half2float(((const __half *) blk)[0]);
|
|
const float dmin = __half2float(((const __half *) blk)[1]);
|
|
const uint8_t * scales = blk + 4;
|
|
const uint8_t q = blk[(KIND == MKLLM_QUANT_Q4K ? 16 : 48)
|
|
+ 32 * group + l];
|
|
uint8_t sc0, m0, sc1, m1;
|
|
mkllm_scale_min_k4(2 * group, scales, &sc0, &m0);
|
|
mkllm_scale_min_k4(2 * group + 1, scales, &sc1, &m1);
|
|
float q0 = (float) (q & 0x0F);
|
|
float q1 = (float) (q >> 4);
|
|
if (KIND == MKLLM_QUANT_Q5K) {
|
|
const uint8_t qh = blk[16 + l];
|
|
q0 += (qh & (1u << (2 * group))) ? 16.0f : 0.0f;
|
|
q1 += (qh & (1u << (2 * group + 1))) ? 16.0f : 0.0f;
|
|
}
|
|
v0 = __float2bfloat16(d * (float) sc0 * q0 - dmin * (float) m0);
|
|
v1 = __float2bfloat16(d * (float) sc1 * q1 - dmin * (float) m1);
|
|
}
|
|
smem_w[row * MKLLM_MMQ_SLD + group * 64 + l] = v0;
|
|
smem_w[row * MKLLM_MMQ_SLD + group * 64 + l + 32] = v1;
|
|
}
|
|
} else if (KIND == MKLLM_QUANT_Q6K) {
|
|
for (int p = tid; p < MKLLM_MMQ_BN * 64; p += 256) {
|
|
const int row = p / 64;
|
|
const int t = p - row * 64;
|
|
const int half = t >> 5;
|
|
const int l = t & 31;
|
|
const int is = l >> 4;
|
|
__nv_bfloat16 v0 = __float2bfloat16(0.0f);
|
|
__nv_bfloat16 v1 = __float2bfloat16(0.0f);
|
|
__nv_bfloat16 v2 = __float2bfloat16(0.0f);
|
|
__nv_bfloat16 v3 = __float2bfloat16(0.0f);
|
|
if (row < rows) {
|
|
const uint8_t * blk = src0 + (size_t) (n0 + row) * src0_row_bytes
|
|
+ (size_t) sb * block_bytes;
|
|
const uint8_t * ql = blk + half * 64;
|
|
const uint8_t * qh = blk + 128 + half * 32;
|
|
const int8_t * sc = (const int8_t *) (blk + 192) + half * 8;
|
|
const float d = __half2float(*(const __half *) (blk + 208));
|
|
const int q0 = (int) ((int8_t) ((ql[l] & 0x0F) | ((qh[l] & 3) << 4))) - 32;
|
|
const int q1 = (int) ((int8_t) ((ql[l + 32] & 0x0F) | (((qh[l] >> 2) & 3) << 4))) - 32;
|
|
const int q2 = (int) ((int8_t) ((ql[l] >> 4) | (((qh[l] >> 4) & 3) << 4))) - 32;
|
|
const int q3 = (int) ((int8_t) ((ql[l + 32] >> 4) | (((qh[l] >> 6) & 3) << 4))) - 32;
|
|
v0 = __float2bfloat16(d * (float) sc[is] * (float) q0);
|
|
v1 = __float2bfloat16(d * (float) sc[is + 2] * (float) q1);
|
|
v2 = __float2bfloat16(d * (float) sc[is + 4] * (float) q2);
|
|
v3 = __float2bfloat16(d * (float) sc[is + 6] * (float) q3);
|
|
}
|
|
smem_w[row * MKLLM_MMQ_SLD + half * 128 + l] = v0;
|
|
smem_w[row * MKLLM_MMQ_SLD + half * 128 + l + 32] = v1;
|
|
smem_w[row * MKLLM_MMQ_SLD + half * 128 + l + 64] = v2;
|
|
smem_w[row * MKLLM_MMQ_SLD + half * 128 + l + 96] = v3;
|
|
}
|
|
} else {
|
|
for (int p = tid; p < MKLLM_MMQ_BN * QK_K; p += 256) {
|
|
const int row = p / QK_K;
|
|
const int col = p - row * QK_K;
|
|
__nv_bfloat16 v = __float2bfloat16(0.0f);
|
|
if (row < rows) {
|
|
const uint8_t * blk = src0 + (size_t) (n0 + row) * src0_row_bytes
|
|
+ (size_t) sb * block_bytes;
|
|
v = __float2bfloat16(mkllm_deq_q80_at(blk, col));
|
|
}
|
|
smem_w[row * MKLLM_MMQ_SLD + col] = v;
|
|
}
|
|
}
|
|
// Cast BM x 256 activations to RN-even BF16.
|
|
for (int p = 0; p < (MKLLM_MMQ_BM * QK_K) / 256; p++) {
|
|
const int idx = tid + p * 256;
|
|
const int col = idx / QK_K;
|
|
const int kk = idx - col * QK_K;
|
|
__nv_bfloat16 v = __float2bfloat16(0.0f);
|
|
if (col < cols) {
|
|
v = __float2bfloat16(src1[(size_t) (m0 + col) * src1_col_elems + k0 + kk]);
|
|
}
|
|
smem_x[col * MKLLM_MMQ_SLD + kk] = v;
|
|
}
|
|
__syncthreads();
|
|
|
|
#if defined(__CUDA_ARCH__) && __CUDA_ARCH__ >= 800
|
|
#pragma unroll
|
|
for (int kk = 0; kk < QK_K; kk += 16) {
|
|
wmma::load_matrix_sync(
|
|
a_frag, smem_w + (warp_n * 16) * MKLLM_MMQ_SLD + kk, MKLLM_MMQ_SLD);
|
|
wmma::load_matrix_sync(
|
|
b_frag, smem_x + (warp_m * 16) * MKLLM_MMQ_SLD + kk, MKLLM_MMQ_SLD);
|
|
wmma::mma_sync(c_frag, a_frag, b_frag, c_frag);
|
|
}
|
|
#else
|
|
const int n_base = warp_n * 16;
|
|
const int m_base = warp_m * 16;
|
|
#pragma unroll
|
|
for (int i = 0; i < 8; i++) {
|
|
const int nr = n_base + (i >> 2) * 8 + (lane >> 2);
|
|
const int mc = m_base + (i & 3) * 4 + (lane & 3);
|
|
float sum = acc[i];
|
|
const __nv_bfloat16 * wr = smem_w + nr * MKLLM_MMQ_SLD;
|
|
const __nv_bfloat16 * xr = smem_x + mc * MKLLM_MMQ_SLD;
|
|
for (int kk = 0; kk < QK_K; kk++) {
|
|
sum += (float) wr[kk] * (float) xr[kk];
|
|
}
|
|
acc[i] = sum;
|
|
}
|
|
#endif
|
|
__syncthreads();
|
|
}
|
|
|
|
// Safe edge store: reuse the weight tile SMEM so partial N/M never
|
|
// writes past dst. Combined static+dynamic shared stays under 48 KiB.
|
|
#if defined(__CUDA_ARCH__) && __CUDA_ARCH__ >= 800
|
|
float * c_tile = (float *) smem_raw;
|
|
wmma::store_matrix_sync(
|
|
c_tile + (warp_m * 16) * MKLLM_MMQ_BN + (warp_n * 16),
|
|
c_frag, MKLLM_MMQ_BN, wmma::mem_col_major);
|
|
__syncthreads();
|
|
for (int p = tid; p < MKLLM_MMQ_BN * MKLLM_MMQ_BM; p += 256) {
|
|
const int nr = p % MKLLM_MMQ_BN;
|
|
const int mc = p / MKLLM_MMQ_BN;
|
|
if (nr < rows && mc < cols) {
|
|
dst[(size_t) (m0 + mc) * dst_col_elems + (n0 + nr)] =
|
|
c_tile[mc * MKLLM_MMQ_BN + nr];
|
|
}
|
|
}
|
|
#else
|
|
for (int i = 0; i < 8; i++) {
|
|
const int nr = warp_n * 16 + (i >> 2) * 8 + (lane >> 2);
|
|
const int mc = warp_m * 16 + (i & 3) * 4 + (lane & 3);
|
|
if (nr < rows && mc < cols) {
|
|
dst[(size_t) (m0 + mc) * dst_col_elems + (n0 + nr)] = acc[i];
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
extern "C" cudaError_t mkllm_mmq_quant(
|
|
int kind,
|
|
const void * src0, const float * src1, float * dst,
|
|
int K, int N, int M,
|
|
size_t src0_row_bytes, size_t src1_col_elems, size_t dst_col_elems,
|
|
cudaStream_t stream) {
|
|
if (K <= 0 || N <= 0 || M <= 0 || (K % QK_K) != 0) {
|
|
return cudaErrorInvalidValue;
|
|
}
|
|
dim3 block(256);
|
|
dim3 grid((N + MKLLM_MMQ_BN - 1) / MKLLM_MMQ_BN, (M + MKLLM_MMQ_BM - 1) / MKLLM_MMQ_BM);
|
|
const size_t shared = (size_t) (MKLLM_MMQ_BN + MKLLM_MMQ_BM) * MKLLM_MMQ_SLD
|
|
* sizeof(__nv_bfloat16);
|
|
switch (kind) {
|
|
case MKLLM_QUANT_Q4K:
|
|
mkllm_mmq_qk_kernel<MKLLM_QUANT_Q4K><<<grid, block, shared, stream>>>(
|
|
(const uint8_t *) src0, src1, dst, K, N, M,
|
|
src0_row_bytes, src1_col_elems, dst_col_elems);
|
|
break;
|
|
case MKLLM_QUANT_Q5K:
|
|
mkllm_mmq_qk_kernel<MKLLM_QUANT_Q5K><<<grid, block, shared, stream>>>(
|
|
(const uint8_t *) src0, src1, dst, K, N, M,
|
|
src0_row_bytes, src1_col_elems, dst_col_elems);
|
|
break;
|
|
case MKLLM_QUANT_Q80:
|
|
mkllm_mmq_qk_kernel<MKLLM_QUANT_Q80><<<grid, block, shared, stream>>>(
|
|
(const uint8_t *) src0, src1, dst, K, N, M,
|
|
src0_row_bytes, src1_col_elems, dst_col_elems);
|
|
break;
|
|
case MKLLM_QUANT_Q6K:
|
|
mkllm_mmq_qk_kernel<MKLLM_QUANT_Q6K><<<grid, block, shared, stream>>>(
|
|
(const uint8_t *) src0, src1, dst, K, N, M,
|
|
src0_row_bytes, src1_col_elems, dst_col_elems);
|
|
break;
|
|
default:
|
|
return cudaErrorInvalidValue;
|
|
}
|
|
return cudaGetLastError();
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Q4_K prefill MMQ, llama.cpp b10430 (4c1a0af40d88c7fbb3b15c85bf2e8016d1d5b64c).
|
|
// Ported from ggml-cuda/mmq.cuh, mmq-load-tiles.cuh, mmq-vec-dot.cuh,
|
|
// mma.cuh, quantize.cu. Copyright (c) ggml authors. MIT license.
|
|
//
|
|
// Fixed tile: I=128, J=128, K=256, 256 threads / 8 warps.
|
|
// Same NVIDIA GENERIC table on 3090/4090/5090 (sm80+ m16n8k32).
|
|
// Compiled on the target box (MAKEPAD_GGML_CUDA_ARCH); not a fat binary.
|
|
// block_q8_1_mmq is K-block-major across tokens; DS4 stores half2(d, sum)
|
|
// where sum is the unquantized float partial (not reconstructed from q8).
|
|
// Q4_K tile: padded stride 76, s8 nibbles + half2(d*sc, -dmin*m).
|
|
// vec_dot is llama.cpp q8_1 x q8_1 MMA: one m16n8k32 per expanded
|
|
// 32-value group. Host rejects M tails.
|
|
// Q4_K J=128 is default-on in the llama executor (MKLLM_DISABLE_Q4K_MMQ=1
|
|
// restores slab). Q6_K J=128 is the matching llama.cpp MMA path for the
|
|
// Q6 weights in Q4_K_M. Neither touches libs/ggml CUDA.
|
|
// ---------------------------------------------------------------------------
|
|
|
|
#define MKLLM_QK8_1 32
|
|
#define MKLLM_QK8_1_MMQ 128
|
|
#define MKLLM_QI8_1 8
|
|
#define MKLLM_MMQ_I 128
|
|
#define MKLLM_MMQ_J 128
|
|
#define MKLLM_MMQ_NWARPS 8
|
|
#define MKLLM_MMQ_TILE_NE_K 32
|
|
#define MKLLM_MMQ_TILE_Y_K (MKLLM_MMQ_TILE_NE_K + MKLLM_MMQ_TILE_NE_K / MKLLM_QI8_1)
|
|
#define MKLLM_MMQ_SRAM_STRIDE 76
|
|
#define MKLLM_MMQ_ITER_K 256
|
|
|
|
struct mkllm_block_q8_1_mmq {
|
|
union {
|
|
float d4[4];
|
|
half2 ds4[4];
|
|
};
|
|
int8_t qs[MKLLM_QK8_1_MMQ];
|
|
};
|
|
|
|
static_assert(sizeof(mkllm_block_q8_1_mmq) == 144, "block_q8_1_mmq size");
|
|
|
|
static __device__ __forceinline__ int mkllm_unpack_scales_q45_K(const int * scales, const int ksc) {
|
|
return ((scales[(ksc % 2) + (ksc != 0)] >> (4 * (ksc & (ksc / 2)))) & 0x0F0F0F0F)
|
|
| ((scales[ksc / 2] >> (2 * (ksc % 2))) & 0x30303030);
|
|
}
|
|
|
|
struct mkllm_tile_16x8 {
|
|
int x[4];
|
|
};
|
|
struct mkllm_tile_8x8 {
|
|
int x[2];
|
|
};
|
|
|
|
static __device__ __forceinline__ int mkllm_tile_c_i(int l) {
|
|
return ((l / 2) * 8) + ((int) threadIdx.x / 4);
|
|
}
|
|
|
|
static __device__ __forceinline__ int mkllm_tile_c_j(int l) {
|
|
return (((int) threadIdx.x % 4) * 2) + (l % 2);
|
|
}
|
|
|
|
// llama.cpp mma.cuh:791-798 load_ldmatrix<tile<16,8>>:
|
|
// xs = xs0 + (tid % I)*stride + (tid / I)*(J/2), I=16 J=8.
|
|
static __device__ __forceinline__ void mkllm_ldmatrix_a(mkllm_tile_16x8 & t, const int * xs0, int stride) {
|
|
const int * xs = (const int *) xs0
|
|
+ ((int) threadIdx.x % 16) * stride + ((int) threadIdx.x / 16) * 4;
|
|
asm volatile("ldmatrix.sync.aligned.m8n8.x4.b16 {%0, %1, %2, %3}, [%4];"
|
|
: "=r"(t.x[0]), "=r"(t.x[1]), "=r"(t.x[2]), "=r"(t.x[3])
|
|
: "l"(xs));
|
|
}
|
|
|
|
static __device__ __forceinline__ void mkllm_load_generic_b(mkllm_tile_8x8 & t, const int * xs0, int stride) {
|
|
const int i = (int) threadIdx.x / 4;
|
|
const int j0 = (int) threadIdx.x % 4;
|
|
t.x[0] = xs0[i * stride + j0];
|
|
t.x[1] = xs0[i * stride + 4 + j0];
|
|
}
|
|
|
|
static __device__ __forceinline__ void mkllm_mma_s8_16x8x32(
|
|
mkllm_tile_16x8 & d, const mkllm_tile_16x8 & a, const mkllm_tile_8x8 & b) {
|
|
#if __CUDA_ARCH__ >= 800
|
|
// b10430 mma.cuh ~942-948: Ampere/Ada m16n8k32. Incomplete Turing
|
|
// 2x m8n8k16 is not equivalent; first port is sm80+ only.
|
|
asm volatile(
|
|
"mma.sync.aligned.m16n8k32.row.col.s32.s8.s8.s32"
|
|
" {%0, %1, %2, %3}, {%4, %5, %6, %7}, {%8, %9}, {%0, %1, %2, %3};"
|
|
: "+r"(d.x[0]), "+r"(d.x[1]), "+r"(d.x[2]), "+r"(d.x[3])
|
|
: "r"(a.x[0]), "r"(a.x[1]), "r"(a.x[2]), "r"(a.x[3]), "r"(b.x[0]), "r"(b.x[1]));
|
|
#else
|
|
(void) a;
|
|
(void) b;
|
|
d.x[0] = d.x[1] = d.x[2] = d.x[3] = 0;
|
|
#endif
|
|
}
|
|
|
|
#if 0
|
|
// Homemade MMQ process/load/dot/write retired: official mul_mat_q_process_tile.
|
|
static __device__ __forceinline__ void mkllm_mmq_q4k_load_tiles(
|
|
const char * __restrict__ x, int * __restrict__ x_tile,
|
|
int kbx0, int i_max, int stride) {
|
|
int * x_qs = x_tile;
|
|
half2 * x_dm = (half2 *) (x_qs + 2 * MKLLM_MMQ_TILE_NE_K);
|
|
#pragma unroll
|
|
for (int i0 = 0; i0 < MKLLM_MMQ_I; i0 += MKLLM_MMQ_NWARPS) {
|
|
// llama.cpp load_tiles_q4_K need_check=false (mmq.cuh:2073-2075):
|
|
// no i=min(i,i_max); Qwen N is a multiple of 128.
|
|
const int i = i0 + (int) threadIdx.y;
|
|
(void) i_max;
|
|
const uint8_t * blk = (const uint8_t *) x + ((size_t) (kbx0 + i * stride) * 144);
|
|
const int txi = (int) threadIdx.x;
|
|
const int qs0 = ((const int *) (blk + 16))[txi];
|
|
x_qs[i * MKLLM_MMQ_SRAM_STRIDE + 16 * (txi / 8) + (txi % 8) + 0] = (qs0 >> 0) & 0x0F0F0F0F;
|
|
x_qs[i * MKLLM_MMQ_SRAM_STRIDE + 16 * (txi / 8) + (txi % 8) + 8] = (qs0 >> 4) & 0x0F0F0F0F;
|
|
}
|
|
#pragma unroll
|
|
for (int i0 = 0; i0 < MKLLM_MMQ_I; i0 += MKLLM_MMQ_NWARPS * 16) {
|
|
const int i = (i0 + (int) threadIdx.y * 16 + (int) threadIdx.x / 2) % MKLLM_MMQ_I;
|
|
const uint8_t * blk = (const uint8_t *) x + ((size_t) (kbx0 + i * stride) * 144);
|
|
const int * scales = (const int *) (blk + 4);
|
|
const int ksc = (int) threadIdx.x % 2;
|
|
const int sc32 = mkllm_unpack_scales_q45_K(scales, ksc + 0);
|
|
const int m32 = mkllm_unpack_scales_q45_K(scales, ksc + 2);
|
|
const uint8_t * sc8 = (const uint8_t *) &sc32;
|
|
const uint8_t * m8 = (const uint8_t *) &m32;
|
|
const half2 dm = __hmul2(((const half2 *) blk)[0], make_half2(1.0f, -1.0f));
|
|
#pragma unroll
|
|
for (int l = 0; l < 4; ++l) {
|
|
x_dm[i * MKLLM_MMQ_SRAM_STRIDE + 4 * ksc + l] =
|
|
__hmul2(dm, make_half2((float) sc8[l], (float) m8[l]));
|
|
}
|
|
}
|
|
}
|
|
|
|
static __device__ __forceinline__ void mkllm_mmq_q4k_vec_dot(
|
|
const int * __restrict__ x, const int * __restrict__ y,
|
|
float * __restrict__ sum, int k00) {
|
|
constexpr int ntx = 2;
|
|
constexpr int rows_per_warp = 32;
|
|
y += ((int) threadIdx.y % ntx) * (8 * MKLLM_MMQ_TILE_Y_K);
|
|
const int * x_qs = x;
|
|
const half2 * x_dm = (const half2 *) x_qs + 2 * MKLLM_MMQ_TILE_NE_K;
|
|
const int * y_qs = y + 4;
|
|
const half2 * y_dm = (const half2 *) y;
|
|
mkllm_tile_16x8 A[ntx][MKLLM_MMQ_TILE_NE_K / MKLLM_QI8_1];
|
|
float2 dmA[ntx][2][MKLLM_MMQ_TILE_NE_K / MKLLM_QI8_1];
|
|
const int i0 = ((int) threadIdx.y / ntx) * rows_per_warp;
|
|
#pragma unroll
|
|
for (int n = 0; n < ntx; ++n) {
|
|
#pragma unroll
|
|
for (int k01 = 0; k01 < MKLLM_MMQ_TILE_NE_K; k01 += MKLLM_QI8_1) {
|
|
mkllm_ldmatrix_a(A[n][k01 / MKLLM_QI8_1],
|
|
x_qs + (i0 + n * 16) * MKLLM_MMQ_SRAM_STRIDE + (k00 + k01),
|
|
MKLLM_MMQ_SRAM_STRIDE);
|
|
}
|
|
#pragma unroll
|
|
for (int l = 0; l < 2; ++l) {
|
|
const int i = i0 + n * 16 + mkllm_tile_c_i(2 * l);
|
|
#pragma unroll
|
|
for (int k01 = 0; k01 < MKLLM_MMQ_TILE_NE_K; k01 += MKLLM_QI8_1) {
|
|
dmA[n][l][k01 / MKLLM_QI8_1] =
|
|
__half22float2(x_dm[i * MKLLM_MMQ_SRAM_STRIDE + (k00 + k01) / MKLLM_QI8_1]);
|
|
}
|
|
}
|
|
}
|
|
#pragma unroll
|
|
for (int j0 = 0; j0 < MKLLM_MMQ_J; j0 += ntx * 8) {
|
|
#pragma unroll
|
|
for (int k01 = 0; k01 < MKLLM_MMQ_TILE_NE_K; k01 += MKLLM_QI8_1) {
|
|
mkllm_tile_8x8 B;
|
|
float2 dsB[2];
|
|
mkllm_load_generic_b(B, y_qs + j0 * MKLLM_MMQ_TILE_Y_K + k01, MKLLM_MMQ_TILE_Y_K);
|
|
#pragma unroll
|
|
for (int l = 0; l < 2; ++l) {
|
|
const int j = j0 + mkllm_tile_c_j(l);
|
|
dsB[l] = __half22float2(y_dm[j * MKLLM_MMQ_TILE_Y_K + k01 / MKLLM_QI8_1]);
|
|
}
|
|
#pragma unroll
|
|
for (int n = 0; n < ntx; ++n) {
|
|
mkllm_tile_16x8 C;
|
|
C.x[0] = C.x[1] = C.x[2] = C.x[3] = 0;
|
|
mkllm_mma_s8_16x8x32(C, A[n][k01 / MKLLM_QI8_1], B);
|
|
#pragma unroll
|
|
for (int l = 0; l < 4; ++l) {
|
|
sum[(j0 / 8 + n) * 4 + l] +=
|
|
dmA[n][l / 2][k01 / MKLLM_QI8_1].x * dsB[l % 2].x * (float) C.x[l];
|
|
sum[(j0 / 8 + n) * 4 + l] +=
|
|
dmA[n][l / 2][k01 / MKLLM_QI8_1].y * dsB[l % 2].y;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static __device__ __forceinline__ void mkllm_mmq_q4k_process_tile(
|
|
const char * __restrict__ x, const int * __restrict__ y,
|
|
int * __restrict__ tile_x, int * __restrict__ tile_y, float * __restrict__ sum,
|
|
int offset_x, int j0, int i_max, int stride_row_x, int y_stride,
|
|
int kb0_start, int kb0_stop) {
|
|
#pragma unroll
|
|
for (int s = 0; s < 64; ++s) {
|
|
sum[s] = 0.0f;
|
|
}
|
|
for (int kb0 = kb0_start; kb0 < kb0_stop; ++kb0) {
|
|
mkllm_mmq_q4k_load_tiles(x, tile_x, offset_x + kb0, i_max, stride_row_x);
|
|
{
|
|
const int * by0 = y + (j0 * (int) (sizeof(mkllm_block_q8_1_mmq) / sizeof(int)))
|
|
+ (kb0 * 2) * y_stride;
|
|
#pragma unroll
|
|
for (int l0 = 0; l0 < MKLLM_MMQ_J * MKLLM_MMQ_TILE_Y_K; l0 += 256) {
|
|
const int l = l0 + (int) threadIdx.y * 32 + (int) threadIdx.x;
|
|
tile_y[l] = by0[l];
|
|
}
|
|
}
|
|
__syncthreads();
|
|
mkllm_mmq_q4k_vec_dot(tile_x, tile_y, sum, 0);
|
|
__syncthreads();
|
|
{
|
|
const int * by0 = y + (j0 * (int) (sizeof(mkllm_block_q8_1_mmq) / sizeof(int)))
|
|
+ (kb0 * 2 + 1) * y_stride;
|
|
#pragma unroll
|
|
for (int l0 = 0; l0 < MKLLM_MMQ_J * MKLLM_MMQ_TILE_Y_K; l0 += 256) {
|
|
const int l = l0 + (int) threadIdx.y * 32 + (int) threadIdx.x;
|
|
tile_y[l] = by0[l];
|
|
}
|
|
}
|
|
__syncthreads();
|
|
mkllm_mmq_q4k_vec_dot(tile_x, tile_y, sum, MKLLM_MMQ_TILE_NE_K);
|
|
__syncthreads();
|
|
}
|
|
}
|
|
|
|
// llama.cpp mmq.cuh:3224 mmq_write_back_mma<…, need_check=false>:
|
|
// j>j_max stays (always), i>i_max only if need_check. Official Qwen path
|
|
// is N%128==0 and M%128==0 so both checks are dead; omit them so the
|
|
// store is a straight indexed write like the false specialization.
|
|
static __device__ __forceinline__ void mkllm_mmq_write_j128(
|
|
const float * __restrict__ sum, float * __restrict__ dst,
|
|
int i0, int j0, int i_max, int j_max, int stride_col_dst) {
|
|
(void) i_max;
|
|
(void) j_max;
|
|
const int ntx = 2;
|
|
const int wi0 = ((int) threadIdx.y / ntx) * 32;
|
|
#pragma unroll
|
|
for (int jj = 0; jj < MKLLM_MMQ_J; jj += 16) {
|
|
#pragma unroll
|
|
for (int nt = 0; nt < ntx; ++nt) {
|
|
#pragma unroll
|
|
for (int l = 0; l < 4; ++l) {
|
|
const int j = jj + ((int) threadIdx.y % ntx) * 8 + mkllm_tile_c_j(l);
|
|
const int i = wi0 + nt * 16 + mkllm_tile_c_i(l);
|
|
dst[(size_t) (j0 + j) * (size_t) stride_col_dst + (size_t) (i0 + i)] =
|
|
sum[(jj / 8 + nt) * 4 + l];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static __global__ void __launch_bounds__(256, 1) mkllm_mmq_q4k_j128_kernel(
|
|
const char * __restrict__ x, const int * __restrict__ y, float * __restrict__ dst,
|
|
int k, int n, int m, int stride_row_x, int stride_col_dst) {
|
|
const int i0 = (int) blockIdx.x * MKLLM_MMQ_I;
|
|
const int j0 = (int) blockIdx.y * MKLLM_MMQ_J;
|
|
const int i_max = n - i0 - 1;
|
|
const int j_max = m - j0 - 1;
|
|
extern __shared__ int smem[];
|
|
int * tile_y = smem + MKLLM_MMQ_J;
|
|
int * tile_x = tile_y + MKLLM_MMQ_J * MKLLM_MMQ_TILE_Y_K;
|
|
float sum[64];
|
|
const int nblocks = k / QK_K;
|
|
const int y_stride = m * (int) (sizeof(mkllm_block_q8_1_mmq) / sizeof(int));
|
|
mkllm_mmq_q4k_process_tile(
|
|
x, y, tile_x, tile_y, sum, i0 * stride_row_x, j0, i_max, stride_row_x, y_stride,
|
|
0, nblocks);
|
|
mkllm_mmq_write_j128(sum, dst, i0, j0, i_max, j_max, stride_col_dst);
|
|
}
|
|
|
|
// llama.cpp mul_mat_q stream-K (mmq.cuh ~3524): nsm blocks walk a 1D
|
|
// (it, jt, kb0) space. Complete tiles write dst; a trailing partial K
|
|
// slice goes to tmp_fixup and is added by the fixup kernel.
|
|
static __global__ void __launch_bounds__(256, 1) mkllm_mmq_q4k_streamk_kernel(
|
|
const char * __restrict__ x, const int * __restrict__ y, float * __restrict__ dst,
|
|
float * __restrict__ tmp_fixup,
|
|
int k, int n, int m, int stride_row_x, int stride_col_dst) {
|
|
const int ntx = m / MKLLM_MMQ_J;
|
|
const int nty = (n + MKLLM_MMQ_I - 1) / MKLLM_MMQ_I;
|
|
const int nblocks = k / QK_K;
|
|
const int64_t total = (int64_t) ntx * nty * nblocks;
|
|
int64_t kbc = (int64_t) blockIdx.x * total / gridDim.x;
|
|
int64_t kbc_stop = (int64_t) (blockIdx.x + 1) * total / gridDim.x;
|
|
int kb0_start = (int) (kbc % nblocks);
|
|
int kb0_stop = (int) min((int64_t) nblocks, (int64_t) kb0_start + kbc_stop - kbc);
|
|
extern __shared__ int smem[];
|
|
int * tile_y = smem + MKLLM_MMQ_J;
|
|
int * tile_x = tile_y + MKLLM_MMQ_J * MKLLM_MMQ_TILE_Y_K;
|
|
float sum[64];
|
|
const int y_stride = m * (int) (sizeof(mkllm_block_q8_1_mmq) / sizeof(int));
|
|
while (kbc < kbc_stop && kb0_stop == nblocks) {
|
|
const int it = (int) (kbc / ((int64_t) ntx * nblocks));
|
|
const int jt = (int) ((kbc / nblocks) % ntx);
|
|
const int i0 = it * MKLLM_MMQ_I;
|
|
const int j0 = jt * MKLLM_MMQ_J;
|
|
mkllm_mmq_q4k_process_tile(
|
|
x, y, tile_x, tile_y, sum, i0 * stride_row_x, j0, n - i0 - 1,
|
|
stride_row_x, y_stride, kb0_start, kb0_stop);
|
|
mkllm_mmq_write_j128(sum, dst, i0, j0, n - i0 - 1, m - j0 - 1, stride_col_dst);
|
|
kbc += nblocks;
|
|
kbc -= kbc % nblocks;
|
|
kb0_start = 0;
|
|
kb0_stop = (int) min((int64_t) nblocks, kbc_stop - kbc);
|
|
}
|
|
if (kbc >= kbc_stop) {
|
|
return;
|
|
}
|
|
const int it = (int) (kbc / ((int64_t) ntx * nblocks));
|
|
const int jt = (int) ((kbc / nblocks) % ntx);
|
|
const int i0 = it * MKLLM_MMQ_I;
|
|
const int j0 = jt * MKLLM_MMQ_J;
|
|
mkllm_mmq_q4k_process_tile(
|
|
x, y, tile_x, tile_y, sum, i0 * stride_row_x, j0, n - i0 - 1,
|
|
stride_row_x, y_stride, kb0_start, kb0_stop);
|
|
mkllm_mmq_write_j128(
|
|
sum, tmp_fixup + (size_t) blockIdx.x * MKLLM_MMQ_I * MKLLM_MMQ_J,
|
|
0, 0, MKLLM_MMQ_I - 1, MKLLM_MMQ_J - 1, MKLLM_MMQ_I);
|
|
}
|
|
|
|
static __global__ void mkllm_mmq_streamk_fixup_kernel(
|
|
const float * __restrict__ tmp_last_tile, float * __restrict__ dst,
|
|
int k, int n, int m, int stride_col_dst) {
|
|
const int ntx = m / MKLLM_MMQ_J;
|
|
const int nty = (n + MKLLM_MMQ_I - 1) / MKLLM_MMQ_I;
|
|
const int nblocks = k / QK_K;
|
|
const int64_t total = (int64_t) ntx * nty * nblocks;
|
|
const int bidx0 = (int) blockIdx.x;
|
|
int64_t kbc0 = (int64_t) bidx0 * total / gridDim.x;
|
|
int64_t kbc0_stop = (int64_t) (bidx0 + 1) * total / gridDim.x;
|
|
const bool did_not_have_any_data = kbc0 == kbc0_stop;
|
|
const bool wrote_beginning_of_tile = (kbc0 % nblocks) == 0;
|
|
const bool did_not_write_last = (kbc0 / nblocks == kbc0_stop / nblocks) && (kbc0_stop % nblocks != 0);
|
|
if (did_not_have_any_data || wrote_beginning_of_tile || did_not_write_last) {
|
|
return;
|
|
}
|
|
float sum[64];
|
|
#pragma unroll
|
|
for (int s = 0; s < 64; ++s) {
|
|
sum[s] = 0.0f;
|
|
}
|
|
bool any_fixup = false;
|
|
int64_t bidx = bidx0 - 1;
|
|
int64_t kbc_stop = kbc0;
|
|
while (bidx >= 0) {
|
|
int64_t kbc = bidx * total / gridDim.x;
|
|
if (kbc == kbc_stop) {
|
|
bidx--;
|
|
kbc_stop = kbc;
|
|
continue;
|
|
}
|
|
any_fixup = true;
|
|
const float * tile = tmp_last_tile + (size_t) bidx * MKLLM_MMQ_I * MKLLM_MMQ_J;
|
|
const int ntxw = 2;
|
|
const int wi0 = ((int) threadIdx.y / ntxw) * 32;
|
|
#pragma unroll
|
|
for (int jj = 0; jj < MKLLM_MMQ_J; jj += 16) {
|
|
#pragma unroll
|
|
for (int nt = 0; nt < ntxw; ++nt) {
|
|
#pragma unroll
|
|
for (int l = 0; l < 4; ++l) {
|
|
const int j = jj + ((int) threadIdx.y % ntxw) * 8 + mkllm_tile_c_j(l);
|
|
const int i = wi0 + nt * 16 + mkllm_tile_c_i(l);
|
|
sum[(jj / 8 + nt) * 4 + l] += tile[(size_t) j * MKLLM_MMQ_I + i];
|
|
}
|
|
}
|
|
}
|
|
if ((kbc % nblocks) == 0 || (kbc / nblocks) < (kbc0 / nblocks)) {
|
|
break;
|
|
}
|
|
bidx--;
|
|
kbc_stop = kbc;
|
|
}
|
|
if (!any_fixup) {
|
|
return;
|
|
}
|
|
const int it = (int) (kbc0 / ((int64_t) ntx * nblocks));
|
|
const int jt = (int) ((kbc0 / nblocks) % ntx);
|
|
const int i0 = it * MKLLM_MMQ_I;
|
|
const int j0 = jt * MKLLM_MMQ_J;
|
|
const int ntxw = 2;
|
|
const int wi0 = ((int) threadIdx.y / ntxw) * 32;
|
|
#pragma unroll
|
|
for (int jj = 0; jj < MKLLM_MMQ_J; jj += 16) {
|
|
#pragma unroll
|
|
for (int nt = 0; nt < ntxw; ++nt) {
|
|
#pragma unroll
|
|
for (int l = 0; l < 4; ++l) {
|
|
const int j = jj + ((int) threadIdx.y % ntxw) * 8 + mkllm_tile_c_j(l);
|
|
const int i = wi0 + nt * 16 + mkllm_tile_c_i(l);
|
|
if (j0 + j < m && i0 + i < n) {
|
|
dst[(size_t) (j0 + j) * (size_t) stride_col_dst + (size_t) (i0 + i)] +=
|
|
sum[(jj / 8 + nt) * 4 + l];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static __global__ void mkllm_quantize_mmq_ds4_kernel(
|
|
const float * __restrict__ x, mkllm_block_q8_1_mmq * __restrict__ y,
|
|
int k, int m, int stride_col) {
|
|
const int64_t i0 = ((int64_t) blockDim.x * blockIdx.y + threadIdx.x) * 4;
|
|
if (i0 >= k) {
|
|
return;
|
|
}
|
|
const int col = (int) blockIdx.x;
|
|
if (col >= m) {
|
|
return;
|
|
}
|
|
const float4 xi = ((const float4 *) (x + (size_t) col * (size_t) stride_col))[i0 / 4];
|
|
float amax = fabsf(xi.x);
|
|
amax = fmaxf(amax, fabsf(xi.y));
|
|
amax = fmaxf(amax, fabsf(xi.z));
|
|
amax = fmaxf(amax, fabsf(xi.w));
|
|
#pragma unroll
|
|
for (int off = 4; off > 0; off >>= 1) {
|
|
amax = fmaxf(amax, __shfl_xor_sync(0xffffffffu, amax, off, 32));
|
|
}
|
|
float sum = xi.x + xi.y + xi.z + xi.w;
|
|
#pragma unroll
|
|
for (int off = 4; off > 0; off >>= 1) {
|
|
sum += __shfl_xor_sync(0xffffffffu, sum, off, 32);
|
|
}
|
|
// b10430 quantize.cu ~515-521: d = 1.0f/d_inv; zero-guard amax==0.
|
|
const float d_inv = amax > 0.0f ? 127.0f / amax : 0.0f;
|
|
char4 q;
|
|
q.x = (int8_t) roundf(xi.x * d_inv);
|
|
q.y = (int8_t) roundf(xi.y * d_inv);
|
|
q.z = (int8_t) roundf(xi.z * d_inv);
|
|
q.w = (int8_t) roundf(xi.w * d_inv);
|
|
const float d = d_inv > 0.0f ? 1.0f / d_inv : 0.0f;
|
|
const int k_block = (int) (i0 / MKLLM_QK8_1_MMQ);
|
|
const int iqs = (int) (i0 % MKLLM_QK8_1_MMQ);
|
|
mkllm_block_q8_1_mmq * blk = &y[(size_t) k_block * (size_t) m + (size_t) col];
|
|
((char4 *) blk->qs)[iqs / 4] = q;
|
|
if (iqs % MKLLM_QK8_1 == 0) {
|
|
blk->ds4[iqs / MKLLM_QK8_1] = make_half2(d, sum);
|
|
}
|
|
}
|
|
|
|
extern "C" cudaError_t mkllm_quantize_mmq_ds4(
|
|
const float * x, void * y, int k, int m, int stride_col, cudaStream_t stream) {
|
|
if (k <= 0 || m <= 0 || stride_col < k || (k % MKLLM_QK8_1_MMQ) != 0
|
|
|| (stride_col % 4) != 0) {
|
|
return cudaErrorInvalidValue;
|
|
}
|
|
const int block_num_y = (k + 4 * 128 - 1) / (4 * 128);
|
|
dim3 grid(m, block_num_y);
|
|
mkllm_quantize_mmq_ds4_kernel<<<grid, 128, 0, stream>>>(
|
|
x, (mkllm_block_q8_1_mmq *) y, k, m, stride_col);
|
|
return cudaGetLastError();
|
|
}
|
|
|
|
// llama.cpp J=128 MMQ for every kind the executor can hand us. Replaces the
|
|
// old per-type mkllm_mmq_q{4,5,6}k_j128 entry points: the launcher was already
|
|
// templated on ggml_type, so one dispatch covers Q4_K/Q5_K/Q6_K and the
|
|
// UD- kinds (Q3_K, IQ4_XS, IQ4_NL, IQ3_S) alike.
|
|
//
|
|
// `stride_row_x` counts BLOCKS of x's type, so the lower bound depends on the
|
|
// kind's block length (32 values for iq4_nl, 256 otherwise).
|
|
extern "C" cudaError_t mkllm_mmq_kind_j128(
|
|
int kind, const void * x, const void * y, float * dst,
|
|
int k, int n, int m, int stride_row_x, int stride_col_dst,
|
|
int nsm, float * tmp_fixup, cudaStream_t stream) {
|
|
if (k <= 0 || n <= 0 || m <= 0 || (k % QK_K) != 0 || (m % 128) != 0
|
|
|| stride_col_dst < n) {
|
|
return cudaErrorInvalidValue;
|
|
}
|
|
const int blk_elems = (kind == MKLLM_QUANT_IQ4NL) ? QK4_NL : QK_K;
|
|
if (stride_row_x < k / blk_elems) {
|
|
return cudaErrorInvalidValue;
|
|
}
|
|
if ((mkllm_kind_route_mask(kind) & MKLLM_ROUTE_MMQ) == 0) {
|
|
return cudaErrorInvalidValue;
|
|
}
|
|
switch (kind) {
|
|
case MKLLM_QUANT_Q4K:
|
|
return mkllm_launch_mul_mat_q<GGML_TYPE_Q4_K>(
|
|
x, y, dst, k, n, m, stride_row_x, stride_col_dst, nsm, tmp_fixup, stream);
|
|
case MKLLM_QUANT_Q5K:
|
|
return mkllm_launch_mul_mat_q<GGML_TYPE_Q5_K>(
|
|
x, y, dst, k, n, m, stride_row_x, stride_col_dst, nsm, tmp_fixup, stream);
|
|
case MKLLM_QUANT_Q6K:
|
|
return mkllm_launch_mul_mat_q<GGML_TYPE_Q6_K>(
|
|
x, y, dst, k, n, m, stride_row_x, stride_col_dst, nsm, tmp_fixup, stream);
|
|
case MKLLM_QUANT_Q3K:
|
|
return mkllm_launch_mul_mat_q<GGML_TYPE_Q3_K>(
|
|
x, y, dst, k, n, m, stride_row_x, stride_col_dst, nsm, tmp_fixup, stream);
|
|
case MKLLM_QUANT_IQ4XS:
|
|
return mkllm_launch_mul_mat_q<GGML_TYPE_IQ4_XS>(
|
|
x, y, dst, k, n, m, stride_row_x, stride_col_dst, nsm, tmp_fixup, stream);
|
|
case MKLLM_QUANT_IQ4NL:
|
|
return mkllm_launch_mul_mat_q<GGML_TYPE_IQ4_NL>(
|
|
x, y, dst, k, n, m, stride_row_x, stride_col_dst, nsm, tmp_fixup, stream);
|
|
case MKLLM_QUANT_IQ3S:
|
|
return mkllm_launch_mul_mat_q<GGML_TYPE_IQ3_S>(
|
|
x, y, dst, k, n, m, stride_row_x, stride_col_dst, nsm, tmp_fixup, stream);
|
|
default:
|
|
return cudaErrorInvalidValue;
|
|
}
|
|
}
|
|
|
|
// Q5_K J=128: llama.cpp load_tiles_q5_K + the same q8_1 MMA vec_dot as Q4_K.
|
|
#define MKLLM_QI5_K_MMQ (QK_K / (4 * 2))
|
|
|
|
#if 0
|
|
static __device__ __forceinline__ void mkllm_mmq_q5k_load_tiles(
|
|
const char * __restrict__ x, int * __restrict__ x_tile,
|
|
int kbx0, int i_max, int stride) {
|
|
(void) i_max;
|
|
int * x_qs = x_tile;
|
|
half2 * x_dm = (half2 *) (x_qs + 2 * MKLLM_MMQ_TILE_NE_K);
|
|
#pragma unroll
|
|
for (int i0 = 0; i0 < MKLLM_MMQ_I; i0 += MKLLM_MMQ_NWARPS) {
|
|
const int i = i0 + (int) threadIdx.y;
|
|
const mkllm_block_q5_K * bxi =
|
|
(const mkllm_block_q5_K *) x + kbx0 + i * stride;
|
|
const int txi = (int) threadIdx.x;
|
|
const int ky = 2 * txi;
|
|
const int ql = mkllm_get_int_b4(bxi->qs, txi);
|
|
const int ql0 = (ql >> 0) & 0x0F0F0F0F;
|
|
const int ql1 = (ql >> 4) & 0x0F0F0F0F;
|
|
const int qh = mkllm_get_int_b4(bxi->qh, txi % (MKLLM_QI5_K_MMQ / 4));
|
|
const int qh0 = ((qh >> (2 * (txi / (MKLLM_QI5_K_MMQ / 4)) + 0)) << 4) & 0x10101010;
|
|
const int qh1 = ((qh >> (2 * (txi / (MKLLM_QI5_K_MMQ / 4)) + 1)) << 4) & 0x10101010;
|
|
const int kq0 = ky - ky % (MKLLM_QI5_K_MMQ / 2) + txi % (MKLLM_QI5_K_MMQ / 4) + 0;
|
|
const int kq1 = ky - ky % (MKLLM_QI5_K_MMQ / 2) + txi % (MKLLM_QI5_K_MMQ / 4)
|
|
+ MKLLM_QI5_K_MMQ / 4;
|
|
x_qs[i * MKLLM_MMQ_SRAM_STRIDE + kq0] = ql0 | qh0;
|
|
x_qs[i * MKLLM_MMQ_SRAM_STRIDE + kq1] = ql1 | qh1;
|
|
}
|
|
#pragma unroll
|
|
for (int i0 = 0; i0 < MKLLM_MMQ_I; i0 += MKLLM_MMQ_NWARPS * 16) {
|
|
const int i = (i0 + (int) threadIdx.y * 16 + (int) threadIdx.x / 2) % MKLLM_MMQ_I;
|
|
const mkllm_block_q5_K * bxi =
|
|
(const mkllm_block_q5_K *) x + kbx0 + i * stride;
|
|
const int * scales = (const int *) bxi->scales;
|
|
const int ksc = (int) threadIdx.x % 2;
|
|
const int sc32 = mkllm_unpack_scales_q45_K(scales, ksc + 0);
|
|
const int m32 = mkllm_unpack_scales_q45_K(scales, ksc + 2);
|
|
const uint8_t * sc8 = (const uint8_t *) &sc32;
|
|
const uint8_t * m8 = (const uint8_t *) &m32;
|
|
const half2 dm = __hmul2(bxi->dm, make_half2(1.0f, -1.0f));
|
|
#pragma unroll
|
|
for (int l = 0; l < 4; ++l) {
|
|
x_dm[i * MKLLM_MMQ_SRAM_STRIDE + 4 * ksc + l] =
|
|
__hmul2(dm, make_half2((float) sc8[l], (float) m8[l]));
|
|
}
|
|
}
|
|
}
|
|
|
|
static __device__ __forceinline__ void mkllm_mmq_q5k_process_tile(
|
|
const char * __restrict__ x, const int * __restrict__ y,
|
|
int * __restrict__ tile_x, int * __restrict__ tile_y, float * __restrict__ sum,
|
|
int offset_x, int j0, int i_max, int stride_row_x, int y_stride,
|
|
int kb0_start, int kb0_stop) {
|
|
#pragma unroll
|
|
for (int s = 0; s < 64; ++s) {
|
|
sum[s] = 0.0f;
|
|
}
|
|
for (int kb0 = kb0_start; kb0 < kb0_stop; ++kb0) {
|
|
mkllm_mmq_q5k_load_tiles(x, tile_x, offset_x + kb0, i_max, stride_row_x);
|
|
{
|
|
const int * by0 = y + (j0 * (int) (sizeof(mkllm_block_q8_1_mmq) / sizeof(int)))
|
|
+ (kb0 * 2) * y_stride;
|
|
#pragma unroll
|
|
for (int l0 = 0; l0 < MKLLM_MMQ_J * MKLLM_MMQ_TILE_Y_K; l0 += 256) {
|
|
const int l = l0 + (int) threadIdx.y * 32 + (int) threadIdx.x;
|
|
tile_y[l] = by0[l];
|
|
}
|
|
}
|
|
__syncthreads();
|
|
mkllm_mmq_q4k_vec_dot(tile_x, tile_y, sum, 0);
|
|
__syncthreads();
|
|
{
|
|
const int * by0 = y + (j0 * (int) (sizeof(mkllm_block_q8_1_mmq) / sizeof(int)))
|
|
+ (kb0 * 2 + 1) * y_stride;
|
|
#pragma unroll
|
|
for (int l0 = 0; l0 < MKLLM_MMQ_J * MKLLM_MMQ_TILE_Y_K; l0 += 256) {
|
|
const int l = l0 + (int) threadIdx.y * 32 + (int) threadIdx.x;
|
|
tile_y[l] = by0[l];
|
|
}
|
|
}
|
|
__syncthreads();
|
|
mkllm_mmq_q4k_vec_dot(tile_x, tile_y, sum, MKLLM_MMQ_TILE_NE_K);
|
|
__syncthreads();
|
|
}
|
|
}
|
|
|
|
static __global__ void __launch_bounds__(256, 1) mkllm_mmq_q5k_j128_kernel(
|
|
const char * __restrict__ x, const int * __restrict__ y, float * __restrict__ dst,
|
|
int k, int n, int m, int stride_row_x, int stride_col_dst) {
|
|
const int i0 = (int) blockIdx.x * MKLLM_MMQ_I;
|
|
const int j0 = (int) blockIdx.y * MKLLM_MMQ_J;
|
|
extern __shared__ int smem[];
|
|
int * tile_y = smem + MKLLM_MMQ_J;
|
|
int * tile_x = tile_y + MKLLM_MMQ_J * MKLLM_MMQ_TILE_Y_K;
|
|
float sum[64];
|
|
const int nblocks = k / QK_K;
|
|
const int y_stride = m * (int) (sizeof(mkllm_block_q8_1_mmq) / sizeof(int));
|
|
mkllm_mmq_q5k_process_tile(
|
|
x, y, tile_x, tile_y, sum, i0 * stride_row_x, j0, n - i0 - 1, stride_row_x, y_stride,
|
|
0, nblocks);
|
|
mkllm_mmq_write_j128(sum, dst, i0, j0, n - i0 - 1, m - j0 - 1, stride_col_dst);
|
|
}
|
|
|
|
// Same stream-K walker as Q4_K: llama.cpp uses the same q8_1 MMA for Q5_K.
|
|
static __global__ void __launch_bounds__(256, 1) mkllm_mmq_q5k_streamk_kernel(
|
|
const char * __restrict__ x, const int * __restrict__ y, float * __restrict__ dst,
|
|
float * __restrict__ tmp_fixup,
|
|
int k, int n, int m, int stride_row_x, int stride_col_dst) {
|
|
const int ntx = m / MKLLM_MMQ_J;
|
|
const int nty = (n + MKLLM_MMQ_I - 1) / MKLLM_MMQ_I;
|
|
const int nblocks = k / QK_K;
|
|
const int64_t total = (int64_t) ntx * nty * nblocks;
|
|
int64_t kbc = (int64_t) blockIdx.x * total / gridDim.x;
|
|
int64_t kbc_stop = (int64_t) (blockIdx.x + 1) * total / gridDim.x;
|
|
int kb0_start = (int) (kbc % nblocks);
|
|
int kb0_stop = (int) min((int64_t) nblocks, (int64_t) kb0_start + kbc_stop - kbc);
|
|
extern __shared__ int smem[];
|
|
int * tile_y = smem + MKLLM_MMQ_J;
|
|
int * tile_x = tile_y + MKLLM_MMQ_J * MKLLM_MMQ_TILE_Y_K;
|
|
float sum[64];
|
|
const int y_stride = m * (int) (sizeof(mkllm_block_q8_1_mmq) / sizeof(int));
|
|
while (kbc < kbc_stop && kb0_stop == nblocks) {
|
|
const int it = (int) (kbc / ((int64_t) ntx * nblocks));
|
|
const int jt = (int) ((kbc / nblocks) % ntx);
|
|
const int i0 = it * MKLLM_MMQ_I;
|
|
const int j0 = jt * MKLLM_MMQ_J;
|
|
mkllm_mmq_q5k_process_tile(
|
|
x, y, tile_x, tile_y, sum, i0 * stride_row_x, j0, n - i0 - 1,
|
|
stride_row_x, y_stride, kb0_start, kb0_stop);
|
|
mkllm_mmq_write_j128(sum, dst, i0, j0, n - i0 - 1, m - j0 - 1, stride_col_dst);
|
|
kbc += nblocks;
|
|
kbc -= kbc % nblocks;
|
|
kb0_start = 0;
|
|
kb0_stop = (int) min((int64_t) nblocks, kbc_stop - kbc);
|
|
}
|
|
if (kbc >= kbc_stop) {
|
|
return;
|
|
}
|
|
const int it = (int) (kbc / ((int64_t) ntx * nblocks));
|
|
const int jt = (int) ((kbc / nblocks) % ntx);
|
|
const int i0 = it * MKLLM_MMQ_I;
|
|
const int j0 = jt * MKLLM_MMQ_J;
|
|
mkllm_mmq_q5k_process_tile(
|
|
x, y, tile_x, tile_y, sum, i0 * stride_row_x, j0, n - i0 - 1,
|
|
stride_row_x, y_stride, kb0_start, kb0_stop);
|
|
mkllm_mmq_write_j128(
|
|
sum, tmp_fixup + (size_t) blockIdx.x * MKLLM_MMQ_I * MKLLM_MMQ_J,
|
|
0, 0, MKLLM_MMQ_I - 1, MKLLM_MMQ_J - 1, MKLLM_MMQ_I);
|
|
}
|
|
|
|
extern "C" cudaError_t mkllm_mmq_q5k_j128(
|
|
const void * x, const void * y, float * dst,
|
|
int k, int n, int m, int stride_row_x, int stride_col_dst,
|
|
int nsm, float * tmp_fixup, cudaStream_t stream) {
|
|
if (k <= 0 || n <= 0 || m <= 0 || (k % QK_K) != 0 || (m % 128) != 0
|
|
|| stride_row_x < (k / QK_K) || stride_col_dst < n) {
|
|
return cudaErrorInvalidValue;
|
|
}
|
|
return mkllm_launch_mul_mat_q<GGML_TYPE_Q5_K>(
|
|
x, y, dst, k, n, m, stride_row_x, stride_col_dst, nsm, tmp_fixup, stream);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Q6_K J=128 MMA. llama.cpp mmq.cuh load_tiles_q6_K + vec_dot_q6_K_q8_1_mma
|
|
// (Turing/Ampere). Y uses D4 (float d per 32 values), not DS4. X tile
|
|
// stride is MMQ_MMA_TILE_X_K_Q6_K = 76. MMA is m16n8k16, not k32.
|
|
// Isolated to the llama executor; ggml CUDA is not modified.
|
|
// ---------------------------------------------------------------------------
|
|
|
|
#define MKLLM_MMQ_TILE_X_K_Q6 (2 * MKLLM_MMQ_TILE_NE_K + MKLLM_MMQ_TILE_NE_K / MKLLM_QI6_K \
|
|
+ MKLLM_MMQ_TILE_NE_K / 8 + 7)
|
|
static_assert(MKLLM_MMQ_TILE_X_K_Q6 == 76, "Q6 MMA tile stride");
|
|
static_assert(MKLLM_MMQ_TILE_X_K_Q6 % 8 == 4, "Q6 MMA tile padding");
|
|
|
|
struct mkllm_tile_16x4 {
|
|
int x[2];
|
|
};
|
|
struct mkllm_tile_8x4 {
|
|
int x[1];
|
|
};
|
|
|
|
static __device__ __forceinline__ int mkllm_vsubss4(int a, int b) {
|
|
return __vsubss4(a, b);
|
|
}
|
|
|
|
static __device__ __forceinline__ void mkllm_ldmatrix_a16x4(
|
|
mkllm_tile_16x4 & t, const int * xs0, int stride) {
|
|
const int * xs = xs0 + ((int) threadIdx.x % 16) * stride;
|
|
asm volatile("ldmatrix.sync.aligned.m8n8.x2.b16 {%0, %1}, [%2];"
|
|
: "=r"(t.x[0]), "=r"(t.x[1])
|
|
: "l"(xs));
|
|
}
|
|
|
|
static __device__ __forceinline__ void mkllm_load_generic_b8x4(
|
|
mkllm_tile_8x4 & t, const int * xs0, int stride) {
|
|
const int i = (int) threadIdx.x / 4;
|
|
const int j0 = (int) threadIdx.x % 4;
|
|
t.x[0] = xs0[i * stride + j0];
|
|
}
|
|
|
|
static __device__ __forceinline__ void mkllm_mma_s8_16x8x16(
|
|
mkllm_tile_16x8 & d, const mkllm_tile_16x4 & a, const mkllm_tile_8x4 & b) {
|
|
#if __CUDA_ARCH__ >= 800
|
|
asm volatile(
|
|
"mma.sync.aligned.m16n8k16.row.col.s32.s8.s8.s32"
|
|
" {%0, %1, %2, %3}, {%4, %5}, {%6}, {%0, %1, %2, %3};"
|
|
: "+r"(d.x[0]), "+r"(d.x[1]), "+r"(d.x[2]), "+r"(d.x[3])
|
|
: "r"(a.x[0]), "r"(a.x[1]), "r"(b.x[0]));
|
|
#else
|
|
(void) a;
|
|
(void) b;
|
|
d.x[0] = d.x[1] = d.x[2] = d.x[3] = 0;
|
|
#endif
|
|
}
|
|
|
|
static __device__ __forceinline__ void mkllm_mmq_q6k_load_tiles(
|
|
const char * __restrict__ x, int * __restrict__ x_tile,
|
|
int kbx0, int i_max, int stride) {
|
|
(void) i_max;
|
|
int * x_qs = x_tile;
|
|
float * x_df = (float *) (x_qs + 2 * MKLLM_MMQ_TILE_NE_K);
|
|
int * x_sc = (int *) (x_df + MKLLM_MMQ_TILE_NE_K / MKLLM_QI6_K);
|
|
#pragma unroll
|
|
for (int i0 = 0; i0 < MKLLM_MMQ_I; i0 += MKLLM_MMQ_NWARPS) {
|
|
const int i = i0 + (int) threadIdx.y;
|
|
const mkllm_block_q6_K * bxi =
|
|
(const mkllm_block_q6_K *) x + kbx0 + i * stride;
|
|
const int txi = (int) threadIdx.x;
|
|
const int ql = mkllm_get_int_b2(bxi->ql, txi);
|
|
const int ql0 = (ql >> 0) & 0x0F0F0F0F;
|
|
const int ql1 = (ql >> 4) & 0x0F0F0F0F;
|
|
const int qh = mkllm_get_int_b2(
|
|
bxi->qh, (MKLLM_QI6_K / 4) * (txi / (MKLLM_QI6_K / 2)) + txi % (MKLLM_QI6_K / 4));
|
|
const int qh0 = ((qh >> ((txi & 0x08) >> 2)) << 4) & 0x30303030;
|
|
const int qh1 = (qh >> ((txi & 0x08) >> 2)) & 0x30303030;
|
|
const int kq0 = 2 * txi - txi % (MKLLM_QI6_K / 2) + 0;
|
|
const int kq1 = 2 * txi - txi % (MKLLM_QI6_K / 2) + MKLLM_QI6_K / 2;
|
|
x_qs[i * MKLLM_MMQ_TILE_X_K_Q6 + kq0] = mkllm_vsubss4(ql0 | qh0, 0x20202020);
|
|
x_qs[i * MKLLM_MMQ_TILE_X_K_Q6 + kq1] = mkllm_vsubss4(ql1 | qh1, 0x20202020);
|
|
}
|
|
#pragma unroll
|
|
for (int i0 = 0; i0 < MKLLM_MMQ_I; i0 += MKLLM_MMQ_NWARPS * 32) {
|
|
const int i = (i0 + (int) threadIdx.y * 32 + (int) threadIdx.x) % MKLLM_MMQ_I;
|
|
const mkllm_block_q6_K * bxi =
|
|
(const mkllm_block_q6_K *) x + kbx0 + i * stride;
|
|
x_df[i * MKLLM_MMQ_TILE_X_K_Q6] = bxi->d;
|
|
}
|
|
#pragma unroll
|
|
for (int i0 = 0; i0 < MKLLM_MMQ_I; i0 += MKLLM_MMQ_NWARPS * 8) {
|
|
const int i = (i0 + (int) threadIdx.y * 8 + (int) threadIdx.x / 4) % MKLLM_MMQ_I;
|
|
const mkllm_block_q6_K * bxi =
|
|
(const mkllm_block_q6_K *) x + kbx0 + i * stride
|
|
+ ((int) threadIdx.x % 4) / 4;
|
|
x_sc[i * MKLLM_MMQ_TILE_X_K_Q6 + (int) threadIdx.x % 4] =
|
|
mkllm_get_int_b2(bxi->scales, (int) threadIdx.x % 4);
|
|
}
|
|
}
|
|
|
|
static __device__ __forceinline__ void mkllm_mmq_q6k_vec_dot(
|
|
const int * __restrict__ x, const int * __restrict__ y,
|
|
float * __restrict__ sum, int k00) {
|
|
constexpr int ntx = 2;
|
|
constexpr int rows_per_warp = 32;
|
|
y += ((int) threadIdx.y % ntx) * (8 * MKLLM_MMQ_TILE_Y_K);
|
|
const int * x_qs = x;
|
|
const float * x_df = (const float *) x_qs + 2 * MKLLM_MMQ_TILE_NE_K;
|
|
const int * x_sc = (const int *) x_df + MKLLM_MMQ_TILE_NE_K / MKLLM_QI6_K;
|
|
const int * y_qs = y + 4;
|
|
const float * y_df = (const float *) y;
|
|
const int i0 = ((int) threadIdx.y / ntx) * rows_per_warp;
|
|
mkllm_tile_16x4 A[ntx][8];
|
|
int scA[ntx][2][8];
|
|
float dA[ntx][2];
|
|
#pragma unroll
|
|
for (int n = 0; n < ntx; ++n) {
|
|
#pragma unroll
|
|
for (int k01 = 0; k01 < MKLLM_MMQ_TILE_NE_K; k01 += 8) {
|
|
const int k0 = k00 + k01;
|
|
mkllm_ldmatrix_a16x4(
|
|
A[n][k01 / 4 + 0],
|
|
x_qs + (i0 + n * 16) * MKLLM_MMQ_TILE_X_K_Q6 + (k0 + 0),
|
|
MKLLM_MMQ_TILE_X_K_Q6);
|
|
mkllm_ldmatrix_a16x4(
|
|
A[n][k01 / 4 + 1],
|
|
x_qs + (i0 + n * 16) * MKLLM_MMQ_TILE_X_K_Q6 + (k0 + 4),
|
|
MKLLM_MMQ_TILE_X_K_Q6);
|
|
}
|
|
#pragma unroll
|
|
for (int k01 = 0; k01 < MKLLM_MMQ_TILE_NE_K; k01 += 16) {
|
|
const int k0 = k00 + k01;
|
|
#pragma unroll
|
|
for (int l = 0; l < 2; ++l) {
|
|
const int i = i0 + n * 16 + mkllm_tile_c_i(2 * l);
|
|
const int sc_packed = x_sc[i * MKLLM_MMQ_TILE_X_K_Q6 + k0 / 16];
|
|
const int8_t * sc = (const int8_t *) &sc_packed;
|
|
#pragma unroll
|
|
for (int ksc = 0; ksc < 4; ++ksc) {
|
|
scA[n][l][k01 / 4 + ksc] = sc[ksc];
|
|
}
|
|
}
|
|
}
|
|
#pragma unroll
|
|
for (int l = 0; l < 2; ++l) {
|
|
const int i = i0 + n * 16 + mkllm_tile_c_i(2 * l);
|
|
dA[n][l] = x_df[i * MKLLM_MMQ_TILE_X_K_Q6];
|
|
}
|
|
}
|
|
#pragma unroll
|
|
for (int j0 = 0; j0 < MKLLM_MMQ_J; j0 += ntx * 8) {
|
|
float tmp[ntx][4];
|
|
#pragma unroll
|
|
for (int n = 0; n < ntx; ++n) {
|
|
#pragma unroll
|
|
for (int l = 0; l < 4; ++l) {
|
|
tmp[n][l] = 0.0f;
|
|
}
|
|
}
|
|
#pragma unroll
|
|
for (int k01 = 0; k01 < MKLLM_MMQ_TILE_NE_K; k01 += 8) {
|
|
mkllm_tile_8x4 B[2];
|
|
float dB[2];
|
|
mkllm_load_generic_b8x4(B[0], y_qs + j0 * MKLLM_MMQ_TILE_Y_K + 0 + k01,
|
|
MKLLM_MMQ_TILE_Y_K);
|
|
mkllm_load_generic_b8x4(B[1], y_qs + j0 * MKLLM_MMQ_TILE_Y_K + 4 + k01,
|
|
MKLLM_MMQ_TILE_Y_K);
|
|
#pragma unroll
|
|
for (int l = 0; l < 2; ++l) {
|
|
const int j = j0 + mkllm_tile_c_j(l);
|
|
dB[l] = y_df[j * MKLLM_MMQ_TILE_Y_K + k01 / MKLLM_QI8_1];
|
|
}
|
|
#pragma unroll
|
|
for (int n = 0; n < ntx; ++n) {
|
|
mkllm_tile_16x8 C0;
|
|
mkllm_tile_16x8 C1;
|
|
C0.x[0] = C0.x[1] = C0.x[2] = C0.x[3] = 0;
|
|
C1.x[0] = C1.x[1] = C1.x[2] = C1.x[3] = 0;
|
|
mkllm_mma_s8_16x8x16(C0, A[n][k01 / 4 + 0], B[0]);
|
|
mkllm_mma_s8_16x8x16(C1, A[n][k01 / 4 + 1], B[1]);
|
|
#pragma unroll
|
|
for (int l = 0; l < 4; ++l) {
|
|
tmp[n][l] += (C0.x[l] * scA[n][l / 2][k01 / 4 + 0]
|
|
+ C1.x[l] * scA[n][l / 2][k01 / 4 + 1]) * dB[l % 2];
|
|
}
|
|
}
|
|
}
|
|
#pragma unroll
|
|
for (int n = 0; n < ntx; ++n) {
|
|
#pragma unroll
|
|
for (int l = 0; l < 4; ++l) {
|
|
sum[(j0 / 8 + n) * 4 + l] += tmp[n][l] * dA[n][l / 2];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static __device__ __forceinline__ void mkllm_mmq_q6k_process_tile(
|
|
const char * __restrict__ x, const int * __restrict__ y,
|
|
int * __restrict__ tile_x, int * __restrict__ tile_y, float * __restrict__ sum,
|
|
int offset_x, int j0, int i_max, int stride_row_x, int y_stride,
|
|
int kb0_start, int kb0_stop) {
|
|
#pragma unroll
|
|
for (int s = 0; s < 64; ++s) {
|
|
sum[s] = 0.0f;
|
|
}
|
|
for (int kb0 = kb0_start; kb0 < kb0_stop; ++kb0) {
|
|
mkllm_mmq_q6k_load_tiles(x, tile_x, offset_x + kb0, i_max, stride_row_x);
|
|
{
|
|
const int * by0 = y + (j0 * (int) (sizeof(mkllm_block_q8_1_mmq) / sizeof(int)))
|
|
+ (kb0 * 2) * y_stride;
|
|
#pragma unroll
|
|
for (int l0 = 0; l0 < MKLLM_MMQ_J * MKLLM_MMQ_TILE_Y_K; l0 += 256) {
|
|
const int l = l0 + (int) threadIdx.y * 32 + (int) threadIdx.x;
|
|
tile_y[l] = by0[l];
|
|
}
|
|
}
|
|
__syncthreads();
|
|
mkllm_mmq_q6k_vec_dot(tile_x, tile_y, sum, 0);
|
|
__syncthreads();
|
|
{
|
|
const int * by0 = y + (j0 * (int) (sizeof(mkllm_block_q8_1_mmq) / sizeof(int)))
|
|
+ (kb0 * 2 + 1) * y_stride;
|
|
#pragma unroll
|
|
for (int l0 = 0; l0 < MKLLM_MMQ_J * MKLLM_MMQ_TILE_Y_K; l0 += 256) {
|
|
const int l = l0 + (int) threadIdx.y * 32 + (int) threadIdx.x;
|
|
tile_y[l] = by0[l];
|
|
}
|
|
}
|
|
__syncthreads();
|
|
mkllm_mmq_q6k_vec_dot(tile_x, tile_y, sum, MKLLM_MMQ_TILE_NE_K);
|
|
__syncthreads();
|
|
}
|
|
}
|
|
|
|
static __global__ void __launch_bounds__(256, 1) mkllm_mmq_q6k_j128_kernel(
|
|
const char * __restrict__ x, const int * __restrict__ y, float * __restrict__ dst,
|
|
int k, int n, int m, int stride_row_x, int stride_col_dst) {
|
|
const int i0 = (int) blockIdx.x * MKLLM_MMQ_I;
|
|
const int j0 = (int) blockIdx.y * MKLLM_MMQ_J;
|
|
extern __shared__ int smem[];
|
|
int * tile_y = smem + MKLLM_MMQ_J;
|
|
int * tile_x = tile_y + MKLLM_MMQ_J * MKLLM_MMQ_TILE_Y_K;
|
|
float sum[64];
|
|
const int nblocks = k / QK_K;
|
|
const int y_stride = m * (int) (sizeof(mkllm_block_q8_1_mmq) / sizeof(int));
|
|
mkllm_mmq_q6k_process_tile(
|
|
x, y, tile_x, tile_y, sum, i0 * stride_row_x, j0, n - i0 - 1, stride_row_x, y_stride,
|
|
0, nblocks);
|
|
mkllm_mmq_write_j128(sum, dst, i0, j0, n - i0 - 1, m - j0 - 1, stride_col_dst);
|
|
}
|
|
|
|
static __global__ void __launch_bounds__(256, 1) mkllm_mmq_q6k_streamk_kernel(
|
|
const char * __restrict__ x, const int * __restrict__ y, float * __restrict__ dst,
|
|
float * __restrict__ tmp_fixup,
|
|
int k, int n, int m, int stride_row_x, int stride_col_dst) {
|
|
const int ntx = m / MKLLM_MMQ_J;
|
|
const int nty = (n + MKLLM_MMQ_I - 1) / MKLLM_MMQ_I;
|
|
const int nblocks = k / QK_K;
|
|
const int64_t total = (int64_t) ntx * nty * nblocks;
|
|
int64_t kbc = (int64_t) blockIdx.x * total / gridDim.x;
|
|
int64_t kbc_stop = (int64_t) (blockIdx.x + 1) * total / gridDim.x;
|
|
int kb0_start = (int) (kbc % nblocks);
|
|
int kb0_stop = (int) min((int64_t) nblocks, (int64_t) kb0_start + kbc_stop - kbc);
|
|
extern __shared__ int smem[];
|
|
int * tile_y = smem + MKLLM_MMQ_J;
|
|
int * tile_x = tile_y + MKLLM_MMQ_J * MKLLM_MMQ_TILE_Y_K;
|
|
float sum[64];
|
|
const int y_stride = m * (int) (sizeof(mkllm_block_q8_1_mmq) / sizeof(int));
|
|
while (kbc < kbc_stop && kb0_stop == nblocks) {
|
|
const int it = (int) (kbc / ((int64_t) ntx * nblocks));
|
|
const int jt = (int) ((kbc / nblocks) % ntx);
|
|
const int i0 = it * MKLLM_MMQ_I;
|
|
const int j0 = jt * MKLLM_MMQ_J;
|
|
mkllm_mmq_q6k_process_tile(
|
|
x, y, tile_x, tile_y, sum, i0 * stride_row_x, j0, n - i0 - 1,
|
|
stride_row_x, y_stride, kb0_start, kb0_stop);
|
|
mkllm_mmq_write_j128(sum, dst, i0, j0, n - i0 - 1, m - j0 - 1, stride_col_dst);
|
|
kbc += nblocks;
|
|
kbc -= kbc % nblocks;
|
|
kb0_start = 0;
|
|
kb0_stop = (int) min((int64_t) nblocks, kbc_stop - kbc);
|
|
}
|
|
if (kbc >= kbc_stop) {
|
|
return;
|
|
}
|
|
const int it = (int) (kbc / ((int64_t) ntx * nblocks));
|
|
const int jt = (int) ((kbc / nblocks) % ntx);
|
|
const int i0 = it * MKLLM_MMQ_I;
|
|
const int j0 = jt * MKLLM_MMQ_J;
|
|
mkllm_mmq_q6k_process_tile(
|
|
x, y, tile_x, tile_y, sum, i0 * stride_row_x, j0, n - i0 - 1,
|
|
stride_row_x, y_stride, kb0_start, kb0_stop);
|
|
mkllm_mmq_write_j128(
|
|
sum, tmp_fixup + (size_t) blockIdx.x * MKLLM_MMQ_I * MKLLM_MMQ_J,
|
|
0, 0, MKLLM_MMQ_I - 1, MKLLM_MMQ_J - 1, MKLLM_MMQ_I);
|
|
}
|
|
#endif
|
|
|
|
static __global__ void mkllm_quantize_mmq_d4_kernel(
|
|
const float * __restrict__ x, mkllm_block_q8_1_mmq * __restrict__ y,
|
|
int k, int m, int stride_col) {
|
|
const int64_t i0 = ((int64_t) blockDim.x * blockIdx.y + threadIdx.x) * 4;
|
|
if (i0 >= k) {
|
|
return;
|
|
}
|
|
const int col = (int) blockIdx.x;
|
|
if (col >= m) {
|
|
return;
|
|
}
|
|
const float4 xi = ((const float4 *) (x + (size_t) col * (size_t) stride_col))[i0 / 4];
|
|
float amax = fabsf(xi.x);
|
|
amax = fmaxf(amax, fabsf(xi.y));
|
|
amax = fmaxf(amax, fabsf(xi.z));
|
|
amax = fmaxf(amax, fabsf(xi.w));
|
|
#pragma unroll
|
|
for (int off = 4; off > 0; off >>= 1) {
|
|
amax = fmaxf(amax, __shfl_xor_sync(0xffffffffu, amax, off, 32));
|
|
}
|
|
const float d_inv = amax > 0.0f ? 127.0f / amax : 0.0f;
|
|
char4 q;
|
|
q.x = (int8_t) roundf(xi.x * d_inv);
|
|
q.y = (int8_t) roundf(xi.y * d_inv);
|
|
q.z = (int8_t) roundf(xi.z * d_inv);
|
|
q.w = (int8_t) roundf(xi.w * d_inv);
|
|
const float d = d_inv > 0.0f ? 1.0f / d_inv : 0.0f;
|
|
const int k_block = (int) (i0 / MKLLM_QK8_1_MMQ);
|
|
const int iqs = (int) (i0 % MKLLM_QK8_1_MMQ);
|
|
mkllm_block_q8_1_mmq * blk = &y[(size_t) k_block * (size_t) m + (size_t) col];
|
|
((char4 *) blk->qs)[iqs / 4] = q;
|
|
if (iqs % MKLLM_QK8_1 == 0) {
|
|
blk->d4[iqs / MKLLM_QK8_1] = d;
|
|
}
|
|
}
|
|
|
|
extern "C" cudaError_t mkllm_quantize_mmq_d4(
|
|
const float * x, void * y, int k, int m, int stride_col, cudaStream_t stream) {
|
|
if (k <= 0 || m <= 0 || stride_col < k || (k % MKLLM_QK8_1_MMQ) != 0
|
|
|| (stride_col % 4) != 0) {
|
|
return cudaErrorInvalidValue;
|
|
}
|
|
const int block_num_y = (k + 4 * 128 - 1) / (4 * 128);
|
|
dim3 grid(m, block_num_y);
|
|
mkllm_quantize_mmq_d4_kernel<<<grid, 128, 0, stream>>>(
|
|
x, (mkllm_block_q8_1_mmq *) y, k, m, stride_col);
|
|
return cudaGetLastError();
|
|
}
|
|
|
|
// Strided f32 -> contiguous bf16 (activation cast for GEMM), 2D [K, M].
|
|
static __global__ void mkllm_cast_f32_bf16_kernel(
|
|
const uint8_t * __restrict__ src, __nv_bfloat16 * __restrict__ dst,
|
|
int K, int M, size_t src_nb0, size_t src_nb1) {
|
|
const int k = blockIdx.x * blockDim.x + threadIdx.x;
|
|
const int m = blockIdx.y;
|
|
if (k >= K || m >= M) return;
|
|
const float v = *(const float *) (src + (size_t) m * src_nb1 + (size_t) k * src_nb0);
|
|
dst[(size_t) m * K + k] = __float2bfloat16(v);
|
|
}
|
|
|
|
extern "C" cudaError_t mkllm_cast_f32_bf16(
|
|
const void * src, void * dst, int K, int M,
|
|
size_t src_nb0, size_t src_nb1, cudaStream_t stream) {
|
|
dim3 block(256);
|
|
dim3 grid((K + 255) / 256, M);
|
|
mkllm_cast_f32_bf16_kernel<<<grid, block, 0, stream>>>(
|
|
(const uint8_t *) src, (__nv_bfloat16 *) dst, K, M, src_nb0, src_nb1);
|
|
return cudaGetLastError();
|
|
}
|
|
|
|
// The activation cast for GEMMs whose weights are already f16 and so need no
|
|
// dequant slab. It writes the activation TWICE — once rounded to f16, once as
|
|
// the f16 of what that rounding threw away — because one f16 copy is not
|
|
// accurate enough for this.
|
|
//
|
|
// A single f16 activation loses 11 bits, and a vision tower is the worst
|
|
// place to lose them: a handful of channels carry activations orders of
|
|
// magnitude larger than the rest, so their absolute rounding error is
|
|
// correspondingly enormous. Measured on the Qwen3-VL tower against the f32
|
|
// reference kernel, a plain f16 cast moved the output embeddings by 8e-4 to
|
|
// 1.7e-2 relative RMS depending on the page — an order of magnitude past the
|
|
// gate, and page-dependent precisely because it is those outliers that decide
|
|
// it.
|
|
//
|
|
// hi + lo is exact to ~2^-22 of the original instead of 2^-11, at the price
|
|
// of running the GEMM twice (the second accumulating on top of the first).
|
|
// The weight side loses nothing either way: it is stored f16, so the f32
|
|
// reference kernel reads exactly these bits too. Both halves are clamped into
|
|
// f16 range so a value past 65504 degrades instead of turning into a NaN.
|
|
static __global__ void mkllm_cast_f32_f16_split_kernel(
|
|
const uint8_t * __restrict__ src, __half * __restrict__ hi, __half * __restrict__ lo,
|
|
int K, int M, size_t src_nb0, size_t src_nb1) {
|
|
const int k = blockIdx.x * blockDim.x + threadIdx.x;
|
|
const int m = blockIdx.y;
|
|
if (k >= K || m >= M) return;
|
|
const float v = *(const float *) (src + (size_t) m * src_nb1 + (size_t) k * src_nb0);
|
|
const float clamped = fminf(fmaxf(v, -65504.0f), 65504.0f);
|
|
const __half h = __float2half_rn(clamped);
|
|
const float r = fminf(fmaxf(v - __half2float(h), -65504.0f), 65504.0f);
|
|
const size_t o = (size_t) m * K + k;
|
|
hi[o] = h;
|
|
lo[o] = __float2half_rn(r);
|
|
}
|
|
|
|
extern "C" cudaError_t mkllm_cast_f32_f16_split(
|
|
const void * src, void * hi, void * lo, int K, int M,
|
|
size_t src_nb0, size_t src_nb1, cudaStream_t stream) {
|
|
dim3 block(256);
|
|
dim3 grid((K + 255) / 256, M);
|
|
mkllm_cast_f32_f16_split_kernel<<<grid, block, 0, stream>>>(
|
|
(const uint8_t *) src, (__half *) hi, (__half *) lo, K, M, src_nb0, src_nb1);
|
|
return cudaGetLastError();
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// General strided batched mat-mul, f16 or f32 A x f32 B -> f32:
|
|
// dst[n, m, b2, b3] = sum_k A[k, n, b2 % a_ne2, b3 % a_ne3] * B[k, m, b2, b3]
|
|
// Used for the non-flash attention QK^T and PV products (arbitrary views).
|
|
// ---------------------------------------------------------------------------
|
|
|
|
template <typename W>
|
|
static __global__ void mkllm_mul_mat_batched_kernel(
|
|
const uint8_t * __restrict__ a, const uint8_t * __restrict__ b,
|
|
uint8_t * __restrict__ dst,
|
|
int K, int N, int M, int ne2, int ne3, int a_ne2, int a_ne3,
|
|
size_t a_nb0, size_t a_nb1, size_t a_nb2, size_t a_nb3,
|
|
size_t b_nb0, size_t b_nb1, size_t b_nb2, size_t b_nb3,
|
|
size_t d_nb0, size_t d_nb1, size_t d_nb2, size_t d_nb3) {
|
|
const int n = blockIdx.x * blockDim.y + threadIdx.y;
|
|
const int m = blockIdx.y;
|
|
const int batch = blockIdx.z;
|
|
if (n >= N) return;
|
|
const int i2 = batch % ne2;
|
|
const int i3 = batch / ne2;
|
|
const int lane = threadIdx.x;
|
|
// ggml mul_mat batch broadcast: src0 batch = src1 batch / (ne / a_ne)
|
|
// (consecutive grouping — GQA heads), NOT modulo.
|
|
const int a_i2 = i2 / (ne2 / a_ne2);
|
|
const int a_i3 = i3 / (ne3 / a_ne3);
|
|
const uint8_t * a_base = a + (size_t) a_i2 * a_nb2 + (size_t) a_i3 * a_nb3
|
|
+ (size_t) n * a_nb1;
|
|
const uint8_t * b_base = b + (size_t) i2 * b_nb2 + (size_t) i3 * b_nb3 + (size_t) m * b_nb1;
|
|
float acc = 0.0f;
|
|
for (int k = lane; k < K; k += 32) {
|
|
const float av = (float) *(const W *) (a_base + (size_t) k * a_nb0);
|
|
const float bv = *(const float *) (b_base + (size_t) k * b_nb0);
|
|
acc += av * bv;
|
|
}
|
|
for (int off = 16; off > 0; off >>= 1) {
|
|
acc += __shfl_down_sync(0xffffffff, acc, off);
|
|
}
|
|
if (lane == 0) {
|
|
*(float *) (dst + (size_t) i3 * d_nb3 + (size_t) i2 * d_nb2 + (size_t) m * d_nb1
|
|
+ (size_t) n * d_nb0) = acc;
|
|
}
|
|
}
|
|
|
|
extern "C" cudaError_t mkllm_mul_mat_batched(
|
|
int a_is_f16,
|
|
const void * a, const void * b, void * dst,
|
|
int K, int N, int M, int ne2, int ne3, int a_ne2, int a_ne3,
|
|
size_t a_nb0, size_t a_nb1, size_t a_nb2, size_t a_nb3,
|
|
size_t b_nb0, size_t b_nb1, size_t b_nb2, size_t b_nb3,
|
|
size_t d_nb0, size_t d_nb1, size_t d_nb2, size_t d_nb3,
|
|
cudaStream_t stream) {
|
|
dim3 block(32, 4);
|
|
dim3 grid((N + 3) / 4, M, ne2 * ne3);
|
|
if (a_is_f16) {
|
|
mkllm_mul_mat_batched_kernel<__half><<<grid, block, 0, stream>>>(
|
|
(const uint8_t *) a, (const uint8_t *) b, (uint8_t *) dst,
|
|
K, N, M, ne2, ne3, a_ne2, a_ne3,
|
|
a_nb0, a_nb1, a_nb2, a_nb3, b_nb0, b_nb1, b_nb2, b_nb3,
|
|
d_nb0, d_nb1, d_nb2, d_nb3);
|
|
} else {
|
|
mkllm_mul_mat_batched_kernel<float><<<grid, block, 0, stream>>>(
|
|
(const uint8_t *) a, (const uint8_t *) b, (uint8_t *) dst,
|
|
K, N, M, ne2, ne3, a_ne2, a_ne3,
|
|
a_nb0, a_nb1, a_nb2, a_nb3, b_nb0, b_nb1, b_nb2, b_nb3,
|
|
d_nb0, d_nb1, d_nb2, d_nb3);
|
|
}
|
|
return cudaGetLastError();
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// get_rows / set_rows
|
|
// ---------------------------------------------------------------------------
|
|
|
|
static __global__ void mkllm_get_rows_f32_kernel(
|
|
const uint8_t * __restrict__ src, const int32_t * __restrict__ rows,
|
|
float * __restrict__ dst, int ne0, int nrows, size_t src_nb1, size_t dst_nb1) {
|
|
const int r = blockIdx.y;
|
|
const int i = blockIdx.x * blockDim.x + threadIdx.x;
|
|
if (r >= nrows || i >= ne0) return;
|
|
const float * s = (const float *) (src + (size_t) rows[r] * src_nb1);
|
|
*(float *) ((uint8_t *) dst + (size_t) r * dst_nb1 + (size_t) i * 4) = s[i];
|
|
}
|
|
|
|
extern "C" cudaError_t mkllm_get_rows_f32(
|
|
const void * src, const int32_t * rows, void * dst,
|
|
int ne0, int nrows, size_t src_nb1, size_t dst_nb1, cudaStream_t stream) {
|
|
dim3 block(256);
|
|
dim3 grid((ne0 + 255) / 256, nrows);
|
|
mkllm_get_rows_f32_kernel<<<grid, block, 0, stream>>>(
|
|
(const uint8_t *) src, rows, (float *) dst, ne0, nrows, src_nb1, dst_nb1);
|
|
return cudaGetLastError();
|
|
}
|
|
|
|
template <int KIND>
|
|
static __global__ void mkllm_get_rows_quant_kernel(
|
|
const uint8_t * __restrict__ src, const int32_t * __restrict__ rows,
|
|
float * __restrict__ dst, int ne0, int nrows, size_t src_nb1, size_t dst_nb1) {
|
|
const int r = blockIdx.y;
|
|
const int i = blockIdx.x * blockDim.x + threadIdx.x;
|
|
if (r >= nrows || i >= ne0) return;
|
|
const uint8_t * row_bytes = src + (size_t) rows[r] * src_nb1;
|
|
const uint8_t * blk = row_bytes + (size_t) (i / QK_K) * mkllm_quant_block_bytes_dev(KIND);
|
|
*(float *) ((uint8_t *) dst + (size_t) r * dst_nb1 + (size_t) i * 4) =
|
|
mkllm_deq_at(KIND, blk, i % QK_K);
|
|
}
|
|
|
|
extern "C" cudaError_t mkllm_get_rows_quant(
|
|
int kind, const void * src, const int32_t * rows, void * dst,
|
|
int ne0, int nrows, size_t src_nb1, size_t dst_nb1, cudaStream_t stream) {
|
|
dim3 block(256);
|
|
dim3 grid((ne0 + 255) / 256, nrows);
|
|
switch (kind) {
|
|
case MKLLM_QUANT_Q4K:
|
|
mkllm_get_rows_quant_kernel<MKLLM_QUANT_Q4K><<<grid, block, 0, stream>>>(
|
|
(const uint8_t *) src, rows, (float *) dst, ne0, nrows, src_nb1, dst_nb1);
|
|
break;
|
|
case MKLLM_QUANT_Q5K:
|
|
mkllm_get_rows_quant_kernel<MKLLM_QUANT_Q5K><<<grid, block, 0, stream>>>(
|
|
(const uint8_t *) src, rows, (float *) dst, ne0, nrows, src_nb1, dst_nb1);
|
|
break;
|
|
case MKLLM_QUANT_Q80:
|
|
mkllm_get_rows_quant_kernel<MKLLM_QUANT_Q80><<<grid, block, 0, stream>>>(
|
|
(const uint8_t *) src, rows, (float *) dst, ne0, nrows, src_nb1, dst_nb1);
|
|
break;
|
|
case MKLLM_QUANT_Q6K:
|
|
mkllm_get_rows_quant_kernel<MKLLM_QUANT_Q6K><<<grid, block, 0, stream>>>(
|
|
(const uint8_t *) src, rows, (float *) dst, ne0, nrows, src_nb1, dst_nb1);
|
|
break;
|
|
// The IQ/Q3_K kinds route through the shared row-dequant in
|
|
// iq_convert.cuh instead of the legacy per-element selector.
|
|
case MKLLM_QUANT_Q3K:
|
|
case MKLLM_QUANT_IQ4XS:
|
|
case MKLLM_QUANT_IQ4NL:
|
|
case MKLLM_QUANT_IQ3S:
|
|
return mkllm_get_rows_iq_f32(
|
|
kind, src, rows, dst, ne0, nrows, src_nb1, dst_nb1, stream);
|
|
default:
|
|
return cudaErrorInvalidValue;
|
|
}
|
|
return cudaGetLastError();
|
|
}
|
|
|
|
// set_rows: dst[rows[r], :] = src[r, :], f32 src rows into f32 or f16 dst.
|
|
template <typename D>
|
|
static __global__ void mkllm_set_rows_kernel(
|
|
const uint8_t * __restrict__ src, const int32_t * __restrict__ rows,
|
|
uint8_t * __restrict__ dst, int ne0, int nrows,
|
|
size_t src_nb1, size_t dst_nb1) {
|
|
const int r = blockIdx.y;
|
|
const int i = blockIdx.x * blockDim.x + threadIdx.x;
|
|
if (r >= nrows || i >= ne0) return;
|
|
const float v = *(const float *) (src + (size_t) r * src_nb1 + (size_t) i * 4);
|
|
D * out = (D *) (dst + (size_t) rows[r] * dst_nb1);
|
|
out[i] = (D) v;
|
|
}
|
|
|
|
extern "C" cudaError_t mkllm_set_rows(
|
|
int dst_is_f16, const void * src, const int32_t * rows, void * dst,
|
|
int ne0, int nrows, size_t src_nb1, size_t dst_nb1, cudaStream_t stream) {
|
|
dim3 block(256);
|
|
dim3 grid((ne0 + 255) / 256, nrows);
|
|
if (dst_is_f16) {
|
|
mkllm_set_rows_kernel<__half><<<grid, block, 0, stream>>>(
|
|
(const uint8_t *) src, rows, (uint8_t *) dst, ne0, nrows, src_nb1, dst_nb1);
|
|
} else {
|
|
mkllm_set_rows_kernel<float><<<grid, block, 0, stream>>>(
|
|
(const uint8_t *) src, rows, (uint8_t *) dst, ne0, nrows, src_nb1, dst_nb1);
|
|
}
|
|
return cudaGetLastError();
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Masked softmax (soft_max_ext): dst = softmax(x*scale + mask) per row.
|
|
// x [ncols, ne1, ne2], mask f32 [ncols, mask_ne1] broadcast over ne2.
|
|
// ---------------------------------------------------------------------------
|
|
|
|
static __global__ void mkllm_softmax_mask_kernel(
|
|
const uint8_t * __restrict__ x, const uint8_t * __restrict__ mask,
|
|
uint8_t * __restrict__ dst,
|
|
int ncols, int ne1, float scale,
|
|
size_t x_nb1, size_t x_nb2, size_t mask_nb1, size_t d_nb1, size_t d_nb2) {
|
|
const int i1 = blockIdx.x;
|
|
const int i2 = blockIdx.y;
|
|
const int lane = threadIdx.x;
|
|
const float * xr = (const float *) (x + (size_t) i2 * x_nb2 + (size_t) i1 * x_nb1);
|
|
const float * mr = mask ? (const float *) (mask + (size_t) (i1 % ne1) * mask_nb1) : nullptr;
|
|
float * dr = (float *) (dst + (size_t) i2 * d_nb2 + (size_t) i1 * d_nb1);
|
|
|
|
extern __shared__ float sm_red[];
|
|
float maxv = -INFINITY;
|
|
for (int c = lane; c < ncols; c += blockDim.x) {
|
|
const float v = xr[c] * scale + (mr ? mr[c] : 0.0f);
|
|
maxv = fmaxf(maxv, v);
|
|
}
|
|
sm_red[lane] = maxv;
|
|
__syncthreads();
|
|
for (int off = blockDim.x >> 1; off > 0; off >>= 1) {
|
|
if (lane < off) sm_red[lane] = fmaxf(sm_red[lane], sm_red[lane + off]);
|
|
__syncthreads();
|
|
}
|
|
maxv = sm_red[0];
|
|
__syncthreads();
|
|
float sum = 0.0f;
|
|
for (int c = lane; c < ncols; c += blockDim.x) {
|
|
const float v = expf(xr[c] * scale + (mr ? mr[c] : 0.0f) - maxv);
|
|
dr[c] = v;
|
|
sum += v;
|
|
}
|
|
sm_red[lane] = sum;
|
|
__syncthreads();
|
|
for (int off = blockDim.x >> 1; off > 0; off >>= 1) {
|
|
if (lane < off) sm_red[lane] += sm_red[lane + off];
|
|
__syncthreads();
|
|
}
|
|
sum = sm_red[0];
|
|
const float inv = sum > 0.0f ? 1.0f / sum : 0.0f;
|
|
for (int c = lane; c < ncols; c += blockDim.x) {
|
|
dr[c] *= inv;
|
|
}
|
|
}
|
|
|
|
extern "C" cudaError_t mkllm_softmax_mask(
|
|
const void * x, const void * mask, void * dst,
|
|
int ncols, int ne1, int ne2, float scale,
|
|
size_t x_nb1, size_t x_nb2, size_t mask_nb1, size_t d_nb1, size_t d_nb2,
|
|
cudaStream_t stream) {
|
|
dim3 block(256);
|
|
dim3 grid(ne1, ne2);
|
|
mkllm_softmax_mask_kernel<<<grid, block, 256 * sizeof(float), stream>>>(
|
|
(const uint8_t *) x, (const uint8_t *) mask, (uint8_t *) dst,
|
|
ncols, ne1, scale, x_nb1, x_nb2, mask_nb1, d_nb1, d_nb2);
|
|
return cudaGetLastError();
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Flash-attention decode (small n_q): one block per (head, query).
|
|
// q f32 [D, nq, H], k f16 [D, kc, Hkv], v f16 [Dv, kc, Hkv], mask f16
|
|
// [kc, nq_pad], dst f32 [Dv, H, nq]. Online softmax, f32 accumulation.
|
|
// ---------------------------------------------------------------------------
|
|
|
|
static __global__ void mkllm_flash_decode_kernel(
|
|
const uint8_t * __restrict__ q, const uint8_t * __restrict__ k,
|
|
const uint8_t * __restrict__ v, const uint8_t * __restrict__ mask,
|
|
uint8_t * __restrict__ dst,
|
|
int D, int Dv, int kc, int n_q, int H, int Hkv, float scale,
|
|
size_t q_nb1, size_t q_nb2,
|
|
size_t k_nb1, size_t k_nb2,
|
|
size_t v_nb1, size_t v_nb2,
|
|
size_t m_nb1,
|
|
size_t d_nb1, size_t d_nb2) {
|
|
const int head = blockIdx.x;
|
|
const int iq = blockIdx.y;
|
|
const int lane = threadIdx.x; // blockDim.x = 128
|
|
const int kv_head = head / (H / Hkv);
|
|
|
|
const float * qv = (const float *) (q + (size_t) head * q_nb2 + (size_t) iq * q_nb1);
|
|
const __half * mrow = (const __half *) (mask + (size_t) iq * m_nb1);
|
|
const uint8_t * kh = k + (size_t) kv_head * k_nb2;
|
|
const uint8_t * vh = v + (size_t) kv_head * v_nb2;
|
|
|
|
extern __shared__ float fd_smem[];
|
|
float * qs = fd_smem; // D floats
|
|
float * red = fd_smem + D; // blockDim.x floats
|
|
float * acc = fd_smem + D + blockDim.x; // Dv floats
|
|
for (int i = lane; i < D; i += blockDim.x) {
|
|
qs[i] = qv[i];
|
|
}
|
|
for (int i = lane; i < Dv; i += blockDim.x) {
|
|
acc[i] = 0.0f;
|
|
}
|
|
__shared__ float m_running, l_running;
|
|
if (lane == 0) { m_running = -INFINITY; l_running = 0.0f; }
|
|
__syncthreads();
|
|
|
|
for (int key = 0; key < kc; key++) {
|
|
const float mval = __half2float(mrow[key]);
|
|
float s;
|
|
if (mval == -INFINITY || mval < -65500.0f) {
|
|
s = -INFINITY;
|
|
} else {
|
|
const __half * krow = (const __half *) (kh + (size_t) key * k_nb1);
|
|
float partial = 0.0f;
|
|
for (int i = lane; i < D; i += blockDim.x) {
|
|
partial += qs[i] * __half2float(krow[i]);
|
|
}
|
|
red[lane] = partial;
|
|
__syncthreads();
|
|
for (int off = blockDim.x >> 1; off > 0; off >>= 1) {
|
|
if (lane < off) red[lane] += red[lane + off];
|
|
__syncthreads();
|
|
}
|
|
s = red[0] * scale + mval;
|
|
}
|
|
__syncthreads();
|
|
if (s != -INFINITY) {
|
|
// Every lane reads m_running before lane 0 rewrites it below;
|
|
// the barrier separates the read phase from the write.
|
|
const float m_new = fmaxf(m_running, s);
|
|
const float corr = expf(m_running - m_new);
|
|
const float p = expf(s - m_new);
|
|
__syncthreads();
|
|
const __half * vrow = (const __half *) (vh + (size_t) key * v_nb1);
|
|
for (int i = lane; i < Dv; i += blockDim.x) {
|
|
acc[i] = acc[i] * corr + p * __half2float(vrow[i]);
|
|
}
|
|
if (lane == 0) {
|
|
l_running = l_running * corr + p;
|
|
m_running = m_new;
|
|
}
|
|
}
|
|
__syncthreads();
|
|
}
|
|
|
|
float * out = (float *) (dst + (size_t) iq * d_nb2 + (size_t) head * d_nb1);
|
|
const float inv = l_running > 0.0f ? 1.0f / l_running : 0.0f;
|
|
for (int i = lane; i < Dv; i += blockDim.x) {
|
|
out[i] = acc[i] * inv;
|
|
}
|
|
}
|
|
|
|
extern "C" cudaError_t mkllm_flash_decode(
|
|
const void * q, const void * k, const void * v, const void * mask, void * dst,
|
|
int D, int Dv, int kc, int n_q, int H, int Hkv, float scale,
|
|
size_t q_nb1, size_t q_nb2, size_t k_nb1, size_t k_nb2,
|
|
size_t v_nb1, size_t v_nb2, size_t m_nb1, size_t d_nb1, size_t d_nb2,
|
|
cudaStream_t stream) {
|
|
dim3 block(128);
|
|
dim3 grid(H, n_q);
|
|
const size_t shared = (size_t) (D + 128 + Dv) * sizeof(float);
|
|
mkllm_flash_decode_kernel<<<grid, block, shared, stream>>>(
|
|
(const uint8_t *) q, (const uint8_t *) k, (const uint8_t *) v,
|
|
(const uint8_t *) mask, (uint8_t *) dst,
|
|
D, Dv, kc, n_q, H, Hkv, scale,
|
|
q_nb1, q_nb2, k_nb1, k_nb2, v_nb1, v_nb2, m_nb1, d_nb1, d_nb2);
|
|
return cudaGetLastError();
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Norms: rows along dim0 (contiguous), arbitrary higher-dim strides.
|
|
// ---------------------------------------------------------------------------
|
|
|
|
template <int L2>
|
|
static __global__ void mkllm_norm_kernel(
|
|
const uint8_t * __restrict__ x, uint8_t * __restrict__ dst,
|
|
int ne0, int ne1, int ne2, float eps,
|
|
size_t x_nb1, size_t x_nb2, size_t x_nb3,
|
|
size_t d_nb1, size_t d_nb2, size_t d_nb3) {
|
|
const int i1 = blockIdx.x % ne1;
|
|
const int i23 = blockIdx.x / ne1;
|
|
const int i2 = i23 % ne2;
|
|
const int i3 = i23 / ne2;
|
|
const int lane = threadIdx.x;
|
|
const float * xr = (const float *) (x + (size_t) i3 * x_nb3 + (size_t) i2 * x_nb2
|
|
+ (size_t) i1 * x_nb1);
|
|
float * dr = (float *) (dst + (size_t) i3 * d_nb3 + (size_t) i2 * d_nb2
|
|
+ (size_t) i1 * d_nb1);
|
|
extern __shared__ float nrm_red[];
|
|
float sum = 0.0f;
|
|
for (int c = lane; c < ne0; c += blockDim.x) {
|
|
const float v = xr[c];
|
|
sum += v * v;
|
|
}
|
|
nrm_red[lane] = sum;
|
|
__syncthreads();
|
|
for (int off = blockDim.x >> 1; off > 0; off >>= 1) {
|
|
if (lane < off) nrm_red[lane] += nrm_red[lane + off];
|
|
__syncthreads();
|
|
}
|
|
sum = nrm_red[0];
|
|
float denom;
|
|
if (L2) {
|
|
denom = 1.0f / sqrtf(fmaxf(sum, eps));
|
|
} else {
|
|
denom = rsqrtf(sum / (float) ne0 + eps);
|
|
}
|
|
for (int c = lane; c < ne0; c += blockDim.x) {
|
|
dr[c] = xr[c] * denom;
|
|
}
|
|
}
|
|
|
|
extern "C" cudaError_t mkllm_norm(
|
|
int l2, const void * x, void * dst,
|
|
int ne0, int ne1, int ne2, int ne3, float eps,
|
|
size_t x_nb1, size_t x_nb2, size_t x_nb3,
|
|
size_t d_nb1, size_t d_nb2, size_t d_nb3, cudaStream_t stream) {
|
|
// llama.cpp norm.cu:297-307: 256 threads if ncols<1024, else 1024.
|
|
const int nthreads = (!l2 && ne0 >= 1024) ? 1024 : 256;
|
|
dim3 block((unsigned) nthreads);
|
|
dim3 grid(ne1 * ne2 * ne3);
|
|
const size_t shared = (size_t) nthreads * sizeof(float);
|
|
if (l2) {
|
|
mkllm_norm_kernel<1><<<grid, block, shared, stream>>>(
|
|
(const uint8_t *) x, (uint8_t *) dst, ne0, ne1, ne2, eps,
|
|
x_nb1, x_nb2, x_nb3, d_nb1, d_nb2, d_nb3);
|
|
} else {
|
|
mkllm_norm_kernel<0><<<grid, block, shared, stream>>>(
|
|
(const uint8_t *) x, (uint8_t *) dst, ne0, ne1, ne2, eps,
|
|
x_nb1, x_nb2, x_nb3, d_nb1, d_nb2, d_nb3);
|
|
}
|
|
return cudaGetLastError();
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// LayerNorm (ggml GGML_OP_NORM): mean/variance over dim0, no affine.
|
|
//
|
|
// `mkllm_norm` above is RMS/L2 math (sum of squares, no centring) and is NOT a
|
|
// substitute. Transcribed from llama.cpp norm.cu:12-52 norm_f32: one pass
|
|
// accumulating (sum, sum of squares), var = E[x^2] - E[x]^2.
|
|
// ---------------------------------------------------------------------------
|
|
|
|
static __global__ void mkllm_layer_norm_kernel(
|
|
const uint8_t * __restrict__ x, uint8_t * __restrict__ dst,
|
|
int ne0, int ne1, int ne2, float eps,
|
|
size_t x_nb1, size_t x_nb2, size_t x_nb3,
|
|
size_t d_nb1, size_t d_nb2, size_t d_nb3) {
|
|
const int i1 = blockIdx.x % ne1;
|
|
const int i23 = blockIdx.x / ne1;
|
|
const int i2 = i23 % ne2;
|
|
const int i3 = i23 / ne2;
|
|
const int lane = threadIdx.x;
|
|
const float * xr = (const float *) (x + (size_t) i3 * x_nb3 + (size_t) i2 * x_nb2
|
|
+ (size_t) i1 * x_nb1);
|
|
float * dr = (float *) (dst + (size_t) i3 * d_nb3 + (size_t) i2 * d_nb2
|
|
+ (size_t) i1 * d_nb1);
|
|
|
|
extern __shared__ float lnrm_red[];
|
|
float * red_sum = lnrm_red;
|
|
float * red_sqr = lnrm_red + blockDim.x;
|
|
|
|
float sum = 0.0f;
|
|
float sqr = 0.0f;
|
|
for (int c = lane; c < ne0; c += blockDim.x) {
|
|
const float v = xr[c];
|
|
sum += v;
|
|
sqr += v * v;
|
|
}
|
|
red_sum[lane] = sum;
|
|
red_sqr[lane] = sqr;
|
|
__syncthreads();
|
|
for (int off = blockDim.x >> 1; off > 0; off >>= 1) {
|
|
if (lane < off) {
|
|
red_sum[lane] += red_sum[lane + off];
|
|
red_sqr[lane] += red_sqr[lane + off];
|
|
}
|
|
__syncthreads();
|
|
}
|
|
const float mean = red_sum[0] / (float) ne0;
|
|
const float var = red_sqr[0] / (float) ne0 - mean * mean;
|
|
const float inv_std = rsqrtf(var + eps);
|
|
for (int c = lane; c < ne0; c += blockDim.x) {
|
|
dr[c] = (xr[c] - mean) * inv_std;
|
|
}
|
|
}
|
|
|
|
extern "C" cudaError_t mkllm_layer_norm(
|
|
const void * x, void * dst,
|
|
int ne0, int ne1, int ne2, int ne3, float eps,
|
|
size_t x_nb1, size_t x_nb2, size_t x_nb3,
|
|
size_t d_nb1, size_t d_nb2, size_t d_nb3, cudaStream_t stream) {
|
|
// llama.cpp norm.cu:297-307: 256 threads if ncols<1024, else 1024.
|
|
const int nthreads = ne0 >= 1024 ? 1024 : 256;
|
|
dim3 block((unsigned) nthreads);
|
|
dim3 grid(ne1 * ne2 * ne3);
|
|
const size_t shared = (size_t) nthreads * 2 * sizeof(float);
|
|
mkllm_layer_norm_kernel<<<grid, block, shared, stream>>>(
|
|
(const uint8_t *) x, (uint8_t *) dst, ne0, ne1, ne2, eps,
|
|
x_nb1, x_nb2, x_nb3, d_nb1, d_nb2, d_nb3);
|
|
return cudaGetLastError();
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// rope multi (MROPE/IMROPE), f32, transcribed from kernel_rope_multi.
|
|
// src [ne0, ne1, ne2, ne3] (dim0 = head_dim, ne2 = tokens), pos i32 with 4
|
|
// planes of ne2 entries.
|
|
// ---------------------------------------------------------------------------
|
|
|
|
static __device__ __forceinline__ float mkllm_rope_yarn_ramp(float low, float high, int i0) {
|
|
const float y = ((float) (i0 / 2) - low) / fmaxf(0.001f, high - low);
|
|
return 1.0f - fminf(1.0f, fmaxf(0.0f, y));
|
|
}
|
|
|
|
static __global__ void mkllm_rope_multi_kernel(
|
|
const uint8_t * __restrict__ src, const int32_t * __restrict__ pos,
|
|
uint8_t * __restrict__ dst,
|
|
int ne0, int ne1, int ne2, int is_imrope,
|
|
int n_dims, int sect_0, int sect_1, int sect_2, int sect_3,
|
|
float freq_base, float freq_scale, float ext_factor, float attn_factor,
|
|
float corr_dim0, float corr_dim1,
|
|
size_t s_nb0, size_t s_nb1, size_t s_nb2, size_t s_nb3,
|
|
size_t d_nb0, size_t d_nb1, size_t d_nb2, size_t d_nb3) {
|
|
const int i1 = blockIdx.x;
|
|
const int i2 = blockIdx.y;
|
|
const int i3 = blockIdx.z;
|
|
const float inv_ndims = -1.0f / (float) n_dims;
|
|
|
|
for (int i0 = 2 * threadIdx.x; i0 < ne0; i0 += 2 * blockDim.x) {
|
|
if (i0 < n_dims) {
|
|
const int ic = i0 / 2;
|
|
const int sect_dims = sect_0 + sect_1 + sect_2 + sect_3;
|
|
const int sec_w01 = sect_0 + sect_1;
|
|
const int sec_w012 = sect_0 + sect_1 + sect_2;
|
|
const int sector = ic % sect_dims;
|
|
float theta_base;
|
|
if (is_imrope) {
|
|
if (sector % 3 == 1 && sector < 3 * sect_1) {
|
|
theta_base = (float) pos[i2 + ne2 * 1];
|
|
} else if (sector % 3 == 2 && sector < 3 * sect_2) {
|
|
theta_base = (float) pos[i2 + ne2 * 2];
|
|
} else if (sector % 3 == 0 && sector < 3 * sect_0) {
|
|
theta_base = (float) pos[i2 + ne2 * 0];
|
|
} else {
|
|
theta_base = (float) pos[i2 + ne2 * 3];
|
|
}
|
|
} else {
|
|
if (sector < sect_0) {
|
|
theta_base = (float) pos[i2];
|
|
} else if (sector < sec_w01) {
|
|
theta_base = (float) pos[i2 + ne2 * 1];
|
|
} else if (sector < sec_w012) {
|
|
theta_base = (float) pos[i2 + ne2 * 2];
|
|
} else {
|
|
theta_base = (float) pos[i2 + ne2 * 3];
|
|
}
|
|
}
|
|
const float theta_extrap = theta_base * powf(freq_base, inv_ndims * (float) i0);
|
|
float mscale = attn_factor;
|
|
float theta = freq_scale * theta_extrap;
|
|
if (ext_factor != 0.0f) {
|
|
const float ramp = mkllm_rope_yarn_ramp(corr_dim0, corr_dim1, i0) * ext_factor;
|
|
theta = theta * (1.0f - ramp) + theta_extrap * ramp;
|
|
mscale *= 1.0f + 0.1f * logf(1.0f / freq_scale);
|
|
}
|
|
const float cos_t = cosf(theta) * mscale;
|
|
const float sin_t = sinf(theta) * mscale;
|
|
const float * sp = (const float *) (src + (size_t) i3 * s_nb3 + (size_t) i2 * s_nb2
|
|
+ (size_t) i1 * s_nb1 + (size_t) ic * s_nb0);
|
|
float * dp = (float *) (dst + (size_t) i3 * d_nb3 + (size_t) i2 * d_nb2
|
|
+ (size_t) i1 * d_nb1 + (size_t) ic * d_nb0);
|
|
const float x0 = sp[0];
|
|
const float x1 = sp[n_dims / 2];
|
|
dp[0] = x0 * cos_t - x1 * sin_t;
|
|
dp[n_dims / 2] = x0 * sin_t + x1 * cos_t;
|
|
} else {
|
|
const float * sp = (const float *) (src + (size_t) i3 * s_nb3 + (size_t) i2 * s_nb2
|
|
+ (size_t) i1 * s_nb1 + (size_t) i0 * s_nb0);
|
|
float * dp = (float *) (dst + (size_t) i3 * d_nb3 + (size_t) i2 * d_nb2
|
|
+ (size_t) i1 * d_nb1 + (size_t) i0 * d_nb0);
|
|
dp[0] = sp[0];
|
|
dp[1] = sp[1];
|
|
}
|
|
}
|
|
}
|
|
|
|
extern "C" cudaError_t mkllm_rope_multi(
|
|
const void * src, const int32_t * pos, void * dst,
|
|
int ne0, int ne1, int ne2, int ne3, int is_imrope,
|
|
int n_dims, int sect_0, int sect_1, int sect_2, int sect_3,
|
|
float freq_base, float freq_scale, float ext_factor, float attn_factor,
|
|
float corr_dim0, float corr_dim1,
|
|
size_t s_nb0, size_t s_nb1, size_t s_nb2, size_t s_nb3,
|
|
size_t d_nb0, size_t d_nb1, size_t d_nb2, size_t d_nb3,
|
|
cudaStream_t stream) {
|
|
dim3 block(64);
|
|
dim3 grid(ne1, ne2, ne3);
|
|
mkllm_rope_multi_kernel<<<grid, block, 0, stream>>>(
|
|
(const uint8_t *) src, pos, (uint8_t *) dst,
|
|
ne0, ne1, ne2, is_imrope, n_dims, sect_0, sect_1, sect_2, sect_3,
|
|
freq_base, freq_scale, ext_factor, attn_factor, corr_dim0, corr_dim1,
|
|
s_nb0, s_nb1, s_nb2, s_nb3, d_nb0, d_nb1, d_nb2, d_nb3);
|
|
return cudaGetLastError();
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// rope vision (GGML_ROPE_TYPE_VISION = 24), f32.
|
|
//
|
|
// NOT a flag on rope_multi: it uses TWO sections (not four), the pair split is
|
|
// at `n_dims` (not `n_dims/2`), the loop covers `2*n_dims` (not `n_dims`), and
|
|
// theta uses `pow(freq_base, 2*inv_ndims*p)` on the sector index `p` rather
|
|
// than `pow(freq_base, inv_ndims*i0)`. Routing VISION into rope_multi is
|
|
// silently wrong, not fatal. Transcribed from kernel_rope_vision
|
|
// (ggml-metal.metal:4363-4427), the Metal oracle for this graph.
|
|
// ---------------------------------------------------------------------------
|
|
|
|
static __global__ void mkllm_rope_vision_kernel(
|
|
const uint8_t * __restrict__ src, const int32_t * __restrict__ pos,
|
|
uint8_t * __restrict__ dst,
|
|
int ne0, int ne1, int ne2,
|
|
int n_dims, int sect_0, int sect_1,
|
|
float freq_base, float freq_scale, float ext_factor, float attn_factor,
|
|
float corr_dim0, float corr_dim1,
|
|
size_t s_nb0, size_t s_nb1, size_t s_nb2, size_t s_nb3,
|
|
size_t d_nb0, size_t d_nb1, size_t d_nb2, size_t d_nb3) {
|
|
const int i1 = blockIdx.x;
|
|
const int i2 = blockIdx.y;
|
|
const int i3 = blockIdx.z;
|
|
const float inv_ndims = -1.0f / (float) n_dims;
|
|
|
|
for (int i0 = 2 * threadIdx.x; i0 < ne0; i0 += 2 * blockDim.x) {
|
|
if (i0 < 2 * n_dims) {
|
|
const int ic = i0 / 2;
|
|
const int sect_dims = sect_0 + sect_1;
|
|
const int sector = ic % sect_dims;
|
|
|
|
float p;
|
|
float theta_base;
|
|
if (sector < sect_1) {
|
|
p = (float) sector;
|
|
theta_base = (float) pos[i2];
|
|
} else {
|
|
p = (float) (sector - sect_0);
|
|
theta_base = (float) pos[i2 + ne2];
|
|
}
|
|
|
|
const float theta_extrap = theta_base * powf(freq_base, 2.0f * inv_ndims * p);
|
|
float mscale = attn_factor;
|
|
float theta = freq_scale * theta_extrap;
|
|
if (ext_factor != 0.0f) {
|
|
const float ramp = mkllm_rope_yarn_ramp(corr_dim0, corr_dim1, i0) * ext_factor;
|
|
theta = theta * (1.0f - ramp) + theta_extrap * ramp;
|
|
mscale *= 1.0f + 0.1f * logf(1.0f / freq_scale);
|
|
}
|
|
const float cos_t = cosf(theta) * mscale;
|
|
const float sin_t = sinf(theta) * mscale;
|
|
|
|
const float * sp = (const float *) (src + (size_t) i3 * s_nb3
|
|
+ (size_t) i2 * s_nb2 + (size_t) i1 * s_nb1 + (size_t) ic * s_nb0);
|
|
float * dp = (float *) (dst + (size_t) i3 * d_nb3
|
|
+ (size_t) i2 * d_nb2 + (size_t) i1 * d_nb1 + (size_t) ic * d_nb0);
|
|
|
|
const float x0 = sp[0];
|
|
const float x1 = sp[n_dims];
|
|
|
|
dp[0] = x0 * cos_t - x1 * sin_t;
|
|
dp[n_dims] = x0 * sin_t + x1 * cos_t;
|
|
} else {
|
|
const float * sp = (const float *) (src + (size_t) i3 * s_nb3
|
|
+ (size_t) i2 * s_nb2 + (size_t) i1 * s_nb1 + (size_t) i0 * s_nb0);
|
|
float * dp = (float *) (dst + (size_t) i3 * d_nb3
|
|
+ (size_t) i2 * d_nb2 + (size_t) i1 * d_nb1 + (size_t) i0 * d_nb0);
|
|
dp[0] = sp[0];
|
|
dp[1] = sp[1];
|
|
}
|
|
}
|
|
}
|
|
|
|
extern "C" cudaError_t mkllm_rope_vision(
|
|
const void * src, const int32_t * pos, void * dst,
|
|
int ne0, int ne1, int ne2, int ne3,
|
|
int n_dims, int sect_0, int sect_1,
|
|
float freq_base, float freq_scale, float ext_factor, float attn_factor,
|
|
float corr_dim0, float corr_dim1,
|
|
size_t s_nb0, size_t s_nb1, size_t s_nb2, size_t s_nb3,
|
|
size_t d_nb0, size_t d_nb1, size_t d_nb2, size_t d_nb3,
|
|
cudaStream_t stream) {
|
|
dim3 block(64);
|
|
dim3 grid(ne1, ne2, ne3);
|
|
mkllm_rope_vision_kernel<<<grid, block, 0, stream>>>(
|
|
(const uint8_t *) src, pos, (uint8_t *) dst,
|
|
ne0, ne1, ne2, n_dims, sect_0, sect_1,
|
|
freq_base, freq_scale, ext_factor, attn_factor, corr_dim0, corr_dim1,
|
|
s_nb0, s_nb1, s_nb2, s_nb3, d_nb0, d_nb1, d_nb2, d_nb3);
|
|
return cudaGetLastError();
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// upscale / interpolate (ggml GGML_OP_UPSCALE), f32, bilinear with the
|
|
// optional antialias triangle filter. Transcribed from
|
|
// kernel_upscale_bilinear_f32 (ggml-metal.metal:4854-4933).
|
|
// ---------------------------------------------------------------------------
|
|
|
|
static __global__ void mkllm_upscale_bilinear_kernel(
|
|
const uint8_t * __restrict__ src, uint8_t * __restrict__ dst,
|
|
int ne00, int ne01, int ne0, int ne1,
|
|
float sf2, float sf3, float sfx, float sfy, float poffs, int aa,
|
|
size_t s_nb00, size_t s_nb01, size_t s_nb02, size_t s_nb03,
|
|
size_t d_nb1, size_t d_nb2, size_t d_nb3) {
|
|
const int i1 = blockIdx.x;
|
|
const int i2 = blockIdx.y;
|
|
const int i3 = blockIdx.z;
|
|
|
|
// Metal divides the int index by a float scale and truncates on assign.
|
|
const int i03 = (int) ((float) i3 / sf3);
|
|
const int i02 = (int) ((float) i2 / sf2);
|
|
|
|
const float f01 = ((float) i1 + poffs) / sfy - poffs;
|
|
const int i01 = max(0, min(ne01 - 1, (int) floorf(f01)));
|
|
const int i01p = max(0, min(ne01 - 1, i01 + 1));
|
|
const float fd1 = fmaxf(0.0f, fminf(1.0f, f01 - (float) i01));
|
|
|
|
const uint8_t * s0 = src + (size_t) i03 * s_nb03 + (size_t) i02 * s_nb02;
|
|
float * dp = (float *) (dst + (size_t) i3 * d_nb3 + (size_t) i2 * d_nb2
|
|
+ (size_t) i1 * d_nb1);
|
|
|
|
if (aa) {
|
|
const float support0 = fmaxf(1.0f, 1.0f / sfx);
|
|
const float invscale0 = 1.0f / support0;
|
|
const float support1 = fmaxf(1.0f, 1.0f / sfy);
|
|
const float invscale1 = 1.0f / support1;
|
|
|
|
for (int i0 = threadIdx.x; i0 < ne0; i0 += blockDim.x) {
|
|
const float f00 = ((float) i0 + poffs) / sfx - poffs;
|
|
|
|
const int x_min = max(0, (int) floorf(f00 - support0 + poffs));
|
|
const int x_max = min(ne00, (int) ceilf(f00 + support0 + poffs));
|
|
const int y_min = max(0, (int) floorf(f01 - support1 + poffs));
|
|
const int y_max = min(ne01, (int) ceilf(f01 + support1 + poffs));
|
|
|
|
float sum = 0.0f;
|
|
float wsum = 0.0f;
|
|
for (int sy = y_min; sy < y_max; ++sy) {
|
|
const float wy = fmaxf(0.0f, 1.0f - fabsf((float) sy - f01) * invscale1);
|
|
for (int sx = x_min; sx < x_max; ++sx) {
|
|
const float wx = fmaxf(0.0f, 1.0f - fabsf((float) sx - f00) * invscale0);
|
|
const float w = wx * wy;
|
|
const float * sp = (const float *) (s0 + (size_t) sy * s_nb01
|
|
+ (size_t) sx * s_nb00);
|
|
sum += (*sp) * w;
|
|
wsum += w;
|
|
}
|
|
}
|
|
dp[i0] = wsum > 0.0f ? sum / wsum : 0.0f;
|
|
}
|
|
} else {
|
|
for (int i0 = threadIdx.x; i0 < ne0; i0 += blockDim.x) {
|
|
const float f00 = ((float) i0 + poffs) / sfx - poffs;
|
|
const int i00 = max(0, min(ne00 - 1, (int) floorf(f00)));
|
|
const int i00p = max(0, min(ne00 - 1, i00 + 1));
|
|
const float fd0 = fmaxf(0.0f, fminf(1.0f, f00 - (float) i00));
|
|
|
|
const float s00 = *(const float *) (s0 + (size_t) i01 * s_nb01 + (size_t) i00 * s_nb00);
|
|
const float s10 = *(const float *) (s0 + (size_t) i01 * s_nb01 + (size_t) i00p * s_nb00);
|
|
const float s01 = *(const float *) (s0 + (size_t) i01p * s_nb01 + (size_t) i00 * s_nb00);
|
|
const float s11 = *(const float *) (s0 + (size_t) i01p * s_nb01 + (size_t) i00p * s_nb00);
|
|
|
|
dp[i0] = s00 * (1.0f - fd0) * (1.0f - fd1)
|
|
+ s10 * fd0 * (1.0f - fd1)
|
|
+ s01 * (1.0f - fd0) * fd1
|
|
+ s11 * fd0 * fd1;
|
|
}
|
|
}
|
|
}
|
|
|
|
extern "C" cudaError_t mkllm_upscale_bilinear(
|
|
const void * src, void * dst,
|
|
int ne00, int ne01, int ne0, int ne1, int ne2, int ne3,
|
|
float sf2, float sf3, float sfx, float sfy, float poffs, int aa,
|
|
size_t s_nb00, size_t s_nb01, size_t s_nb02, size_t s_nb03,
|
|
size_t d_nb1, size_t d_nb2, size_t d_nb3,
|
|
cudaStream_t stream) {
|
|
dim3 block(64);
|
|
dim3 grid(ne1, ne2, ne3);
|
|
mkllm_upscale_bilinear_kernel<<<grid, block, 0, stream>>>(
|
|
(const uint8_t *) src, (uint8_t *) dst,
|
|
ne00, ne01, ne0, ne1, sf2, sf3, sfx, sfy, poffs, aa,
|
|
s_nb00, s_nb01, s_nb02, s_nb03, d_nb1, d_nb2, d_nb3);
|
|
return cudaGetLastError();
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Elementwise: unary, glu (split), binary with ggml modulo-broadcast.
|
|
// ---------------------------------------------------------------------------
|
|
|
|
#define MKLLM_UNARY_ABS 0
|
|
#define MKLLM_UNARY_SGN 1
|
|
#define MKLLM_UNARY_NEG 2
|
|
#define MKLLM_UNARY_STEP 3
|
|
#define MKLLM_UNARY_TANH 4
|
|
#define MKLLM_UNARY_ELU 5
|
|
#define MKLLM_UNARY_RELU 6
|
|
#define MKLLM_UNARY_SIGMOID 7
|
|
#define MKLLM_UNARY_GELU 8
|
|
#define MKLLM_UNARY_GELU_QUICK 9
|
|
#define MKLLM_UNARY_SILU 10
|
|
#define MKLLM_UNARY_HARDSWISH 11
|
|
#define MKLLM_UNARY_HARDSIGMOID 12
|
|
#define MKLLM_UNARY_EXP 13
|
|
#define MKLLM_UNARY_EXPM1 14
|
|
#define MKLLM_UNARY_SOFTPLUS 15
|
|
#define MKLLM_UNARY_GELU_ERF 16
|
|
|
|
static __device__ __forceinline__ float mkllm_unary_apply(int op, float x) {
|
|
switch (op) {
|
|
case MKLLM_UNARY_ABS: return fabsf(x);
|
|
case MKLLM_UNARY_SGN: return x > 0.0f ? 1.0f : (x < 0.0f ? -1.0f : 0.0f);
|
|
case MKLLM_UNARY_NEG: return -x;
|
|
case MKLLM_UNARY_STEP: return x > 0.0f ? 1.0f : 0.0f;
|
|
case MKLLM_UNARY_TANH: return tanhf(x);
|
|
case MKLLM_UNARY_ELU: return x > 0.0f ? x : expm1f(x);
|
|
case MKLLM_UNARY_RELU: return fmaxf(x, 0.0f);
|
|
case MKLLM_UNARY_SIGMOID: return 1.0f / (1.0f + expf(-x));
|
|
case MKLLM_UNARY_GELU:
|
|
return 0.5f * x * (1.0f + tanhf(0.79788456080286535588f * (x + 0.044715f * x * x * x)));
|
|
case MKLLM_UNARY_GELU_QUICK: return x * (1.0f / (1.0f + expf(-1.702f * x)));
|
|
case MKLLM_UNARY_SILU: return x / (1.0f + expf(-x));
|
|
case MKLLM_UNARY_HARDSWISH: return x * fminf(1.0f, fmaxf(0.0f, (x + 3.0f) / 6.0f));
|
|
case MKLLM_UNARY_HARDSIGMOID: return fminf(1.0f, fmaxf(0.0f, (x + 3.0f) / 6.0f));
|
|
case MKLLM_UNARY_EXP: return expf(x);
|
|
case MKLLM_UNARY_EXPM1: return expm1f(x);
|
|
case MKLLM_UNARY_SOFTPLUS: return x > 20.0f ? x : log1pf(expf(x));
|
|
case MKLLM_UNARY_GELU_ERF: return 0.5f * x * (1.0f + erff(x * 0.70710678118654752440f));
|
|
default: return x;
|
|
}
|
|
}
|
|
|
|
static __global__ void mkllm_unary_kernel(
|
|
const float * __restrict__ x, float * __restrict__ dst, size_t n, int op) {
|
|
const size_t i = (size_t) blockIdx.x * blockDim.x + threadIdx.x;
|
|
if (i >= n) return;
|
|
dst[i] = mkllm_unary_apply(op, x[i]);
|
|
}
|
|
|
|
extern "C" cudaError_t mkllm_unary(
|
|
const void * x, void * dst, size_t n, int op, cudaStream_t stream) {
|
|
const int block = 256;
|
|
const int grid = (int) ((n + block - 1) / block);
|
|
mkllm_unary_kernel<<<grid, block, 0, stream>>>((const float *) x, (float *) dst, n, op);
|
|
return cudaGetLastError();
|
|
}
|
|
|
|
// llama.cpp unary.cu:254-273 unary_gated_op_kernel / unary_gated_cuda,
|
|
// launched by ggml_cuda_op_unary_mul (unary.cu:603) for SILU/SIGMOID/SOFTPLUS.
|
|
// k = nelements, n = ncols (ne[0]), o0/o1 = row strides in elements.
|
|
static __global__ void mkllm_unary_mul_kernel(
|
|
const float * __restrict__ x, const float * __restrict__ g,
|
|
float * __restrict__ dst, size_t k, int n, int op,
|
|
size_t o0, size_t o1) {
|
|
const size_t i = (size_t) blockIdx.x * blockDim.x + threadIdx.x;
|
|
if (i >= k) return;
|
|
const size_t j0 = (i / (size_t) n) * o0 + (i % (size_t) n);
|
|
const size_t j1 = o0 == o1 ? j0 : (i / (size_t) n) * o1 + (i % (size_t) n);
|
|
dst[i] = mkllm_unary_apply(op, x[j0]) * g[j1];
|
|
}
|
|
|
|
extern "C" cudaError_t mkllm_unary_mul(
|
|
const void * x, const void * g, void * dst,
|
|
size_t k, int n, int op, size_t o0, size_t o1, cudaStream_t stream) {
|
|
if (k == 0 || n <= 0) {
|
|
return cudaErrorInvalidValue;
|
|
}
|
|
const int block = 256;
|
|
const int grid = (int) ((k + (size_t) block - 1) / (size_t) block);
|
|
mkllm_unary_mul_kernel<<<grid, block, 0, stream>>>(
|
|
(const float *) x, (const float *) g, (float *) dst, k, n, op, o0, o1);
|
|
return cudaGetLastError();
|
|
}
|
|
|
|
#define MKLLM_GLU_REGLU 0
|
|
#define MKLLM_GLU_GEGLU 1
|
|
#define MKLLM_GLU_SWIGLU 2
|
|
#define MKLLM_GLU_SWIGLU_OAI 3
|
|
#define MKLLM_GLU_GEGLU_ERF 4
|
|
#define MKLLM_GLU_GEGLU_QUICK 5
|
|
|
|
static __global__ void mkllm_glu_kernel(
|
|
const float * __restrict__ a, const float * __restrict__ b,
|
|
float * __restrict__ dst, size_t n, int op) {
|
|
const size_t i = (size_t) blockIdx.x * blockDim.x + threadIdx.x;
|
|
if (i >= n) return;
|
|
const float x = a[i];
|
|
float g;
|
|
switch (op) {
|
|
case MKLLM_GLU_REGLU: g = fmaxf(x, 0.0f); break;
|
|
case MKLLM_GLU_GEGLU:
|
|
g = 0.5f * x * (1.0f + tanhf(0.79788456080286535588f * (x + 0.044715f * x * x * x)));
|
|
break;
|
|
case MKLLM_GLU_SWIGLU: g = x / (1.0f + expf(-x)); break;
|
|
case MKLLM_GLU_GEGLU_ERF: g = 0.5f * x * (1.0f + erff(x * 0.70710678118654752440f)); break;
|
|
case MKLLM_GLU_GEGLU_QUICK: g = x * (1.0f / (1.0f + expf(-1.702f * x))); break;
|
|
default: g = x / (1.0f + expf(-x)); break;
|
|
}
|
|
dst[i] = g * b[i];
|
|
}
|
|
|
|
extern "C" cudaError_t mkllm_glu(
|
|
const void * a, const void * b, void * dst, size_t n, int op, cudaStream_t stream) {
|
|
const int block = 256;
|
|
const int grid = (int) ((n + block - 1) / block);
|
|
mkllm_glu_kernel<<<grid, block, 0, stream>>>(
|
|
(const float *) a, (const float *) b, (float *) dst, n, op);
|
|
return cudaGetLastError();
|
|
}
|
|
|
|
#define MKLLM_BIN_ADD 0
|
|
#define MKLLM_BIN_SUB 1
|
|
#define MKLLM_BIN_MUL 2
|
|
#define MKLLM_BIN_DIV 3
|
|
|
|
static __global__ void mkllm_binary_kernel(
|
|
const uint8_t * __restrict__ a, const uint8_t * __restrict__ b,
|
|
uint8_t * __restrict__ dst, int op,
|
|
int ne0, int ne1, int ne2, int ne3,
|
|
int b_ne0, int b_ne1, int b_ne2, int b_ne3,
|
|
size_t a_nb0, size_t a_nb1, size_t a_nb2, size_t a_nb3,
|
|
size_t b_nb0, size_t b_nb1, size_t b_nb2, size_t b_nb3,
|
|
size_t d_nb0, size_t d_nb1, size_t d_nb2, size_t d_nb3) {
|
|
const int i0 = blockIdx.x * blockDim.x + threadIdx.x;
|
|
if (i0 >= ne0) return;
|
|
// Rows on y, higher dims on z, each grid-strided: ne1 * ne2 * ne3 packed
|
|
// into one grid dimension overflows the 65535 limit on y/z at shapes this
|
|
// runtime really produces (BS-RoFormer's trunk is [384, 1101, 62], so the
|
|
// packed form asks for 68262 blocks and the launch fails outright with
|
|
// "invalid argument"). Striding keeps every shape launchable.
|
|
const int n23 = ne2 * ne3;
|
|
for (int i23 = blockIdx.z; i23 < n23; i23 += gridDim.z) {
|
|
const int i2 = i23 % ne2;
|
|
const int i3 = i23 / ne2;
|
|
for (int i1 = blockIdx.y; i1 < ne1; i1 += gridDim.y) {
|
|
const float av = *(const float *) (a + (size_t) i3 * a_nb3 + (size_t) i2 * a_nb2
|
|
+ (size_t) i1 * a_nb1 + (size_t) i0 * a_nb0);
|
|
const float bv = *(const float *) (b + (size_t) (i3 % b_ne3) * b_nb3
|
|
+ (size_t) (i2 % b_ne2) * b_nb2 + (size_t) (i1 % b_ne1) * b_nb1
|
|
+ (size_t) (i0 % b_ne0) * b_nb0);
|
|
float r;
|
|
switch (op) {
|
|
case MKLLM_BIN_ADD: r = av + bv; break;
|
|
case MKLLM_BIN_SUB: r = av - bv; break;
|
|
case MKLLM_BIN_MUL: r = av * bv; break;
|
|
default: r = av / bv; break;
|
|
}
|
|
*(float *) (dst + (size_t) i3 * d_nb3 + (size_t) i2 * d_nb2 + (size_t) i1 * d_nb1
|
|
+ (size_t) i0 * d_nb0) = r;
|
|
}
|
|
}
|
|
}
|
|
|
|
extern "C" cudaError_t mkllm_binary(
|
|
int op, const void * a, const void * b, void * dst,
|
|
int ne0, int ne1, int ne2, int ne3,
|
|
int b_ne0, int b_ne1, int b_ne2, int b_ne3,
|
|
size_t a_nb0, size_t a_nb1, size_t a_nb2, size_t a_nb3,
|
|
size_t b_nb0, size_t b_nb1, size_t b_nb2, size_t b_nb3,
|
|
size_t d_nb0, size_t d_nb1, size_t d_nb2, size_t d_nb3,
|
|
cudaStream_t stream) {
|
|
dim3 block(256);
|
|
const unsigned max_dim = 65535;
|
|
dim3 grid(
|
|
(unsigned) ((ne0 + 255) / 256),
|
|
(unsigned) (ne1 > 0 ? (ne1 < (int) max_dim ? ne1 : (int) max_dim) : 1),
|
|
(unsigned) (ne2 * ne3 > 0 ? (ne2 * ne3 < (int) max_dim ? ne2 * ne3 : (int) max_dim) : 1));
|
|
mkllm_binary_kernel<<<grid, block, 0, stream>>>(
|
|
(const uint8_t *) a, (const uint8_t *) b, (uint8_t *) dst, op,
|
|
ne0, ne1, ne2, ne3, b_ne0, b_ne1, b_ne2, b_ne3,
|
|
a_nb0, a_nb1, a_nb2, a_nb3, b_nb0, b_nb1, b_nb2, b_nb3,
|
|
d_nb0, d_nb1, d_nb2, d_nb3);
|
|
return cudaGetLastError();
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// gated_delta_net — exact transcription of kernel_gated_delta_net_impl in
|
|
// libs/ggml/src/backend/metal/ggml/ggml-metal.metal (the oracle semantics):
|
|
// value-head -> q/k-head mapping is MODULO (i01 = i21 % ne01), the gate
|
|
// decays the state BEFORE the k-dot, beta/gate are indexed flat [G, H, T, B],
|
|
// and the state is stored transposed (column-major rows contiguous).
|
|
// One warp per state column; each lane holds sv/32 state elements.
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// llama.cpp gated_delta_net.cu S_v=128: one warp per column, coalesced
|
|
// i = r*32+lane, 4 warps/block, launch_bounds(128,2). Same recurrence as
|
|
// the generic kernel below (KDA and scalar-gate both).
|
|
template <int GATE_G>
|
|
static __global__ void __launch_bounds__(128, 2) mkllm_gdn_sv128_kernel(
|
|
const float * __restrict__ q, const float * __restrict__ k,
|
|
const float * __restrict__ v, const float * __restrict__ g,
|
|
const float * __restrict__ b, const float * __restrict__ s,
|
|
float * __restrict__ dst,
|
|
int h, int n_tokens, int n_seqs,
|
|
int q_heads, int k_heads,
|
|
size_t q_h_elems, size_t q_t_elems, size_t q_s_elems,
|
|
size_t k_h_elems, size_t k_t_elems, size_t k_s_elems,
|
|
size_t v_h_elems, size_t v_t_elems, size_t v_s_elems) {
|
|
constexpr int sv = 128;
|
|
constexpr int warp_size = 32;
|
|
constexpr int rows_per_lane = sv / warp_size;
|
|
const int col = (int) blockIdx.x * (int) blockDim.y + (int) threadIdx.y;
|
|
const int head = (int) blockIdx.y;
|
|
const int seq = (int) blockIdx.z;
|
|
const int lane = (int) threadIdx.x;
|
|
if (col >= sv) {
|
|
return;
|
|
}
|
|
const int i01 = head % q_heads;
|
|
const int i11 = head % k_heads;
|
|
const float scale = rsqrtf((float) sv);
|
|
const float * s_ptr = s + ((size_t) seq * h + head) * sv * sv + (size_t) col * sv;
|
|
float s_shard[rows_per_lane];
|
|
#pragma unroll
|
|
for (int r = 0; r < rows_per_lane; ++r) {
|
|
s_shard[r] = s_ptr[r * warp_size + lane];
|
|
}
|
|
const float * q_ptr = q + (size_t) seq * q_s_elems + (size_t) i01 * q_h_elems;
|
|
const float * k_ptr = k + (size_t) seq * k_s_elems + (size_t) i11 * k_h_elems;
|
|
const float * v_ptr = v + (size_t) seq * v_s_elems + (size_t) head * v_h_elems;
|
|
const float * b_ptr = b + ((size_t) seq * n_tokens * h + head);
|
|
const float * g_ptr = g + ((size_t) seq * n_tokens * h + head) * GATE_G;
|
|
float * dst_attn = dst + ((size_t) seq * n_tokens * h + head) * sv + col;
|
|
for (int t = 0; t < n_tokens; ++t) {
|
|
float k_reg[rows_per_lane];
|
|
float q_reg[rows_per_lane];
|
|
#pragma unroll
|
|
for (int r = 0; r < rows_per_lane; ++r) {
|
|
const int i = r * warp_size + lane;
|
|
k_reg[r] = k_ptr[i];
|
|
q_reg[r] = q_ptr[i];
|
|
}
|
|
float s_k = 0.0f;
|
|
if (GATE_G == 1) {
|
|
const float g_exp = expf(g_ptr[0]);
|
|
#pragma unroll
|
|
for (int r = 0; r < rows_per_lane; ++r) {
|
|
s_shard[r] *= g_exp;
|
|
s_k += s_shard[r] * k_reg[r];
|
|
}
|
|
} else {
|
|
#pragma unroll
|
|
for (int r = 0; r < rows_per_lane; ++r) {
|
|
const int i = r * warp_size + lane;
|
|
s_shard[r] *= expf(g_ptr[i]);
|
|
s_k += s_shard[r] * k_reg[r];
|
|
}
|
|
}
|
|
#pragma unroll
|
|
for (int off = 16; off > 0; off >>= 1) {
|
|
s_k += __shfl_xor_sync(0xffffffff, s_k, off);
|
|
}
|
|
const float d = (v_ptr[col] - s_k) * b_ptr[0];
|
|
float y = 0.0f;
|
|
#pragma unroll
|
|
for (int r = 0; r < rows_per_lane; ++r) {
|
|
s_shard[r] += k_reg[r] * d;
|
|
y += s_shard[r] * q_reg[r];
|
|
}
|
|
#pragma unroll
|
|
for (int off = 16; off > 0; off >>= 1) {
|
|
y += __shfl_xor_sync(0xffffffff, y, off);
|
|
}
|
|
if (lane == 0) {
|
|
dst_attn[(size_t) t * h * sv] = y * scale;
|
|
}
|
|
q_ptr += q_t_elems;
|
|
k_ptr += k_t_elems;
|
|
v_ptr += v_t_elems;
|
|
b_ptr += h;
|
|
g_ptr += (size_t) h * GATE_G;
|
|
}
|
|
float * dst_state = dst + (size_t) n_seqs * n_tokens * h * sv
|
|
+ ((size_t) seq * h + head) * sv * sv + (size_t) col * sv;
|
|
#pragma unroll
|
|
for (int r = 0; r < rows_per_lane; ++r) {
|
|
dst_state[r * warp_size + lane] = s_shard[r];
|
|
}
|
|
}
|
|
|
|
static __global__ void mkllm_gdn_kernel(
|
|
const float * __restrict__ q, const float * __restrict__ k,
|
|
const float * __restrict__ v, const float * __restrict__ g,
|
|
const float * __restrict__ b, const float * __restrict__ s,
|
|
float * __restrict__ dst,
|
|
int sv, int h, int n_tokens, int n_seqs, int gate_g,
|
|
int q_heads, int k_heads,
|
|
size_t q_h_elems, size_t q_t_elems, size_t q_s_elems,
|
|
size_t k_h_elems, size_t k_t_elems, size_t k_s_elems,
|
|
size_t v_h_elems, size_t v_t_elems, size_t v_s_elems) {
|
|
const int per_lane = sv / 32;
|
|
const int col = blockIdx.x * blockDim.y + threadIdx.y;
|
|
const int head = blockIdx.y;
|
|
const int seq = blockIdx.z;
|
|
const int lane = threadIdx.x;
|
|
if (col >= sv) return;
|
|
|
|
const int i01 = head % q_heads;
|
|
const int i11 = head % k_heads;
|
|
const float scale = rsqrtf((float) sv);
|
|
|
|
const float * s_ptr = s + ((size_t) seq * h + head) * sv * sv + (size_t) col * sv;
|
|
float ls[8]; // sv <= 256
|
|
for (int j = 0; j < per_lane; j++) {
|
|
ls[j] = s_ptr[lane * per_lane + j];
|
|
}
|
|
|
|
const float * q_ptr = q + (size_t) seq * q_s_elems + (size_t) i01 * q_h_elems;
|
|
const float * k_ptr = k + (size_t) seq * k_s_elems + (size_t) i11 * k_h_elems;
|
|
const float * v_ptr = v + (size_t) seq * v_s_elems + (size_t) head * v_h_elems;
|
|
const float * b_ptr = b + ((size_t) seq * n_tokens * h + head);
|
|
const float * g_ptr = g + ((size_t) seq * n_tokens * h + head) * gate_g;
|
|
float * dst_attn = dst + ((size_t) seq * n_tokens * h + head) * sv + col;
|
|
|
|
for (int t = 0; t < n_tokens; t++) {
|
|
float s_k = 0.0f;
|
|
if (gate_g == 1) {
|
|
const float g_exp = expf(g_ptr[0]);
|
|
for (int j = 0; j < per_lane; j++) {
|
|
const int is = lane * per_lane + j;
|
|
ls[j] *= g_exp;
|
|
s_k += ls[j] * k_ptr[is];
|
|
}
|
|
} else {
|
|
for (int j = 0; j < per_lane; j++) {
|
|
const int is = lane * per_lane + j;
|
|
ls[j] *= expf(g_ptr[is]);
|
|
s_k += ls[j] * k_ptr[is];
|
|
}
|
|
}
|
|
for (int off = 16; off > 0; off >>= 1) {
|
|
s_k += __shfl_xor_sync(0xffffffff, s_k, off);
|
|
}
|
|
const float d = (v_ptr[col] - s_k) * b_ptr[0];
|
|
float y = 0.0f;
|
|
for (int j = 0; j < per_lane; j++) {
|
|
const int is = lane * per_lane + j;
|
|
ls[j] += k_ptr[is] * d;
|
|
y += ls[j] * q_ptr[is];
|
|
}
|
|
for (int off = 16; off > 0; off >>= 1) {
|
|
y += __shfl_xor_sync(0xffffffff, y, off);
|
|
}
|
|
if (lane == 0) {
|
|
dst_attn[(size_t) t * h * sv] = y * scale;
|
|
}
|
|
q_ptr += q_t_elems;
|
|
k_ptr += k_t_elems;
|
|
v_ptr += v_t_elems;
|
|
b_ptr += h;
|
|
g_ptr += (size_t) h * gate_g;
|
|
}
|
|
|
|
float * dst_state = dst + (size_t) n_seqs * n_tokens * h * sv
|
|
+ ((size_t) seq * h + head) * sv * sv + (size_t) col * sv;
|
|
for (int j = 0; j < per_lane; j++) {
|
|
dst_state[lane * per_lane + j] = ls[j];
|
|
}
|
|
}
|
|
|
|
#include "fattn/gated_delta_net.cuh"
|
|
|
|
extern "C" cudaError_t mkllm_gated_delta_net(
|
|
const void * q, const void * k, const void * v, const void * g,
|
|
const void * b, const void * s, void * dst,
|
|
int sv, int h, int n_tokens, int n_seqs, int gate_g,
|
|
int q_heads, int k_heads,
|
|
size_t q_h_elems, size_t q_t_elems, size_t q_s_elems,
|
|
size_t k_h_elems, size_t k_t_elems, size_t k_s_elems,
|
|
size_t v_h_elems, size_t v_t_elems, size_t v_s_elems,
|
|
int state_checkpoints,
|
|
cudaStream_t stream) {
|
|
if (sv % 32 != 0 || sv > 256) {
|
|
return cudaErrorInvalidValue;
|
|
}
|
|
// Non-zero => write one state per token, `sv*sv*h*n_seqs` floats apart.
|
|
const int64_t state_ckpt_stride = state_checkpoints
|
|
? (int64_t) sv * sv * h * n_seqs
|
|
: 0;
|
|
const bool official_sv = sv == 16 || sv == 32 || sv == 64 || sv == 128;
|
|
const bool official_ok = official_sv && q_heads == k_heads
|
|
&& q_h_elems == k_h_elems && q_t_elems == k_t_elems
|
|
&& q_s_elems == k_s_elems && (gate_g == 1 || gate_g == sv)
|
|
&& h > 0 && n_tokens > 0 && n_seqs > 0 && q_heads > 0;
|
|
if (official_ok) {
|
|
const float scale = 1.0f / sqrtf((float) sv);
|
|
const int64_t sb1 = 1;
|
|
const int64_t sb2 = h;
|
|
const int64_t sb3 = (int64_t) n_tokens * h;
|
|
const int64_t rq3 = 1;
|
|
if (gate_g == sv) {
|
|
launch_gated_delta_net<true>(
|
|
(const float *) q, (const float *) k, (const float *) v,
|
|
(const float *) g, (const float *) b, (const float *) s,
|
|
(float *) dst, sv, h, n_tokens, n_seqs,
|
|
(int64_t) q_h_elems, (int64_t) q_t_elems, (int64_t) q_s_elems,
|
|
(int64_t) v_h_elems, (int64_t) v_t_elems, (int64_t) v_s_elems,
|
|
sb1, sb2, sb3, q_heads, rq3, scale, state_ckpt_stride, stream);
|
|
} else {
|
|
launch_gated_delta_net<false>(
|
|
(const float *) q, (const float *) k, (const float *) v,
|
|
(const float *) g, (const float *) b, (const float *) s,
|
|
(float *) dst, sv, h, n_tokens, n_seqs,
|
|
(int64_t) q_h_elems, (int64_t) q_t_elems, (int64_t) q_s_elems,
|
|
(int64_t) v_h_elems, (int64_t) v_t_elems, (int64_t) v_s_elems,
|
|
sb1, sb2, sb3, q_heads, rq3, scale, state_ckpt_stride, stream);
|
|
}
|
|
return cudaGetLastError();
|
|
}
|
|
if (state_checkpoints) {
|
|
// Only the official kernel emits per-token states; fail closed rather
|
|
// than silently leaving the checkpoint rows unwritten.
|
|
return cudaErrorInvalidValue;
|
|
}
|
|
const int cols_per_block = 4;
|
|
dim3 block(32, cols_per_block);
|
|
dim3 grid((sv + cols_per_block - 1) / cols_per_block, h, n_seqs);
|
|
if (sv == 128 && gate_g == 1) {
|
|
mkllm_gdn_sv128_kernel<1><<<grid, block, 0, stream>>>(
|
|
(const float *) q, (const float *) k, (const float *) v, (const float *) g,
|
|
(const float *) b, (const float *) s, (float *) dst,
|
|
h, n_tokens, n_seqs, q_heads, k_heads,
|
|
q_h_elems, q_t_elems, q_s_elems, k_h_elems, k_t_elems, k_s_elems,
|
|
v_h_elems, v_t_elems, v_s_elems);
|
|
} else if (sv == 128 && gate_g == 128) {
|
|
mkllm_gdn_sv128_kernel<128><<<grid, block, 0, stream>>>(
|
|
(const float *) q, (const float *) k, (const float *) v, (const float *) g,
|
|
(const float *) b, (const float *) s, (float *) dst,
|
|
h, n_tokens, n_seqs, q_heads, k_heads,
|
|
q_h_elems, q_t_elems, q_s_elems, k_h_elems, k_t_elems, k_s_elems,
|
|
v_h_elems, v_t_elems, v_s_elems);
|
|
} else {
|
|
mkllm_gdn_kernel<<<grid, block, 0, stream>>>(
|
|
(const float *) q, (const float *) k, (const float *) v, (const float *) g,
|
|
(const float *) b, (const float *) s, (float *) dst,
|
|
sv, h, n_tokens, n_seqs, gate_g, q_heads, k_heads,
|
|
q_h_elems, q_t_elems, q_s_elems, k_h_elems, k_t_elems, k_s_elems,
|
|
v_h_elems, v_t_elems, v_s_elems);
|
|
}
|
|
return cudaGetLastError();
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// ssm_conv (ggml op layout): src0 [T + d_conv - 1, d_inner, n_seqs] with the
|
|
// position axis on dim0, weight [d_conv, d_inner], dst [d_inner, T, n_seqs].
|
|
// dst(i, t, s) = sum_k src0(t + k, i, s) * w(k, i)
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// llama.cpp ssm-conv.cu:3-47 ssm_conv_f32, apply_silu from
|
|
// ggml-cuda.cu:4006 GGML_OP_SSM_CONV + SILU.
|
|
template <int apply_silu>
|
|
static __global__ void mkllm_ssm_conv_kernel(
|
|
const uint8_t * __restrict__ src0, const uint8_t * __restrict__ weight,
|
|
uint8_t * __restrict__ dst,
|
|
int d_conv, int d_inner, int n_tokens,
|
|
size_t s_nb0, size_t s_nb1, size_t s_nb2,
|
|
size_t w_nb1, size_t d_nb0, size_t d_nb1, size_t d_nb2) {
|
|
const int i = blockIdx.x * blockDim.x + threadIdx.x;
|
|
const int t = blockIdx.y;
|
|
const int s = blockIdx.z;
|
|
if (i >= d_inner) return;
|
|
(void) n_tokens;
|
|
const uint8_t * base = src0 + (size_t) s * s_nb2 + (size_t) i * s_nb1;
|
|
const float * w = (const float *) (weight + (size_t) i * w_nb1);
|
|
float sum = 0.0f;
|
|
for (int k = 0; k < d_conv; k++) {
|
|
sum += *(const float *) (base + (size_t) (t + k) * s_nb0) * w[k];
|
|
}
|
|
if (apply_silu) {
|
|
sum = sum / (1.0f + expf(-sum));
|
|
}
|
|
*(float *) (dst + (size_t) s * d_nb2 + (size_t) t * d_nb1 + (size_t) i * d_nb0) = sum;
|
|
}
|
|
|
|
extern "C" cudaError_t mkllm_ssm_conv(
|
|
const void * src0, const void * weight, void * dst,
|
|
int d_conv, int d_inner, int n_tokens, int n_seqs, int apply_silu,
|
|
size_t s_nb0, size_t s_nb1, size_t s_nb2,
|
|
size_t w_nb1, size_t d_nb0, size_t d_nb1, size_t d_nb2,
|
|
cudaStream_t stream) {
|
|
dim3 block(256);
|
|
dim3 grid((d_inner + 255) / 256, n_tokens, n_seqs);
|
|
if (apply_silu) {
|
|
mkllm_ssm_conv_kernel<1><<<grid, block, 0, stream>>>(
|
|
(const uint8_t *) src0, (const uint8_t *) weight, (uint8_t *) dst,
|
|
d_conv, d_inner, n_tokens, s_nb0, s_nb1, s_nb2, w_nb1, d_nb0, d_nb1, d_nb2);
|
|
} else {
|
|
mkllm_ssm_conv_kernel<0><<<grid, block, 0, stream>>>(
|
|
(const uint8_t *) src0, (const uint8_t *) weight, (uint8_t *) dst,
|
|
d_conv, d_inner, n_tokens, s_nb0, s_nb1, s_nb2, w_nb1, d_nb0, d_nb1, d_nb2);
|
|
}
|
|
return cudaGetLastError();
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Strided copy (Cont / Concat lowering / Cpy): element-typed, src and dst
|
|
// arbitrary strides, same element size (2 or 4 bytes).
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// Source and destination may have DIFFERENT extents with the same element
|
|
// count (Cont can flatten/reshape while materializing). Each thread owns one
|
|
// flat logical index and decomposes it independently through the source and
|
|
// destination extents before applying that side's byte strides.
|
|
template <typename T>
|
|
static __global__ void mkllm_copy_strided_kernel(
|
|
const uint8_t * __restrict__ src, uint8_t * __restrict__ dst,
|
|
size_t total,
|
|
int s_ne0, int s_ne1, int s_ne2,
|
|
int d_ne0, int d_ne1, int d_ne2,
|
|
size_t s_nb0, size_t s_nb1, size_t s_nb2, size_t s_nb3,
|
|
size_t d_nb0, size_t d_nb1, size_t d_nb2, size_t d_nb3) {
|
|
const size_t flat = (size_t) blockIdx.x * blockDim.x + threadIdx.x;
|
|
if (flat >= total) return;
|
|
|
|
size_t rem = flat;
|
|
const size_t si0 = rem % s_ne0; rem /= s_ne0;
|
|
const size_t si1 = rem % s_ne1; rem /= s_ne1;
|
|
const size_t si2 = rem % s_ne2;
|
|
const size_t si3 = rem / s_ne2;
|
|
|
|
rem = flat;
|
|
const size_t di0 = rem % d_ne0; rem /= d_ne0;
|
|
const size_t di1 = rem % d_ne1; rem /= d_ne1;
|
|
const size_t di2 = rem % d_ne2;
|
|
const size_t di3 = rem / d_ne2;
|
|
|
|
*(T *) (dst + di3 * d_nb3 + di2 * d_nb2 + di1 * d_nb1 + di0 * d_nb0) =
|
|
*(const T *) (src + si3 * s_nb3 + si2 * s_nb2 + si1 * s_nb1 + si0 * s_nb0);
|
|
}
|
|
|
|
extern "C" cudaError_t mkllm_copy_strided(
|
|
int elem_size, const void * src, void * dst,
|
|
int s_ne0, int s_ne1, int s_ne2, int s_ne3,
|
|
int d_ne0, int d_ne1, int d_ne2, int d_ne3,
|
|
size_t s_nb0, size_t s_nb1, size_t s_nb2, size_t s_nb3,
|
|
size_t d_nb0, size_t d_nb1, size_t d_nb2, size_t d_nb3,
|
|
cudaStream_t stream) {
|
|
const size_t total = (size_t) d_ne0 * d_ne1 * d_ne2 * d_ne3;
|
|
const size_t src_total = (size_t) s_ne0 * s_ne1 * s_ne2 * s_ne3;
|
|
if (total != src_total) {
|
|
return cudaErrorInvalidValue;
|
|
}
|
|
dim3 block(256);
|
|
dim3 grid((unsigned) ((total + 255) / 256));
|
|
if (elem_size == 2) {
|
|
mkllm_copy_strided_kernel<uint16_t><<<grid, block, 0, stream>>>(
|
|
(const uint8_t *) src, (uint8_t *) dst, total,
|
|
s_ne0, s_ne1, s_ne2, d_ne0, d_ne1, d_ne2,
|
|
s_nb0, s_nb1, s_nb2, s_nb3, d_nb0, d_nb1, d_nb2, d_nb3);
|
|
} else {
|
|
mkllm_copy_strided_kernel<uint32_t><<<grid, block, 0, stream>>>(
|
|
(const uint8_t *) src, (uint8_t *) dst, total,
|
|
s_ne0, s_ne1, s_ne2, d_ne0, d_ne1, d_ne2,
|
|
s_nb0, s_nb1, s_nb2, s_nb3, d_nb0, d_nb1, d_nb2, d_nb3);
|
|
}
|
|
return cudaGetLastError();
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// llama.cpp fattn-mma-f16 for Qwen3.8-27B prefill: D=256, GQA>4, n_tokens
|
|
// large -> ncols1=8, ncols2=8 (fattn.cu switch_ncols2 / switch_ncols1).
|
|
// Ampere config is used on sm86/89/120. Ada stream-K when cc>=890.
|
|
// (device lambdas rewritten as functors; host sees Ampere configs.)
|
|
// ---------------------------------------------------------------------------
|
|
|
|
#include "fattn/fattn-mma-f16.cuh"
|
|
|
|
static void mkllm_fattn_trace_launch(
|
|
const char * kind, int D, int n_q, int kc, int H, int Hkv,
|
|
int nsm, int max_blocks_per_sm, int parallel_blocks, int ntiles_dst,
|
|
int ntiles_KV, int stream_k, unsigned gx, unsigned gy, unsigned gz) {
|
|
// First few host launches only. Official launch_fattn (fattn-common.cuh:990)
|
|
// for VEC is stream_k=false; pb is occupancy-capped then efficiency-swept.
|
|
static int prints = 0;
|
|
if (prints >= 8) {
|
|
return;
|
|
}
|
|
fprintf(stderr,
|
|
"fattn.launch: kind=%s D=%d n_q=%d n_kv=%d H=%d Hkv=%d nsm=%d occ=%d "
|
|
"pb=%d ntiles_dst=%d ntiles_KV=%d stream_k=%d grid=(%u,%u,%u) "
|
|
"pad256=%d\n",
|
|
kind, D, n_q, kc, H, Hkv, nsm, max_blocks_per_sm, parallel_blocks,
|
|
ntiles_dst, ntiles_KV, stream_k, gx, gy, gz,
|
|
(kc + 255) & ~255);
|
|
++prints;
|
|
}
|
|
|
|
// The MMA launcher, templated on the tile shape so the GQA ratio can pick
|
|
// it. `ncols2` is how many query heads of one KV head a tile covers, and
|
|
// `ncols1` how many tokens; their product is the 64-column tile every size
|
|
// below is derived from, so a different split costs nothing in shared memory.
|
|
template <int ncols1, int ncols2>
|
|
static cudaError_t mkllm_fattn_mma_launch(
|
|
const void * q, const void * k, const void * v, const void * mask, void * dst,
|
|
int kc, int n_q, int H, int Hkv, float scale,
|
|
size_t q_nb1, size_t q_nb2, size_t k_nb1, size_t k_nb2,
|
|
size_t v_nb1, size_t v_nb2, size_t m_nb1,
|
|
int nsm, int cc, float * tmp_fixup, cudaStream_t stream) {
|
|
constexpr int DKQ = 256;
|
|
constexpr int DV = 256;
|
|
constexpr int ncols = ncols1 * ncols2;
|
|
constexpr bool use_logit_softcap = false;
|
|
constexpr bool V_is_K_view = false;
|
|
const int nthreads = ggml_cuda_fattn_mma_get_nthreads(DKQ, DV, ncols, cc);
|
|
const int nbatch_fa = ggml_cuda_fattn_mma_get_nbatch_fa(DKQ, DV, ncols, cc);
|
|
const int nbatch_K2 = ggml_cuda_fattn_mma_get_nbatch_K2(DKQ, DV, ncols, cc);
|
|
const int nbatch_V2 = ggml_cuda_fattn_mma_get_nbatch_V2(DKQ, DV, ncols, cc);
|
|
const int nbatch_combine = ggml_cuda_fattn_mma_get_nbatch_combine(DKQ, DV, ncols, cc);
|
|
const bool Q_in_reg = ggml_cuda_fattn_mma_get_Q_in_reg(DKQ, DV, ncols, cc);
|
|
const int nstages = ggml_cuda_fattn_mma_get_nstages(DKQ, DV, ncols1, ncols2, cc);
|
|
const int cols_per_warp = 16;
|
|
const int nwarps = nthreads / 32;
|
|
const size_t nbytes_shared_KV_1stage = (size_t) nbatch_fa
|
|
* (size_t) (nbatch_K2 > nbatch_V2 ? nbatch_K2 + 4 : nbatch_V2 + 4) * sizeof(half2);
|
|
const size_t nbytes_shared_KV_2stage = (size_t) nbatch_fa
|
|
* (size_t) (nbatch_K2 + 4 + nbatch_V2 + 4) * sizeof(half2);
|
|
const size_t nbytes_shared_Q = (size_t) ncols * (size_t) (DKQ / 2 + 4) * sizeof(half2);
|
|
const size_t nbytes_shared_mask = (size_t) ncols1 * (size_t) (nbatch_fa / 2 + 4) * sizeof(half2);
|
|
const size_t nbytes_shared_combine = (size_t) nwarps * (size_t) cols_per_warp
|
|
* (size_t) (nbatch_combine + 4) * sizeof(half2);
|
|
const size_t nbytes_shared_KV = nstages <= 1 ? nbytes_shared_KV_1stage : nbytes_shared_KV_2stage;
|
|
const size_t nbytes_shared_total = nbytes_shared_combine > (Q_in_reg
|
|
? (nbytes_shared_Q > nbytes_shared_KV + nbytes_shared_mask
|
|
? nbytes_shared_Q : nbytes_shared_KV + nbytes_shared_mask)
|
|
: nbytes_shared_Q + nbytes_shared_KV + nbytes_shared_mask)
|
|
? nbytes_shared_combine
|
|
: (Q_in_reg
|
|
? (nbytes_shared_Q > nbytes_shared_KV + nbytes_shared_mask
|
|
? nbytes_shared_Q : nbytes_shared_KV + nbytes_shared_mask)
|
|
: nbytes_shared_Q + nbytes_shared_KV + nbytes_shared_mask);
|
|
|
|
fattn_kernel_t fattn_kernel =
|
|
flash_attn_ext_f16<DKQ, DV, ncols1, ncols2, use_logit_softcap, V_is_K_view>;
|
|
cudaError_t err = cudaFuncSetAttribute(
|
|
fattn_kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, (int) nbytes_shared_total);
|
|
if (err != cudaSuccess) {
|
|
return err;
|
|
}
|
|
|
|
const int ntiles_x = (n_q + ncols1 - 1) / ncols1;
|
|
const int gqa_ratio = H / Hkv;
|
|
const int ntiles_z_gqa = (gqa_ratio + ncols2 - 1) / ncols2;
|
|
const int ntiles_dst = ntiles_x * ntiles_z_gqa * Hkv;
|
|
const int ntiles_KV = (kc + nbatch_fa - 1) / nbatch_fa;
|
|
dim3 block_dim(32, nwarps, 1);
|
|
int max_blocks_per_sm = 1;
|
|
err = cudaOccupancyMaxActiveBlocksPerMultiprocessor(
|
|
&max_blocks_per_sm, fattn_kernel, block_dim.x * block_dim.y, nbytes_shared_total);
|
|
if (err != cudaSuccess || max_blocks_per_sm <= 0) {
|
|
max_blocks_per_sm = 1;
|
|
}
|
|
const bool ada_stream_k = cc >= GGML_CUDA_CC_ADA_LOVELACE;
|
|
const int max_blocks = max_blocks_per_sm * (nsm > 0 ? nsm : 1);
|
|
const int tiles_nwaves = (ntiles_dst + max_blocks - 1) / max_blocks;
|
|
const int tiles_efficiency_percent = max_blocks * tiles_nwaves > 0
|
|
? 100 * ntiles_dst / (max_blocks * tiles_nwaves) : 0;
|
|
const int nblocks_stream_k = ntiles_KV * ntiles_dst < max_blocks
|
|
? ntiles_KV * ntiles_dst : max_blocks;
|
|
const bool use_stream_k = (ada_stream_k || tiles_efficiency_percent < 75)
|
|
&& tmp_fixup != nullptr && nsm > 0;
|
|
dim3 blocks_num;
|
|
if (use_stream_k) {
|
|
blocks_num = dim3(nblocks_stream_k, 1, 1);
|
|
} else {
|
|
blocks_num = dim3(ntiles_x, 1, ntiles_z_gqa * Hkv);
|
|
}
|
|
char kind[16];
|
|
snprintf(kind, sizeof(kind), "mma%dx%d", ncols1, ncols2);
|
|
mkllm_fattn_trace_launch(
|
|
kind, DKQ, n_q, kc, H, Hkv, nsm, max_blocks_per_sm,
|
|
use_stream_k ? nblocks_stream_k : 1, ntiles_dst, ntiles_KV,
|
|
use_stream_k ? 1 : 0, blocks_num.x, blocks_num.y, blocks_num.z);
|
|
|
|
const uint3 ne01 = init_fastdiv_values((uint32_t) n_q);
|
|
const uint32_t n_head_log2 = 1u << (uint32_t) floorf(log2f((float) H));
|
|
fattn_kernel<<<blocks_num, block_dim, nbytes_shared_total, stream>>>(
|
|
(const char *) q, (const char *) k, (const char *) v, (const char *) mask,
|
|
nullptr, nullptr, (float *) dst, use_stream_k ? (float2 *) tmp_fixup : nullptr,
|
|
scale, 0.0f, 1.0f, 1.0f, n_head_log2, 0.0f,
|
|
DKQ, ne01, H, 1,
|
|
(int32_t) q_nb1, (int32_t) q_nb2, 0,
|
|
DKQ, kc, Hkv, 1,
|
|
(int32_t) k_nb1, (int32_t) k_nb2, 0,
|
|
(int32_t) v_nb1, (int32_t) v_nb2, 0,
|
|
n_q, 1, 1,
|
|
(int32_t) m_nb1, 0, 0);
|
|
err = cudaGetLastError();
|
|
if (err != cudaSuccess) {
|
|
return err;
|
|
}
|
|
if (use_stream_k && (ntiles_dst % (int) blocks_num.x != 0)) {
|
|
dim3 block_dim_combine(DV, 1, 1);
|
|
dim3 blocks_num_combine(blocks_num.x, ncols1, ncols2);
|
|
flash_attn_stream_k_fixup<DV, ncols1, ncols2>
|
|
<<<blocks_num_combine, block_dim_combine, 0, stream>>>(
|
|
(float *) dst, (const float2 *) tmp_fixup,
|
|
n_q, H, 1, kc, Hkv, nbatch_fa);
|
|
return cudaGetLastError();
|
|
}
|
|
return cudaSuccess;
|
|
}
|
|
|
|
extern "C" cudaError_t mkllm_fattn_mma_f16(
|
|
const void * q, const void * k, const void * v, const void * mask, void * dst,
|
|
int D, int Dv, int kc, int n_q, int H, int Hkv, float scale,
|
|
size_t q_nb1, size_t q_nb2, size_t k_nb1, size_t k_nb2,
|
|
size_t v_nb1, size_t v_nb2, size_t m_nb1, size_t d_nb1, size_t d_nb2,
|
|
int nsm, int cc, float * tmp_fixup, cudaStream_t stream) {
|
|
(void) d_nb1;
|
|
(void) d_nb2;
|
|
if (D != 256 || Dv != 256 || n_q <= 0 || kc <= 0 || H <= 0 || Hkv <= 0
|
|
|| H % Hkv != 0 || q == nullptr || k == nullptr || v == nullptr
|
|
|| mask == nullptr || dst == nullptr) {
|
|
return cudaErrorInvalidValue;
|
|
}
|
|
// llama.cpp fattn.cu switch_ncols2: the GQA ratio decides how many query
|
|
// heads share a tile. Getting this wrong is not slow, it is wrong — a
|
|
// tile that claims 8 query heads of a 4-to-1 model reads heads that do
|
|
// not exist — which is why the caller used to hand anything below 8 to
|
|
// the generic kernel instead. Chandra 2 and Qwen3.5-9B are 16 heads over
|
|
// 4 KV heads, i.e. exactly the ratio that was being turned away, and
|
|
// their prefill spent 84% of itself in that generic kernel.
|
|
#define MKLLM_FATTN_MMA_ARGS \
|
|
q, k, v, mask, dst, kc, n_q, H, Hkv, scale, \
|
|
q_nb1, q_nb2, k_nb1, k_nb2, v_nb1, v_nb2, m_nb1, nsm, cc, tmp_fixup, stream
|
|
const int gqa_ratio = H / Hkv;
|
|
if (gqa_ratio % 8 == 0) {
|
|
return mkllm_fattn_mma_launch<8, 8>(MKLLM_FATTN_MMA_ARGS);
|
|
}
|
|
if (gqa_ratio % 4 == 0) {
|
|
return mkllm_fattn_mma_launch<16, 4>(MKLLM_FATTN_MMA_ARGS);
|
|
}
|
|
if (gqa_ratio % 2 == 0) {
|
|
// The rung llama.cpp's switch_ncols2 ladder has and this port was
|
|
// missing: ncols2 = 2 serves every even ratio the bigger tiles
|
|
// cannot, and Qwen3.8-27B is 24 heads over 4 KV heads — ratio 6.
|
|
// Without it the caller's gate sent that model to the generic
|
|
// FlashDecode kernel: measured 11-20x slower past 8k context, flat
|
|
// 85-91 tok/s restored with the tile.
|
|
return mkllm_fattn_mma_launch<32, 2>(MKLLM_FATTN_MMA_ARGS);
|
|
}
|
|
return cudaErrorInvalidValue;
|
|
#undef MKLLM_FATTN_MMA_ARGS
|
|
}
|
|
|
|
extern "C" size_t mkllm_fattn_mma_fixup_bytes(int nsm) {
|
|
// llama.cpp dst_tmp_meta: nblocks * ncols * (2 + DV/2) float2
|
|
// nblocks <= nsm * occupancy; occupancy 2, use 8 for slack.
|
|
if (nsm <= 0) {
|
|
return 0;
|
|
}
|
|
return (size_t) nsm * 8 * 64 * 130 * sizeof(float2);
|
|
}
|
|
|
|
// llama.cpp fattn.cu:402-404 + fattn-vec.cuh:522-532:
|
|
// Ada+ decode, F16 K/V, n_q==1, not (GQA>4 && KV>=8192) ->
|
|
// flash_attn_ext_vec<256, 1, F16, F16, false>, launch_fattn stream_k=false.
|
|
#include "fattn/fattn-vec.cuh"
|
|
|
|
extern "C" size_t mkllm_fattn_vec_tmp_bytes(int n_q, int H, int kc) {
|
|
if (n_q <= 0 || H <= 0 || kc <= 0) {
|
|
return 0;
|
|
}
|
|
const int ntiles_KV = (kc + 256 - 1) / 256;
|
|
const int pb = ntiles_KV < 1 ? 1 : ntiles_KV;
|
|
return (size_t) pb * (size_t) n_q * (size_t) H
|
|
* (256 * sizeof(float) + sizeof(float2));
|
|
}
|
|
|
|
extern "C" cudaError_t mkllm_fattn_vec_f16(
|
|
const void * q, const void * k, const void * v, const void * mask, void * dst,
|
|
int D, int Dv, int kc, int n_q, int H, int Hkv, float scale,
|
|
size_t q_nb1, size_t q_nb2, size_t k_nb1, size_t k_nb2,
|
|
size_t v_nb1, size_t v_nb2, size_t m_nb1, size_t d_nb1, size_t d_nb2,
|
|
int nsm, float * tmp, cudaStream_t stream) {
|
|
(void) d_nb1;
|
|
(void) d_nb2;
|
|
(void) Dv;
|
|
if (D != 256 || n_q != 1 || kc <= 0 || H <= 0 || Hkv <= 0
|
|
|| H % Hkv != 0 || q == nullptr || k == nullptr || v == nullptr
|
|
|| mask == nullptr || dst == nullptr || tmp == nullptr) {
|
|
return cudaErrorInvalidValue;
|
|
}
|
|
constexpr int DKQ = 256;
|
|
constexpr int ncols1 = 1;
|
|
constexpr int ncols2 = 1;
|
|
constexpr int nthreads = 128;
|
|
constexpr int nwarps = nthreads / 32;
|
|
constexpr int nbatch_fa = DKQ;
|
|
fattn_kernel_t fattn_kernel =
|
|
flash_attn_ext_vec<DKQ, ncols1, GGML_TYPE_F16, GGML_TYPE_F16, false>;
|
|
dim3 block_dim(32, nwarps, 1);
|
|
int max_blocks_per_sm = 1;
|
|
cudaError_t err = cudaOccupancyMaxActiveBlocksPerMultiprocessor(
|
|
&max_blocks_per_sm, fattn_kernel, block_dim.x * block_dim.y, 0);
|
|
if (err != cudaSuccess || max_blocks_per_sm <= 0) {
|
|
max_blocks_per_sm = 1;
|
|
}
|
|
const int ntiles_x = (n_q + ncols1 - 1) / ncols1;
|
|
const int gqa_ratio = H / Hkv;
|
|
const int ntiles_z_gqa = (gqa_ratio + ncols2 - 1) / ncols2;
|
|
const int ntiles_dst = ntiles_x * ntiles_z_gqa * Hkv;
|
|
const int ntiles_KV = (kc + nbatch_fa - 1) / nbatch_fa;
|
|
int parallel_blocks = max_blocks_per_sm < ntiles_KV ? max_blocks_per_sm : ntiles_KV;
|
|
const int blocks_per_wave = (nsm > 0 ? nsm : 1) * max_blocks_per_sm;
|
|
int nwaves_best = 0;
|
|
int efficiency_percent_best = 0;
|
|
for (int pb_test = parallel_blocks; pb_test <= ntiles_KV; ++pb_test) {
|
|
const int nblocks_total = ntiles_dst * pb_test;
|
|
const int nwaves = (nblocks_total + blocks_per_wave - 1) / blocks_per_wave;
|
|
const int efficiency_percent = 100 * nblocks_total / (nwaves * blocks_per_wave);
|
|
if (efficiency_percent_best >= 95 && nwaves > nwaves_best) {
|
|
break;
|
|
}
|
|
if (efficiency_percent > efficiency_percent_best) {
|
|
nwaves_best = nwaves;
|
|
efficiency_percent_best = efficiency_percent;
|
|
parallel_blocks = pb_test;
|
|
}
|
|
}
|
|
dim3 blocks_num((unsigned) ntiles_x, (unsigned) parallel_blocks,
|
|
(unsigned) (ntiles_z_gqa * Hkv));
|
|
mkllm_fattn_trace_launch(
|
|
"vec", D, n_q, kc, H, Hkv, nsm, max_blocks_per_sm, parallel_blocks,
|
|
ntiles_dst, ntiles_KV, 0, blocks_num.x, blocks_num.y, blocks_num.z);
|
|
float * dst_ptr = (float *) dst;
|
|
float2 * meta_ptr = nullptr;
|
|
if (parallel_blocks > 1) {
|
|
dst_ptr = tmp;
|
|
meta_ptr = (float2 *) (tmp + (size_t) parallel_blocks * (size_t) n_q
|
|
* (size_t) H * DKQ);
|
|
}
|
|
const uint3 ne01 = init_fastdiv_values((uint32_t) n_q);
|
|
const uint32_t n_head_log2 = 1u << (uint32_t) floorf(log2f((float) H));
|
|
fattn_kernel<<<blocks_num, block_dim, 0, stream>>>(
|
|
(const char *) q, (const char *) k, (const char *) v, (const char *) mask,
|
|
nullptr, nullptr, dst_ptr, meta_ptr,
|
|
scale, 0.0f, 1.0f, 1.0f, n_head_log2, 0.0f,
|
|
D, ne01, H, 1,
|
|
(int32_t) q_nb1, (int32_t) q_nb2, 0,
|
|
D, kc, Hkv, 1,
|
|
(int32_t) k_nb1, (int32_t) k_nb2, 0,
|
|
(int32_t) v_nb1, (int32_t) v_nb2, 0,
|
|
n_q, 1, 1,
|
|
(int32_t) m_nb1, 0, 0);
|
|
err = cudaGetLastError();
|
|
if (err != cudaSuccess) {
|
|
return err;
|
|
}
|
|
if (parallel_blocks > 1) {
|
|
dim3 block_combine(DKQ, 1, 1);
|
|
dim3 grid_combine((unsigned) n_q, (unsigned) H, 1);
|
|
const size_t shared = (size_t) parallel_blocks * sizeof(float2);
|
|
flash_attn_combine_results<DKQ><<<grid_combine, block_combine, shared, stream>>>(
|
|
dst_ptr, meta_ptr, (float *) dst, parallel_blocks);
|
|
return cudaGetLastError();
|
|
}
|
|
return cudaSuccess;
|
|
}
|