ember.cpp 0.1.0
C++ inference engine for Llama-family and paperkiln models (CPU, CUDA, WebAssembly)
Loading...
Searching...
No Matches
ember_tiled Namespace Reference

Functions

__host__ __device__ int tiled_chunk (int L, int min_chunk, int max_chunks)
 
__host__ __device__ int tiled_nchunks (int L, int chunk)
 
__host__ __device__ int tiled_grid_chunks (int max_len, int min_chunk, int max_chunks)
 
__host__ bool tiled_shape_ok (int heads, int head_dim, int max_len, int heads_max, int min_chunk, int max_chunks)
 
__host__ size_t tiled_shm_bytes (int head_dim, int max_len, int min_chunk, int max_chunks)
 
__device__ __forceinline__ void tiled_split_block (const float *__restrict__ Qh, const float *__restrict__ K, const float *__restrict__ V, int L, int c, int kvh, int num_kv_heads, int head_dim, float scale, int min_chunk, int max_chunks, float *__restrict__ part)
 
__device__ __forceinline__ void tiled_merge_block (const float *__restrict__ base, float *__restrict__ out_h, int L, int head_dim, int min_chunk, int max_chunks)
 

Variables

constexpr int kTiledThreads = 128
 
constexpr int kTiledMinChunk = 32
 
constexpr int kTiledMaxChunks = 64
 
constexpr int kTiledChunkMax = 2048
 
constexpr int kTiledHeadDimMax = 256
 

Function Documentation

◆ tiled_chunk()

__host__ __device__ int ember_tiled::tiled_chunk ( int  L,
int  min_chunk,
int  max_chunks 
)
inline

Definition at line 35 of file decode_attn_tiled.cuh.

35 {
36 const int c = (L + max_chunks - 1) / max_chunks;
37 return c < min_chunk ? min_chunk : c;
38}
float c
size_t j

References c, and j.

Referenced by tiled_merge_block(), tiled_shape_ok(), tiled_shm_bytes(), and tiled_split_block().

◆ tiled_grid_chunks()

__host__ __device__ int ember_tiled::tiled_grid_chunks ( int  max_len,
int  min_chunk,
int  max_chunks 
)
inline

Definition at line 41 of file decode_attn_tiled.cuh.

41 {
42 const int n = (max_len + min_chunk - 1) / min_chunk;
43 return n < max_chunks ? n : max_chunks;
44}
int n

References j, and n.

Referenced by ember_kvq::partial_floats().

◆ tiled_merge_block()

__device__ __forceinline__ void ember_tiled::tiled_merge_block ( const float *__restrict__  base,
float *__restrict__  out_h,
int  L,
int  head_dim,
int  min_chunk,
int  max_chunks 
)

Definition at line 152 of file decode_attn_tiled.cuh.

153 {
154 const int nc = tiled_nchunks(L, tiled_chunk(L, min_chunk, max_chunks)); // <= max_chunks <= 64
155 const int stride = head_dim + 2;
157 __shared__ float inv_s;
158 const int tid = threadIdx.x;
159 if (tid < 32) {
160 const int c0 = tid, c1 = tid + 32;
161 const float m0 = c0 < nc ? base[(size_t)c0 * stride] : -INFINITY;
162 const float m1 = c1 < nc ? base[(size_t)c1 * stride] : -INFINITY;
163 float M = fmaxf(m0, m1);
164 for (int o = 16; o > 0; o >>= 1) M = fmaxf(M, __shfl_xor_sync(0xffffffff, M, o));
165 const float w0 = c0 < nc ? expf(m0 - M) : 0.0f;
166 const float w1 = c1 < nc ? expf(m1 - M) : 0.0f;
167 w[c0] = w0;
168 w[c1] = w1;
169 // Explicit roundings: no contraction into an fma, so every kernel that inlines this adds the same values.
170 const float l0 = c0 < nc ? __fmul_rn(w0, base[(size_t)c0 * stride + 1]) : 0.0f;
171 const float l1 = c1 < nc ? __fmul_rn(w1, base[(size_t)c1 * stride + 1]) : 0.0f;
172 float l = __fadd_rn(l0, l1);
173 for (int o = 16; o > 0; o >>= 1) l += __shfl_xor_sync(0xffffffff, l, o);
174 if (tid == 0) inv_s = l > 0.0f ? 1.0f / l : 0.0f;
175 }
177 const float inv = inv_s;
178 for (int d = tid; d < head_dim; d += blockDim.x) {
179 float acc = 0.0f;
180 const float* p = base + 2 + d;
181#pragma unroll 4
182 for (int c = 0; c < nc; ++c) acc = fmaf(w[c], p[(size_t)c * stride], acc);
183 out_h[d] = acc * inv;
184 }
185}
const float * p[kIrDepthSourcesMax]
__host__ __device__ int tiled_nchunks(int L, int chunk)
constexpr int kTiledMaxChunks
__host__ __device__ int tiled_chunk(int L, int min_chunk, int max_chunks)

