ember.cpp
0.1.0
C++ inference engine for Llama-family and paperkiln models (CPU, CUDA, WebAssembly)
Loading...
Searching...
No Matches
verify_kernels.h
Go to the documentation of this file.
1
// Kernels of the speculative verification pass (speculative_verify.cpp, EMBER_SPEC=k on CUDA).
2
//
3
// A verification pass runs n = 1 + draft rows (positions pos .. pos + n - 1) through every layer in one CUDA graph,
4
// built like the graph decode (model.cpp forward_device_graph). Every kernel here computes each row with the decode
5
// step's arithmetic: the same statements, the same reduction order, the same attention chunking. Only the linear
6
// layers have a choice (VerifyLinear): kRows is the decode step's own cuBLAS call per row, and kFixed is
7
// fixed_linear, the decode step's linear kernel when the decode runs on it (EMBER_DECODE_LINEAR=fixed, opt-in;
8
// experiments/spec_r1_2026-10-28). Apart from fixed_linear these kernels serve only
9
// the pass. pos lives in device memory (pos_dev[0]) and the n
10
// token ids after it (pos_dev[1 .. n]), so one captured graph per n serves every position.
11
#ifndef EMBER_VERIFY_KERNELS_H
12
#define EMBER_VERIFY_KERNELS_H
13
14
#ifdef HAS_CUDA
15
#include <cublas_v2.h>
16
#include <cuda_runtime.h>
17
18
namespace
ember_verify
{
19
20
constexpr
int
kMaxRows
= 16;
// rows per pass on this path (EMBER_SPEC <= 15)
21
22
// The linear layers Y[n][O] = X[n][I] W[O][I]^T, FP32.
23
enum class
VerifyLinear
{
24
kSmallM
= 0,
// one warp per output, all n rows at once: the weights are read once (not the decode's order)
25
kGemm
= 1,
// cublasSgemm with n columns (the batched prefill's call; not the decode's order)
26
kGemvBatched
= 2,
// cublasSgemvStridedBatched, the weight shared (stride 0)
27
kRows
= 3,
// the decode step's cublasSgemm(n = 1), once per row: the decode's arithmetic exactly
28
kFixed
= 4,
// fixed_linear: kSmallM's kernel with a scalar twin for any shape; each row's arithmetic is the
29
// same for every n, so with the decode step on fixed_linear (n = 1) a row is the decode's bits
30
};
31
32
// Y[n][O] = X[n][I] W[O][I]^T, one warp per output and every row at once, each row's dot product a fixed fma chain
33
// (lane l takes the inputs 4 (l + 32 j) .. 4 (l + 32 j) + 3, j = 0, 1, ..., in order) and a fixed butterfly over
34
// the warp. A row's result depends only on its own X row and W, never on n, the other rows, the alignment or the
35
// stream: the float4 kernel (I % 4 == 0, X and W 16-byte aligned) and its scalar twin (any other shape) compute the
36
// same chain. 1 <= n <= kMaxRows.
37
void
fixed_linear
(
const
float
*
X
,
const
float
*
W
,
float
*
Y
,
int
n
,
int
O
,
int
I
,
cudaStream_t
st
);
38
39
// Per row of the last pass: the largest logit, the first and last index holding it, the largest logit below it
40
// (-inf if none), and whether the row holds a NaN. Enough to reproduce api.cpp's greedy sampler without the row.
41
struct
RowPick
{
42
float
max
;
43
float
second
;
44
int
first
;
45
int
last
;
46
int
nan
;
47
};
48
49
// out[r][i] = table[ids[r]][i] (decode_embed_kernel's rule per row); ids = pos_dev + 1.
50
void
embed_rows
(
const
float
*
table
,
float
*
out
,
const
int
*
pos_dev
,
int
n
,
int
hidden
,
int
vocab,
cudaStream_t
st
);
51
// rmsnorm_vector_cuda_optimized per row (its deterministic kernel under EMBER_DETERMINISTIC).
52
void
rmsnorm_rows
(
const
float
*
x
,
const
float
*
w
,
float
*
out
,
int
n
,
int
hidden
,
float
eps
,
cudaStream_t
st
);
53
// rope_devpos_cuda per row at position pos + r; x is [n][heads * head_dim].
54
void
rope_rows
(
float
*
x
,
int
n
,
int
heads
,
int
head_dim
,
const
float
*
freqs
,
const
int
*
pos_dev
,
bool
adjacent
,
55
cudaStream_t
st
);
56
// kv_write_devpos_cuda per row at position pos + r; k, v are [n][row].
57
void
kv_write_rows
(
float
*
k_cache
,
float
*
v_cache
,
const
float
*
k
,
const
float
*
v
,
const
int
*
pos_dev
,
int
n
,
int
row
,
58
cudaStream_t
st
);
59
// The graph decode's split-K attention per row (row r attends to pos + r + 1 keys), the kernel and chunking
60
// attention_devpos_cuda uses (the tiled kernel's block bodies, decode_attn_tiled.cuh, by default; EMBER_DECODE_ATTN=split
61
// the 256-key kernel for max_len positions). scratch: attention_rows_scratch_floats(...). False when the decode itself
62
// would not use a split-K kernel for this shape (EMBER_DECODE_ATTN=legacy, or past its limits): the caller then takes
63
// another path.
64
size_t
attention_rows_scratch_floats
(
int
n
,
int
heads
,
int
head_dim
,
int
max_len);
65
// q, out: rows row0 .. row0 + n - 1 of the pass (row0 = 0 for all rows at once; the per-row form launches it with
66
// n = 1 per row, for measurement).
67
bool
attention_rows
(
const
float
* q,
const
float
*
k_cache
,
const
float
*
v_cache
,
float
*
out
,
int
n
,
int
row0
,
68
int
heads
,
int
head_dim
,
float
scale
,
int
max_len,
int
kv_heads
,
const
int
*
pos_dev
,
69
float
*
scratch
,
cudaStream_t
st
);
70
// Y[n][O] = X[n][I] W[O][I]^T. kSmallM needs n <= kMaxRows, I % 4 == 0 and 16-byte aligned X, W (else it runs as
71
// kGemm). The handle's stream is set to st.
72
void
linear_rows
(
cublasHandle_t
h
,
VerifyLinear
mode
,
const
float
*
X
,
const
float
*
W
,
float
*
Y
,
int
n
,
int
O
,
int
I
,
73
cudaStream_t
st
);
74
// picks[r] for logits [n][vocab].
75
void
select_rows
(
const
float
*
logits
,
int
n
,
int
vocab,
RowPick
*
picks
,
cudaStream_t
st
);
76
77
}
// namespace ember_verify
78
79
#endif
// HAS_CUDA
80
#endif
// EMBER_VERIFY_KERNELS_H
n
int n
Definition
graph_ir_cuda.cu:326
v
std::vector< float > v
Definition
graph_ir_expr.cpp:257
k
K k
Definition
graph_ir_recurrence.cpp:124
out
node out
Definition
graph_ir_schema.h:9
scale
node scale
Definition
graph_ir_schema.h:9
j
size_t j
Definition
graph_ir_validate.cpp:222
verify_kernels.h
Generated by
1.9.8