Support DeepSeek V4 Flash 0731

This commit is contained in:
Georg Bauer
2026-08-29 20:28:50 +02:00
parent ad855b321e
commit f1c177b754
23 changed files with 10510 additions and 2324 deletions

View File

@@ -319,6 +319,228 @@ kernel void kernel_dsv4_compressor_pack_ratio4(
}
}
// Decode already holds the complete ratio-4 recurrent window in state layout:
// eight rows of two head_dim planes. Pack the previous plane from rows 0..3
// and the current plane from rows 4..7 directly into the transposed [head_dim,
// 8] layout consumed by the exact GGML softmax/multiply/sum sequence. KV and
// score move together; no arithmetic or reduction order changes.
kernel void kernel_dsv4_compressor_pack_ratio4_decode_ggml(
constant ds4_metal_args_dsv4_compressor_pack_ratio4 & args,
device const uint * state_kv,
device const uint * state_score,
device uint * packed_kv,
device uint * packed_score,
uint row [[threadgroup_position_in_grid]],
uint tid [[thread_index_in_threadgroup]]) {
if (row >= 8u || args.head_dim == 0u || args.n_threads == 0u) {
return;
}
const uint64_t state_row_stride = 2ull * args.head_dim;
const uint64_t src_plane = row >= 4u ? args.head_dim : 0u;
for (uint col = tid; col < args.head_dim; col += args.n_threads) {
const uint64_t src = (uint64_t)row * state_row_stride +
src_plane + col;
const uint64_t dst = (uint64_t)col * 8u + row;
packed_kv[dst] = state_kv[src];
packed_score[dst] = state_score[src];
}
}
// Exact decode specialization for the first two operations in GGML's
// softmax -> multiply -> sum_rows compressor reduction. The normalized
// softmax values are deliberately materialized in device memory and reloaded
// after a device barrier before the in-place product, preserving the dispatch
// boundary's float store/load semantics. The final sum remains the standalone
// eight-thread sum_rows kernel: changing a 32-thread group into the original
// eight-thread reduction inside this kernel would make its threadgroup
// barriers non-uniform or alter simd_sum's active-lane topology.
kernel void kernel_dsv4_compressor_exact_softmax_product_ratio4(
constant ds4_metal_args_dsv4_compressor_pack_ratio4 & args,
device const float * packed_kv,
device const float * packed_score,
device float * softmax,
device float * product,
threadgroup float * softmax_scratch [[threadgroup(0)]],
uint row [[threadgroup_position_in_grid]],
uint tid [[thread_position_in_threadgroup]]) {
if (row >= args.head_dim || args.n_comp != 1u ||
args.n_threads != 32u) {
return;
}
device const float4 * score4 =
(device const float4 *)(packed_score + (uint64_t)row * 8u);
device float4 * softmax4 =
(device float4 *)(softmax + (uint64_t)row * 8u);
const float scale = (float)args.replay;
const float zero = (float)(args.n_comp - 1u);
// Match kernel_soft_max_f32_4(width=8, nth=32) literally. Only lanes zero
// and one own float4s, while all 32 lanes participate in both reductions.
float4 lmax4 = -INFINITY;
for (int i00 = (int)tid; i00 < 2; i00 += 32) {
lmax4 = fmax(lmax4, score4[i00] * scale + (float4)zero);
}
const float lmax =
MAX(MAX(lmax4[0], lmax4[1]), MAX(lmax4[2], lmax4[3]));
const float max_val = simd_max(lmax);
float4 lsum4 = 0.0f;
for (int i00 = (int)tid; i00 < 2; i00 += 32) {
const float4 exp_score4 =
exp((score4[i00] * scale + (float4)zero) - max_val);
lsum4 += exp_score4;
softmax4[i00] = exp_score4;
}
const float lsum =
lsum4[0] + lsum4[1] + lsum4[2] + lsum4[3];
threadgroup_barrier(mem_flags::mem_none);
const float sum = simd_sum(lsum);
const float inv_sum = 1.0f / sum;
for (int i00 = (int)tid; i00 < 2; i00 += 32) {
softmax4[i00] *= inv_sum;
}
// Force the same normalized-softmax device store/reload boundary that the
// separate multiply dispatch observes.
threadgroup_barrier(mem_flags::mem_device);
device volatile const float * reloaded_softmax =
(device volatile const float *)(softmax + (uint64_t)row * 8u);
device const float * kv_row = packed_kv + (uint64_t)row * 8u;
device float * product_row = product + (uint64_t)row * 8u;
// Match kernel_bin_fuse_f32_f32_f32(width=8, nth=4): four lanes each
// process their low element followed by the element four positions later.
if (tid < 4u) {
for (uint i0 = tid; i0 < 8u; i0 += 4u) {
float value = kv_row[i0];
value *= reloaded_softmax[i0];
product_row[i0] = value;
}
}
// All 32 lanes reach the final device barrier. The following standalone
// sum_rows dispatch performs the required global reload and exact TG8
// two-stage simd_sum topology.
threadgroup_barrier(mem_flags::mem_device);
(void)softmax_scratch;
}
// Exact one-dispatch ratio-4 decode pool. This specializes the three-dispatch
// pack -> exact softmax/product -> sum_rows chain above without changing any
// floating-point operation or reduction topology. The normalized softmax and
// product are still materialized and volatile-reloaded through device memory.
// The two simd_sum calls in the final reduction execute under an eight-lane
// active mask, exactly matching kernel_sum_rows_f32_f32's original TG8.
kernel void kernel_dsv4_compressor_exact_pool_ratio4_decode_ggml(
constant ds4_metal_args_dsv4_compressor_pack_ratio4 & args,
device const float * state_kv,
device const float * state_score,
device float * softmax,
device float * product,
device float * dst,
threadgroup float * sum_scratch [[threadgroup(0)]],
uint col [[threadgroup_position_in_grid]],
uint tid [[thread_position_in_threadgroup]]) {
if (col >= args.head_dim || args.n_comp != 1u ||
args.n_threads != 32u) {
return;
}
const uint64_t state_row_stride = 2ull * args.head_dim;
const float scale = (float)args.replay;
const float zero = (float)(args.n_comp - 1u);
// Match the packed float4 ownership: lane 0 owns rows 0..3 and lane 1
// rows 4..7. The gather itself is an integer-addressed bit-preserving load.
float4 score_values = -INFINITY;
if (tid < 2u) {
const uint row0 = 4u * tid;
for (uint j = 0u; j < 4u; ++j) {
const uint row = row0 + j;
const uint64_t src = (uint64_t)row * state_row_stride +
(row >= 4u ? args.head_dim : 0u) + col;
score_values[j] = state_score[src];
}
}
const uint64_t scratch_base = (uint64_t)col * 8u;
device float4 * softmax4 =
(device float4 *)(softmax + scratch_base);
// Verbatim kernel_soft_max_f32_4(width=8, nth=32) arithmetic.
float4 lmax4 = -INFINITY;
for (int i00 = (int)tid; i00 < 2; i00 += 32) {
lmax4 = fmax(lmax4, score_values * scale + (float4)zero);
}
const float lmax =
MAX(MAX(lmax4[0], lmax4[1]), MAX(lmax4[2], lmax4[3]));
const float max_val = simd_max(lmax);
float4 lsum4 = 0.0f;
for (int i00 = (int)tid; i00 < 2; i00 += 32) {
const float4 exp_score4 =
exp((score_values * scale + (float4)zero) - max_val);
lsum4 += exp_score4;
softmax4[i00] = exp_score4;
}
const float lsum =
lsum4[0] + lsum4[1] + lsum4[2] + lsum4[3];
threadgroup_barrier(mem_flags::mem_none);
const float sum = simd_sum(lsum);
const float inv_sum = 1.0f / sum;
for (int i00 = (int)tid; i00 < 2; i00 += 32) {
softmax4[i00] *= inv_sum;
}
threadgroup_barrier(mem_flags::mem_device);
device volatile const float * reloaded_softmax =
(device volatile const float *)(softmax + scratch_base);
device float * product_row = product + scratch_base;
// Verbatim width=8, TG4 multiply ownership: low element, then +4.
if (tid < 4u) {
for (uint i0 = tid; i0 < 8u; i0 += 4u) {
const uint64_t src = (uint64_t)i0 * state_row_stride +
(i0 >= 4u ? args.head_dim : 0u) + col;
float value = state_kv[src];
value *= reloaded_softmax[i0];
product_row[i0] = value;
}
}
// Preserve the product dispatch's device store/reload boundary.
threadgroup_barrier(mem_flags::mem_device);
device volatile const float * reloaded_product =
(device volatile const float *)product_row;
// Reproduce kernel_sum_rows_f32_f32(width=8, TG8) literally. MSL defines
// simdgroup collectives over active lanes, so the branch recreates the
// original eight-lane partial SIMD group inside this 32-thread group.
sum_scratch[tid] = 0.0f;
float row_sum = 0.0f;
if (tid < 8u) {
row_sum += reloaded_product[tid];
row_sum = simd_sum(row_sum);
}
threadgroup_barrier(mem_flags::mem_threadgroup);
if (tid == 0u) {
sum_scratch[0] = row_sum;
}
threadgroup_barrier(mem_flags::mem_threadgroup);
if (tid < 8u) {
row_sum = sum_scratch[tid];
row_sum = simd_sum(row_sum);
if (tid == 0u) {
dst[col] = row_sum;
}
}
}
// Ratio-4 compression keeps two 4-row halves of recurrent state. After an
// emitted compressed row, the second half becomes the next window's previous
// half. The old encoder expressed this as four generic copies; this DS4-specific