References c, j, kTiledMaxChunks, p, tiled_chunk(), and tiled_nchunks().

◆ tiled_nchunks()

__host__ __device__ int ember_tiled::tiled_nchunks ( int  L,
int  chunk 
)
inline

Definition at line 39 of file decode_attn_tiled.cuh.

39{ return (L + chunk - 1) / chunk; }

References j.

Referenced by tiled_merge_block().

◆ tiled_shape_ok()

__host__ bool ember_tiled::tiled_shape_ok ( int  heads,
int  head_dim,
int  max_len,
int  heads_max,
int  min_chunk,
int  max_chunks 
)
inline

Definition at line 46 of file decode_attn_tiled.cuh.

47 {
50}
constexpr int kTiledHeadDimMax
constexpr int kTiledChunkMax

References j, kTiledChunkMax, kTiledHeadDimMax, kTiledMaxChunks, and tiled_chunk().

Referenced by ember_kvq::decode_cuda().

◆ tiled_shm_bytes()

__host__ size_t ember_tiled::tiled_shm_bytes ( int  head_dim,
int  max_len,
int  min_chunk,
int  max_chunks 
)
inline

Definition at line 52 of file decode_attn_tiled.cuh.

52 {
53 return (size_t)(head_dim + tiled_chunk(max_len, min_chunk, max_chunks)) * sizeof(float);
54}

References j, and tiled_chunk().

◆ tiled_split_block()

__device__ __forceinline__ void ember_tiled::tiled_split_block ( const float *__restrict__  Qh,
const float *__restrict__  K,
const float *__restrict__  V,
int  L,
int  c,
int  kvh,
int  num_kv_heads,
int  head_dim,
float  scale,
int  min_chunk,
int  max_chunks,
float *__restrict__  part 
)

Definition at line 58 of file decode_attn_tiled.cuh.

