Skip to content

Commit 3d37f74

Browse files
voidstackloopclaude
andcommitted
Merge master into feat/hybrid-moe-expert-cache
Resolve the conflict with d454a20 (llama: create new fused op for QSA mask, ggml-org#30640) in ggml-cuda.cu: master added GGML_OP_TOP_K_MASK where this branch added GGML_OP_CUSTOM (the mailbox ops) to the compute dispatch and to supports_op. Keep both cases. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 parents 811bff4 + d454a20 commit 3d37f74

26 files changed

Lines changed: 1007 additions & 39 deletions

‎common/jinja/lexer.cpp‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
#include "lexer.h"
22
#include "runtime.h"
3+
#include "unicode.h"
34

45
#include <cctype>
6+
#include <charconv>
57
#include <functional>
68
#include <map>
79
#include <string>
@@ -75,6 +77,24 @@ lexer_result lexer::tokenize(const std::string & source) {
7577
}
7678
// add escaped char
7779
char escaped_char = src[pos++];
80+
if (escaped_char == 'x') {
81+
if (pos + 1 >= src.size()) {
82+
throw lexer_exception("incomplete hexadecimal escape", source, pos);
83+
}
84+
85+
unsigned int value = 0;
86+
const char * first = &src[pos];
87+
const char * last = first + 2;
88+
89+
auto [ptr, ec] = std::from_chars(first, last, value, 16);
90+
if (ec != std::errc{} || ptr != last) {
91+
throw lexer_exception("invalid hexadecimal escape", source, pos);
92+
}
93+
94+
str += common_unicode_cpt_to_utf8(value);
95+
pos += 2;
96+
continue;
97+
}
7898
if (escape_chars.find(escaped_char) == escape_chars.end()) {
7999
throw lexer_exception(std::string("unknown escape character \\") + escaped_char, source, pos);
80100
}

‎ggml/include/ggml-rpc.h‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ extern "C" {
88

99
#define RPC_PROTO_MAJOR_VERSION 8
1010
#define RPC_PROTO_MINOR_VERSION 0
11-
#define RPC_PROTO_PATCH_VERSION 0
11+
#define RPC_PROTO_PATCH_VERSION 1
1212

1313
#ifdef __cplusplus
14-
static_assert(GGML_OP_COUNT == 101, "GGML_OP_COUNT has changed - update RPC_PROTO_PATCH_VERSION");
14+
static_assert(GGML_OP_COUNT == 102, "GGML_OP_COUNT has changed - update RPC_PROTO_PATCH_VERSION");
1515
#endif
1616

1717
#define GGML_RPC_MAX_SERVERS 16

‎ggml/include/ggml.h‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,7 @@ extern "C" {
585585
GGML_OP_DSV4_HC_COMB,
586586
GGML_OP_DSV4_HC_PRE,
587587
GGML_OP_DSV4_HC_POST,
588+
GGML_OP_TOP_K_MASK,
588589

589590
GGML_OP_UNARY,
590591

@@ -2738,6 +2739,26 @@ extern "C" {
27382739
struct ggml_tensor * post,
27392740
struct ggml_tensor * comb);
27402741

2742+
// top-k sparse attention mask: keep the mask cells of the live picks and the extra cells, set the rest to -inf
2743+
//
2744+
// mask: [n_kv, m1, m2, m3] f16 or f32, broadcast to the rows of top_k
2745+
// top_k: [n_top, ne1, ne2, ne3] i32, picks of each row
2746+
// score: [n_pick, ne1, ne2, ne3] f32, can be NULL: a pick is live when its score is above -inf, else every pick is live
2747+
// blocks: [n_blk, n_pick] i32, can be NULL: the member cells of each pick, else a pick is a cell
2748+
// extra: [n_extra, ne1, ne2, ne3] i32, can be NULL: cells of each row kept without a pick
2749+
// res: [n_kv, ne1, ne2, ne3] same type as mask
2750+
//
2751+
// res[c, i] = mask[c, i % m] when c is a cell of a live pick of row i or an extra cell of row i, else -inf
2752+
// picks and cells out of range are skipped
2753+
//
2754+
GGML_API struct ggml_tensor * ggml_top_k_mask(
2755+
struct ggml_context * ctx,
2756+
struct ggml_tensor * mask,
2757+
struct ggml_tensor * top_k,
2758+
struct ggml_tensor * score,
2759+
struct ggml_tensor * blocks,
2760+
struct ggml_tensor * extra);
2761+
27412762
// custom operators
27422763

27432764
typedef void (*ggml_custom1_op_t)(struct ggml_tensor * dst , const struct ggml_tensor * a, int ith, int nth, void * userdata);

‎ggml/src/ggml-backend-meta.cpp‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1055,7 +1055,8 @@ static struct ggml_backend_meta_split_state ggml_backend_meta_get_split_state(
10551055
} break;
10561056
case GGML_OP_DSV4_HC_COMB:
10571057
case GGML_OP_DSV4_HC_PRE:
1058-
case GGML_OP_DSV4_HC_POST: {
1058+
case GGML_OP_DSV4_HC_POST:
1059+
case GGML_OP_TOP_K_MASK: {
10591060
split_state = handle_generic(src_ss, /*scalar_only =*/ true);
10601061
} break;
10611062
case GGML_OP_UNARY: {

‎ggml/src/ggml-cpu/ggml-cpu.c‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2124,6 +2124,10 @@ static void ggml_compute_forward(struct ggml_compute_params * params, struct ggm
21242124
{
21252125
ggml_compute_forward_dsv4_hc_post(params, tensor);
21262126
} break;
2127+
case GGML_OP_TOP_K_MASK:
2128+
{
2129+
ggml_compute_forward_top_k_mask(params, tensor);
2130+
} break;
21272131
case GGML_OP_MAP_CUSTOM1:
21282132
{
21292133
ggml_compute_forward_map_custom1(params, tensor);
@@ -2307,6 +2311,7 @@ static int ggml_get_n_tasks(struct ggml_tensor * node, int n_threads) {
23072311
case GGML_OP_DSV4_HC_COMB:
23082312
case GGML_OP_DSV4_HC_PRE:
23092313
case GGML_OP_DSV4_HC_POST:
2314+
case GGML_OP_TOP_K_MASK:
23102315
{
23112316
n_tasks = n_threads;
23122317
} break;

‎ggml/src/ggml-cpu/ops.cpp‎

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11736,6 +11736,92 @@ void ggml_compute_forward_dsv4_hc_post(
1173611736
}
1173711737
}
1173811738

11739+
// ggml_compute_forward_top_k_mask
11740+
11741+
void ggml_compute_forward_top_k_mask(
11742+
const ggml_compute_params * params,
11743+
ggml_tensor * dst) {
11744+
const ggml_tensor * mask = dst->src[0];
11745+
const ggml_tensor * top_k = dst->src[1];
11746+
const ggml_tensor * score = dst->src[2];
11747+
const ggml_tensor * blocks = dst->src[3];
11748+
const ggml_tensor * extra = dst->src[4];
11749+
11750+
GGML_ASSERT(dst->type == mask->type);
11751+
GGML_ASSERT(dst->nb[0] == ggml_type_size(dst->type));
11752+
11753+
const int64_t n_kv = mask->ne[0];
11754+
const int64_t ne1 = dst->ne[1];
11755+
const int64_t ne2 = dst->ne[2];
11756+
const int64_t n_rows = ggml_nrows(dst);
11757+
const int64_t n_top = top_k->ne[0];
11758+
const int64_t n_blk = blocks ? blocks->ne[0] : 1;
11759+
const int64_t n_pick = blocks ? blocks->ne[1] : score ? score->ne[0] : n_kv;
11760+
const int64_t n_extra = extra ? extra->ne[0] : 0;
11761+
11762+
const size_t ts = ggml_type_size(dst->type);
11763+
11764+
const int ith = params->ith;
11765+
const int nth = params->nth;
11766+
11767+
for (int64_t ir = ith; ir < n_rows; ir += nth) {
11768+
const int64_t i1 = ir % ne1;
11769+
const int64_t i2 = (ir / ne1) % ne2;
11770+
const int64_t i3 = ir / (ne1*ne2);
11771+
11772+
const char * mask_row = (const char *) mask->data
11773+
+ (i1 % mask->ne[1])*mask->nb[1] + (i2 % mask->ne[2])*mask->nb[2] + (i3 % mask->ne[3])*mask->nb[3];
11774+
char * dst_row = (char *) dst->data + i1*dst->nb[1] + i2*dst->nb[2] + i3*dst->nb[3];
11775+
11776+
if (dst->type == GGML_TYPE_F16) {
11777+
const ggml_fp16_t ninf = GGML_CPU_FP32_TO_FP16(-INFINITY);
11778+
for (int64_t c = 0; c < n_kv; ++c) {
11779+
((ggml_fp16_t *) dst_row)[c] = ninf;
11780+
}
11781+
} else {
11782+
for (int64_t c = 0; c < n_kv; ++c) {
11783+
((float *) dst_row)[c] = -INFINITY;
11784+
}
11785+
}
11786+
11787+
auto keep = [&](int32_t c) {
11788+
if (c >= 0 && c < n_kv) {
11789+
memcpy(dst_row + c*ts, mask_row + c*ts, ts);
11790+
}
11791+
};
11792+
11793+
const char * top_k_row = (const char *) top_k->data + i1*top_k->nb[1] + i2*top_k->nb[2] + i3*top_k->nb[3];
11794+
11795+
for (int64_t j = 0; j < n_top; ++j) {
11796+
const int32_t p = *(const int32_t *) (top_k_row + j*top_k->nb[0]);
11797+
if (p < 0 || p >= n_pick) {
11798+
continue;
11799+
}
11800+
if (score) {
11801+
const float s = *(const float *) ((const char *) score->data
11802+
+ p*score->nb[0] + i1*score->nb[1] + i2*score->nb[2] + i3*score->nb[3]);
11803+
if (!(s > -INFINITY)) {
11804+
continue;
11805+
}
11806+
}
11807+
if (!blocks) {
11808+
keep(p);
11809+
continue;
11810+
}
11811+
for (int64_t k = 0; k < n_blk; ++k) {
11812+
keep(*(const int32_t *) ((const char *) blocks->data + k*blocks->nb[0] + p*blocks->nb[1]));
11813+
}
11814+
}
11815+
11816+
if (extra) {
11817+
const char * extra_row = (const char *) extra->data + i1*extra->nb[1] + i2*extra->nb[2] + i3*extra->nb[3];
11818+
for (int64_t j = 0; j < n_extra; ++j) {
11819+
keep(*(const int32_t *) (extra_row + j*extra->nb[0]));
11820+
}
11821+
}
11822+
}
11823+
}
11824+
1173911825
// ggml_compute_forward_rwkv_wkv7
1174011826

1174111827
static void ggml_compute_forward_rwkv_wkv7_f32(

‎ggml/src/ggml-cpu/ops.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ void ggml_compute_forward_lightning_indexer(const struct ggml_compute_params * p
108108
void ggml_compute_forward_dsv4_hc_comb(const struct ggml_compute_params * params, struct ggml_tensor * dst);
109109
void ggml_compute_forward_dsv4_hc_pre(const struct ggml_compute_params * params, struct ggml_tensor * dst);
110110
void ggml_compute_forward_dsv4_hc_post(const struct ggml_compute_params * params, struct ggml_tensor * dst);
111+
void ggml_compute_forward_top_k_mask(const struct ggml_compute_params * params, struct ggml_tensor * dst);
111112
void ggml_compute_forward_map_custom1(const struct ggml_compute_params * params, struct ggml_tensor * dst);
112113
void ggml_compute_forward_map_custom2(const struct ggml_compute_params * params, struct ggml_tensor * dst);
113114
void ggml_compute_forward_map_custom3(const struct ggml_compute_params * params, struct ggml_tensor * dst);

‎ggml/src/ggml-cuda/ggml-cuda.cu‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
#include "ggml-cuda/cumsum.cuh"
7272
#include "ggml-cuda/fill.cuh"
7373
#include "ggml-cuda/lightning-indexer.cuh"
74+
#include "ggml-cuda/top-k-mask.cuh"
7475
#include "ggml.h"
7576

7677
#include <algorithm>
@@ -2634,6 +2635,9 @@ static bool ggml_cuda_compute_forward(ggml_backend_cuda_context & ctx, struct gg
26342635
case GGML_OP_LIGHTNING_INDEXER:
26352636
ggml_cuda_lightning_indexer(ctx, dst);
26362637
break;
2638+
case GGML_OP_TOP_K_MASK:
2639+
ggml_cuda_op_top_k_mask(ctx, dst);
2640+
break;
26372641
case GGML_OP_CUSTOM:
26382642
ggml_cuda_op_mailbox(ctx, dst);
26392643
break;
@@ -6072,6 +6076,8 @@ static bool ggml_backend_cuda_device_supports_op(ggml_backend_dev_t dev, const g
60726076
return true;
60736077
case GGML_OP_LIGHTNING_INDEXER:
60746078
return ggml_cuda_lightning_indexer_supported(dev_ctx->device, op);
6079+
case GGML_OP_TOP_K_MASK:
6080+
return op->type == GGML_TYPE_F16 || op->type == GGML_TYPE_F32;
60756081
case GGML_OP_CUSTOM:
60766082
return ggml_cuda_mailbox_op_supported(op);
60776083

‎ggml/src/ggml-cuda/top-k-mask.cu‎

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
#include "top-k-mask.cuh"
2+
#include "convert.cuh"
3+
4+
#define CUDA_TOP_K_MASK_BLOCK_SIZE 512
5+
6+
// one block per row: fill the row with -inf, then copy the mask cells of the live picks and the extra cells
7+
template <typename T>
8+
static __global__ void top_k_mask_kernel(
9+
const T * mask, const int32_t * top_k, const float * score, const int32_t * blocks, const int32_t * extra, T * dst,
10+
const int64_t n_kv, const int64_t ne1, const int64_t ne2,
11+
const int64_t m1, const int64_t m2, const int64_t m3,
12+
const int64_t n_top, const int64_t n_pick, const int64_t n_blk, const int64_t n_extra,
13+
const int64_t sm1, const int64_t sm2, const int64_t sm3,
14+
const int64_t sd1, const int64_t sd2, const int64_t sd3,
15+
const int64_t sk0, const int64_t sk1, const int64_t sk2, const int64_t sk3,
16+
const int64_t ss0, const int64_t ss1, const int64_t ss2, const int64_t ss3,
17+
const int64_t sb0, const int64_t sb1,
18+
const int64_t se0, const int64_t se1, const int64_t se2, const int64_t se3,
19+
const bool vec) {
20+
ggml_cuda_pdl_lc();
21+
22+
const int64_t ir = blockIdx.x;
23+
const int64_t i1 = ir % ne1;
24+
const int64_t i2 = (ir / ne1) % ne2;
25+
const int64_t i3 = ir / (ne1*ne2);
26+
27+
const T * mask_row = mask + (i1 % m1)*sm1 + (i2 % m2)*sm2 + (i3 % m3)*sm3;
28+
T * dst_row = dst + i1*sd1 + i2*sd2 + i3*sd3;
29+
const int32_t * top_k_row = top_k + i1*sk1 + i2*sk2 + i3*sk3;
30+
31+
ggml_cuda_pdl_sync();
32+
33+
const T ninf = ggml_cuda_cast<T>(-INFINITY);
34+
if (vec) {
35+
constexpr int nv = sizeof(int4)/sizeof(T);
36+
T fill[nv];
37+
#pragma unroll
38+
for (int j = 0; j < nv; ++j) {
39+
fill[j] = ninf;
40+
}
41+
const int4 fill4 = *(const int4 *) fill;
42+
for (int64_t i = threadIdx.x; i < n_kv/nv; i += blockDim.x) {
43+
((int4 *) dst_row)[i] = fill4;
44+
}
45+
} else {
46+
for (int64_t i = threadIdx.x; i < n_kv; i += blockDim.x) {
47+
dst_row[i] = ninf;
48+
}
49+
}
50+
51+
__syncthreads();
52+
53+
// one thread per pick or extra cell; cells kept twice get the same value
54+
for (int64_t i = threadIdx.x; i < n_top + n_extra; i += blockDim.x) {
55+
if (i >= n_top) {
56+
const int32_t c = extra[(i - n_top)*se0 + i1*se1 + i2*se2 + i3*se3];
57+
if (c >= 0 && c < n_kv) {
58+
dst_row[c] = mask_row[c];
59+
}
60+
continue;
61+
}
62+
63+
const int32_t p = top_k_row[i*sk0];
64+
if (p < 0 || p >= n_pick) {
65+
continue;
66+
}
67+
if (score && !(score[p*ss0 + i1*ss1 + i2*ss2 + i3*ss3] > -INFINITY)) {
68+
continue;
69+
}
70+
if (!blocks) {
71+
if (p < n_kv) {
72+
dst_row[p] = mask_row[p];
73+
}
74+
continue;
75+
}
76+
for (int64_t k = 0; k < n_blk; ++k) {
77+
const int32_t c = blocks[k*sb0 + p*sb1];
78+
if (c >= 0 && c < n_kv) {
79+
dst_row[c] = mask_row[c];
80+
}
81+
}
82+
}
83+
}
84+
85+
template <typename T>
86+
static void top_k_mask_cuda(ggml_backend_cuda_context & ctx, ggml_tensor * dst) {
87+
const ggml_tensor * mask = dst->src[0];
88+
const ggml_tensor * top_k = dst->src[1];
89+
const ggml_tensor * score = dst->src[2];
90+
const ggml_tensor * blocks = dst->src[3];
91+
const ggml_tensor * extra = dst->src[4];
92+
93+
const int64_t n_kv = mask->ne[0];
94+
95+
GGML_ASSERT(dst->nb[0] == sizeof(T) && mask->nb[0] == sizeof(T));
96+
GGML_ASSERT(dst->nb[1] % sizeof(T) == 0 && mask->nb[1] % sizeof(T) == 0);
97+
98+
// 16-byte stores need every dst row aligned
99+
const bool vec = (n_kv*sizeof(T)) % sizeof(int4) == 0 &&
100+
dst->nb[1] % sizeof(int4) == 0 && dst->nb[2] % sizeof(int4) == 0 && dst->nb[3] % sizeof(int4) == 0 &&
101+
(uintptr_t) dst->data % sizeof(int4) == 0;
102+
103+
const int64_t n_pick = blocks ? blocks->ne[1] : score ? score->ne[0] : n_kv;
104+
105+
const size_t si = sizeof(int32_t);
106+
const size_t sf = sizeof(float);
107+
108+
const dim3 block_dims(CUDA_TOP_K_MASK_BLOCK_SIZE, 1, 1);
109+
const dim3 grid_dims(ggml_nrows(dst), 1, 1);
110+
const ggml_cuda_kernel_launch_params launch_params = ggml_cuda_kernel_launch_params(grid_dims, block_dims, 0, ctx.stream());
111+
112+
ggml_cuda_kernel_launch(top_k_mask_kernel<T>, launch_params,
113+
(const T *) mask->data, (const int32_t *) top_k->data,
114+
score ? (const float *) score->data : nullptr,
115+
blocks ? (const int32_t *) blocks->data : nullptr,
116+
extra ? (const int32_t *) extra->data : nullptr,
117+
(T *) dst->data,
118+
n_kv, dst->ne[1], dst->ne[2],
119+
mask->ne[1], mask->ne[2], mask->ne[3],
120+
top_k->ne[0], n_pick, blocks ? blocks->ne[0] : 1, extra ? extra->ne[0] : 0,
121+
mask->nb[1]/sizeof(T), mask->nb[2]/sizeof(T), mask->nb[3]/sizeof(T),
122+
dst->nb[1]/sizeof(T), dst->nb[2]/sizeof(T), dst->nb[3]/sizeof(T),
123+
top_k->nb[0]/si, top_k->nb[1]/si, top_k->nb[2]/si, top_k->nb[3]/si,
124+
score ? score->nb[0]/sf : 0, score ? score->nb[1]/sf : 0, score ? score->nb[2]/sf : 0, score ? score->nb[3]/sf : 0,
125+
blocks ? blocks->nb[0]/si : 0, blocks ? blocks->nb[1]/si : 0,
126+
extra ? extra->nb[0]/si : 0, extra ? extra->nb[1]/si : 0, extra ? extra->nb[2]/si : 0, extra ? extra->nb[3]/si : 0,
127+
vec);
128+
}
129+
130+
void ggml_cuda_op_top_k_mask(ggml_backend_cuda_context & ctx, ggml_tensor * dst) {
131+
switch (dst->type) {
132+
case GGML_TYPE_F16:
133+
top_k_mask_cuda<half>(ctx, dst);
134+
break;
135+
case GGML_TYPE_F32:
136+
top_k_mask_cuda<float>(ctx, dst);
137+
break;
138+
default:
139+
GGML_ABORT("unsupported type");
140+
}
141+
}

‎ggml/src/ggml-cuda/top-k-mask.cuh‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#include "common.cuh"
2+
3+
void ggml_cuda_op_top_k_mask(ggml_backend_cuda_context & ctx, ggml_tensor * dst);

0 commit comments

Comments
 (0)