61 {
62 const int chunk = tiled_chunk(L, min_chunk, max_chunks);
63 const int k0 = c * chunk;
64 if (k0 >= L) return; // past nchunks(L): the merge does not read this slot
65 const int k1 = min(L, k0 + chunk);
66 const int n = k1 - k0;
67 const int d4n = head_dim >> 2;
69 float4* q4 = tiled_sm4; // [head_dim / 4]
70 float* s = reinterpret_cast<float*>(q4 + d4n); // [chunk]
71 __shared__ float red[kTiledThreads / 32];
72 __shared__ float4 pacc4[kTiledThreads]; // [G][head_dim / 4]
73 const int tid = threadIdx.x, lane = tid & 31, warp = tid >> 5;
74 const float4* Q4 = reinterpret_cast<const float4*>(Qh);
75 for (int j = tid; j < d4n; j += kTiledThreads) q4[j] = Q4[j];
77 const size_t row4 = (size_t)num_kv_heads * d4n; // float4s per cache position
78 const float4* K4 = reinterpret_cast<const float4*>(K) + (size_t)kvh * d4n;
79 const float4* V4 = reinterpret_cast<const float4*>(V) + (size_t)kvh * d4n;
80 // Scores: quad (tid >> 2) takes key kb + quad; lane (tid & 3) the float4 slices ql, ql + 4, ...
81 const int quad = tid >> 2, ql = tid & 3;
82 for (int kb = k0; kb < k1; kb += kTiledThreads / 4) {
83 const int k = kb + quad;
84 float dot = 0.0f;
85 if (k < k1) {
86 const float4* kr = K4 + (size_t)k * row4;
87#pragma unroll 4
88 for (int j = ql; j < d4n; j += 4) {
89 const float4 a = q4[j];
90 const float4 b = __ldg(kr + j);
91 dot = fmaf(a.x, b.x, dot);
92 dot = fmaf(a.y, b.y, dot);
93 dot = fmaf(a.z, b.z, dot);
94 dot = fmaf(a.w, b.w, dot);
95 }
96 }
97 dot += __shfl_xor_sync(0xffffffff, dot, 1);
98 dot += __shfl_xor_sync(0xffffffff, dot, 2);
99 if (k < k1 && ql == 0) s[k - k0] = dot * scale;
100 }
102 float mx = -INFINITY;
103 for (int i = tid; i < n; i += kTiledThreads) mx = fmaxf(mx, s[i]);
104 for (int o = 16; o > 0; o >>= 1) mx = fmaxf(mx, __shfl_xor_sync(0xffffffff, mx, o));
105 if (lane == 0) red[warp] = mx;
107 mx = red[0];
108 for (int w = 1; w < kTiledThreads / 32; ++w) mx = fmaxf(mx, red[w]);
110 float sum = 0.0f;
111 for (int i = tid; i < n; i += kTiledThreads) {
112 const float e = expf(s[i] - mx);
113 s[i] = e;
114 sum += e;
115 }
116 for (int o = 16; o > 0; o >>= 1) sum += __shfl_xor_sync(0xffffffff, sum, o);
117 if (lane == 0) red[warp] = sum;
118 __syncthreads(); // also publishes s[] = e^{s - max} to the value pass
119 if (tid == 0) {
120 float l = 0.0f;
121 for (int w = 0; w < kTiledThreads / 32; ++w) l += red[w];
122 part[0] = mx;
123 part[1] = l;
124 }
125 // Values: group g = tid / d4n adds keys g, g + G, ... (G = 128 / d4n groups; threads past G * d4n idle).
126 const int G = kTiledThreads / d4n;
127 const int g = tid / d4n, j = tid - g * d4n;
128 if (g < G) {
129 float4 acc = make_float4(0.0f, 0.0f, 0.0f, 0.0f);
130 const float4* vc = V4 + (size_t)k0 * row4 + j;
131#pragma unroll 4
132 for (int i = g; i < n; i += G) {
133 const float w = s[i];
134 const float4 v = __ldg(vc + (size_t)i * row4);
135 acc.x = fmaf(w, v.x, acc.x);
136 acc.y = fmaf(w, v.y, acc.y);
137 acc.z = fmaf(w, v.z, acc.z);
138 acc.w = fmaf(w, v.w, acc.w);
139 }
140 pacc4[g * d4n + j] = acc;
141 }
143 const float* pacc = reinterpret_cast<const float*>(pacc4);
144 for (int d = tid; d < head_dim; d += kTiledThreads) {
145 float a = 0.0f;
146 for (int gg = 0; gg < G; ++gg) a += pacc[gg * head_dim + d];
147 part[2 + d] = a;
148 }
149}
std::vector< float > v
std::vector< E > a
node scale
constexpr int kTiledThreads

References a, c, j, k, kTiledThreads, n, scale, tiled_chunk(), and v.

Variable Documentation

◆ kTiledChunkMax

constexpr int ember_tiled::kTiledChunkMax = 2048
constexpr

Definition at line 32 of file decode_attn_tiled.cuh.

Referenced by tiled_shape_ok().

◆ kTiledHeadDimMax

constexpr int ember_tiled::kTiledHeadDimMax = 256
constexpr

Definition at line 33 of file decode_attn_tiled.cuh.

Referenced by tiled_shape_ok().

◆ kTiledMaxChunks

constexpr int ember_tiled::kTiledMaxChunks = 64
constexpr

Definition at line 31 of file decode_attn_tiled.cuh.

Referenced by decode_attention_tiles(), tiled_merge_block(), and tiled_shape_ok().

◆ kTiledMinChunk

constexpr int ember_tiled::kTiledMinChunk = 32
constexpr

Definition at line 30 of file decode_attn_tiled.cuh.

Referenced by decode_attention_tiles().

◆ kTiledThreads

constexpr int ember_tiled::kTiledThreads = 128
constexpr

Definition at line 29 of file decode_attn_tiled.cuh.

Referenced by tiled_split_block().