Files
ds4/ds4_metal.m
T

52092 lines
2.3 MiB
Plaintext

#import <Foundation/Foundation.h>
#import <Metal/Metal.h>
#include <execinfo.h>
#include <stdint.h>
#include <inttypes.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include <float.h>
#include <fcntl.h>
#include <limits.h>
#include <time.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/sysctl.h>
#include <mach/mach.h>
#include <mach-o/dyld.h>
#include <objc/runtime.h>
#include "ds4.h"
#include "ds4_gpu.h"
#include "ds4_image.h"
/*
* Objective-C Metal glue for the C engine.
*
* The C code owns model semantics and graph scheduling. This file owns only
* Metal objects: device/queue/library setup, mmap-backed weight views, command
* batching, persistent tensors, scratch buffers, and thin wrappers around the
* kernel files in the metal directory. Keeping this boundary narrow makes the
* inference path readable from C while still using Objective-C where Metal
* requires it.
*/
enum {
DS4_METAL_TENSOR_Q4_0 = 2,
DS4_METAL_TENSOR_Q8_0 = 8,
DS4_METAL_TENSOR_Q2_K = 10,
DS4_METAL_TENSOR_Q4_K = 12,
DS4_METAL_TENSOR_Q5_K = 13,
DS4_METAL_TENSOR_Q6_K = 14,
DS4_METAL_TENSOR_Q8_K = 15,
DS4_METAL_TENSOR_IQ2_XXS = 16,
DS4_METAL_TENSOR_MXFP4 = 39,
};
@class DS4MetalQ4ExpertTable;
static id<MTLDevice> g_device;
static id<MTLCommandQueue> g_queue;
static id<MTLLibrary> g_library;
static id<MTLCommandBuffer> g_batch_cb;
static id<MTLComputeCommandEncoder> g_batch_enc;
static BOOL g_batch_encoder_concurrent;
static BOOL g_batch_has_work;
/* ---- Encoder timeline diagnostic (DS4_METAL_ENCODER_TIMELINE=<file>) ----
* Gives every batch dispatch group its own compute pass with stage-boundary
* GPU timestamps, so one decode token can be read as a per-kernel GPU
* timeline (durations and the gaps between them) without ending command
* buffers. Pass boundaries add a little serialization, so absolute numbers
* are an upper bound; the anatomy is what this exists for. Off by default. */
#define DS4_TIMELINE_NAME_MAX 80
#define DS4_TIMELINE_SAMPLES_PER_BUFFER 4096
typedef struct {
uintptr_t caller;
uint32_t n_dispatch;
uint32_t tg[3];
uint32_t tpt[3];
char kernel[DS4_TIMELINE_NAME_MAX];
} ds4_timeline_rec;
@interface DS4TimelineBatch : NSObject {
@public
NSMutableArray<id<MTLCounterSampleBuffer>> *samples;
ds4_timeline_rec *recs;
uint32_t count;
uint32_t cap;
uint64_t seq;
}
@end
@implementation DS4TimelineBatch
- (void)dealloc { free(recs); }
@end
static BOOL g_timeline_enabled;
static FILE *g_timeline_file;
static pthread_mutex_t g_timeline_mutex = PTHREAD_MUTEX_INITIALIZER;
static DS4TimelineBatch *g_timeline_batch;
static uint64_t g_timeline_seq;
static NSMutableDictionary<NSNumber *, NSString *> *g_timeline_pso_names;
static id<MTLCounterSet> g_timeline_counter_set;
static BOOL g_timeline_encoder_hooked;
static void ds4_gpu_timeline_note_pso_name(id pso, NSString *name) {
if (!g_timeline_enabled || !pso || !name) return;
pthread_mutex_lock(&g_timeline_mutex);
if (!g_timeline_pso_names) g_timeline_pso_names = [NSMutableDictionary dictionary];
g_timeline_pso_names[@((uintptr_t)pso)] = name;
pthread_mutex_unlock(&g_timeline_mutex);
}
@interface NSObject (DS4TimelineSwizzle)
- (id)ds4_tl_newComputePipelineStateWithFunction:(id<MTLFunction>)fn
error:(NSError * __autoreleasing *)error NS_RETURNS_RETAINED;
- (id)ds4_tl_newComputePipelineStateWithDescriptor:(MTLComputePipelineDescriptor *)descriptor
options:(MTLPipelineOption)options
reflection:(MTLAutoreleasedComputePipelineReflection *)reflection
error:(NSError * __autoreleasing *)error NS_RETURNS_RETAINED;
- (void)ds4_tl_setComputePipelineState:(id<MTLComputePipelineState>)pso;
- (void)ds4_tl_dispatchThreadgroups:(MTLSize)tg threadsPerThreadgroup:(MTLSize)tpt;
- (void)ds4_tl_dispatchThreads:(MTLSize)threads threadsPerThreadgroup:(MTLSize)tpt;
@end
static ds4_timeline_rec *ds4_gpu_timeline_current_rec(void) {
DS4TimelineBatch *b = g_timeline_batch;
if (!b || b->count == 0) return NULL;
return &b->recs[b->count - 1];
}
@implementation NSObject (DS4TimelineSwizzle)
- (id)ds4_tl_newComputePipelineStateWithFunction:(id<MTLFunction>)fn
error:(NSError * __autoreleasing *)error {
id pso = [self ds4_tl_newComputePipelineStateWithFunction:fn error:error];
ds4_gpu_timeline_note_pso_name(pso, [fn name]);
return pso;
}
- (id)ds4_tl_newComputePipelineStateWithDescriptor:(MTLComputePipelineDescriptor *)descriptor
options:(MTLPipelineOption)options
reflection:(MTLAutoreleasedComputePipelineReflection *)reflection
error:(NSError * __autoreleasing *)error {
id pso = [self ds4_tl_newComputePipelineStateWithDescriptor:descriptor
options:options
reflection:reflection
error:error];
ds4_gpu_timeline_note_pso_name(pso, [[descriptor computeFunction] name]);
return pso;
}
- (void)ds4_tl_setComputePipelineState:(id<MTLComputePipelineState>)pso {
[self ds4_tl_setComputePipelineState:pso];
ds4_timeline_rec *rec = ds4_gpu_timeline_current_rec();
if (!rec) return;
NSString *name = nil;
pthread_mutex_lock(&g_timeline_mutex);
if (g_timeline_pso_names) name = g_timeline_pso_names[@((uintptr_t)pso)];
pthread_mutex_unlock(&g_timeline_mutex);
const char *cname = name ? [name UTF8String] : "?";
if (rec->kernel[0] == '\0') {
snprintf(rec->kernel, sizeof(rec->kernel), "%s", cname);
} else if (strstr(rec->kernel, cname) == NULL) {
size_t used = strlen(rec->kernel);
if (used + 1 < sizeof(rec->kernel))
snprintf(rec->kernel + used, sizeof(rec->kernel) - used, "+%s", cname);
}
}
- (void)ds4_tl_dispatchThreadgroups:(MTLSize)tg threadsPerThreadgroup:(MTLSize)tpt {
[self ds4_tl_dispatchThreadgroups:tg threadsPerThreadgroup:tpt];
ds4_timeline_rec *rec = ds4_gpu_timeline_current_rec();
if (!rec) return;
if (rec->n_dispatch == 0) {
rec->tg[0] = (uint32_t)tg.width; rec->tg[1] = (uint32_t)tg.height; rec->tg[2] = (uint32_t)tg.depth;
rec->tpt[0] = (uint32_t)tpt.width; rec->tpt[1] = (uint32_t)tpt.height; rec->tpt[2] = (uint32_t)tpt.depth;
}
rec->n_dispatch++;
}
- (void)ds4_tl_dispatchThreads:(MTLSize)threads threadsPerThreadgroup:(MTLSize)tpt {
[self ds4_tl_dispatchThreads:threads threadsPerThreadgroup:tpt];
ds4_timeline_rec *rec = ds4_gpu_timeline_current_rec();
if (!rec) return;
if (rec->n_dispatch == 0) {
rec->tg[0] = (uint32_t)((threads.width + tpt.width - 1) / (tpt.width ? tpt.width : 1));
rec->tg[1] = (uint32_t)((threads.height + tpt.height - 1) / (tpt.height ? tpt.height : 1));
rec->tg[2] = (uint32_t)((threads.depth + tpt.depth - 1) / (tpt.depth ? tpt.depth : 1));
rec->tpt[0] = (uint32_t)tpt.width; rec->tpt[1] = (uint32_t)tpt.height; rec->tpt[2] = (uint32_t)tpt.depth;
}
rec->n_dispatch++;
}
@end
static void ds4_gpu_timeline_swizzle(Class cls, SEL orig, SEL repl) {
Method m_orig = class_getInstanceMethod(cls, orig);
Method m_repl = class_getInstanceMethod(cls, repl);
if (!m_orig || !m_repl) {
fprintf(stderr, "ds4: timeline hook missing for %s\n", sel_getName(orig));
return;
}
if (class_addMethod(cls, orig, method_getImplementation(m_repl),
method_getTypeEncoding(m_orig))) {
class_replaceMethod(cls, repl, method_getImplementation(m_orig),
method_getTypeEncoding(m_orig));
} else {
method_exchangeImplementations(m_orig, m_repl);
}
}
/* Called once the device exists: opens the log and hooks pipeline creation so
* every pipeline made afterwards has a kernel name. */
static void ds4_gpu_timeline_probe(id<MTLDevice> device) {
static int probed;
if (probed) return;
probed = 1;
const char *path = getenv("DS4_METAL_ENCODER_TIMELINE");
if (!path || !path[0] || !device) return;
g_timeline_file = fopen(path, "a");
if (!g_timeline_file) {
fprintf(stderr, "ds4: cannot open encoder timeline %s\n", path);
return;
}
for (id<MTLCounterSet> cs in [device counterSets]) {
if ([[cs name] isEqualToString:MTLCommonCounterSetTimestamp]) g_timeline_counter_set = cs;
}
if (!g_timeline_counter_set ||
![device supportsCounterSampling:MTLCounterSamplingPointAtStageBoundary]) {
fprintf(stderr, "ds4: encoder timeline unsupported on this device\n");
fclose(g_timeline_file);
g_timeline_file = NULL;
return;
}
g_timeline_enabled = YES;
Class dev_cls = object_getClass(device);
ds4_gpu_timeline_swizzle(dev_cls,
@selector(newComputePipelineStateWithFunction:error:),
@selector(ds4_tl_newComputePipelineStateWithFunction:error:));
ds4_gpu_timeline_swizzle(dev_cls,
@selector(newComputePipelineStateWithDescriptor:options:reflection:error:),
@selector(ds4_tl_newComputePipelineStateWithDescriptor:options:reflection:error:));
fprintf(g_timeline_file, "# ds4 encoder timeline; slide=0x%llx pid=%d\n",
(unsigned long long)_dyld_get_image_vmaddr_slide(0), (int)getpid());
fprintf(g_timeline_file, "# B <seq> <n_encoders> <gpu_start_ns> <gpu_end_ns>\n");
fprintf(g_timeline_file, "# E <seq> <idx> <start_ns> <end_ns> <dur_us> <gap_us> <caller_unslid> <n_dispatch> <tg> <tpt> <kernel>\n");
fflush(g_timeline_file);
fprintf(stderr, "ds4: encoder timeline -> %s\n", path);
}
static void ds4_gpu_timeline_hook_encoder(id<MTLComputeCommandEncoder> enc) {
if (g_timeline_encoder_hooked || !enc) return;
g_timeline_encoder_hooked = YES;
Class cls = object_getClass(enc);
ds4_gpu_timeline_swizzle(cls, @selector(setComputePipelineState:),
@selector(ds4_tl_setComputePipelineState:));
ds4_gpu_timeline_swizzle(cls, @selector(dispatchThreadgroups:threadsPerThreadgroup:),
@selector(ds4_tl_dispatchThreadgroups:threadsPerThreadgroup:));
ds4_gpu_timeline_swizzle(cls, @selector(dispatchThreads:threadsPerThreadgroup:),
@selector(ds4_tl_dispatchThreads:threadsPerThreadgroup:));
}
static void ds4_gpu_timeline_resolve(DS4TimelineBatch *b, id<MTLCommandBuffer> cb) {
if (!b || !g_timeline_file) return;
pthread_mutex_lock(&g_timeline_mutex);
fprintf(g_timeline_file, "B %llu %u %.0f %.0f\n", (unsigned long long)b->seq, b->count,
cb.GPUStartTime * 1e9, cb.GPUEndTime * 1e9);
uint64_t prev_end = 0;
for (uint32_t i = 0; i < b->count; i++) {
const uint32_t sb_index = i / (DS4_TIMELINE_SAMPLES_PER_BUFFER / 2);
const uint32_t slot = (i % (DS4_TIMELINE_SAMPLES_PER_BUFFER / 2)) * 2u;
uint64_t start = 0, end = 0;
if (sb_index < [b->samples count]) {
NSData *d = [b->samples[sb_index] resolveCounterRange:NSMakeRange(slot, 2)];
if (d && [d length] >= 2 * sizeof(uint64_t)) {
const uint64_t *ts = (const uint64_t *)[d bytes];
start = ts[0];
end = ts[1];
}
}
const ds4_timeline_rec *r = &b->recs[i];
const double dur_us = (end >= start) ? (double)(end - start) / 1000.0 : -1.0;
const double gap_us = (prev_end && start >= prev_end) ? (double)(start - prev_end) / 1000.0 : 0.0;
fprintf(g_timeline_file, "E %llu %u %llu %llu %.2f %.2f 0x%llx %u %ux%ux%u %ux%ux%u %s\n",
(unsigned long long)b->seq, i,
(unsigned long long)start, (unsigned long long)end, dur_us, gap_us,
(unsigned long long)(r->caller - (uintptr_t)_dyld_get_image_vmaddr_slide(0)),
r->n_dispatch, r->tg[0], r->tg[1], r->tg[2], r->tpt[0], r->tpt[1], r->tpt[2],
r->kernel[0] ? r->kernel : "?");
if (end) prev_end = end;
}
fflush(g_timeline_file);
pthread_mutex_unlock(&g_timeline_mutex);
}
/* Attach to a freshly created batch command buffer. */
static void ds4_gpu_timeline_attach(id<MTLCommandBuffer> cb) {
if (!g_timeline_enabled || !cb) return;
DS4TimelineBatch *b = [DS4TimelineBatch new];
b->samples = [NSMutableArray array];
b->cap = 1024;
b->recs = calloc(b->cap, sizeof(ds4_timeline_rec));
b->count = 0;
b->seq = ++g_timeline_seq;
g_timeline_batch = b;
[cb addCompletedHandler:^(id<MTLCommandBuffer> done) {
ds4_gpu_timeline_resolve(b, done);
}];
}
static id<MTLComputeCommandEncoder> ds4_gpu_timeline_new_encoder(
id<MTLCommandBuffer> cb, BOOL concurrent, uintptr_t caller) {
DS4TimelineBatch *b = g_timeline_batch;
if (!b) return nil;
const uint32_t per_buffer = DS4_TIMELINE_SAMPLES_PER_BUFFER / 2;
const uint32_t sb_index = b->count / per_buffer;
if (sb_index >= [b->samples count]) {
MTLCounterSampleBufferDescriptor *d = [MTLCounterSampleBufferDescriptor new];
d.counterSet = g_timeline_counter_set;
d.storageMode = MTLStorageModeShared;
d.sampleCount = DS4_TIMELINE_SAMPLES_PER_BUFFER;
NSError *error = nil;
id<MTLCounterSampleBuffer> sb = [g_device newCounterSampleBufferWithDescriptor:d error:&error];
if (!sb) {
fprintf(stderr, "ds4: timeline sample buffer failed: %s\n",
[[error localizedDescription] UTF8String]);
return nil;
}
[b->samples addObject:sb];
}
if (b->count == b->cap) {
b->cap *= 2;
b->recs = realloc(b->recs, (size_t)b->cap * sizeof(ds4_timeline_rec));
}
const uint32_t slot = (b->count % per_buffer) * 2u;
MTLComputePassDescriptor *pd = [MTLComputePassDescriptor computePassDescriptor];
pd.dispatchType = concurrent ? MTLDispatchTypeConcurrent : MTLDispatchTypeSerial;
MTLComputePassSampleBufferAttachmentDescriptor *att = pd.sampleBufferAttachments[0];
att.sampleBuffer = b->samples[sb_index];
att.startOfEncoderSampleIndex = slot;
att.endOfEncoderSampleIndex = slot + 1u;
id<MTLComputeCommandEncoder> enc = [cb computeCommandEncoderWithDescriptor:pd];
if (!enc) return nil;
ds4_timeline_rec *rec = &b->recs[b->count++];
memset(rec, 0, sizeof(*rec));
rec->caller = caller;
ds4_gpu_timeline_hook_encoder(enc);
return enc;
}
/* Blit encoder with timeline sampling (blit passes are otherwise invisible
* to the encoder timeline and show up as gaps). */
static id<MTLBlitCommandEncoder> ds4_gpu_blit_encoder(id<MTLCommandBuffer> cb, const char *label,
uint32_t n_ops) {
DS4TimelineBatch *b = g_timeline_batch;
if (!g_timeline_enabled || !b || cb != g_batch_cb) return [cb blitCommandEncoder];
const uint32_t per_buffer = DS4_TIMELINE_SAMPLES_PER_BUFFER / 2;
const uint32_t sb_index = b->count / per_buffer;
if (sb_index >= [b->samples count]) {
MTLCounterSampleBufferDescriptor *d = [MTLCounterSampleBufferDescriptor new];
d.counterSet = g_timeline_counter_set;
d.storageMode = MTLStorageModeShared;
d.sampleCount = DS4_TIMELINE_SAMPLES_PER_BUFFER;
NSError *error = nil;
id<MTLCounterSampleBuffer> sb = [g_device newCounterSampleBufferWithDescriptor:d error:&error];
if (!sb) return [cb blitCommandEncoder];
[b->samples addObject:sb];
}
if (b->count == b->cap) {
b->cap *= 2;
b->recs = realloc(b->recs, (size_t)b->cap * sizeof(ds4_timeline_rec));
}
const uint32_t slot = (b->count % per_buffer) * 2u;
MTLBlitPassDescriptor *pd = [MTLBlitPassDescriptor blitPassDescriptor];
MTLBlitPassSampleBufferAttachmentDescriptor *att = pd.sampleBufferAttachments[0];
att.sampleBuffer = b->samples[sb_index];
att.startOfEncoderSampleIndex = slot;
att.endOfEncoderSampleIndex = slot + 1u;
id<MTLBlitCommandEncoder> enc = [cb blitCommandEncoderWithDescriptor:pd];
if (!enc) return [cb blitCommandEncoder];
ds4_timeline_rec *rec = &b->recs[b->count++];
memset(rec, 0, sizeof(*rec));
rec->caller = (uintptr_t)__builtin_return_address(0);
rec->n_dispatch = n_ops;
snprintf(rec->kernel, sizeof(rec->kernel), "blit:%s", label ? label : "?");
return enc;
}
static void ds4_gpu_parallel_ffn_reset_state(BOOL close_encoder);
static NSMutableArray<id<MTLCommandBuffer>> *g_pending_cbs;
static id<MTLSharedEvent> g_selected_readback_event;
static uint64_t g_selected_readback_event_value;
static id<MTLComputePipelineState> g_set_rows_f32_i32_pipeline;
static id<MTLComputePipelineState> g_get_rows_f32_pipeline;
static id<MTLComputePipelineState> g_get_rows_f16_pipeline;
static id<MTLComputePipelineState> g_get_rows_i32_pipeline;
static id<MTLComputePipelineState> g_get_rows_q8_0_pipeline;
static id<MTLComputePipelineState> g_get_rows_q4_0_pipeline;
static id<MTLComputePipelineState> g_get_rows_q4_K_pipeline;
static id<MTLComputePipelineState> g_repeat_f32_pipeline;
static id<MTLComputePipelineState> g_concat_pipeline;
static id<MTLComputePipelineState> g_cpy_f32_f32_pipeline;
static id<MTLComputePipelineState> g_cpy_f32_f16_pipeline;
static id<MTLComputePipelineState> g_cpy_contig_f32_f16_pipeline;
static id<MTLComputePipelineState> g_cpy_f16_f32_pipeline;
static id<MTLComputePipelineState> g_cpy_f16_f16_pipeline;
static id<MTLComputePipelineState> g_cpy_contig_f16_f32_pipeline;
static id<MTLComputePipelineState> g_cpy_contig_f16_f16_pipeline;
static id<MTLComputePipelineState> g_flash_kv_stage_f16_pipeline;
static id<MTLComputePipelineState> g_swiglu_pipeline;
static id<MTLComputePipelineState> g_swiglu_flat_pipeline;
static id<MTLComputePipelineState> g_add_pipeline;
static id<MTLComputePipelineState> g_add2_pipeline;
static id<MTLComputePipelineState> g_add3_pipeline;
static id<MTLComputePipelineState> g_moe_sum6_pipeline;
static id<MTLComputePipelineState> g_moe_sum8_pipeline;
static id<MTLComputePipelineState> g_mul_pipeline;
static id<MTLComputePipelineState> g_rms_norm_pipeline;
static id<MTLComputePipelineState> g_rms_norm_plain_pipeline;
static id<MTLComputePipelineState> g_add_rms_norm_pipeline;
static id<MTLComputePipelineState> g_rms_norm_scale_pipeline;
static id<MTLComputePipelineState> g_dsv4_qkv_rms_norm_pipeline;
static id<MTLComputePipelineState> g_dsv4_head_rms_norm_rope_tail_pipeline;
static bool g_use_dsv4_head_rms_norm_rope_tail_pipeline;
static id<MTLComputePipelineState> g_hc_split_sinkhorn_pipeline;
static id<MTLComputePipelineState> g_hc_split_weighted_sum_pipeline;
static id<MTLComputePipelineState> g_hc_split_weighted_sum_norm_pipeline;
static id<MTLComputePipelineState> g_dsv4_hc_producer_pre_norm_pipeline;
static id<MTLComputePipelineState> g_dsv4_hc_expand_producer_pre_norm_pipeline;
static NSMutableDictionary<NSString *, id<MTLBuffer>> *g_dsv4_hc_barrier_cache;
static NSMutableDictionary<NSString *, NSNumber *> *g_dsv4_hc_barrier_gen;
static id<MTLComputePipelineState> g_hc_weighted_sum_pipeline;
static id<MTLComputePipelineState> g_output_hc_weights4_pipeline;
static uint32_t g_test_flags;
static id<MTLComputePipelineState> g_hc_expand_pipeline;
static id<MTLComputePipelineState> g_unary_sigmoid_pipeline;
static id<MTLComputePipelineState> g_unary_silu_pipeline;
static id<MTLComputePipelineState> g_unary_softplus_pipeline;
static id<MTLComputePipelineState> g_unary_sqrt_pipeline;
static id<MTLComputePipelineState> g_unary_clamp_pipeline;
static id<MTLComputePipelineState> g_unary_scale_pipeline;
static id<MTLComputePipelineState> g_unary_fill_pipeline;
static id<MTLComputePipelineState> g_unary_fill_f16_pipeline;
static id<MTLComputePipelineState> g_bin_mul_scalar_pipeline;
static id<MTLComputePipelineState> g_bin_div_row_pipeline;
static id<MTLComputePipelineState> g_moe_mul_mv_id_iq2_xxs_pipeline;
static id<MTLComputePipelineState> g_moe_mul_mv_id_iq2_xxs_pair_pipeline;
static id<MTLComputePipelineState> g_moe_mul_mv_id_iq2_xxs_pair_swiglu_pipeline;
static id<MTLComputePipelineState> g_moe_mul_mv_id_iq2_xxs_pair_swiglu_pack2_pipeline;
static id<MTLComputePipelineState> g_moe_mul_mv_id_q2_k_pipeline;
static id<MTLComputePipelineState> g_moe_mul_mv_id_q2_k_sum6_pipeline;
static id<MTLComputePipelineState> g_moe_mul_mv_id_iq2_xxs_sum6_pipeline;
static id<MTLComputePipelineState> g_moe_mul_mv_id_q4_k_pipeline;
static id<MTLComputePipelineState> g_moe_mul_mv_id_q4_k_pair_pipeline;
static id<MTLComputePipelineState> g_moe_mul_mv_id_q4_k_pair_swiglu_pipeline;
static id<MTLComputePipelineState> g_moe_mul_mv_id_q4_k_sum6_pipeline;
static id<MTLComputePipelineState> g_moe_mul_mv_group_q4_k_pair_swiglu_pipeline;
static id<MTLComputePipelineState> g_moe_mul_mv_group_q4_k_sum6_pipeline;
static id<MTLComputePipelineState> g_moe_mul_mv_group6_q4_k_pair_swiglu_pipeline;
static id<MTLComputePipelineState> g_moe_mul_mv_group6_q4_k_sum6_pipeline;
static id<MTLComputePipelineState> g_moe_mul_mv_group8_q4_k_pair_swiglu_pipeline;
static id<MTLComputePipelineState> g_moe_mul_mv_group8_q4_k_sum6_pipeline;
static id<MTLComputePipelineState> g_moe_mul_mv_group24_q4_k_id_pipeline;
static id<MTLComputePipelineState> g_moe_mul_mv_group24_q4_k_sum6_pipeline;
static id<MTLComputePipelineState> g_moe_mul_mv_slots6_iq2_xxs_pair_swiglu_pipeline;
static id<MTLComputePipelineState> g_moe_mul_mv_slots6_q2_k_sum6_pipeline;
static id<MTLComputePipelineState> g_moe_mul_mv_slots6_q4_k_pair_swiglu_pipeline;
static id<MTLComputePipelineState> g_moe_mul_mv_slots6_q4_k_sum6_pipeline;
static id<MTLComputePipelineState> g_moe_mul_mv_id_mxfp4_pipeline;
static id<MTLComputePipelineState> g_moe_mul_mv_id_mxfp4_pair_swiglu_pipeline;
static id<MTLComputePipelineState> g_moe_mul_mv_id_mxfp4_sum6_pipeline;
static id<MTLComputePipelineState> g_moe_mul_mv_id_mxfp4_pair_swiglu_pipeline_nsg1;
static id<MTLComputePipelineState> g_moe_mul_mv_id_mxfp4_sum6_pipeline_nsg1;
static id<MTLComputePipelineState> g_moe_mul_mv_id_mxfp4_pair_swiglu_pipeline_nsg1_tg_multiple;
static id<MTLComputePipelineState> g_moe_mul_mv_id_mxfp4_sum6_pipeline_nsg1_tg_multiple;
static id<MTLComputePipelineState> g_moe_mul_mv_id_mxfp4_pair_swiglu_fixed_route_pipeline_nsg1;
static id<MTLComputePipelineState> g_moe_mul_mv_id_mxfp4_sum6_fixed_route_pipeline_nsg1;
static id<MTLComputePipelineState> g_moe_mul_mv_id_mxfp4_sum6_fixed_route_full_rows_pipeline_nsg1;
static id<MTLComputePipelineState> g_moe_mul_mv_id_mxfp4_pair_swiglu_fixed_route_static_pipeline_nsg1;
static id<MTLComputePipelineState> g_moe_mul_mv_id_mxfp4_sum6_fixed_route_full_rows_static_pipeline_nsg1;
static id<MTLComputePipelineState> g_moe_mul_mv_id_mxfp4_pair_swiglu_tp_static_pipeline_nsg1;
static id<MTLComputePipelineState> g_moe_mul_mv_id_mxfp4_sum6_tp_full_rows_static_pipeline_nsg1;
static id<MTLComputePipelineState> g_moe_mul_mv_slots6_mxfp4_pair_swiglu_pipeline;
static id<MTLComputePipelineState> g_moe_mul_mv_slots6_mxfp4_sum6_pipeline;
static id<MTLComputePipelineState> g_moe_mul_mv_addr_iq2_xxs_pair_swiglu_pipeline;
static id<MTLComputePipelineState> g_moe_mul_mv_addr_iq2_xxs_pipeline;
static id<MTLComputePipelineState> g_moe_mul_mv_addr_q2_k_sum6_pipeline;
static id<MTLComputePipelineState> g_moe_mul_mv_addr_iq2_xxs_pair_swiglu_masked_pipeline;
static id<MTLComputePipelineState> g_moe_mul_mv_addr_q2_k_sum6_masked_pipeline;
static id<MTLComputePipelineState> g_moe_stream_expert_cache_validate_pipeline;
static id<MTLComputePipelineState> g_moe_q4_gather_slots6_pipeline;
static id<MTLComputePipelineState> g_moe_mul_mv_table_q4_k_pair_swiglu_pipeline;
static id<MTLComputePipelineState> g_moe_mul_mv_table_q4_k_sum6_pipeline;
static id<MTLComputePipelineState> g_moe_mul_mv_addr_q4_k_pair_swiglu_pipeline;
static id<MTLComputePipelineState> g_moe_mul_mv_addr_q4_k_sum6_pipeline;
static id<MTLArgumentEncoder> g_moe_table_q4_pair_gate_encoder;
static id<MTLArgumentEncoder> g_moe_table_q4_pair_up_encoder;
static id<MTLArgumentEncoder> g_moe_table_q4_sum_down_encoder;
static id<MTLComputePipelineState> g_rope_tail_batch_pipeline;
static id<MTLComputePipelineState> g_rope_tail_inplace_pair_pipeline;
static id<MTLComputePipelineState> g_rope_tail_inplace_pair_shared4_pipeline;
static id<MTLComputePipelineState> g_rope_tail_inplace_pair_affine_pipeline;
static id<MTLComputePipelineState> g_dsv4_fp8_kv_quantize_pipeline;
static id<MTLComputePipelineState> g_dsv4_indexer_qat_pipeline;
static id<MTLComputePipelineState> g_dsv4_kv_fp8_store_pipeline;
static id<MTLComputePipelineState> g_dsv4_kv_rope_fp8_store_pipeline;
static id<MTLComputePipelineState> g_dsv4_ratio4_shift_pipeline;
static id<MTLComputePipelineState> g_dsv4_compressor_pack_ratio4_pipeline;
static id<MTLComputePipelineState> g_dsv4_compressor_pack_ratio4_decode_ggml_pipeline;
static id<MTLComputePipelineState> g_dsv4_compressor_exact_softmax_product_pipeline;
static id<MTLComputePipelineState> g_dsv4_compressor_exact_pool_ratio4_pipeline;
static id<MTLComputePipelineState> g_dsv4_softmax_pool_ratio4_direct_pipeline;
static id<MTLComputePipelineState> g_dsv4_softmax_pool_pipeline;
static id<MTLComputePipelineState> g_soft_max_f32_pipeline;
static id<MTLComputePipelineState> g_soft_max_f32_4_pipeline;
static id<MTLComputePipelineState> g_argsort_f32_i32_desc_pipeline;
static id<MTLComputePipelineState> g_argsort_merge_f32_i32_desc_pipeline;
static id<MTLComputePipelineState> g_sum_rows_f32_f32_pipeline;
static id<MTLComputePipelineState> g_dsv4_topk_mask_pipeline;
static id<MTLComputePipelineState> g_dsv4_topk_mask_scatter_pipeline;
static id<MTLComputePipelineState> g_dsv4_indexer_weighted_sum_pipeline;
static id<MTLComputePipelineState> g_dsv4_indexer_score_one_direct_pipeline;
static id<MTLComputePipelineState> g_dsv4_compressor_store_one_pipeline;
static id<MTLComputePipelineState> g_dsv4_sort_i32_rows_asc_pipeline;
static id<MTLComputePipelineState> g_dsv4_indexed_attention_heads8_pipeline;
static id<MTLComputePipelineState> g_dsv4_indexed_attention_heads8_rb16_pipeline;
static id<MTLComputePipelineState> g_dsv4_indexed_attention_heads16_dual_pipeline;
static id<MTLComputePipelineState> g_dsv4_indexed_attention_heads8_split_pipeline;
static id<MTLComputePipelineState> g_dsv4_indexed_attention_heads8_split_reduce_pipeline;
static id<MTLComputePipelineState> g_dsv4_softplus_sqrt_pipeline;
static id<MTLComputePipelineState> g_dsv4_router_finalize_one_pipeline;
static id<MTLComputePipelineState> g_dsv4_router_finalize_one_simd_pipeline;
static id<MTLComputePipelineState> g_dsv4_router_finalize_weights_one_simd_pipeline;
static id<MTLComputePipelineState> g_dsv4_router_transform_finalize_weights_one_simd_pipeline;
static id<MTLComputePipelineState> g_dsv4_router_select_visual_batch_pipeline;
static NSCache<NSString *, id<MTLBuffer>> *g_dsv4_completion_cache;
static __weak id<MTLBuffer> g_dsv4_hc_producer_last_mix_buffer;
static NSUInteger g_dsv4_hc_producer_last_mix_offset;
static id<MTLBuffer> g_dsv4_hc_producer_last_completion;
static id<MTLComputePipelineState> g_dsv4_router_weights_one_pipeline;
static id<MTLComputePipelineState> g_glm_router_select_one_pipeline;
static id<MTLComputePipelineState> g_glm_kv_lora_rms_norm_pipeline;
static id<MTLComputePipelineState> g_glm_k_b_project_pipeline;
static id<MTLComputePipelineState> g_glm_store_compact_kv_pipeline;
static id<MTLComputePipelineState> g_glm_qkv_norm_store_compact_kv_pipeline;
static id<MTLComputePipelineState> g_glm_store_indexer_k_pipeline;
static id<MTLComputePipelineState> g_glm53_indexer_pool_update_pipeline;
static id<MTLComputePipelineState> g_glm_build_kv_cache_pipeline;
static id<MTLComputePipelineState> g_glm_build_kv_cache_decode_group4_pipeline;
static id<MTLComputePipelineState> g_glm_build_kv_cache_flash_pipeline;
static id<MTLComputePipelineState> g_glm_attention_full_pipeline;
static id<MTLComputePipelineState> g_glm_fill_selected_range_pipeline;
static id<MTLComputePipelineState> g_glm_fill_selected_range_batch_pipeline;
static id<MTLComputePipelineState> g_glm53_expand_pool_selection_pipeline;
static id<MTLComputePipelineState> g_glm_indexer_rope_tail_pipeline;
static id<MTLComputePipelineState> g_glm_indexer_score_one_pipeline;
static id<MTLComputePipelineState> g_glm_indexer_score_one_direct_pipeline;
static id<MTLComputePipelineState> g_glm_indexer_score_one_wide_pipeline;
static id<MTLComputePipelineState> g_glm_indexer_scores_batch_pipeline;
static id<MTLComputePipelineState> g_glm_indexer_scores_tiled_pipeline;
static id<MTLComputePipelineState> g_glm_indexer_scores_tiled_f32_pipeline;
static id<MTLComputePipelineState> g_glm_qk_lowrank_pipeline;
static id<MTLComputePipelineState> g_glm_qk_lowrank_glm52_pipeline;
static id<MTLComputePipelineState> g_glm_qk_lowrank_glm52_sg_pipeline;
static id<MTLComputePipelineState> g_glm_qk_lowrank_batch_pipeline;
static id<MTLComputePipelineState> g_glm_qk_lowrank_batch_glm52_t4_pipeline;
static id<MTLComputePipelineState> g_glm_value_project_q8_0_pipeline;
static id<MTLComputePipelineState> g_glm_value_project_q8_0_batch_heads_pipeline;
static id<MTLComputePipelineState> g_glm_value_project_q8_0_batch_heads_mma_pipeline;
static id<MTLComputePipelineState> g_glm_attention_indexed_decode_pipeline;
static id<MTLComputePipelineState> g_glm_attention_indexed_decode_split_group8_partial_pipeline;
static id<MTLComputePipelineState> g_glm_attention_indexed_decode_split_group8_partial_valid_fullheads_pipeline;
static id<MTLComputePipelineState> g_glm_attention_indexed_decode_split_group8_reduce_pipeline;
static id<MTLComputePipelineState> g_glm_attention_indexed_decode_split_group8_reduce16_pipeline;
static id<MTLComputePipelineState> g_glm_attention_indexed_batch_pipeline;
static id<MTLComputePipelineState> g_glm_attention_indexed_batch_group2_pipeline;
static id<MTLComputePipelineState> g_glm_attention_indexed_batch_q2_group4_pipeline;
static id<MTLComputePipelineState> g_glm_attention_indexed_batch_group8_pipeline;
static id<MTLComputePipelineState> g_glm_attention_indexed_batch_lora_group8_vec_pipeline;
static id<MTLComputePipelineState> g_glm_attention_indexed_batch_lora_group8_vec_valid_pipeline;
static id<MTLComputePipelineState> g_glm_attention_indexed_batch_lora_group8_vec_valid_fullheads_pipeline;
static id<MTLComputePipelineState> g_glm_attention_indexed_batch_lora_group8_vec_causal_pipeline;
static id<MTLComputePipelineState> g_glm_attention_indexed_batch_lora_group8_vec_causal_fullheads_pipeline;
static id<MTLComputePipelineState> g_glm_q4_k_pair_swiglu_f32_pipeline;
static id<MTLComputePipelineState> g_glm_q4_k_pair_swiglu2_f32_pipeline;
static id<MTLComputePipelineState> g_glm_q4_k_pair_swiglu4_f32_pipeline;
static id<MTLComputePipelineState> g_glm_q4_k_pair_swiglu2_mapped_f32_pipeline;
static id<MTLComputePipelineState> g_glm_q4_k_pair_swiglu2_mapped_row_f32_pipeline;
static id<MTLComputePipelineState> g_glm_q2_k_pair_swiglu_f32_pipeline;
static id<MTLComputePipelineState> g_glm_q2_k_addr_pair_swiglu2_f32_pipeline;
static id<MTLComputePipelineState> g_glm_q2_k_addr_pair_swiglu2_masked_f32_pipeline;
static id<MTLComputePipelineState> g_glm_q4_k_addr_pair_swiglu_f32_pipeline;
static id<MTLComputePipelineState> g_glm_q4_k_addr_pair_swiglu_masked_f32_pipeline;
static id<MTLComputePipelineState> g_glm_q2_k_down_f32_pipeline;
static id<MTLComputePipelineState> g_glm_q4_k_down_f32_pipeline;
static id<MTLComputePipelineState> g_glm_q2_k_addr_down_f32_pipeline;
static id<MTLComputePipelineState> g_glm_q4_k_addr_down_f32_pipeline;
static id<MTLComputePipelineState> g_glm_q5_k_pair_swiglu_f32_pipeline;
static id<MTLComputePipelineState> g_glm_q5_k_pair_swiglu_mapped_f32_pipeline;
static id<MTLComputePipelineState> g_glm_q5_k_pair_swiglu_mapped_row_f32_pipeline;
static id<MTLComputePipelineState> g_glm_q5_k_down_f32_pipeline;
static id<MTLComputePipelineState> g_glm_q6_k_down_f32_pipeline;
static id<MTLComputePipelineState> g_dsv4_router_weights_batch_pipeline;
static id<MTLComputePipelineState> g_dsv4_hc_expand4_pipeline;
static NSMutableDictionary<NSString *, id<MTLComputePipelineState>> *g_pipeline_cache;
enum {
DS4_METAL_DECODE_PIPELINE_FAST_CACHE_SLOTS = 64,
DS4_METAL_DECODE_PIPELINE_FAST_NAME_BYTES = 96,
};
/*
* Sentinel nxpsg stored by the single-constant ds4_gpu_get_mul_mv_pipeline
* path, which has no second SIMD-group constant. The extended
* ds4_gpu_get_mul_mv_ext_pipeline path derives nxpsg as a positive divisor of
* the 32-row output tile, so INT16_MIN can never collide with a real value.
*/
#define DS4_METAL_DECODE_PIPELINE_FAST_NXPSG_NONE INT16_MIN
_Static_assert(
(DS4_METAL_DECODE_PIPELINE_FAST_CACHE_SLOTS &
(DS4_METAL_DECODE_PIPELINE_FAST_CACHE_SLOTS - 1u)) == 0,
"decode pipeline fast cache slot count must be a power of two");
typedef struct {
id<MTLComputePipelineState> __strong pipeline;
uint64_t hash;
int16_t nsg;
int16_t nxpsg;
uint16_t name_len;
bool used;
char name[DS4_METAL_DECODE_PIPELINE_FAST_NAME_BYTES];
} ds4_gpu_decode_pipeline_fast_cache_entry;
static ds4_gpu_decode_pipeline_fast_cache_entry
g_decode_pipeline_fast_cache[DS4_METAL_DECODE_PIPELINE_FAST_CACHE_SLOTS];
/* Command encoding and the existing NSMutableDictionary pipeline cache are
* backend-serialized; this hot-path mirror intentionally shares that model. */
static bool g_decode_pipeline_fast_lookup_active;
static uint32_t g_decode_pipeline_fast_cache_entries;
static NSMutableDictionary<NSString *, id<MTLBuffer>> *g_model_buffer_cache;
static NSMutableDictionary<NSString *, DS4MetalQ4ExpertTable *> *g_q4_expert_table_cache;
static NSMutableDictionary<NSString *, id> *g_q4_expert_layer_residency_cache;
static NSMutableArray<id<MTLBuffer>> *g_transient_buffers;
static id g_model_residency_set;
typedef struct {
id<MTLBuffer> __strong mask;
id<MTLBuffer> __strong blk;
NSUInteger mask_bytes;
NSUInteger blk_bytes;
uint32_t kind;
uint32_t n_tokens;
uint32_t n_comp;
uint32_t n_keys;
uint32_t window;
uint32_t ratio;
uint32_t nqptg;
uint32_t ncpsg;
bool has_kvpad;
bool bc_mask;
bool valid;
bool blk_ready;
} ds4_gpu_zero_prefix_prefill_mask_cache_entry;
enum {
DS4_GPU_PREFILL_MASK_CACHE_RAW = 1,
DS4_GPU_PREFILL_MASK_CACHE_RATIO4 = 2,
DS4_GPU_PREFILL_MASK_CACHE_RATIO128 = 3,
DS4_GPU_PREFILL_MASK_CACHE_SLOTS = 3,
};
static ds4_gpu_zero_prefix_prefill_mask_cache_entry
g_zero_prefix_prefill_mask_cache[DS4_GPU_PREFILL_MASK_CACHE_SLOTS];
static void ds4_gpu_invalidate_zero_prefix_prefill_block_maps(void);
static id<MTLBuffer> g_flash_attn_mask_buffer;
static id<MTLBuffer> g_flash_attn_zero_mask_buffer;
static id<MTLBuffer> g_flash_attn_pad_buffer;
static id<MTLBuffer> g_flash_attn_tmp_buffer;
static id<MTLBuffer> g_flash_attn_blk_buffer;
static id<MTLBuffer> g_flash_attn_ring_buffer;
static id<MTLBuffer> g_flash_attn_kv_buffer;
static id<MTLBuffer> g_glm_flash_attn_mask_buffer;
static id<MTLBuffer> g_compressor_pool_kv_buffer;
static id<MTLBuffer> g_compressor_pool_score_buffer;
static id<MTLBuffer> g_compressor_pool_score_cont_buffer;
static id<MTLBuffer> g_compressor_pool_softmax_buffer;
static id<MTLBuffer> g_compressor_pool_product_buffer;
static id<MTLBuffer> g_compressor_store_ape_buffer;
static id<MTLBuffer> g_compressor_store_score_buffer;
static id<MTLBuffer> g_embed_rows_buffer;
static id<MTLBuffer> g_router_selection_buffer;
static id<MTLBuffer> g_router_weight_sum_buffer;
static id<MTLBuffer> g_indexer_head_scores_buffer;
static id<MTLBuffer> g_indexer_topk_buffer;
static id<MTLBuffer> g_topk_select_buffer;
static NSUInteger g_topk_select_bytes;
static uint32_t g_topk_select_parity;
static id<MTLBuffer> g_indexed_topk_buffer;
static id<MTLBuffer> g_f16_round_scratch_buffer;
static id<MTLBuffer> g_q8_prefill_scratch_buffer;
static id<MTLBuffer> g_qwen4_conv_halo_buffer;
static NSUInteger g_qwen4_conv_halo_bytes;
static id<MTLBuffer> g_raw_store_round_buffer;
static id<MTLBuffer> g_moe_gate_scratch_buffer;
static id<MTLBuffer> g_moe_down_scratch_buffer;
static id<MTLBuffer> g_moe_id_map_buffer;
static id<MTLBuffer> g_moe_packed_rhs_buffer;
static NSUInteger g_moe_packed_rhs_bytes;
static id<MTLBuffer> g_moe_q4_gate_slots_buffer;
static id<MTLBuffer> g_moe_q4_up_slots_buffer;
static id<MTLBuffer> g_moe_q4_down_slots_buffer;
static id<MTLBuffer> g_attn_out_group_ids_buffer;
static int g_model_fd = -1;
static const void *g_model_map_ptr;
static uint64_t g_model_map_size;
static uint64_t g_model_mapped_offset;
static uint64_t g_model_mapped_size;
static uint64_t g_model_mapped_max_tensor_bytes;
static uint64_t g_tensor_alloc_live_bytes;
static uint64_t g_tensor_alloc_peak_bytes;
static pthread_mutex_t g_tensor_mu = PTHREAD_MUTEX_INITIALIZER;
static uintptr_t *g_tensor_live_slots;
static size_t g_tensor_live_cap;
static size_t g_tensor_live_count;
static size_t g_tensor_live_tombs;
static uint64_t g_model_wrap_count;
static uint64_t g_model_wrap_bytes;
static uint64_t g_model_wrap_max_bytes;
static uint64_t g_model_buffer_cache_bytes;
static uint64_t g_model_buffer_cache_evictions;
static int g_model_buffer_cache_over_limit;
static uint64_t g_stream_expert_cache_bytes;
static uint64_t g_stream_expert_cache_expert_bytes;
static uint32_t g_stream_expert_cache_entry_count;
static uint32_t g_stream_expert_cache_budget_override;
static uint64_t g_stream_expert_cache_hits;
static uint64_t g_stream_expert_cache_misses;
static uint64_t g_stream_expert_cache_evictions;
static uint64_t g_stream_expert_cache_wraps;
static uint64_t g_stream_expert_cache_clock;
static uint64_t g_stream_expert_cache_evict_advise_bytes;
static uint64_t g_stream_expert_cache_willneed_advise_bytes;
static uint64_t g_stream_expert_cache_pread_bytes;
static double g_stream_expert_cache_pread_ms;
static uint64_t g_stream_expert_cache_buffer_allocs;
static uint64_t g_stream_expert_cache_buffer_reuses;
static uint64_t g_stream_expert_cache_decode_tokens;
static uint32_t g_stream_expert_cache_first_decode_layer = UINT32_MAX;
static uint64_t g_stream_expert_cache_hotness_decay_token;
static uint64_t g_stream_expert_timing_selected_calls;
static double g_stream_expert_timing_selected_read_ms;
static double g_stream_expert_timing_selected_sync_ms;
static double g_stream_expert_timing_selected_copy_ms;
static double g_stream_expert_timing_selected_bind_ms;
static uint64_t g_stream_expert_timing_split_layers;
static uint64_t g_stream_expert_timing_split_resident_experts;
static uint64_t g_stream_expert_timing_split_missing_experts;
static double g_stream_expert_timing_split_resident_ms;
static double g_stream_expert_timing_split_missing_ms;
static double g_stream_expert_timing_split_missing_load_ms;
static double g_stream_expert_timing_split_missing_slot_ms;
static double g_stream_expert_timing_split_missing_prune_ms;
static double g_stream_expert_timing_split_missing_addr_ms;
static double g_stream_expert_timing_split_missing_wait_ms;
static uint64_t g_stream_expert_timing_load_calls;
static double g_stream_expert_timing_load_prepare_ms;
static double g_stream_expert_timing_load_pread_ms;
static double g_stream_expert_timing_load_install_ms;
static uint64_t g_stream_expert_timing_prepare_batch_reuse_calls;
static double g_stream_expert_timing_prepare_batch_reuse_ms;
static uint64_t g_stream_expert_timing_prepare_buffer_calls;
static double g_stream_expert_timing_prepare_buffer_ms;
static uint64_t g_stream_expert_timing_prepare_task_experts;
static double g_stream_expert_timing_prepare_task_ms;
static uint64_t g_stream_expert_timing_reuse_scan_calls;
static uint64_t g_stream_expert_timing_reuse_scan_entries;
static double g_stream_expert_timing_reuse_scan_ms;
static double g_stream_expert_timing_reuse_clear_ms;
static uint64_t g_stream_expert_timing_readahead_calls;
static uint64_t g_stream_expert_timing_readahead_bytes;
static double g_stream_expert_timing_readahead_ms;
static uint64_t g_stream_expert_timing_cache_all_resident_layers;
static uint64_t g_stream_expert_timing_cache_all_missing_layers;
static uint64_t g_stream_expert_timing_cache_mixed_layers;
static uint64_t g_stream_expert_timing_cache_resident_experts;
static uint64_t g_stream_expert_timing_cache_missing_experts;
typedef struct {
uint64_t selected_calls;
double selected_read_ms;
double selected_sync_ms;
double selected_copy_ms;
double selected_bind_ms;
uint64_t split_layers;
uint64_t split_resident_experts;
uint64_t split_missing_experts;
double split_resident_ms;
double split_missing_ms;
double split_missing_load_ms;
double split_missing_slot_ms;
double split_missing_prune_ms;
double split_missing_addr_ms;
double split_missing_wait_ms;
uint64_t load_calls;
double load_prepare_ms;
double load_pread_ms;
double load_install_ms;
uint64_t prepare_batch_reuse_calls;
double prepare_batch_reuse_ms;
uint64_t prepare_buffer_calls;
double prepare_buffer_ms;
uint64_t prepare_task_experts;
double prepare_task_ms;
uint64_t reuse_scan_calls;
uint64_t reuse_scan_entries;
double reuse_scan_ms;
double reuse_clear_ms;
uint64_t readahead_calls;
uint64_t readahead_bytes;
double readahead_ms;
uint64_t cache_all_resident_layers;
uint64_t cache_all_missing_layers;
uint64_t cache_mixed_layers;
uint64_t cache_resident_experts;
uint64_t cache_missing_experts;
} ds4_gpu_stream_expert_timing_snapshot;
static ds4_gpu_stream_expert_timing_snapshot g_stream_expert_timing_last_report;
static int g_stream_prefill_batch_selected_addr_building;
static int g_glm_stream_expert_addr_table_building;
static uint64_t g_model_residency_count;
static int g_model_residency_added_to_queue;
static int g_glm_model_mode;
static int g_ssd_streaming_mode;
static int g_glm_streaming_prefill_full_layer_runtime;
static int g_metal4_runtime_available;
static int g_metal4_family_supported;
static int g_metal4_queue_supported;
static int g_metal4_m5_neural_accelerators_hint;
static int g_metal4_tensor_api_enabled;
static int g_metal4_tensor_api_compile_supported;
static char g_metal_device_name[128];
static int ds4_gpu_model_map_log_enabled(void);
static int ds4_gpu_stream_expert_cache_note_expert_size(
uint64_t gate_expert_bytes,
uint64_t down_expert_bytes);
static uint32_t ds4_gpu_stream_expert_cache_configured_budget(void);
static void ds4_gpu_stream_expert_cache_clear_all(int reset_stats);
static void ds4_gpu_stream_expert_pending_load_clear(void);
static void ds4_gpu_stream_expert_pread_pool_shutdown(void);
static int ds4_gpu_stream_expert_timing_summary_enabled(void);
static int ds4_gpu_stream_expert_cache_entry_protected(
uint32_t layer,
uint32_t expert,
uint32_t protect_layer,
const int32_t *protect_ids,
uint32_t n_protect);
/* The async selected-load worker registers itself so cache paths that would
* flush/wait on command buffers (a race against the encoding thread) fail
* the load instead; the caller then retries on the main thread. */
static pthread_t g_stream_expert_service_thread;
static int g_stream_expert_service_thread_set;
void ds4_gpu_stream_expert_cache_note_service_thread(void) {
g_stream_expert_service_thread = pthread_self();
g_stream_expert_service_thread_set = 1;
}
static int ds4_gpu_stream_expert_cache_on_service_thread(void) {
return g_stream_expert_service_thread_set &&
pthread_equal(pthread_self(), g_stream_expert_service_thread);
}
static NSUInteger g_flash_attn_mask_bytes;
static NSUInteger g_flash_attn_zero_mask_bytes;
static NSUInteger g_flash_attn_pad_bytes;
static NSUInteger g_flash_attn_tmp_bytes;
static NSUInteger g_flash_attn_blk_bytes;
static NSUInteger g_flash_attn_ring_bytes;
static NSUInteger g_flash_attn_kv_bytes;
static NSUInteger g_glm_flash_attn_mask_bytes;
static uint32_t g_glm_flash_attn_mask_pos0;
static uint32_t g_glm_flash_attn_mask_tokens;
static uint32_t g_glm_flash_attn_mask_cache_len;
static int g_glm_flash_attn_mask_valid;
static NSUInteger g_compressor_pool_kv_bytes;
static NSUInteger g_compressor_pool_score_bytes;
static NSUInteger g_compressor_pool_score_cont_bytes;
static NSUInteger g_compressor_pool_softmax_bytes;
static NSUInteger g_compressor_pool_product_bytes;
static NSUInteger g_compressor_store_ape_bytes;
static NSUInteger g_compressor_store_score_bytes;
static NSUInteger g_embed_rows_bytes;
static NSUInteger g_router_selection_bytes;
static NSUInteger g_router_weight_sum_bytes;
static NSUInteger g_indexer_head_scores_bytes;
static NSUInteger g_indexer_topk_bytes;
static NSUInteger g_indexed_topk_bytes;
static NSUInteger g_f16_round_scratch_bytes;
static NSUInteger g_q8_prefill_scratch_bytes;
static NSUInteger g_raw_store_round_bytes;
static NSUInteger g_moe_gate_scratch_bytes;
static NSUInteger g_moe_down_scratch_bytes;
static NSUInteger g_moe_id_map_bytes;
static NSUInteger g_moe_q4_gate_slots_bytes;
static NSUInteger g_moe_q4_up_slots_bytes;
static NSUInteger g_moe_q4_down_slots_bytes;
static NSUInteger g_attn_out_group_ids_bytes;
static int g_initialized;
static int g_quality_mode;
static int g_mpp_invalid_env_reported;
#define DS4_METAL_MAX_ROUTED_EXPERT_USED 8
static int32_t g_routed_moe_selected_override[DS4_METAL_MAX_ROUTED_EXPERT_USED];
static uint32_t g_routed_moe_selected_override_n;
static int g_moe_selected_trace_record_initialized;
static FILE *g_moe_selected_trace_record_fp;
static uint64_t g_moe_selected_trace_record_count;
static int g_moe_selected_trace_replay_initialized;
static int32_t *g_moe_selected_trace_replay_ids;
static uint64_t g_moe_selected_trace_replay_count;
static uint64_t g_moe_selected_trace_replay_pos;
static double ds4_gpu_gib(uint64_t bytes);
static uint64_t ds4_gpu_system_memory_bytes(void) {
uint64_t bytes = 0;
size_t len = sizeof(bytes);
if (sysctlbyname("hw.memsize", &bytes, &len, NULL, 0) != 0) return 0;
return len == sizeof(bytes) ? bytes : 0;
}
static void ds4_gpu_print_device_summary(void) {
const char *name = g_device.name ? [g_device.name UTF8String] : "unknown Metal device";
uint64_t mem = ds4_gpu_system_memory_bytes();
if (mem) {
double gib = (double)mem / 1024.0 / 1024.0 / 1024.0;
fprintf(stderr, "ds4: Metal device %s, %.2f GiB RAM\n", name, gib);
} else {
fprintf(stderr, "ds4: Metal device %s\n", name);
}
}
#define DS4_METAL_MAX_MODEL_VIEWS 4096
/* Compatibility fallback for callers that cannot provide a parsed GGUF tensor
* span. The normal DS4 engine passes the exact maximum tensor byte size. */
#define DS4_METAL_FALLBACK_MAX_TENSOR_BYTES (4ull * 1024ull * 1024ull * 1024ull)
typedef struct {
__strong id<MTLBuffer> buffer;
const void *model_map;
uint64_t model_size;
uint64_t model_offset;
uint64_t bytes;
} ds4_gpu_model_view;
static ds4_gpu_model_view g_model_views[DS4_METAL_MAX_MODEL_VIEWS];
static uint32_t g_model_view_count;
enum {
DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER = 80,
DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT = 384,
DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED = DS4_METAL_MAX_ROUTED_EXPERT_USED,
DS4_METAL_STREAM_EXPERT_CACHE_MAX_ENTRIES =
DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER *
DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT,
DS4_METAL_STREAM_EXPERT_CACHE_MAX_SLABS = 256,
DS4_METAL_STREAM_EXPERT_HOTNESS_DECAY_TOKENS = 16,
DS4_METAL_STREAM_EXPERT_VALIDATE_WORDS = 16,
};
typedef struct {
uint32_t layer;
uint32_t expert;
uint64_t hits;
} ds4_gpu_moe_selected_hotlist_entry;
static int g_moe_selected_hotlist_initialized;
static uint64_t
g_moe_selected_hotlist_counts[DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER][DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT];
static uint64_t g_moe_selected_hotlist_records;
static uint64_t g_moe_selected_hotlist_selections;
typedef struct {
__strong id<MTLBuffer> gate_buffer;
__strong id<MTLBuffer> up_buffer;
__strong id<MTLBuffer> down_buffer;
const void *model_map;
uint64_t model_size;
uint64_t gate_abs_offset;
uint64_t up_abs_offset;
uint64_t down_abs_offset;
uint64_t gate_expert_bytes;
uint64_t down_expert_bytes;
uint64_t logical_bytes;
uint64_t last_used;
uint64_t use_count;
NSUInteger gate_inner;
NSUInteger up_inner;
NSUInteger down_inner;
uint64_t inflight_seq;
uint32_t slab_slot;
uint8_t valid;
uint8_t slab_backed;
} ds4_gpu_stream_expert_cache_entry;
typedef struct {
__strong id<MTLBuffer> gate_buffer;
__strong id<MTLBuffer> up_buffer;
__strong id<MTLBuffer> down_buffer;
NSUInteger gate_inner;
NSUInteger up_inner;
NSUInteger down_inner;
} ds4_gpu_stream_expert_reusable_buffers;
static ds4_gpu_stream_expert_cache_entry
g_stream_expert_cache[DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER][DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT];
static ds4_gpu_stream_expert_cache_entry
g_stream_full_expert_addr_entry[DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER];
static uint32_t g_stream_expert_cache_layer_count[DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER];
static uint64_t g_stream_expert_cache_layer_hits[DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER];
static uint64_t g_stream_expert_cache_layer_misses[DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER];
static uint64_t g_stream_expert_cache_layer_evictions[DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER];
static uint64_t g_stream_expert_cache_layer_pread_bytes[DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER];
static double g_stream_expert_cache_layer_pread_ms[DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER];
static uint64_t g_stream_expert_cache_layer_last_hits[DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER];
static uint64_t g_stream_expert_cache_layer_last_misses[DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER];
static uint64_t g_stream_expert_cache_layer_last_evictions[DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER];
static uint64_t g_stream_expert_cache_layer_last_pread_bytes[DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER];
static double g_stream_expert_cache_layer_last_pread_ms[DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER];
static uint32_t
g_stream_expert_cache_route_hotness[DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER][DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT];
static id<MTLBuffer> g_stream_expert_cache_gate_addr_buffers[DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER];
static id<MTLBuffer> g_stream_expert_cache_up_addr_buffers[DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER];
static id<MTLBuffer> g_stream_expert_cache_down_addr_buffers[DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER];
static id<MTLBuffer> g_stream_expert_cache_slabs[DS4_METAL_STREAM_EXPERT_CACHE_MAX_SLABS];
static uint32_t g_stream_expert_cache_slab_start_slot[DS4_METAL_STREAM_EXPERT_CACHE_MAX_SLABS];
static uint32_t g_stream_expert_cache_slab_slot_count[DS4_METAL_STREAM_EXPERT_CACHE_MAX_SLABS];
static uint32_t g_stream_expert_cache_slab_slots_used[DS4_METAL_STREAM_EXPERT_CACHE_MAX_SLABS];
static uint32_t g_stream_expert_cache_slab_count;
static uint32_t g_stream_expert_cache_slab_total_slots;
static uint32_t g_stream_expert_cache_free_slots[DS4_METAL_STREAM_EXPERT_CACHE_MAX_ENTRIES];
static uint32_t g_stream_expert_cache_free_slot_count;
static uint8_t g_stream_expert_cache_slab_slot_locked[DS4_METAL_STREAM_EXPERT_CACHE_MAX_ENTRIES];
static uint32_t g_stream_expert_cache_mlock_budget_cap;
static uint8_t g_stream_expert_cache_mlock_relief_applied;
static uint64_t g_stream_expert_cache_mlock_bytes;
static uint64_t g_stream_expert_cache_mlock_fail_bytes;
static uint64_t g_stream_expert_cache_mlock_failures;
static double g_stream_expert_cache_mlock_ms;
static int g_stream_expert_cache_mlock_warned;
static uint64_t g_stream_expert_cache_slab_slot_bytes;
static uint64_t g_stream_expert_cache_cb_seq;
static uint64_t g_stream_expert_cache_done_seq;
static uint64_t g_stream_expert_cache_batch_seq;
static uint64_t g_stream_expert_cache_owned_seq;
static uint64_t g_stream_expert_cache_pending_max_seq;
static id<MTLBuffer> g_stream_compact_gate_addr_buffers[DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER];
static id<MTLBuffer> g_stream_compact_up_addr_buffers[DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER];
static id<MTLBuffer> g_stream_compact_down_addr_buffers[DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER];
static id<MTLBuffer> g_stream_compact_selected_buffers[DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER];
static id<MTLBuffer> g_stream_selected_id_buffers[DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER];
static id<MTLBuffer> g_stream_expert_validate_status_buffer;
@interface DS4MetalTensor : NSObject
@property(nonatomic, strong) id<MTLBuffer> buffer;
@property(nonatomic, assign) uint64_t offset;
@property(nonatomic, assign) uint64_t bytes;
@property(nonatomic, assign) uint8_t owner;
@end
@implementation DS4MetalTensor
@end
@interface DS4MetalQ4ExpertTable : NSObject
@property(nonatomic, strong) id<MTLBuffer> argumentBuffer;
@property(nonatomic, strong) id<MTLBuffer> addressBuffer;
@property(nonatomic, strong) NSMutableArray<id<MTLBuffer>> *expertBuffers;
@property(nonatomic, strong) id residencySet;
@property(nonatomic, assign) BOOL residencySetAddedToQueue;
@property(nonatomic, assign) uint32_t nExpert;
@property(nonatomic, assign) uint64_t expertBytes;
@end
@implementation DS4MetalQ4ExpertTable
- (void)dealloc {
#if TARGET_OS_OSX
if (@available(macOS 15.0, *)) {
if (_residencySet) {
if (_residencySetAddedToQueue &&
g_queue &&
[g_queue respondsToSelector:@selector(removeResidencySet:)]) {
[g_queue removeResidencySet:_residencySet];
}
[_residencySet endResidency];
}
}
#endif
}
@end
@interface DS4MetalQ4LayerResidency : NSObject
@property(nonatomic, strong) id residencySet;
@property(nonatomic, assign) BOOL addedToQueue;
@end
@implementation DS4MetalQ4LayerResidency
- (void)dealloc {
#if TARGET_OS_OSX
if (@available(macOS 15.0, *)) {
if (_residencySet) {
if (_addedToQueue &&
g_queue &&
[g_queue respondsToSelector:@selector(removeResidencySet:)]) {
[g_queue removeResidencySet:_residencySet];
}
[_residencySet endResidency];
}
}
#endif
}
@end
static DS4MetalTensor *ds4_gpu_tensor_obj(ds4_gpu_tensor *tensor) {
return (__bridge DS4MetalTensor *)tensor;
}
static const DS4MetalTensor *ds4_gpu_tensor_const_obj(const ds4_gpu_tensor *tensor) {
return (__bridge const DS4MetalTensor *)tensor;
}
/* C code owns ds4_gpu_tensor handles as retained Objective-C objects. Freeing
* the same opaque handle twice would make the second __bridge_transfer release
* an already-deallocated object, which macOS reports as malloc corruption. The
* live table lets free validate a handle before touching Objective-C state; the
* same mutex also serializes the diagnostic allocation counters. */
static uint64_t ds4_gpu_tensor_ptr_hash(uintptr_t ptr) {
uint64_t x = (uint64_t)(ptr >> 4);
x ^= x >> 33;
x *= UINT64_C(0xff51afd7ed558ccd);
x ^= x >> 33;
x *= UINT64_C(0xc4ceb9fe1a85ec53);
x ^= x >> 33;
return x;
}
static int ds4_gpu_tensor_live_resize_locked(size_t min_cap) {
size_t new_cap = 1024;
while (new_cap < min_cap) new_cap <<= 1;
uintptr_t *new_slots = calloc(new_cap, sizeof(new_slots[0]));
if (!new_slots) return 0;
for (size_t i = 0; i < g_tensor_live_cap; i++) {
const uintptr_t key = g_tensor_live_slots[i];
if (key == 0 || key == UINTPTR_MAX) continue;
size_t idx = (size_t)ds4_gpu_tensor_ptr_hash(key) & (new_cap - 1);
while (new_slots[idx] != 0) idx = (idx + 1) & (new_cap - 1);
new_slots[idx] = key;
}
free(g_tensor_live_slots);
g_tensor_live_slots = new_slots;
g_tensor_live_cap = new_cap;
g_tensor_live_tombs = 0;
return 1;
}
static int ds4_gpu_tensor_live_insert_locked(const void *ptr) {
if (!ptr || (uintptr_t)ptr == UINTPTR_MAX) return 0;
if ((g_tensor_live_count + g_tensor_live_tombs + 1) * 10 >=
g_tensor_live_cap * 7)
{
const size_t min_cap = g_tensor_live_cap ? g_tensor_live_cap * 2 : 1024;
if (!ds4_gpu_tensor_live_resize_locked(min_cap)) return 0;
}
const uintptr_t key = (uintptr_t)ptr;
size_t idx = (size_t)ds4_gpu_tensor_ptr_hash(key) & (g_tensor_live_cap - 1);
size_t tomb = (size_t)-1;
for (;;) {
const uintptr_t cur = g_tensor_live_slots[idx];
if (cur == key) return 0;
if (cur == UINTPTR_MAX) {
if (tomb == (size_t)-1) tomb = idx;
} else if (cur == 0) {
if (tomb != (size_t)-1) {
idx = tomb;
g_tensor_live_tombs--;
}
g_tensor_live_slots[idx] = key;
g_tensor_live_count++;
return 1;
}
idx = (idx + 1) & (g_tensor_live_cap - 1);
}
}
static int ds4_gpu_tensor_live_remove_locked(const void *ptr) {
if (!ptr || g_tensor_live_cap == 0) return 0;
const uintptr_t key = (uintptr_t)ptr;
size_t idx = (size_t)ds4_gpu_tensor_ptr_hash(key) & (g_tensor_live_cap - 1);
for (;;) {
const uintptr_t cur = g_tensor_live_slots[idx];
if (cur == 0) return 0;
if (cur == key) {
g_tensor_live_slots[idx] = UINTPTR_MAX;
g_tensor_live_count--;
g_tensor_live_tombs++;
return 1;
}
idx = (idx + 1) & (g_tensor_live_cap - 1);
}
}
static int ds4_gpu_tensor_track_alloc_locked(
const void *ptr,
uint64_t bytes,
uint64_t *live_snap,
uint64_t *peak_snap)
{
if (!ds4_gpu_tensor_live_insert_locked(ptr)) return 0;
g_tensor_alloc_live_bytes += bytes;
if (g_tensor_alloc_live_bytes > g_tensor_alloc_peak_bytes) {
g_tensor_alloc_peak_bytes = g_tensor_alloc_live_bytes;
}
if (live_snap) *live_snap = g_tensor_alloc_live_bytes;
if (peak_snap) *peak_snap = g_tensor_alloc_peak_bytes;
return 1;
}
static int ds4_gpu_tensor_track_view_locked(const void *ptr) {
return ds4_gpu_tensor_live_insert_locked(ptr);
}
static int ds4_gpu_tensor_prepare_free(
ds4_gpu_tensor *tensor,
uint8_t *owner,
uint64_t *bytes,
uint64_t *live_snap,
uint64_t *peak_snap)
{
pthread_mutex_lock(&g_tensor_mu);
if (!ds4_gpu_tensor_live_remove_locked(tensor)) {
pthread_mutex_unlock(&g_tensor_mu);
fprintf(stderr,
"ds4: Metal tensor free ignored for unknown handle %p\n",
(void *)tensor);
return 0;
}
DS4MetalTensor *obj = ds4_gpu_tensor_obj(tensor);
const uint8_t obj_owner = obj.owner;
const uint64_t obj_bytes = obj.bytes;
if (obj_owner) {
if (obj_bytes <= g_tensor_alloc_live_bytes) {
g_tensor_alloc_live_bytes -= obj_bytes;
} else {
g_tensor_alloc_live_bytes = 0;
}
}
if (owner) *owner = obj_owner;
if (bytes) *bytes = obj_bytes;
if (live_snap) *live_snap = g_tensor_alloc_live_bytes;
if (peak_snap) *peak_snap = g_tensor_alloc_peak_bytes;
pthread_mutex_unlock(&g_tensor_mu);
return 1;
}
static void ds4_gpu_tensor_tracking_reset(void) {
pthread_mutex_lock(&g_tensor_mu);
if (g_tensor_live_count != 0) {
fprintf(stderr,
"ds4: Metal cleanup discarded %zu live tensor handles\n",
g_tensor_live_count);
}
free(g_tensor_live_slots);
g_tensor_live_slots = NULL;
g_tensor_live_cap = 0;
g_tensor_live_count = 0;
g_tensor_live_tombs = 0;
g_tensor_alloc_live_bytes = 0;
g_tensor_alloc_peak_bytes = 0;
pthread_mutex_unlock(&g_tensor_mu);
}
static id<MTLBuffer> ds4_gpu_tensor_buffer(const ds4_gpu_tensor *tensor) {
if (!tensor) return nil;
const DS4MetalTensor *obj = ds4_gpu_tensor_const_obj(tensor);
return obj.buffer;
}
static NSUInteger ds4_gpu_tensor_offset(const ds4_gpu_tensor *tensor) {
if (!tensor) return 0;
const DS4MetalTensor *obj = ds4_gpu_tensor_const_obj(tensor);
return (NSUInteger)obj.offset;
}
static id<MTLCommandBuffer> ds4_gpu_new_command_buffer(void);
static void ds4_gpu_stream_expert_cache_note_owned_created(void);
static id<MTLCommandBuffer> ds4_gpu_command_buffer(int *owned) {
if (g_batch_cb) {
*owned = 0;
return g_batch_cb;
}
*owned = 1;
id<MTLCommandBuffer> cb = ds4_gpu_new_command_buffer();
if (cb) ds4_gpu_stream_expert_cache_note_owned_created();
return cb;
}
static id<MTLComputeCommandEncoder> ds4_gpu_compute_encoder(id<MTLCommandBuffer> cb) {
if (g_batch_cb && cb == g_batch_cb) {
g_batch_has_work = YES;
if (g_timeline_enabled && g_timeline_batch) {
/* Diagnostic: one timestamped pass per dispatch group. A
* concurrent section keeps its single encoder. */
if (g_batch_enc && !g_batch_encoder_concurrent) {
[g_batch_enc endEncoding];
g_batch_enc = nil;
}
if (!g_batch_enc) {
g_batch_enc = ds4_gpu_timeline_new_encoder(
cb, g_batch_encoder_concurrent,
(uintptr_t)__builtin_return_address(0));
}
if (g_batch_enc) return g_batch_enc;
}
if (!g_batch_enc) {
g_batch_enc = g_batch_encoder_concurrent
? [cb computeCommandEncoderWithDispatchType:MTLDispatchTypeConcurrent]
: [cb computeCommandEncoder];
}
return g_batch_enc;
}
return [cb computeCommandEncoder];
}
static void ds4_gpu_end_compute_encoder(id<MTLCommandBuffer> cb, id<MTLComputeCommandEncoder> enc) {
if (!enc) return;
if (g_batch_cb && cb == g_batch_cb && enc == g_batch_enc) return;
[enc endEncoding];
}
static void ds4_gpu_close_batch_encoder(void) {
if (!g_batch_enc) return;
[g_batch_enc endEncoding];
g_batch_enc = nil;
}
static double g_gpu_busy_accum;
static uint64_t g_gpu_busy_cbs;
/* A failed command buffer can leave a cross-threadgroup arrival counter at an
* arbitrary partial value. Drop cached ownership instead of CPU-resetting
* buffers that another in-flight command buffer might still reference; bound
* resources remain retained by their command buffers/transient list. */
static void ds4_gpu_invalidate_completion_counters(void) {
[g_dsv4_completion_cache removeAllObjects];
g_dsv4_hc_producer_last_mix_buffer = nil;
g_dsv4_hc_producer_last_mix_offset = 0;
g_dsv4_hc_producer_last_completion = nil;
}
static int ds4_gpu_wait_command_buffer(id<MTLCommandBuffer> cb, const char *label) {
[cb waitUntilCompleted];
if (getenv("DS4_METAL_CB_TIMES")) {
static double prev_gpu_end;
static uint64_t n_printed;
if (n_printed < 400) {
n_printed++;
fprintf(stderr,
"ds4: cb %s: driver %.0f us, queue-wait %.0f us, gpu %.0f us, gap-from-prev-gpu-end %.0f us\n",
label,
(cb.kernelEndTime - cb.kernelStartTime) * 1e6,
(cb.GPUStartTime - cb.kernelEndTime) * 1e6,
(cb.GPUEndTime - cb.GPUStartTime) * 1e6,
prev_gpu_end ? (cb.GPUStartTime - prev_gpu_end) * 1e6 : 0.0);
}
prev_gpu_end = cb.GPUEndTime;
}
if (getenv("DS4_METAL_GPU_BUSY_PROFILE")) {
const double busy = cb.GPUEndTime - cb.GPUStartTime;
if (busy > 0) g_gpu_busy_accum += busy;
if ((++g_gpu_busy_cbs % 64u) == 0u) {
fprintf(stderr, "ds4: gpu busy accum %.1f ms over %llu cbs\n",
g_gpu_busy_accum * 1000.0,
(unsigned long long)g_gpu_busy_cbs);
}
}
if (cb.status == MTLCommandBufferStatusError) {
fprintf(stderr, "ds4: Metal %s failed: %s\n",
label, [[cb.error localizedDescription] UTF8String]);
ds4_gpu_invalidate_completion_counters();
return 0;
}
return 1;
}
static id<MTLCommandBuffer> ds4_gpu_new_command_buffer(void) {
static int initialized;
static int use_unretained;
if (!initialized) {
use_unretained = getenv("DS4_METAL_UNRETAINED_COMMAND_BUFFERS") != NULL;
initialized = 1;
}
if (use_unretained) {
return [g_queue commandBufferWithUnretainedReferences];
}
return [g_queue commandBuffer];
}
static uint64_t ds4_gpu_exact_view_cache_limit_bytes(void) {
static int initialized;
static uint64_t limit_bytes;
if (initialized) return limit_bytes;
const uint64_t mib = 1024ull * 1024ull;
const uint64_t gib = 1024ull * mib;
limit_bytes = 64ull * gib;
const char *gib_env = getenv("DS4_METAL_EXACT_VIEW_CACHE_GIB");
if (gib_env && gib_env[0]) {
char *end = NULL;
unsigned long long v = strtoull(gib_env, &end, 10);
if (end != gib_env && *end == '\0') {
limit_bytes = v > UINT64_MAX / gib ? UINT64_MAX : (uint64_t)v * gib;
}
}
const char *mib_env = getenv("DS4_METAL_EXACT_VIEW_CACHE_MIB");
if (mib_env && mib_env[0]) {
char *end = NULL;
unsigned long long v = strtoull(mib_env, &end, 10);
if (end != mib_env && *end == '\0') {
limit_bytes = v > UINT64_MAX / mib ? UINT64_MAX : (uint64_t)v * mib;
}
}
initialized = 1;
return limit_bytes;
}
static void ds4_gpu_model_buffer_cache_note_insert(uint64_t bytes) {
if (g_model_buffer_cache_bytes > UINT64_MAX - bytes) {
g_model_buffer_cache_bytes = UINT64_MAX;
} else {
g_model_buffer_cache_bytes += bytes;
}
const uint64_t limit = ds4_gpu_exact_view_cache_limit_bytes();
if (limit != 0 && g_model_buffer_cache_bytes > limit) {
g_model_buffer_cache_over_limit = 1;
}
}
static void ds4_gpu_model_buffer_cache_clear(const char *reason) {
if (!g_model_buffer_cache) {
g_model_buffer_cache_bytes = 0;
g_model_buffer_cache_over_limit = 0;
return;
}
const NSUInteger entries = [g_model_buffer_cache count];
if (entries != 0) {
if (getenv("DS4_METAL_EXACT_VIEW_CACHE_PROFILE") != NULL) {
fprintf(stderr,
"ds4: Metal exact model view cache evict reason=%s entries=%lu bytes=%.2f GiB limit=%.2f GiB\n",
reason ? reason : "unknown",
(unsigned long)entries,
ds4_gpu_gib(g_model_buffer_cache_bytes),
ds4_gpu_gib(ds4_gpu_exact_view_cache_limit_bytes()));
}
[g_model_buffer_cache removeAllObjects];
g_model_buffer_cache_evictions++;
}
g_model_buffer_cache_bytes = 0;
g_model_buffer_cache_over_limit = 0;
}
static void ds4_gpu_model_buffer_cache_maybe_evict(const char *reason) {
if (g_model_buffer_cache_over_limit) {
ds4_gpu_model_buffer_cache_clear(reason);
}
}
static uint64_t ds4_gpu_stream_expert_cache_next_cb_seq(void) {
if (g_stream_expert_cache_cb_seq == UINT64_MAX) {
/*
* A real wrap would require an astronomical number of command buffers.
* Resetting the epoch space is still safer than letting zero become a
* valid in-flight marker.
*/
g_stream_expert_cache_cb_seq = 0;
g_stream_expert_cache_done_seq = 0;
g_stream_expert_cache_batch_seq = 0;
g_stream_expert_cache_owned_seq = 0;
g_stream_expert_cache_pending_max_seq = 0;
for (uint32_t layer = 0; layer < DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER; layer++) {
for (uint32_t expert = 0; expert < DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT; expert++) {
g_stream_expert_cache[layer][expert].inflight_seq = 0;
}
}
}
return ++g_stream_expert_cache_cb_seq;
}
static void ds4_gpu_stream_expert_cache_note_batch_created(void) {
g_stream_expert_cache_batch_seq =
ds4_gpu_stream_expert_cache_next_cb_seq();
}
static void ds4_gpu_stream_expert_cache_note_batch_committed(void) {
if (g_stream_expert_cache_batch_seq > g_stream_expert_cache_pending_max_seq) {
g_stream_expert_cache_pending_max_seq = g_stream_expert_cache_batch_seq;
}
g_stream_expert_cache_batch_seq = 0;
}
static void ds4_gpu_stream_expert_cache_note_owned_created(void) {
g_stream_expert_cache_owned_seq =
ds4_gpu_stream_expert_cache_next_cb_seq();
}
static void ds4_gpu_stream_expert_cache_note_pending_completed(void) {
if (g_stream_expert_cache_pending_max_seq > g_stream_expert_cache_done_seq) {
g_stream_expert_cache_done_seq = g_stream_expert_cache_pending_max_seq;
}
g_stream_expert_cache_pending_max_seq = 0;
}
static void ds4_gpu_stream_expert_cache_note_owned_completed(void) {
if (g_stream_expert_cache_owned_seq > g_stream_expert_cache_done_seq) {
g_stream_expert_cache_done_seq = g_stream_expert_cache_owned_seq;
}
g_stream_expert_cache_owned_seq = 0;
}
static int ds4_gpu_stream_expert_cache_entry_inflight(
const ds4_gpu_stream_expert_cache_entry *e) {
return e && e->valid && e->inflight_seq > g_stream_expert_cache_done_seq;
}
static int ds4_gpu_stream_expert_cache_mark_inflight(
ds4_gpu_stream_expert_cache_entry *e) {
if (!e || !e->valid) return 0;
const uint64_t seq = g_stream_expert_cache_batch_seq ?
g_stream_expert_cache_batch_seq :
g_stream_expert_cache_owned_seq;
if (seq == 0) return 0;
e->inflight_seq = seq;
return 1;
}
static int ds4_gpu_stream_expert_cache_mark_entries_inflight(
ds4_gpu_stream_expert_cache_entry * const *entries,
uint32_t n_entries,
uint32_t active_mask) {
if (!entries || n_entries == 0) return 0;
for (uint32_t i = 0; i < n_entries; i++) {
if (active_mask != 0 && (active_mask & (1u << i)) == 0) continue;
if (!ds4_gpu_stream_expert_cache_mark_inflight(entries[i])) return 0;
}
return 1;
}
static int ds4_gpu_stream_expert_cache_wait_inflight(const char *label);
static int ds4_gpu_wait_pending_command_buffers(const char *label) {
int ok = 1;
for (id<MTLCommandBuffer> pending in g_pending_cbs) {
if (!ds4_gpu_wait_command_buffer(pending, label)) ok = 0;
}
[g_pending_cbs removeAllObjects];
ds4_gpu_stream_expert_cache_note_pending_completed();
if (!ok) ds4_gpu_invalidate_zero_prefix_prefill_block_maps();
return ok;
}
static double g_batch_cb_created_ms;
static double ds4_gpu_now_ms(void);
static void ds4_gpu_queue_keepalive_start(void);
static void ds4_gpu_queue_keepalive_stop_thread(void);
static int ds4_gpu_finish_command_buffer(id<MTLCommandBuffer> cb, int owned, const char *label) {
if (!owned) return 1;
const double t_commit = ds4_gpu_now_ms();
[cb commit];
int ok = ds4_gpu_wait_pending_command_buffers(label);
if (!ds4_gpu_wait_command_buffer(cb, label)) {
ok = 0;
ds4_gpu_invalidate_zero_prefix_prefill_block_maps();
}
if (getenv("DS4_METAL_CB_TIMES")) {
const double t_done = ds4_gpu_now_ms();
struct timespec ts_mono;
clock_gettime(CLOCK_MONOTONIC, &ts_mono);
const double mono_ms = (double)ts_mono.tv_sec * 1e3 + (double)ts_mono.tv_nsec / 1e6;
fprintf(stderr, "ds4: cb '%s': mono %.1f ms: ", label ? label : "?", mono_ms);
fprintf(stderr, "encode %.1f ms, commit->done %.1f ms, gpu span %.1f ms, gpu start +%.1f ms, caller 0x%llx\n",
t_commit - g_batch_cb_created_ms, t_done - t_commit,
(cb.GPUEndTime - cb.GPUStartTime) * 1e3,
cb.GPUStartTime * 1e3 - (mono_ms - (t_done - t_commit)),
(unsigned long long)((uintptr_t)__builtin_return_address(0) - (uintptr_t)_dyld_get_image_vmaddr_slide(0)));
if (t_done - t_commit > 100.0 && (cb.GPUEndTime - cb.GPUStartTime) * 1e3 < 10.0) {
void *frames[12];
const int n = backtrace(frames, 12);
fprintf(stderr, "ds4: cb stall backtrace:");
for (int i = 1; i < n; i++)
fprintf(stderr, " 0x%llx", (unsigned long long)((uintptr_t)frames[i] - (uintptr_t)_dyld_get_image_vmaddr_slide(0)));
fprintf(stderr, "\n");
}
}
ds4_gpu_stream_expert_cache_note_owned_completed();
if (getenv("DS4_METAL_CB_TIMES")) {
uint64_t bytes = 0;
for (id<MTLBuffer> b in g_transient_buffers) bytes += [b length];
fprintf(stderr, "ds4: transient buffers this batch: %lu (%.1f MiB)\n",
(unsigned long)[g_transient_buffers count], bytes / 1048576.0);
}
[g_transient_buffers removeAllObjects];
ds4_gpu_model_buffer_cache_maybe_evict(label);
return ok;
}
static int ds4_gpu_device_name_contains(const char *needle);
static int ds4_gpu_use_m5_private_scratch(void) {
static int initialized;
static int enabled;
if (!initialized) {
enabled = ds4_gpu_device_name_contains("M5");
initialized = 1;
}
return enabled;
}
static int ds4_gpu_scratch_needs_cpu_access(const char *label) {
if (!label) return 0;
return strstr(label, "mask") != NULL ||
strcmp(label, "ds4_attention_output_group_ids") == 0;
}
static MTLResourceOptions ds4_gpu_model_resource_options(void) {
MTLResourceOptions options = MTLResourceStorageModeShared;
if (getenv("DS4_METAL_MODEL_UNTRACKED") != NULL) {
options |= MTLResourceHazardTrackingModeUntracked;
}
return options;
}
static int ds4_gpu_ensure_scratch_buffer(
id<MTLBuffer> __strong *buffer,
NSUInteger *capacity,
NSUInteger bytes,
const char *label) {
if (*buffer && *capacity >= bytes) return 1;
if (bytes == 0) bytes = 1;
if (bytes > NSUIntegerMax) return 0;
MTLResourceOptions options = MTLResourceStorageModeShared;
if (ds4_gpu_use_m5_private_scratch() &&
!ds4_gpu_scratch_needs_cpu_access(label)) {
/*
* M5 scratch buffers that only flow between Metal kernels do not need
* CPU-visible shared storage. This reduces shared-memory traffic and
* residency pressure for the long prefill scratch pools without
* changing the public buffer lifetime model. Keep default hazard
* tracking because the graph reuses these buffers across dependent
* compute encoders.
*/
options = MTLResourceStorageModePrivate;
}
*buffer = [g_device newBufferWithLength:bytes options:options];
if (!*buffer && options != MTLResourceStorageModeShared) {
*buffer = [g_device newBufferWithLength:bytes options:MTLResourceStorageModeShared];
}
if (!*buffer) {
fprintf(stderr, "ds4: failed to allocate Metal scratch buffer %s (%llu bytes)\n",
label, (unsigned long long)bytes);
*capacity = 0;
return 0;
}
(*buffer).label = [NSString stringWithUTF8String:label];
*capacity = bytes;
return 1;
}
static int ds4_gpu_ensure_zero_attention_mask(NSUInteger bytes) {
const NSUInteger capacity = 8192u * sizeof(uint16_t);
if (bytes > capacity) return 0;
if (g_flash_attn_zero_mask_buffer &&
g_flash_attn_zero_mask_bytes >= capacity) {
return 1;
}
if (!ds4_gpu_ensure_scratch_buffer(&g_flash_attn_zero_mask_buffer,
&g_flash_attn_zero_mask_bytes,
capacity,
"ds4_flash_attn_zero_mask")) {
return 0;
}
void *contents = [g_flash_attn_zero_mask_buffer contents];
if (!contents) return 0;
memset(contents, 0, g_flash_attn_zero_mask_bytes);
return 1;
}
static uint64_t round_up_u64(uint64_t v, uint64_t align) {
return (v + align - 1) & ~(align - 1);
}
static uint64_t ds4_gpu_effective_model_max_tensor_bytes(uint64_t map_size, uint64_t max_tensor_bytes) {
if (max_tensor_bytes != 0) return max_tensor_bytes;
return map_size < DS4_METAL_FALLBACK_MAX_TENSOR_BYTES ?
map_size : DS4_METAL_FALLBACK_MAX_TENSOR_BYTES;
}
static id<MTLComputePipelineState> ds4_gpu_get_pipeline(const char *function_name);
static int ds4_gpu_warm_model_views(void);
static double ds4_gpu_gib(uint64_t bytes);
static double ds4_gpu_now_ms(void) {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return ts.tv_sec * 1000.0 + ts.tv_nsec / 1000000.0;
}
static int ds4_gpu_moe_selected_hotlist_cmp(const void *a, const void *b) {
const ds4_gpu_moe_selected_hotlist_entry *ea = a;
const ds4_gpu_moe_selected_hotlist_entry *eb = b;
if (ea->hits < eb->hits) return 1;
if (ea->hits > eb->hits) return -1;
if (ea->layer != eb->layer) return ea->layer < eb->layer ? -1 : 1;
if (ea->expert != eb->expert) return ea->expert < eb->expert ? -1 : 1;
return 0;
}
static int ds4_gpu_moe_selected_hotlist_merge_requested(void) {
return getenv("DS4_MOE_RECORD_SELECTED_HOTLIST_MERGE") != NULL &&
getenv("DS4_MOE_RECORD_SELECTED_HOTLIST_FRESH") == NULL;
}
static int ds4_gpu_moe_selected_hotlist_load_existing(const char *path) {
if (!path || !path[0] || !ds4_gpu_moe_selected_hotlist_merge_requested()) {
return 1;
}
FILE *fp = fopen(path, "rb");
if (!fp) {
if (errno == ENOENT) return 1;
fprintf(stderr, "ds4: failed to open selected hotlist merge file %s\n", path);
return 0;
}
char line[256];
uint64_t lineno = 0;
uint64_t loaded_entries = 0;
uint64_t loaded_hits = 0;
uint64_t header_records = UINT64_MAX;
uint64_t header_selections = UINT64_MAX;
while (fgets(line, sizeof(line), fp)) {
lineno++;
char *p = line;
while (*p && isspace((unsigned char)*p)) p++;
if (*p == '\0') continue;
if (*p == '#') {
unsigned long long value = 0;
if (sscanf(p, "# layer_records %llu", &value) == 1) {
header_records = (uint64_t)value;
} else if (sscanf(p, "# selections %llu", &value) == 1) {
header_selections = (uint64_t)value;
}
continue;
}
errno = 0;
char *end = NULL;
unsigned long layer = strtoul(p, &end, 10);
if (end == p || errno != 0) goto bad_line;
p = end;
while (*p && isspace((unsigned char)*p)) p++;
errno = 0;
unsigned long expert = strtoul(p, &end, 10);
if (end == p || errno != 0) goto bad_line;
p = end;
while (*p && isspace((unsigned char)*p)) p++;
errno = 0;
unsigned long long hits = strtoull(p, &end, 10);
if (end == p || errno != 0) goto bad_line;
if (layer < DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER &&
expert < DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT &&
hits != 0) {
uint64_t *dst = &g_moe_selected_hotlist_counts[layer][expert];
if (*dst > UINT64_MAX - (uint64_t)hits) {
*dst = UINT64_MAX;
} else {
*dst += (uint64_t)hits;
}
loaded_entries++;
if (loaded_hits > UINT64_MAX - (uint64_t)hits) {
loaded_hits = UINT64_MAX;
} else {
loaded_hits += (uint64_t)hits;
}
}
continue;
bad_line:
fprintf(stderr,
"ds4: invalid selected hotlist merge line %" PRIu64 " in %s\n",
lineno,
path);
fclose(fp);
return 0;
}
if (ferror(fp)) {
fprintf(stderr, "ds4: failed to read selected hotlist merge file %s\n", path);
fclose(fp);
return 0;
}
fclose(fp);
g_moe_selected_hotlist_records =
header_records != UINT64_MAX ? header_records : loaded_hits / 6u;
g_moe_selected_hotlist_selections =
header_selections != UINT64_MAX ? header_selections : loaded_hits;
fprintf(stderr,
"ds4: merged selected-id hotlist %s "
"(%" PRIu64 " entries, %" PRIu64 " hits)\n",
path,
loaded_entries,
loaded_hits);
return 1;
}
static void ds4_gpu_moe_selected_hotlist_close(void) {
const char *path = getenv("DS4_MOE_RECORD_SELECTED_HOTLIST");
if (!g_moe_selected_hotlist_initialized || !path || !path[0]) return;
const size_t cap =
(size_t)DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER *
DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT;
ds4_gpu_moe_selected_hotlist_entry *entries =
malloc(cap * sizeof(entries[0]));
if (!entries) {
fprintf(stderr, "ds4: failed to allocate selected hotlist entries\n");
return;
}
size_t n = 0;
for (uint32_t layer = 0;
layer < DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER;
layer++) {
for (uint32_t expert = 0;
expert < DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT;
expert++) {
const uint64_t hits =
g_moe_selected_hotlist_counts[layer][expert];
if (hits == 0) continue;
entries[n++] = (ds4_gpu_moe_selected_hotlist_entry) {
.layer = layer,
.expert = expert,
.hits = hits,
};
}
}
qsort(entries, n, sizeof(entries[0]), ds4_gpu_moe_selected_hotlist_cmp);
FILE *fp = fopen(path, "wb");
if (!fp) {
fprintf(stderr, "ds4: failed to open selected hotlist file %s\n", path);
free(entries);
return;
}
fprintf(fp,
"# ds4 selected-id hotlist v1\n"
"# layer_records %" PRIu64 "\n"
"# selections %" PRIu64 "\n"
"# columns: layer expert hits weight\n",
g_moe_selected_hotlist_records,
g_moe_selected_hotlist_selections);
for (size_t i = 0; i < n; i++) {
fprintf(fp,
"%u %u %" PRIu64 " 0\n",
entries[i].layer,
entries[i].expert,
entries[i].hits);
}
free(entries);
if (fclose(fp) != 0) {
fprintf(stderr, "ds4: failed to close selected hotlist file %s\n", path);
} else {
fprintf(stderr,
"ds4: wrote selected-id hotlist to %s "
"(%" PRIu64 " layer records, %" PRIu64 " selections)\n",
path,
g_moe_selected_hotlist_records,
g_moe_selected_hotlist_selections);
}
}
static int ds4_gpu_moe_selected_hotlist_record(
uint32_t layer,
const int32_t *selected_ids,
uint32_t n_selected,
uint32_t n_total_expert) {
const char *path = getenv("DS4_MOE_RECORD_SELECTED_HOTLIST");
if (!path || !path[0]) return 1;
if (!selected_ids ||
n_selected == 0 ||
n_selected > DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED) {
return 0;
}
if (!g_moe_selected_hotlist_initialized) {
g_moe_selected_hotlist_initialized = 1;
if (!ds4_gpu_moe_selected_hotlist_load_existing(path)) return 0;
atexit(ds4_gpu_moe_selected_hotlist_close);
}
if (layer >= DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER) return 1;
g_moe_selected_hotlist_records++;
for (uint32_t i = 0; i < n_selected; i++) {
if (selected_ids[i] < 0) continue;
const uint32_t expert = (uint32_t)selected_ids[i];
if (expert >= n_total_expert ||
expert >= DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT) {
continue;
}
g_moe_selected_hotlist_counts[layer][expert]++;
g_moe_selected_hotlist_selections++;
}
return 1;
}
static void ds4_gpu_moe_selected_trace_record_close(void) {
if (g_moe_selected_trace_record_fp) {
const char *path = getenv("DS4_MOE_RECORD_SELECTED_IDS");
fclose(g_moe_selected_trace_record_fp);
g_moe_selected_trace_record_fp = NULL;
fprintf(stderr,
"ds4: recorded %" PRIu64 " routed-MoE selected-id entries to %s\n",
g_moe_selected_trace_record_count,
path && path[0] ? path : "(unknown)");
}
}
static int ds4_gpu_moe_selected_trace_record(
const int32_t selected_ids[6],
uint32_t n_selected) {
const char *path = getenv("DS4_MOE_RECORD_SELECTED_IDS");
if (!path || !path[0]) return 1;
if (n_selected != 6) {
fprintf(stderr, "ds4: selected-id recording expects exactly 6 selected experts\n");
return 0;
}
if (!g_moe_selected_trace_record_initialized) {
g_moe_selected_trace_record_initialized = 1;
g_moe_selected_trace_record_fp = fopen(path, "wb");
if (!g_moe_selected_trace_record_fp) {
fprintf(stderr, "ds4: failed to open selected-id record file %s\n", path);
return 0;
}
setvbuf(g_moe_selected_trace_record_fp, NULL, _IOFBF, 1u << 20);
atexit(ds4_gpu_moe_selected_trace_record_close);
}
if (fwrite(selected_ids, sizeof(selected_ids[0]), n_selected, g_moe_selected_trace_record_fp) != n_selected) {
fprintf(stderr, "ds4: failed to write selected-id record file %s\n", path);
return 0;
}
if (fflush(g_moe_selected_trace_record_fp) != 0) {
fprintf(stderr, "ds4: failed to flush selected-id record file %s\n", path);
return 0;
}
g_moe_selected_trace_record_count++;
return 1;
}
static int ds4_gpu_moe_selected_trace_replay(
int32_t selected_ids[6],
uint32_t n_selected) {
const char *path = getenv("DS4_MOE_REPLAY_SELECTED_IDS");
if (!path || !path[0]) return 0;
if (n_selected != 6) {
fprintf(stderr, "ds4: selected-id replay expects exactly 6 selected experts\n");
return -1;
}
if (!g_moe_selected_trace_replay_initialized) {
g_moe_selected_trace_replay_initialized = 1;
FILE *fp = fopen(path, "rb");
if (!fp) {
fprintf(stderr, "ds4: failed to open selected-id replay file %s\n", path);
return -1;
}
if (fseeko(fp, 0, SEEK_END) != 0) {
fprintf(stderr, "ds4: failed to seek selected-id replay file %s\n", path);
fclose(fp);
return -1;
}
const off_t end = ftello(fp);
if (end < 0) {
fprintf(stderr, "ds4: failed to size selected-id replay file %s\n", path);
fclose(fp);
return -1;
}
if (fseeko(fp, 0, SEEK_SET) != 0) {
fprintf(stderr, "ds4: failed to rewind selected-id replay file %s\n", path);
fclose(fp);
return -1;
}
const uint64_t bytes = (uint64_t)end;
const uint64_t entry_bytes = (uint64_t)n_selected * sizeof(selected_ids[0]);
if (bytes == 0 || (bytes % entry_bytes) != 0) {
fprintf(stderr,
"ds4: selected-id replay file %s has invalid size %" PRIu64 "\n",
path,
bytes);
fclose(fp);
return -1;
}
if (bytes > SIZE_MAX) {
fprintf(stderr, "ds4: selected-id replay file %s is too large\n", path);
fclose(fp);
return -1;
}
g_moe_selected_trace_replay_count = bytes / entry_bytes;
g_moe_selected_trace_replay_ids = malloc((size_t)bytes);
if (!g_moe_selected_trace_replay_ids) {
fprintf(stderr, "ds4: failed to allocate selected-id replay buffer\n");
fclose(fp);
return -1;
}
if (fread(g_moe_selected_trace_replay_ids, 1, (size_t)bytes, fp) != (size_t)bytes) {
fprintf(stderr, "ds4: failed to read selected-id replay file %s\n", path);
fclose(fp);
free(g_moe_selected_trace_replay_ids);
g_moe_selected_trace_replay_ids = NULL;
return -1;
}
fclose(fp);
fprintf(stderr,
"ds4: loaded %" PRIu64 " routed-MoE selected-id entries from %s\n",
g_moe_selected_trace_replay_count,
path);
}
if (g_moe_selected_trace_replay_pos >= g_moe_selected_trace_replay_count) {
fprintf(stderr,
"ds4: selected-id replay exhausted after %" PRIu64 " entries\n",
g_moe_selected_trace_replay_pos);
return -1;
}
memcpy(selected_ids,
g_moe_selected_trace_replay_ids + g_moe_selected_trace_replay_pos * n_selected,
(size_t)n_selected * sizeof(selected_ids[0]));
g_moe_selected_trace_replay_pos++;
return 1;
}
static int ds4_gpu_progress_enabled(void) {
return ds4_log_is_tty(stderr);
}
static void ds4_gpu_progress_begin(const char *what) {
if (!ds4_gpu_progress_enabled()) return;
fprintf(stderr, "ds4: %s...", what);
fflush(stderr);
}
static void ds4_gpu_progress_done(void) {
if (!ds4_gpu_progress_enabled()) return;
fputs(" done\n", stderr);
fflush(stderr);
}
static void ds4_gpu_progress_failed(void) {
if (!ds4_gpu_progress_enabled()) return;
fputs(" failed\n", stderr);
fflush(stderr);
}
static void ds4_gpu_model_views_clear(void) {
for (uint32_t i = 0; i < g_model_view_count; i++) {
g_model_views[i].buffer = nil;
g_model_views[i].model_map = NULL;
g_model_views[i].model_size = 0;
g_model_views[i].model_offset = 0;
g_model_views[i].bytes = 0;
}
g_model_view_count = 0;
}
static void ds4_gpu_model_views_remove_map(const void *model_map) {
uint32_t kept = 0;
for (uint32_t i = 0; i < g_model_view_count; i++) {
if (g_model_views[i].model_map != model_map)
g_model_views[kept++] = g_model_views[i];
}
for (uint32_t i = kept; i < g_model_view_count; i++)
g_model_views[i] = (ds4_gpu_model_view){0};
g_model_view_count = kept;
}
static void ds4_gpu_model_residency_clear(void) {
#if TARGET_OS_OSX
if (@available(macOS 15.0, *)) {
if (g_model_residency_set) {
if (g_model_residency_added_to_queue &&
g_queue &&
[g_queue respondsToSelector:@selector(removeResidencySet:)]) {
[g_queue removeResidencySet:g_model_residency_set];
}
[g_model_residency_set endResidency];
[g_model_residency_set removeAllAllocations];
g_model_residency_set = nil;
}
}
#endif
g_model_residency_count = 0;
g_model_residency_added_to_queue = 0;
}
/* TP sharding keeps only this rank's expert ranges warm,
* so whole-view residency requests (which would page in the full file)
* must be skipped; pages fault in lazily through the same view buffers,
* exactly like ssd-streaming mode. */
static int g_model_residency_skipped;
void ds4_gpu_model_residency_skip(int skip) {
g_model_residency_skipped = skip;
}
static int ds4_gpu_model_residency_request_views(void) {
if (g_model_view_count == 0 ||
g_ssd_streaming_mode ||
g_model_residency_skipped ||
getenv("DS4_METAL_NO_RESIDENCY") != NULL) {
return 1;
}
#if TARGET_OS_OSX
if (@available(macOS 15.0, *)) {
/*
* Register all model views as one residency set before inference. This
* is a GPU residency/budgeting hint, not a request to fault the whole
* 80+ GB file into memory. Its purpose is to make the driver see the
* complete set of large shared allocations during setup instead of
* discovering them lazily from the first measured graph command, where
* VM validation and residency accounting would look like model compute.
*/
MTLResidencySetDescriptor *desc = [[MTLResidencySetDescriptor alloc] init];
desc.label = @"ds4_model";
desc.initialCapacity = g_model_view_count;
NSError *error = nil;
g_model_residency_set = [g_device newResidencySetWithDescriptor:desc error:&error];
if (!g_model_residency_set) {
fprintf(stderr, "ds4: Metal model residency set creation failed: %s\n",
[[error localizedDescription] UTF8String]);
return 0;
}
for (uint32_t i = 0; i < g_model_view_count; i++) {
[g_model_residency_set addAllocation:g_model_views[i].buffer];
}
[g_model_residency_set commit];
[g_model_residency_set requestResidency];
if (getenv("DS4_METAL_DISABLE_QUEUE_RESIDENCY_SET") == NULL &&
g_queue &&
[g_queue respondsToSelector:@selector(addResidencySet:)]) {
[g_queue addResidencySet:g_model_residency_set];
g_model_residency_added_to_queue = 1;
}
g_model_residency_count = g_model_view_count;
}
#endif
return 1;
}
static int ds4_gpu_add_model_view_range(
const void *model_map,
uint64_t model_size,
uint64_t map_offset,
uint64_t map_size,
uint64_t max_tensor_bytes,
bool use_default_view_cap,
uint64_t *mapped_model_size_out) {
const uint64_t page = (uint64_t)getpagesize();
const uintptr_t model_addr = (uintptr_t)model_map;
if ((model_addr & (uintptr_t)(page - 1)) != 0) {
fprintf(stderr, "ds4: Metal model mmap base is not page aligned\n");
return 0;
}
if (map_offset > model_size || map_size > model_size - map_offset) {
fprintf(stderr, "ds4: Metal model mapped range is outside the GGUF mapping\n");
return 0;
}
const uint64_t page_model_offset = map_offset & ~(page - 1);
const uint64_t leading = map_offset - page_model_offset;
if (map_size > UINT64_MAX - leading ||
leading + map_size > UINT64_MAX - (page - 1))
{
fprintf(stderr, "ds4: Metal model mapped range overflows page alignment\n");
return 0;
}
const uint64_t mapped_model_size = round_up_u64(leading + map_size, page);
uint64_t max_buffer = (uint64_t)[g_device maxBufferLength];
max_buffer &= ~(page - 1);
/*
* Wrap only the tensor-data part of the GGUF file. Metadata is parsed by the
* CPU and is never dereferenced by kernels, so exposing it to Metal only
* grows the residency set and the VM range the driver must validate.
*
* Metal buffers have a device-specific maximum length, and this model is
* larger than that maximum on the target machines. Creating one no-copy
* buffer per tensor would avoid the length limit, but it would also move a
* lot of VM-object creation and residency bookkeeping into graph setup. The
* stable shape here is a tiny number of page-aligned views created once.
*
* Adjacent views intentionally overlap by more than the largest tensor, plus
* one page for alignment. That invariant guarantees every tensor lies wholly
* inside at least one view, so hot paths pass one buffer and one inner byte
* offset. We never split a weight tensor across command encoders.
*/
if (max_tensor_bytes > map_size) {
fprintf(stderr, "ds4: Metal model max tensor span is larger than a mapped tensor span\n");
return 0;
}
if (max_tensor_bytes > UINT64_MAX - (page - 1)) {
fprintf(stderr, "ds4: Metal model max tensor span overflows page alignment\n");
return 0;
}
const uint64_t max_tensor_rounded = round_up_u64(max_tensor_bytes, page);
if (max_tensor_rounded > UINT64_MAX - page) {
fprintf(stderr, "ds4: Metal model view overlap overflows page slack\n");
return 0;
}
const uint64_t overlap = max_tensor_rounded + page;
if (max_buffer == 0 || max_buffer <= overlap) {
fprintf(stderr,
"ds4: Metal maxBufferLength is too small for DS4 model views "
"(max tensor %.2f GiB, max buffer %.2f GiB)\n",
ds4_gpu_gib(max_tensor_bytes),
ds4_gpu_gib(max_buffer));
return 0;
}
uint64_t view_limit = max_buffer;
const char *view_limit_env = getenv("DS4_METAL_MODEL_VIEW_MAX_GIB");
if (view_limit_env && view_limit_env[0]) {
char *end = NULL;
unsigned long long gib = strtoull(view_limit_env, &end, 10);
if (end != view_limit_env && gib > 0) {
uint64_t env_limit = gib * 1024ull * 1024ull * 1024ull;
env_limit &= ~(page - 1);
if (env_limit > 0) view_limit = env_limit;
}
} else if (use_default_view_cap && mapped_model_size > max_buffer) {
/*
* Very large no-copy buffers can make Metal's VM validation dominate
* startup or the first graph command on multi-hundred-GiB slices. Keep
* ordinary contiguous model mappings unchanged, but let distributed
* span maps use smaller overlapping views when a range already has to
* be split.
*/
const uint64_t default_limit = 128ull * 1024ull * 1024ull * 1024ull;
if (view_limit > default_limit) view_limit = default_limit;
}
if (view_limit > max_buffer) view_limit = max_buffer;
view_limit &= ~(page - 1);
if (view_limit == 0 || view_limit <= overlap) {
fprintf(stderr,
"ds4: Metal model view cap is too small for DS4 model views "
"(cap %.2f GiB, max tensor %.2f GiB)\n",
ds4_gpu_gib(view_limit),
ds4_gpu_gib(max_tensor_bytes));
return 0;
}
const uint64_t step = view_limit - overlap;
uint64_t off = 0;
while (off < mapped_model_size) {
if (g_model_view_count == DS4_METAL_MAX_MODEL_VIEWS) {
fprintf(stderr, "ds4: Metal model needs more mapped views than expected\n");
return 0;
}
uint64_t view_bytes = mapped_model_size - off;
if (view_bytes > view_limit) view_bytes = view_limit;
id<MTLBuffer> buffer = [g_device newBufferWithBytesNoCopy:(void *)(model_addr + page_model_offset + off)
length:(NSUInteger)view_bytes
options:ds4_gpu_model_resource_options()
deallocator:nil];
if (!buffer) {
fprintf(stderr,
"ds4: Metal could not wrap mmaped model view at %.2f GiB, size %.2f GiB\n",
(double)(page_model_offset + off) / (1024.0 * 1024.0 * 1024.0),
(double)view_bytes / (1024.0 * 1024.0 * 1024.0));
return 0;
}
buffer.label = [NSString stringWithFormat:@"ds4_model_view_%u", g_model_view_count];
g_model_views[g_model_view_count].buffer = buffer;
g_model_views[g_model_view_count].model_map = model_map;
g_model_views[g_model_view_count].model_size = model_size;
g_model_views[g_model_view_count].model_offset = page_model_offset + off;
g_model_views[g_model_view_count].bytes = view_bytes;
g_model_view_count++;
g_model_wrap_count++;
g_model_wrap_bytes += view_bytes;
if (view_bytes > g_model_wrap_max_bytes) g_model_wrap_max_bytes = view_bytes;
if (off + view_bytes >= mapped_model_size) break;
off += step;
}
if (mapped_model_size_out) *mapped_model_size_out += mapped_model_size;
return 1;
}
static int ds4_gpu_finish_model_views(
double t0,
uint64_t mapped_model_size,
uint64_t display_offset) {
const double t_mapped = ds4_gpu_now_ms();
const int request_residency =
!g_ssd_streaming_mode &&
getenv("DS4_METAL_NO_RESIDENCY") == NULL;
if (request_residency) ds4_gpu_progress_begin("requesting Metal residency (may take tens of seconds)");
if (!ds4_gpu_model_residency_request_views()) {
if (request_residency) ds4_gpu_progress_failed();
return 0;
}
if (request_residency) ds4_gpu_progress_done();
const double t_resident = ds4_gpu_now_ms();
int warmed = 1;
const double t_warm0 = ds4_gpu_now_ms();
const int warm_model_views = !g_ssd_streaming_mode &&
getenv("DS4_METAL_NO_RESIDENCY") == NULL &&
getenv("DS4_METAL_NO_MODEL_WARMUP") == NULL;
if (warm_model_views) {
/*
* The first GPU command touching no-copy mmap storage can pay command
* queue setup, page-table validation, and shared-allocation residency
* costs. Sample each model view here so timed graph execution starts
* after that one-time work. The stride is intentionally coarse: this is
* a validation touch over the VM ranges, not a full model prefetch. A
* dense prefetch would create exactly the kind of memory pressure and
* startup stalls this path is designed to avoid.
*/
if (g_model_residency_skipped) {
/* TP sharding: a single command buffer binding every
* view demands residency of them all and OOMs; the engine's
* CPU-side sharded warm pre-faults the owned bytes instead. */
warmed = 1;
} else {
ds4_gpu_progress_begin("warming Metal model views");
warmed = ds4_gpu_warm_model_views();
if (warmed) ds4_gpu_progress_done();
else ds4_gpu_progress_failed();
}
}
ds4_gpu_queue_keepalive_start();
const double t_warm = ds4_gpu_now_ms();
if (ds4_gpu_model_map_log_enabled()) {
fprintf(stderr,
"ds4: Metal model views created in %.3f ms, residency requested in %.3f ms, warmup %.3f ms (mapped %.2f MiB from offset %.2f MiB)\n",
t_mapped - t0,
t_resident - t_mapped,
t_warm - t_warm0,
mapped_model_size / 1024.0 / 1024.0,
display_offset / 1024.0 / 1024.0);
}
if (!warmed) return 0;
return 1;
}
static int ds4_gpu_map_model_views(
const void *model_map,
uint64_t model_size,
uint64_t map_offset,
uint64_t map_size,
uint64_t max_tensor_bytes) {
const double t0 = ds4_gpu_now_ms();
uint64_t mapped_model_size = 0;
if (!ds4_gpu_add_model_view_range(model_map,
model_size,
map_offset,
map_size,
max_tensor_bytes,
false,
&mapped_model_size)) {
return 0;
}
return ds4_gpu_finish_model_views(t0, mapped_model_size, map_offset);
}
static id<MTLBuffer> ds4_gpu_new_transient_buffer(NSUInteger bytes, const char *label) {
if (bytes == 0) bytes = 1;
id<MTLBuffer> buffer = [g_device newBufferWithLength:bytes
options:MTLResourceStorageModeShared];
if (!buffer) {
fprintf(stderr, "ds4: failed to allocate Metal transient buffer %s (%llu bytes)\n",
label ? label : "(unnamed)", (unsigned long long)bytes);
return nil;
}
if (label) buffer.label = [NSString stringWithUTF8String:label];
/*
* CPU-filled buffers must survive until their command buffer completes.
* A local ObjC strong variable is not enough when the encoder function
* returns before the caller commits the command buffer.
*/
[g_transient_buffers addObject:buffer];
return buffer;
}
static int ds4_gpu_zero_prefix_prefill_mask_cache_enabled(void) {
if (getenv("DS4_METAL_DISABLE_ZERO_PREFIX_PREFILL_MASK_CACHE") != NULL ||
getenv("DS4_METAL_FLASH_ATTN_STAGE_PROFILE") != NULL) {
return 0;
}
return ds4_gpu_device_name_contains("M3");
}
static ds4_gpu_zero_prefix_prefill_mask_cache_entry *
ds4_gpu_get_zero_prefix_prefill_mask_cache(
uint32_t kind,
uint32_t n_tokens,
uint32_t n_comp,
uint32_t n_keys,
uint32_t window,
uint32_t ratio,
uint32_t nqptg,
uint32_t ncpsg,
bool has_kvpad,
bool bc_mask,
NSUInteger mask_bytes,
NSUInteger blk_bytes,
bool *created) {
if (created) *created = false;
if (!created || !ds4_gpu_zero_prefix_prefill_mask_cache_enabled() ||
mask_bytes == 0 || blk_bytes == 0) {
return NULL;
}
uint32_t slot = UINT32_MAX;
switch (kind) {
case DS4_GPU_PREFILL_MASK_CACHE_RAW: slot = 0; break;
case DS4_GPU_PREFILL_MASK_CACHE_RATIO4: slot = 1; break;
case DS4_GPU_PREFILL_MASK_CACHE_RATIO128: slot = 2; break;
default: return NULL;
}
ds4_gpu_zero_prefix_prefill_mask_cache_entry *entry =
&g_zero_prefix_prefill_mask_cache[slot];
if (entry->valid && entry->mask && entry->blk &&
entry->mask_bytes == mask_bytes && entry->blk_bytes == blk_bytes &&
entry->kind == kind && entry->n_tokens == n_tokens &&
entry->n_comp == n_comp && entry->n_keys == n_keys &&
entry->window == window && entry->ratio == ratio &&
entry->nqptg == nqptg && entry->ncpsg == ncpsg &&
entry->has_kvpad == has_kvpad && entry->bc_mask == bc_mask) {
return entry;
}
id<MTLBuffer> mask = [g_device newBufferWithLength:mask_bytes
options:MTLResourceStorageModeShared];
id<MTLBuffer> blk = [g_device newBufferWithLength:blk_bytes
options:MTLResourceStorageModePrivate];
if (!blk) {
blk = [g_device newBufferWithLength:blk_bytes
options:MTLResourceStorageModeShared];
}
if (!mask || !blk) return NULL;
mask.label = [NSString stringWithFormat:@"ds4_prefill_mask_cache_%u", kind];
blk.label = [NSString stringWithFormat:@"ds4_prefill_blk_cache_%u", kind];
/* A replaced entry may still be referenced by an uncommitted or in-flight
* command buffer. Keep its resources alive with the other batch-scoped
* buffers until command completion instead of mutating or releasing them. */
if (entry->mask) [g_transient_buffers addObject:entry->mask];
if (entry->blk) [g_transient_buffers addObject:entry->blk];
entry->mask = mask;
entry->blk = blk;
entry->mask_bytes = mask_bytes;
entry->blk_bytes = blk_bytes;
entry->kind = kind;
entry->n_tokens = n_tokens;
entry->n_comp = n_comp;
entry->n_keys = n_keys;
entry->window = window;
entry->ratio = ratio;
entry->nqptg = nqptg;
entry->ncpsg = ncpsg;
entry->has_kvpad = has_kvpad;
entry->bc_mask = bc_mask;
/* The caller publishes the entry only after synchronously filling mask. */
entry->valid = false;
entry->blk_ready = false;
*created = true;
return entry;
}
static void ds4_gpu_invalidate_zero_prefix_prefill_block_maps(void) {
for (uint32_t i = 0; i < DS4_GPU_PREFILL_MASK_CACHE_SLOTS; i++) {
g_zero_prefix_prefill_mask_cache[i].blk_ready = false;
}
}
static void ds4_gpu_clear_zero_prefix_prefill_mask_cache(void) {
for (uint32_t i = 0; i < DS4_GPU_PREFILL_MASK_CACHE_SLOTS; i++) {
ds4_gpu_zero_prefix_prefill_mask_cache_entry *entry =
&g_zero_prefix_prefill_mask_cache[i];
entry->mask = nil;
entry->blk = nil;
entry->mask_bytes = 0;
entry->blk_bytes = 0;
entry->kind = 0;
entry->n_tokens = 0;
entry->n_comp = 0;
entry->n_keys = 0;
entry->window = 0;
entry->ratio = 0;
entry->nqptg = 0;
entry->ncpsg = 0;
entry->has_kvpad = false;
entry->bc_mask = false;
entry->valid = false;
entry->blk_ready = false;
}
}
void ds4_gpu_release_zero_prefix_prefill_mask_cache(void) {
/* Layer-major prefill waits each layer before advancing, so its final
* release point has no outstanding cache users. Keep the guard here for
* diagnostic callers that may have an open or asynchronously flushed CB. */
if (!g_initialized || g_batch_cb ||
(g_pending_cbs && [g_pending_cbs count] != 0)) {
return;
}
ds4_gpu_clear_zero_prefix_prefill_mask_cache();
}
static id<MTLComputePipelineState> ds4_gpu_get_mul_mm_pipeline(
const char *function_name,
bool bc_inp,
bool bc_out) {
NSString *key = [NSString stringWithFormat:@"%s_bci=%d_bco=%d",
function_name, bc_inp ? 1 : 0, bc_out ? 1 : 0];
id<MTLComputePipelineState> cached = [g_pipeline_cache objectForKey:key];
if (cached) return cached;
MTLFunctionConstantValues *constants = [[MTLFunctionConstantValues alloc] init];
[constants setConstantValue:&bc_inp type:MTLDataTypeBool atIndex:700];
[constants setConstantValue:&bc_out type:MTLDataTypeBool atIndex:701];
NSError *error = nil;
NSString *name = [NSString stringWithUTF8String:function_name];
id<MTLFunction> fn = [g_library newFunctionWithName:name
constantValues:constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal %s function not found: %s\n",
function_name, [[error localizedDescription] UTF8String]);
return nil;
}
error = nil;
id<MTLComputePipelineState> pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!pipeline) {
fprintf(stderr, "ds4: Metal %s pipeline failed: %s\n",
function_name, [[error localizedDescription] UTF8String]);
return nil;
}
[g_pipeline_cache setObject:pipeline forKey:key];
return pipeline;
}
static id<MTLComputePipelineState> ds4_gpu_get_mul_mm_id_pipeline(
const char *function_name,
bool bc_inp) {
NSString *key = [NSString stringWithFormat:@"%s_bci=%d",
function_name, bc_inp ? 1 : 0];
id<MTLComputePipelineState> cached = [g_pipeline_cache objectForKey:key];
if (cached) return cached;
MTLFunctionConstantValues *constants = [[MTLFunctionConstantValues alloc] init];
[constants setConstantValue:&bc_inp type:MTLDataTypeBool atIndex:700];
NSError *error = nil;
NSString *name = [NSString stringWithUTF8String:function_name];
id<MTLFunction> fn = [g_library newFunctionWithName:name
constantValues:constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal %s function not found: %s\n",
function_name, [[error localizedDescription] UTF8String]);
return nil;
}
error = nil;
id<MTLComputePipelineState> pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!pipeline) {
fprintf(stderr, "ds4: Metal %s pipeline failed: %s\n",
function_name, [[error localizedDescription] UTF8String]);
return nil;
}
[g_pipeline_cache setObject:pipeline forKey:key];
return pipeline;
}
static id<MTLComputePipelineState> ds4_gpu_get_pipeline(
const char *function_name) {
NSString *key = [NSString stringWithFormat:@"%s", function_name];
id<MTLComputePipelineState> cached = [g_pipeline_cache objectForKey:key];
if (cached) {
/* Failed lookups are remembered as NSNull so the error prints once
* per name instead of on every dispatch. */
if (cached == (id<MTLComputePipelineState>)[NSNull null]) return nil;
return cached;
}
NSError *error = nil;
NSString *name = [NSString stringWithUTF8String:function_name];
id<MTLFunction> fn = [g_library newFunctionWithName:name];
if (!fn) {
fprintf(stderr, "ds4: Metal %s function not found\n", function_name);
[g_pipeline_cache setObject:(id<MTLComputePipelineState>)[NSNull null] forKey:key];
return nil;
}
id<MTLComputePipelineState> pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!pipeline) {
fprintf(stderr, "ds4: Metal %s pipeline failed: %s\n",
function_name, [[error localizedDescription] UTF8String]);
[g_pipeline_cache setObject:(id<MTLComputePipelineState>)[NSNull null] forKey:key];
return nil;
}
[g_pipeline_cache setObject:pipeline forKey:key];
return pipeline;
}
static int ds4_gpu_disable_hot_pipeline_statics(void) {
static int initialized;
static int disabled;
if (!initialized) {
disabled = getenv("DS4_METAL_DISABLE_HOT_PIPELINE_STATICS") != NULL;
initialized = 1;
}
return disabled;
}
static id<MTLComputePipelineState> ds4_gpu_hot_pipeline(
id<MTLComputePipelineState> pipeline,
const char *fallback_name) {
if (!ds4_gpu_disable_hot_pipeline_statics()) return pipeline;
return ds4_gpu_get_pipeline(fallback_name);
}
static int ds4_gpu_use_compressor_pair_nr4(void) {
static int initialized;
static int enabled;
if (!initialized) {
enabled = getenv("DS4_METAL_COMPRESSOR_PAIR_NR4") != NULL;
initialized = 1;
}
return enabled;
}
static int ds4_gpu_device_name_contains(const char *needle);
static int ds4_gpu_env_value_eq(const char *v, size_t n, const char *literal) {
size_t m = strlen(literal);
if (n != m) return 0;
for (size_t i = 0; i < n; i++) {
if (tolower((unsigned char)v[i]) != tolower((unsigned char)literal[i])) return 0;
}
return 1;
}
static int ds4_gpu_env_bool(const char *name) {
const char *v = getenv(name);
if (!v) return -1;
while (isspace((unsigned char)*v)) v++;
size_t n = strlen(v);
while (n > 0 && isspace((unsigned char)v[n - 1])) n--;
if (n == 0) return 1;
if (ds4_gpu_env_value_eq(v, n, "1") ||
ds4_gpu_env_value_eq(v, n, "true") ||
ds4_gpu_env_value_eq(v, n, "yes") ||
ds4_gpu_env_value_eq(v, n, "on")) {
return 1;
}
if (ds4_gpu_env_value_eq(v, n, "0") ||
ds4_gpu_env_value_eq(v, n, "false") ||
ds4_gpu_env_value_eq(v, n, "no") ||
ds4_gpu_env_value_eq(v, n, "off")) {
return 0;
}
if (!g_mpp_invalid_env_reported) {
fprintf(stderr,
"ds4: invalid Metal boolean environment value %s=%.*s; treating presence as enabled\n",
name, (int)n, v);
g_mpp_invalid_env_reported = 1;
}
return 1;
}
static uint64_t ds4_gpu_env_u64(const char *name,
uint64_t fallback,
uint64_t min_value,
uint64_t max_value) {
const char *v = getenv(name);
if (!v) return fallback;
while (isspace((unsigned char)*v)) v++;
if (!*v) return fallback;
errno = 0;
char *end = NULL;
unsigned long long parsed = strtoull(v, &end, 10);
if (end == v || errno == ERANGE) return fallback;
while (isspace((unsigned char)*end)) end++;
if (*end) return fallback;
if (parsed < min_value) return fallback;
uint64_t value = (uint64_t)parsed;
if (value > max_value) value = max_value;
return value;
}
static uint32_t ds4_gpu_glm_full_attention_max_cache_len(void) {
/*
* kernel_glm_attention_full stores one score per visible token in
* threadgroup memory, plus 256 reduction slots. Keep the default under
* the 32 KiB envelope used by current Apple GPUs while allowing short
* dense decode to step past the 4096-token prefill boundary.
*/
return 7680u;
}
static uint32_t ds4_gpu_glm_flash_attention_max_cache_len(void) {
/*
* Staged FlashAttention uses fixed-size threadgroup scratch and separate
* KV staging buffers, so it is not bound by the legacy full-attention
* kernel's one-score-per-token threadgroup-memory envelope.
*/
return 8192u;
}
static int ds4_gpu_mpp_available(void) {
return g_metal4_tensor_api_enabled && !g_quality_mode;
}
/*
* Retained Metal4 defaults live here instead of behind user-visible options.
* The public runtime has one automatic accelerated path plus the global
* DS4_METAL_DISABLE_METAL4 comparison switch. Benchmark-only alternatives that
* lost during M5 work are removed or kept out of the dispatch path so future
* changes do not accidentally turn old experiments into new modes.
*/
static int ds4_gpu_use_mpp_attn_out_low_matmul(void) {
return ds4_gpu_mpp_available();
}
enum {
DS4_METAL_ATTN_OUT_MPP_TILE_N = 64,
};
static void ds4_gpu_warn_mpp_fallback(void) {
static int warned;
if (!warned) {
fprintf(stderr, "ds4: accelerated Metal prefill matmul unavailable; falling back to legacy kernel\n");
warned = 1;
}
}
static int ds4_gpu_device_name_contains(const char *needle) {
return g_metal_device_name[0] != '\0' && strstr(g_metal_device_name, needle) != NULL;
}
int ds4_gpu_device_is_pre_m5_apple_silicon(void) {
return strncmp(g_metal_device_name, "Apple M", 7) == 0 &&
g_metal_device_name[7] >= '1' &&
g_metal_device_name[7] <= '4' &&
(g_metal_device_name[8] == '\0' ||
g_metal_device_name[8] == ' ');
}
int ds4_gpu_device_is_m5_apple_silicon(void) {
return strncmp(g_metal_device_name, "Apple M5", 8) == 0 &&
(g_metal_device_name[8] == '\0' ||
g_metal_device_name[8] == ' ');
}
static bool ds4_gpu_ported_m5_decode_feature_enabled(
const char *pre_m5_disable_env,
const char *m5_disable_env) {
if (m5_disable_env && getenv(m5_disable_env) != NULL) return false;
const bool pre_m5 = ds4_gpu_device_is_pre_m5_apple_silicon();
if (pre_m5 &&
(getenv("DS4_METAL_DISABLE_PRE_M5_DECODE_PORTS") != NULL ||
(pre_m5_disable_env && getenv(pre_m5_disable_env) != NULL))) {
return false;
}
return pre_m5 || ds4_gpu_device_is_m5_apple_silicon();
}
static int ds4_gpu_compile_tensor_probe(void) {
#if defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 260000
if (!g_device) return 0;
if (@available(macOS 26.0, *)) {
const char *src =
"#include <metal_stdlib>\n"
"#include <metal_tensor>\n"
"#include <MetalPerformancePrimitives/MetalPerformancePrimitives.h>\n"
"using namespace metal;\n"
"using namespace mpp::tensor_ops;\n"
"kernel void ds4_tensor_probe(\n"
" tensor<device half, dextents<int32_t, 2>> A [[buffer(0)]],\n"
" tensor<device half, dextents<int32_t, 2>> B [[buffer(1)]],\n"
" device float *C [[buffer(2)]],\n"
" uint2 tgid [[threadgroup_position_in_grid]]) {\n"
" auto tA = A.slice(0, (int)tgid.y);\n"
" auto tB = B.slice((int)tgid.x, 0);\n"
" matmul2d<matmul2d_descriptor(16, 16, dynamic_extent), execution_simdgroups<4>> mm;\n"
" auto cT = mm.get_destination_cooperative_tensor<decltype(tA), decltype(tB), float>();\n"
" auto sA = tA.slice(0, 0);\n"
" auto sB = tB.slice(0, 0);\n"
" mm.run(sB, sA, cT);\n"
" auto tC = tensor<device float, dextents<int32_t, 2>, tensor_inline>(C, dextents<int32_t, 2>(16, 16));\n"
" cT.store(tC);\n"
"}\n";
NSError *error = nil;
NSString *source = [NSString stringWithUTF8String:src];
id<MTLLibrary> probe_library = [g_device newLibraryWithSource:source options:[MTLCompileOptions new] error:&error];
if (!probe_library) {
fprintf(stderr, "ds4: Metal 4 tensor API probe compile failed: %s\n",
error ? [[error localizedDescription] UTF8String] : "(unknown)");
return 0;
}
id<MTLFunction> fn = [probe_library newFunctionWithName:@"ds4_tensor_probe"];
if (!fn) {
fprintf(stderr, "ds4: Metal 4 tensor API probe function missing\n");
return 0;
}
error = nil;
id<MTLComputePipelineState> pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!pipeline) {
fprintf(stderr, "ds4: Metal 4 tensor API probe pipeline failed: %s\n",
error ? [[error localizedDescription] UTF8String] : "(unknown)");
return 0;
}
return 1;
}
#endif
return 0;
}
static void ds4_gpu_detect_metal4_features(void) {
g_metal4_runtime_available = 0;
g_metal4_family_supported = 0;
g_metal4_queue_supported = 0;
g_metal4_m5_neural_accelerators_hint = 0;
g_metal4_tensor_api_enabled = 0;
g_metal4_tensor_api_compile_supported = 0;
g_metal_device_name[0] = '\0';
if (!g_device) return;
const char *name = [[g_device name] UTF8String];
if (name) {
snprintf(g_metal_device_name, sizeof(g_metal_device_name), "%s", name);
}
const int metal4_disabled = ds4_gpu_env_bool("DS4_METAL_DISABLE_METAL4") > 0;
#if defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 260000
if (@available(macOS 26.0, *)) {
g_metal4_runtime_available = 1;
g_metal4_family_supported =
!metal4_disabled && [g_device supportsFamily:MTLGPUFamilyMetal4] ? 1 : 0;
g_metal4_queue_supported = [g_device respondsToSelector:@selector(newMTL4CommandQueue)] ? 1 : 0;
/*
* Apple does not currently expose a separate "Neural Accelerator" bit
* through Metal. On public M5 systems the hardware signal is the device
* generation plus Metal 4 support, so keep this as a conservative hint.
*/
if (g_metal4_family_supported && ds4_gpu_device_name_contains("M5")) {
g_metal4_m5_neural_accelerators_hint = 1;
}
if (g_metal4_family_supported) {
const int default_enable =
ds4_gpu_device_name_contains("M5") ||
ds4_gpu_device_name_contains("M6") ||
ds4_gpu_device_name_contains("A19") ||
ds4_gpu_device_name_contains("A20");
/*
* Metal 4 TensorOps are portable in source, but on pre-M5 hardware
* they can map to ordinary shader fallbacks. Keep the automatic
* fast path restricted to hardware generations where the Neural
* Accelerator/TensorOps path is expected to pay off; older Metal
* machines continue to use the established kernels unless a future
* device is explicitly added here.
*/
if (default_enable) {
g_metal4_tensor_api_compile_supported = ds4_gpu_compile_tensor_probe();
g_metal4_tensor_api_enabled = g_metal4_tensor_api_compile_supported;
if (!g_metal4_tensor_api_enabled) {
fprintf(stderr, "ds4: Metal 4 tensor API probe failed; using legacy Metal kernels\n");
}
} else {
fprintf(stderr, "ds4: Metal 4 tensor API disabled for pre-M5/pre-A19 devices\n");
}
}
}
#endif
}
static int ds4_gpu_warm_model_views(void) {
if (g_model_view_count == 0) return 1;
id<MTLComputePipelineState> pipeline = ds4_gpu_get_pipeline("kernel_touch_u8_stride");
if (!pipeline) return 0;
uint64_t stride = 1024ull * 1024ull;
const char *stride_env = getenv("DS4_METAL_MODEL_WARMUP_STRIDE_MB");
if (stride_env && stride_env[0]) {
char *end = NULL;
unsigned long long mb = strtoull(stride_env, &end, 10);
if (end != stride_env && mb > 0 && mb <= 1024) {
stride = mb * 1024ull * 1024ull;
}
}
const char *stride_kb_env = getenv("DS4_METAL_MODEL_WARMUP_STRIDE_KB");
if (stride_kb_env && stride_kb_env[0]) {
char *end = NULL;
unsigned long long kb = strtoull(stride_kb_env, &end, 10);
if (end != stride_kb_env && kb > 0 && kb <= 1024ull * 1024ull) {
stride = kb * 1024ull;
const uint64_t page = (uint64_t)getpagesize();
if (stride < page) stride = page;
}
}
uint64_t total_touches = 0;
for (uint32_t i = 0; i < g_model_view_count; i++) {
total_touches += (g_model_views[i].bytes + stride - 1) / stride;
}
if (total_touches == 0 || total_touches > (uint64_t)NSUIntegerMax) return 0;
const NSUInteger out_bytes = (NSUInteger)total_touches;
id<MTLBuffer> out = [g_device newBufferWithLength:out_bytes
options:MTLResourceStorageModeShared];
if (!out) {
fprintf(stderr, "ds4: Metal model warmup scratch allocation failed\n");
return 0;
}
out.label = @"ds4_model_warmup";
id<MTLCommandBuffer> cb = ds4_gpu_new_command_buffer();
if (!cb) {
fprintf(stderr, "ds4: Metal model warmup command buffer allocation failed\n");
return 0;
}
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
uint64_t dst_offset = 0;
for (uint32_t i = 0; i < g_model_view_count; i++) {
const uint64_t bytes = g_model_views[i].bytes;
const uint64_t n = (bytes + stride - 1) / stride;
[enc setBuffer:g_model_views[i].buffer offset:0 atIndex:0];
[enc setBuffer:out offset:0 atIndex:1];
[enc setBytes:&stride length:sizeof(stride) atIndex:2];
[enc setBytes:&bytes length:sizeof(bytes) atIndex:3];
[enc setBytes:&dst_offset length:sizeof(dst_offset) atIndex:4];
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)((n + 255) / 256), 1, 1)
threadsPerThreadgroup:MTLSizeMake(256, 1, 1)];
dst_offset += n;
}
ds4_gpu_end_compute_encoder(cb, enc);
[cb commit];
[cb waitUntilCompleted];
if (cb.status == MTLCommandBufferStatusError) {
fprintf(stderr, "ds4: Metal model warmup failed: %s\n",
[[cb.error localizedDescription] UTF8String]);
return 0;
}
return 1;
}
static const char *ds4_gpu_mul_mm_id_map0_name(uint32_t ne20) {
switch (ne20) {
case 1: return "kernel_mul_mm_id_map0_ne20_1";
case 2: return "kernel_mul_mm_id_map0_ne20_2";
case 4: return "kernel_mul_mm_id_map0_ne20_4";
case 5: return "kernel_mul_mm_id_map0_ne20_5";
case 6: return "kernel_mul_mm_id_map0_ne20_6";
case 8: return "kernel_mul_mm_id_map0_ne20_8";
case 10: return "kernel_mul_mm_id_map0_ne20_10";
case 16: return "kernel_mul_mm_id_map0_ne20_16";
case 22: return "kernel_mul_mm_id_map0_ne20_22";
default: return NULL;
}
}
static void ds4_gpu_decode_pipeline_fast_cache_reset(void) {
for (uint32_t i = 0; i < DS4_METAL_DECODE_PIPELINE_FAST_CACHE_SLOTS; i++) {
ds4_gpu_decode_pipeline_fast_cache_entry *entry =
&g_decode_pipeline_fast_cache[i];
/* ARC must see every strong release; do not memset this table. */
entry->pipeline = nil;
entry->hash = 0;
entry->nsg = 0;
entry->name_len = 0;
entry->used = false;
entry->name[0] = '\0';
}
g_decode_pipeline_fast_lookup_active = false;
g_decode_pipeline_fast_cache_entries = 0;
}
static bool ds4_gpu_decode_pipeline_fast_key(
const char *function_name,
int16_t nsg,
int16_t nxpsg,
uint16_t *name_len_out,
uint64_t *hash_out) {
if (!function_name || !name_len_out || !hash_out) return false;
uint64_t hash = UINT64_C(14695981039346656037);
size_t name_len = 0;
while (function_name[name_len] != '\0') {
if (name_len + 1u >= DS4_METAL_DECODE_PIPELINE_FAST_NAME_BYTES) {
return false;
}
hash ^= (uint8_t)function_name[name_len++];
hash *= UINT64_C(1099511628211);
}
const uint16_t nsg_bits = (uint16_t)nsg;
hash ^= (uint8_t)nsg_bits;
hash *= UINT64_C(1099511628211);
hash ^= (uint8_t)(nsg_bits >> 8u);
hash *= UINT64_C(1099511628211);
const uint16_t nxpsg_bits = (uint16_t)nxpsg;
hash ^= (uint8_t)nxpsg_bits;
hash *= UINT64_C(1099511628211);
hash ^= (uint8_t)(nxpsg_bits >> 8u);
hash *= UINT64_C(1099511628211);
*name_len_out = (uint16_t)name_len;
*hash_out = hash;
return true;
}
static id<MTLComputePipelineState> ds4_gpu_decode_pipeline_fast_cache_lookup(
const char *function_name,
int16_t nsg,
int16_t nxpsg,
uint16_t name_len,
uint64_t hash) {
const uint32_t mask = DS4_METAL_DECODE_PIPELINE_FAST_CACHE_SLOTS - 1u;
uint32_t slot = (uint32_t)hash & mask;
for (uint32_t probe = 0;
probe < DS4_METAL_DECODE_PIPELINE_FAST_CACHE_SLOTS;
probe++, slot = (slot + 1u) & mask) {
ds4_gpu_decode_pipeline_fast_cache_entry *entry =
&g_decode_pipeline_fast_cache[slot];
if (!entry->used) break;
if (entry->hash == hash &&
entry->nsg == nsg &&
entry->nxpsg == nxpsg &&
entry->name_len == name_len &&
memcmp(entry->name, function_name, name_len) == 0) {
return entry->pipeline;
}
}
return nil;
}
static void ds4_gpu_decode_pipeline_fast_cache_insert(
const char *function_name,
int16_t nsg,
int16_t nxpsg,
uint16_t name_len,
uint64_t hash,
id<MTLComputePipelineState> pipeline) {
if (!pipeline ||
g_decode_pipeline_fast_cache_entries >=
DS4_METAL_DECODE_PIPELINE_FAST_CACHE_SLOTS) {
return;
}
const uint32_t mask = DS4_METAL_DECODE_PIPELINE_FAST_CACHE_SLOTS - 1u;
uint32_t slot = (uint32_t)hash & mask;
for (uint32_t probe = 0;
probe < DS4_METAL_DECODE_PIPELINE_FAST_CACHE_SLOTS;
probe++, slot = (slot + 1u) & mask) {
ds4_gpu_decode_pipeline_fast_cache_entry *entry =
&g_decode_pipeline_fast_cache[slot];
if (entry->used) {
if (entry->hash == hash &&
entry->nsg == nsg &&
entry->nxpsg == nxpsg &&
entry->name_len == name_len &&
memcmp(entry->name, function_name, name_len) == 0) {
entry->pipeline = pipeline;
return;
}
continue;
}
memcpy(entry->name, function_name, name_len);
entry->name[name_len] = '\0';
entry->hash = hash;
entry->nsg = nsg;
entry->nxpsg = nxpsg;
entry->name_len = name_len;
entry->pipeline = pipeline;
entry->used = true;
g_decode_pipeline_fast_cache_entries++;
return;
}
}
int ds4_gpu_set_decode_pipeline_fast_lookup(int enabled) {
const int previous = g_decode_pipeline_fast_lookup_active ? 1 : 0;
g_decode_pipeline_fast_lookup_active = enabled != 0;
return previous;
}
static id<MTLComputePipelineState> ds4_gpu_get_mul_mv_pipeline(
const char *function_name,
int16_t nsg) {
uint16_t fast_name_len = 0;
uint64_t fast_hash = 0;
const bool fast_key_valid =
g_decode_pipeline_fast_lookup_active &&
ds4_gpu_decode_pipeline_fast_key(
function_name, nsg,
DS4_METAL_DECODE_PIPELINE_FAST_NXPSG_NONE,
&fast_name_len, &fast_hash);
if (fast_key_valid) {
id<MTLComputePipelineState> fast_cached =
ds4_gpu_decode_pipeline_fast_cache_lookup(
function_name, nsg,
DS4_METAL_DECODE_PIPELINE_FAST_NXPSG_NONE,
fast_name_len, fast_hash);
if (fast_cached) return fast_cached;
}
NSString *key = [NSString stringWithFormat:@"%s_nsg=%d", function_name, (int)nsg];
id<MTLComputePipelineState> cached = [g_pipeline_cache objectForKey:key];
if (cached) {
if (fast_key_valid) {
ds4_gpu_decode_pipeline_fast_cache_insert(
function_name, nsg,
DS4_METAL_DECODE_PIPELINE_FAST_NXPSG_NONE,
fast_name_len, fast_hash, cached);
}
return cached;
}
MTLFunctionConstantValues *constants = [[MTLFunctionConstantValues alloc] init];
[constants setConstantValue:&nsg type:MTLDataTypeShort atIndex:600];
NSError *error = nil;
NSString *name = [NSString stringWithUTF8String:function_name];
id<MTLFunction> fn = [g_library newFunctionWithName:name
constantValues:constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal %s function not found: %s\n",
function_name, [[error localizedDescription] UTF8String]);
return nil;
}
error = nil;
id<MTLComputePipelineState> pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!pipeline) {
fprintf(stderr, "ds4: Metal %s pipeline failed: %s\n",
function_name, [[error localizedDescription] UTF8String]);
return nil;
}
[g_pipeline_cache setObject:pipeline forKey:key];
if (fast_key_valid) {
ds4_gpu_decode_pipeline_fast_cache_insert(
function_name, nsg,
DS4_METAL_DECODE_PIPELINE_FAST_NXPSG_NONE,
fast_name_len, fast_hash, pipeline);
}
return pipeline;
}
/* The ordinary mul-mv cache key covers function name and nsg only. Keep the
* descriptor-hinted PSO separate so a cache hit cannot erase this compiler
* contract or substitute it for the fallback pipeline. */
static id<MTLComputePipelineState> ds4_gpu_new_mul_mv_tg_multiple_pipeline(
const char *function_name,
int16_t nsg) {
MTLFunctionConstantValues *constants = [[MTLFunctionConstantValues alloc] init];
[constants setConstantValue:&nsg type:MTLDataTypeShort atIndex:600];
NSError *error = nil;
NSString *name = [NSString stringWithUTF8String:function_name];
id<MTLFunction> fn = [g_library newFunctionWithName:name
constantValues:constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal %s tg-multiple function not found: %s\n",
function_name, [[error localizedDescription] UTF8String]);
return nil;
}
MTLComputePipelineDescriptor *descriptor = [[MTLComputePipelineDescriptor alloc] init];
descriptor.label = name;
descriptor.computeFunction = fn;
descriptor.threadGroupSizeIsMultipleOfThreadExecutionWidth = YES;
error = nil;
id<MTLComputePipelineState> pipeline =
[g_device newComputePipelineStateWithDescriptor:descriptor
options:MTLPipelineOptionNone
reflection:nil
error:&error];
if (!pipeline) {
fprintf(stderr, "ds4: Metal %s tg-multiple pipeline failed: %s\n",
function_name, [[error localizedDescription] UTF8String]);
return nil;
}
return pipeline;
}
int ds4_gpu_test_decode_pipeline_fast_lookup(void) {
if (!g_initialized || !g_pipeline_cache || !g_library || !g_device) {
return 0;
}
static const char base_name[] = "kernel_mul_mv_id_mxfp4_f32";
char copied_name[sizeof(base_name)];
memcpy(copied_name, base_name, sizeof(base_name));
ds4_gpu_decode_pipeline_fast_cache_reset();
id<MTLComputePipelineState> baseline =
ds4_gpu_get_mul_mv_pipeline(base_name, 2);
if (!baseline ||
g_decode_pipeline_fast_cache_entries != 0u) {
ds4_gpu_decode_pipeline_fast_cache_reset();
return 0;
}
(void)ds4_gpu_set_decode_pipeline_fast_lookup(1);
id<MTLComputePipelineState> populated =
ds4_gpu_get_mul_mv_pipeline(base_name, 2);
id<MTLComputePipelineState> hit =
ds4_gpu_get_mul_mv_pipeline(copied_name, 2);
uint16_t copied_name_len = 0;
uint64_t copied_hash = 0;
const bool copied_key_valid =
ds4_gpu_decode_pipeline_fast_key(
copied_name, 2,
DS4_METAL_DECODE_PIPELINE_FAST_NXPSG_NONE,
&copied_name_len, &copied_hash);
id<MTLComputePipelineState> direct_hit =
copied_key_valid ?
ds4_gpu_decode_pipeline_fast_cache_lookup(
copied_name, 2,
DS4_METAL_DECODE_PIPELINE_FAST_NXPSG_NONE,
copied_name_len, copied_hash) : nil;
id<MTLComputePipelineState> distinct_name =
ds4_gpu_get_mul_mv_pipeline(
"kernel_mul_mv_id_mxfp4_pair_swiglu_f32", 2);
id<MTLComputePipelineState> distinct_nsg =
ds4_gpu_get_mul_mv_pipeline(base_name, 4);
const bool populated_and_hit =
populated == baseline &&
hit == baseline &&
direct_hit == baseline &&
distinct_name != nil &&
distinct_nsg != nil &&
g_decode_pipeline_fast_cache_entries == 3u;
(void)ds4_gpu_set_decode_pipeline_fast_lookup(0);
const uint32_t entries_before_disabled_lookup =
g_decode_pipeline_fast_cache_entries;
id<MTLComputePipelineState> disabled =
ds4_gpu_get_mul_mv_pipeline(base_name, 2);
const bool inactive_unchanged =
disabled == baseline &&
g_decode_pipeline_fast_cache_entries == entries_before_disabled_lookup;
ds4_gpu_decode_pipeline_fast_cache_reset();
(void)ds4_gpu_set_decode_pipeline_fast_lookup(1);
bool filled = true;
for (uint32_t i = 0;
i < DS4_METAL_DECODE_PIPELINE_FAST_CACHE_SLOTS && filled;
i++) {
char synthetic_name[DS4_METAL_DECODE_PIPELINE_FAST_NAME_BYTES];
const int written = snprintf(
synthetic_name, sizeof(synthetic_name),
"ds4_test_decode_pipeline_fast_%u", i);
uint16_t synthetic_name_len = 0;
uint64_t synthetic_hash = 0;
filled = written > 0 &&
(size_t)written < sizeof(synthetic_name) &&
ds4_gpu_decode_pipeline_fast_key(
synthetic_name, 2,
DS4_METAL_DECODE_PIPELINE_FAST_NXPSG_NONE,
&synthetic_name_len, &synthetic_hash);
if (filled) {
ds4_gpu_decode_pipeline_fast_cache_insert(
synthetic_name, 2,
DS4_METAL_DECODE_PIPELINE_FAST_NXPSG_NONE,
synthetic_name_len,
synthetic_hash, baseline);
}
}
id<MTLComputePipelineState> full_fallback =
ds4_gpu_get_mul_mv_pipeline(base_name, 2);
const bool full_table_unchanged =
filled && full_fallback == baseline &&
g_decode_pipeline_fast_cache_entries ==
DS4_METAL_DECODE_PIPELINE_FAST_CACHE_SLOTS;
ds4_gpu_decode_pipeline_fast_cache_reset();
return populated_and_hit && inactive_unchanged && full_table_unchanged;
}
/*
* Exercises the second SIMD-group constant key shape used by
* ds4_gpu_get_mul_mv_ext_pipeline (name, nsg, nxpsg). Confirms the extended key
* populates and hits, distinguishes a real nxpsg from the single-constant
* sentinel, and coexists with the single-constant mv entries in the shared
* 64-slot table. Uses baseline as a stand-in pipeline object so the guard does
* not depend on compiling a specific extended-kernel function.
*/
int ds4_gpu_test_decode_pipeline_fast_lookup_ext(void) {
if (!g_initialized || !g_pipeline_cache || !g_library || !g_device) {
return 0;
}
static const char base_name[] = "kernel_mul_mv_id_mxfp4_f32";
char copied_name[sizeof(base_name)];
memcpy(copied_name, base_name, sizeof(base_name));
id<MTLComputePipelineState> baseline =
ds4_gpu_get_mul_mv_pipeline(base_name, 2);
if (!baseline) return 0;
ds4_gpu_decode_pipeline_fast_cache_reset();
(void)ds4_gpu_set_decode_pipeline_fast_lookup(1);
const int16_t ext_nsg = 2;
const int16_t ext_nxpsg = 4;
uint16_t ext_name_len = 0;
uint64_t ext_hash = 0;
const bool ext_key_valid =
ds4_gpu_decode_pipeline_fast_key(
copied_name, ext_nsg, ext_nxpsg, &ext_name_len, &ext_hash);
id<MTLComputePipelineState> ext_populated = nil;
if (ext_key_valid) {
ds4_gpu_decode_pipeline_fast_cache_insert(
copied_name, ext_nsg, ext_nxpsg, ext_name_len, ext_hash, baseline);
ext_populated = ds4_gpu_decode_pipeline_fast_cache_lookup(
copied_name, ext_nsg, ext_nxpsg, ext_name_len, ext_hash);
}
/*
* The single-constant sentinel must not match an extended key even when the
* name and nsg agree, proving the two key shapes never alias.
*/
uint16_t none_name_len = 0;
uint64_t none_hash = 0;
const bool none_key_valid =
ds4_gpu_decode_pipeline_fast_key(
copied_name, ext_nsg,
DS4_METAL_DECODE_PIPELINE_FAST_NXPSG_NONE,
&none_name_len, &none_hash);
id<MTLComputePipelineState> none_hit =
none_key_valid ?
ds4_gpu_decode_pipeline_fast_cache_lookup(
copied_name, ext_nsg,
DS4_METAL_DECODE_PIPELINE_FAST_NXPSG_NONE,
none_name_len, none_hash) : nil;
const int16_t alt_nxpsg = 8;
uint16_t alt_name_len = 0;
uint64_t alt_hash = 0;
const bool alt_key_valid =
ds4_gpu_decode_pipeline_fast_key(
copied_name, ext_nsg, alt_nxpsg, &alt_name_len, &alt_hash);
id<MTLComputePipelineState> alt_hit =
alt_key_valid ?
ds4_gpu_decode_pipeline_fast_cache_lookup(
copied_name, ext_nsg, alt_nxpsg, alt_name_len, alt_hash) : nil;
const bool ext_populated_and_hit =
ext_key_valid &&
ext_populated == baseline &&
g_decode_pipeline_fast_cache_entries == 1u &&
none_hit == nil &&
alt_hit == nil;
(void)ds4_gpu_set_decode_pipeline_fast_lookup(0);
ds4_gpu_decode_pipeline_fast_cache_reset();
return ext_populated_and_hit;
}
static id<MTLComputePipelineState> ds4_gpu_get_mul_mv_ext_pipeline(
const char *function_name,
int16_t nsg,
int16_t nxpsg) {
uint16_t fast_name_len = 0;
uint64_t fast_hash = 0;
const bool fast_key_valid =
g_decode_pipeline_fast_lookup_active &&
ds4_gpu_decode_pipeline_fast_key(
function_name, nsg, nxpsg, &fast_name_len, &fast_hash);
if (fast_key_valid) {
id<MTLComputePipelineState> fast_cached =
ds4_gpu_decode_pipeline_fast_cache_lookup(
function_name, nsg, nxpsg, fast_name_len, fast_hash);
if (fast_cached) return fast_cached;
}
NSString *key = [NSString stringWithFormat:@"%s_nsg=%d_nxpsg=%d",
function_name, (int)nsg, (int)nxpsg];
id<MTLComputePipelineState> cached = [g_pipeline_cache objectForKey:key];
if (cached) {
if (fast_key_valid) {
ds4_gpu_decode_pipeline_fast_cache_insert(
function_name, nsg, nxpsg, fast_name_len, fast_hash, cached);
}
return cached;
}
MTLFunctionConstantValues *constants = [[MTLFunctionConstantValues alloc] init];
[constants setConstantValue:&nsg type:MTLDataTypeShort atIndex:600];
[constants setConstantValue:&nxpsg type:MTLDataTypeShort atIndex:601];
NSError *error = nil;
NSString *name = [NSString stringWithUTF8String:function_name];
id<MTLFunction> fn = [g_library newFunctionWithName:name
constantValues:constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal %s function not found: %s\n",
function_name, [[error localizedDescription] UTF8String]);
return nil;
}
error = nil;
id<MTLComputePipelineState> pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!pipeline) {
fprintf(stderr, "ds4: Metal %s pipeline failed: %s\n",
function_name, [[error localizedDescription] UTF8String]);
return nil;
}
[g_pipeline_cache setObject:pipeline forKey:key];
if (fast_key_valid) {
ds4_gpu_decode_pipeline_fast_cache_insert(
function_name, nsg, nxpsg, fast_name_len, fast_hash, pipeline);
}
return pipeline;
}
static id<MTLComputePipelineState> ds4_gpu_get_flash_attn_pad_pipeline(
bool has_mask,
int32_t ncpsg) {
/*
* Decode calls this once per layer with identical arguments, so memoize
* the last hit and skip the NSString key + dictionary lookup on the hot
* path. The generic cache below remains the fallback for new variants.
* The rollback switch restores the dictionary path for same-binary A/B.
*/
static struct {
bool m;
int32_t nc;
id<MTLComputePipelineState> pipeline;
} memo;
const bool memo_disabled =
getenv("DS4_METAL_DISABLE_PRE_M5_FLASH_ATTN_PAD_BLK_MEMO") != NULL;
if (!memo_disabled && memo.pipeline && memo.m == has_mask && memo.nc == ncpsg) {
return memo.pipeline;
}
NSString *key = [NSString stringWithFormat:@"kernel_flash_attn_ext_pad_mask=%d_ncpsg=%d",
has_mask ? 1 : 0, (int)ncpsg];
id<MTLComputePipelineState> cached = [g_pipeline_cache objectForKey:key];
if (cached) {
if (!memo_disabled) {
memo = (typeof(memo)){ has_mask, ncpsg, cached };
}
return cached;
}
MTLFunctionConstantValues *constants = [[MTLFunctionConstantValues alloc] init];
[constants setConstantValue:&has_mask type:MTLDataTypeBool atIndex:100];
[constants setConstantValue:&ncpsg type:MTLDataTypeInt atIndex:125];
NSError *error = nil;
id<MTLFunction> fn = [g_library newFunctionWithName:@"kernel_flash_attn_ext_pad"
constantValues:constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_flash_attn_ext_pad function not found: %s\n",
[[error localizedDescription] UTF8String]);
return nil;
}
error = nil;
id<MTLComputePipelineState> pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!pipeline) {
fprintf(stderr, "ds4: Metal kernel_flash_attn_ext_pad pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
return nil;
}
[g_pipeline_cache setObject:pipeline forKey:key];
if (!memo_disabled) {
memo = (typeof(memo)){ has_mask, ncpsg, pipeline };
}
return pipeline;
}
static id<MTLComputePipelineState> ds4_gpu_get_flash_attn_blk_pipeline(
int32_t nqptg,
int32_t ncpsg) {
/*
* Decode calls this once per layer with identical arguments, so memoize
* the last hit and skip the NSString key + dictionary lookup on the hot
* path. The generic cache below remains the fallback for new variants.
* The rollback switch restores the dictionary path for same-binary A/B.
*/
static struct {
int32_t nq;
int32_t nc;
id<MTLComputePipelineState> pipeline;
} memo;
const bool memo_disabled =
getenv("DS4_METAL_DISABLE_PRE_M5_FLASH_ATTN_PAD_BLK_MEMO") != NULL;
if (!memo_disabled && memo.pipeline && memo.nq == nqptg && memo.nc == ncpsg) {
return memo.pipeline;
}
NSString *key = [NSString stringWithFormat:@"kernel_flash_attn_ext_blk_nqptg=%d_ncpsg=%d",
(int)nqptg, (int)ncpsg];
id<MTLComputePipelineState> cached = [g_pipeline_cache objectForKey:key];
if (cached) {
if (!memo_disabled) {
memo = (typeof(memo)){ nqptg, ncpsg, cached };
}
return cached;
}
MTLFunctionConstantValues *constants = [[MTLFunctionConstantValues alloc] init];
[constants setConstantValue:&nqptg type:MTLDataTypeInt atIndex:224];
[constants setConstantValue:&ncpsg type:MTLDataTypeInt atIndex:225];
NSError *error = nil;
id<MTLFunction> fn = [g_library newFunctionWithName:@"kernel_flash_attn_ext_blk"
constantValues:constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_flash_attn_ext_blk function not found: %s\n",
[[error localizedDescription] UTF8String]);
return nil;
}
error = nil;
id<MTLComputePipelineState> pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!pipeline) {
fprintf(stderr, "ds4: Metal kernel_flash_attn_ext_blk pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
return nil;
}
[g_pipeline_cache setObject:pipeline forKey:key];
if (!memo_disabled) {
memo = (typeof(memo)){ nqptg, ncpsg, pipeline };
}
return pipeline;
}
static id<MTLComputePipelineState> ds4_gpu_get_flash_attn_pipeline(
const char *function_name,
bool has_mask,
bool has_sinks,
bool has_bias,
bool has_scap,
bool has_kvpad,
bool bc_mask,
int32_t ns10,
int32_t ns20,
int32_t nsg) {
/*
* Prefill and batched decode call this once per layer with identical
* arguments, so memoize the last hit and skip the NSString key + dictionary
* lookup on the hot path. The generic cache below remains the fallback for
* new variants. The rollback switch restores the dictionary path for
* same-binary A/B.
*/
static struct {
const char *fn;
bool m, s, b, c, k, bc;
int32_t n10, n20, sg;
id<MTLComputePipelineState> pipeline;
} memo;
const bool memo_disabled =
getenv("DS4_METAL_DISABLE_PRE_M5_FLASH_ATTN_BATCHED_MEMO") != NULL;
if (!memo_disabled && memo.pipeline && memo.fn != NULL &&
strcmp(memo.fn, function_name) == 0 &&
memo.m == has_mask && memo.s == has_sinks && memo.b == has_bias &&
memo.c == has_scap && memo.k == has_kvpad && memo.bc == bc_mask &&
memo.n10 == ns10 && memo.n20 == ns20 && memo.sg == nsg) {
return memo.pipeline;
}
NSString *key = [NSString stringWithFormat:@"%s_mask=%d_sinks=%d_bias=%d_scap=%d_kvpad=%d_bcm=%d_ns10=%d_ns20=%d_nsg=%d",
function_name,
has_mask ? 1 : 0,
has_sinks ? 1 : 0,
has_bias ? 1 : 0,
has_scap ? 1 : 0,
has_kvpad ? 1 : 0,
bc_mask ? 1 : 0,
(int)ns10,
(int)ns20,
(int)nsg];
id<MTLComputePipelineState> cached = [g_pipeline_cache objectForKey:key];
if (cached) {
if (!memo_disabled) {
memo = (typeof(memo)){ function_name, has_mask, has_sinks, has_bias,
has_scap, has_kvpad, bc_mask, ns10, ns20,
nsg, cached };
}
return cached;
}
MTLFunctionConstantValues *constants = [[MTLFunctionConstantValues alloc] init];
[constants setConstantValue:&has_mask type:MTLDataTypeBool atIndex:300];
[constants setConstantValue:&has_sinks type:MTLDataTypeBool atIndex:301];
[constants setConstantValue:&has_bias type:MTLDataTypeBool atIndex:302];
[constants setConstantValue:&has_scap type:MTLDataTypeBool atIndex:303];
[constants setConstantValue:&has_kvpad type:MTLDataTypeBool atIndex:304];
[constants setConstantValue:&bc_mask type:MTLDataTypeBool atIndex:310];
[constants setConstantValue:&ns10 type:MTLDataTypeInt atIndex:320];
[constants setConstantValue:&ns20 type:MTLDataTypeInt atIndex:321];
[constants setConstantValue:&nsg type:MTLDataTypeInt atIndex:322];
NSError *error = nil;
NSString *name = [NSString stringWithUTF8String:function_name];
id<MTLFunction> fn = [g_library newFunctionWithName:name
constantValues:constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal %s function not found: %s\n",
function_name, [[error localizedDescription] UTF8String]);
return nil;
}
error = nil;
id<MTLComputePipelineState> pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!pipeline) {
fprintf(stderr, "ds4: Metal %s pipeline failed: %s\n",
function_name, [[error localizedDescription] UTF8String]);
return nil;
}
[g_pipeline_cache setObject:pipeline forKey:key];
if (!memo_disabled) {
memo = (typeof(memo)){ function_name, has_mask, has_sinks, has_bias,
has_scap, has_kvpad, bc_mask, ns10, ns20,
nsg, pipeline };
}
return pipeline;
}
static id<MTLComputePipelineState> ds4_gpu_get_flash_attn_vec_pipeline(
const char *function_name,
bool has_mask,
bool has_sinks,
bool has_bias,
bool has_scap,
bool has_kvpad,
bool shared_kvpad,
int32_t ns10,
int32_t ns20,
int32_t nsg,
int32_t nwg) {
/*
* Decode calls this once per layer with identical arguments, so memoize
* the last hit and skip the NSString key + dictionary lookup on the hot
* path. The generic cache below remains the fallback for new variants.
*/
static struct {
const char *fn;
bool m, s, b, c, k, sp;
int32_t n10, n20, sg, wg;
id<MTLComputePipelineState> pipeline;
} memo;
if (memo.pipeline && memo.fn != NULL && strcmp(memo.fn, function_name) == 0 &&
memo.m == has_mask && memo.s == has_sinks && memo.b == has_bias &&
memo.c == has_scap && memo.k == has_kvpad && memo.sp == shared_kvpad &&
memo.n10 == ns10 && memo.n20 == ns20 && memo.sg == nsg && memo.wg == nwg) {
return memo.pipeline;
}
NSString *key = [NSString stringWithFormat:@"%s_mask=%d_sinks=%d_bias=%d_scap=%d_kvpad=%d_sharedpad=%d_ns10=%d_ns20=%d_nsg=%d_nwg=%d",
function_name,
has_mask ? 1 : 0,
has_sinks ? 1 : 0,
has_bias ? 1 : 0,
has_scap ? 1 : 0,
has_kvpad ? 1 : 0,
shared_kvpad ? 1 : 0,
(int)ns10,
(int)ns20,
(int)nsg,
(int)nwg];
id<MTLComputePipelineState> cached = [g_pipeline_cache objectForKey:key];
if (cached) {
memo = (typeof(memo)){ function_name, has_mask, has_sinks, has_bias,
has_scap, has_kvpad, shared_kvpad, ns10, ns20,
nsg, nwg, cached };
return cached;
}
MTLFunctionConstantValues *constants = [[MTLFunctionConstantValues alloc] init];
[constants setConstantValue:&has_mask type:MTLDataTypeBool atIndex:400];
[constants setConstantValue:&has_sinks type:MTLDataTypeBool atIndex:401];
[constants setConstantValue:&has_bias type:MTLDataTypeBool atIndex:402];
[constants setConstantValue:&has_scap type:MTLDataTypeBool atIndex:403];
[constants setConstantValue:&has_kvpad type:MTLDataTypeBool atIndex:404];
[constants setConstantValue:&shared_kvpad type:MTLDataTypeBool atIndex:405];
[constants setConstantValue:&ns10 type:MTLDataTypeInt atIndex:420];
[constants setConstantValue:&ns20 type:MTLDataTypeInt atIndex:421];
[constants setConstantValue:&nsg type:MTLDataTypeInt atIndex:422];
[constants setConstantValue:&nwg type:MTLDataTypeInt atIndex:423];
NSError *error = nil;
NSString *name = [NSString stringWithUTF8String:function_name];
id<MTLFunction> fn = [g_library newFunctionWithName:name
constantValues:constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal %s function not found: %s\n",
function_name, [[error localizedDescription] UTF8String]);
return nil;
}
error = nil;
id<MTLComputePipelineState> pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!pipeline) {
fprintf(stderr, "ds4: Metal %s pipeline failed: %s\n",
function_name, [[error localizedDescription] UTF8String]);
return nil;
}
[g_pipeline_cache setObject:pipeline forKey:key];
memo = (typeof(memo)){ function_name, has_mask, has_sinks, has_bias,
has_scap, has_kvpad, shared_kvpad, ns10, ns20,
nsg, nwg, pipeline };
return pipeline;
}
/* Pipeline for the RoPE-fused decode reduce. Same function constants as the
* plain reduce so the split-K geometry is identical. */
static id<MTLComputePipelineState> ds4_gpu_get_flash_attn_reduce_rope_pipeline(
int32_t dv,
int32_t nwg) {
static int32_t memo_dv, memo_nwg;
static id<MTLComputePipelineState> memo_pipeline;
if (memo_pipeline && memo_dv == dv && memo_nwg == nwg) {
return memo_pipeline;
}
NSString *key = [NSString stringWithFormat:@"kernel_flash_attn_ext_vec_reduce_rope_dv=%d_nwg=%d",
(int)dv, (int)nwg];
id<MTLComputePipelineState> cached = [g_pipeline_cache objectForKey:key];
if (cached) {
memo_dv = dv; memo_nwg = nwg; memo_pipeline = cached;
return cached;
}
MTLFunctionConstantValues *constants = [[MTLFunctionConstantValues alloc] init];
[constants setConstantValue:&dv type:MTLDataTypeInt atIndex:500];
[constants setConstantValue:&nwg type:MTLDataTypeInt atIndex:501];
NSError *error = nil;
id<MTLFunction> fn = [g_library newFunctionWithName:@"kernel_flash_attn_ext_vec_reduce_rope"
constantValues:constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_flash_attn_ext_vec_reduce_rope not found: %s\n",
[[error localizedDescription] UTF8String]);
return nil;
}
error = nil;
id<MTLComputePipelineState> pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!pipeline) {
fprintf(stderr, "ds4: Metal kernel_flash_attn_ext_vec_reduce_rope pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
return nil;
}
[g_pipeline_cache setObject:pipeline forKey:key];
memo_dv = dv; memo_nwg = nwg; memo_pipeline = pipeline;
return pipeline;
}
int ds4_gpu_decode_attn_rope_fuse_available(void) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (g_rope_tail_inplace_pair_affine_pipeline == nil) return 0;
if (getenv("DS4_METAL_DISABLE_INPLACE_ROPE_PAIR") != NULL) return 0;
if (getenv("DS4_METAL_DISABLE_AFFINE_ROPE_PAIR") != NULL) return 0;
if (!ds4_gpu_device_name_contains("M3") && !ds4_gpu_device_name_contains("M5")) return 0;
return 1;
}
static id<MTLComputePipelineState> ds4_gpu_get_flash_attn_reduce_pipeline(
int32_t dv,
int32_t nwg) {
/* Same per-layer memo pattern as the vec getter above. */
static int32_t memo_dv, memo_nwg;
static id<MTLComputePipelineState> memo_pipeline;
if (memo_pipeline && memo_dv == dv && memo_nwg == nwg) {
return memo_pipeline;
}
NSString *key = [NSString stringWithFormat:@"kernel_flash_attn_ext_vec_reduce_dv=%d_nwg=%d",
(int)dv, (int)nwg];
id<MTLComputePipelineState> cached = [g_pipeline_cache objectForKey:key];
if (cached) {
memo_dv = dv; memo_nwg = nwg; memo_pipeline = cached;
return cached;
}
MTLFunctionConstantValues *constants = [[MTLFunctionConstantValues alloc] init];
[constants setConstantValue:&dv type:MTLDataTypeInt atIndex:500];
[constants setConstantValue:&nwg type:MTLDataTypeInt atIndex:501];
NSError *error = nil;
id<MTLFunction> fn = [g_library newFunctionWithName:@"kernel_flash_attn_ext_vec_reduce"
constantValues:constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_flash_attn_ext_vec_reduce function not found: %s\n",
[[error localizedDescription] UTF8String]);
return nil;
}
error = nil;
id<MTLComputePipelineState> pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!pipeline) {
fprintf(stderr, "ds4: Metal kernel_flash_attn_ext_vec_reduce pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
return nil;
}
[g_pipeline_cache setObject:pipeline forKey:key];
memo_dv = dv; memo_nwg = nwg; memo_pipeline = pipeline;
return pipeline;
}
static uint32_t ds4_gpu_flash_attn_vec_nsg(uint32_t n_keys, uint32_t nwg, uint32_t ncpsg) {
uint32_t nsg = 1;
while (2u * nwg * nsg * ncpsg < n_keys && nsg < 4u) {
nsg *= 2u;
}
return nsg;
}
static int ds4_gpu_trace_allocs(void) {
static int initialized;
static int enabled;
if (!initialized) {
enabled = getenv("DS4_METAL_TRACE_ALLOCS") != NULL;
initialized = 1;
}
return enabled;
}
static double ds4_gpu_mib(uint64_t bytes) {
return (double)bytes / (1024.0 * 1024.0);
}
static double ds4_gpu_gib(uint64_t bytes) {
return (double)bytes / (1024.0 * 1024.0 * 1024.0);
}
static ds4_gpu_stream_expert_timing_snapshot
ds4_gpu_stream_expert_timing_current(void) {
return (ds4_gpu_stream_expert_timing_snapshot) {
.selected_calls = g_stream_expert_timing_selected_calls,
.selected_read_ms = g_stream_expert_timing_selected_read_ms,
.selected_sync_ms = g_stream_expert_timing_selected_sync_ms,
.selected_copy_ms = g_stream_expert_timing_selected_copy_ms,
.selected_bind_ms = g_stream_expert_timing_selected_bind_ms,
.split_layers = g_stream_expert_timing_split_layers,
.split_resident_experts = g_stream_expert_timing_split_resident_experts,
.split_missing_experts = g_stream_expert_timing_split_missing_experts,
.split_resident_ms = g_stream_expert_timing_split_resident_ms,
.split_missing_ms = g_stream_expert_timing_split_missing_ms,
.split_missing_load_ms =
g_stream_expert_timing_split_missing_load_ms,
.split_missing_slot_ms =
g_stream_expert_timing_split_missing_slot_ms,
.split_missing_prune_ms =
g_stream_expert_timing_split_missing_prune_ms,
.split_missing_addr_ms =
g_stream_expert_timing_split_missing_addr_ms,
.split_missing_wait_ms =
g_stream_expert_timing_split_missing_wait_ms,
.load_calls = g_stream_expert_timing_load_calls,
.load_prepare_ms = g_stream_expert_timing_load_prepare_ms,
.load_pread_ms = g_stream_expert_timing_load_pread_ms,
.load_install_ms = g_stream_expert_timing_load_install_ms,
.prepare_batch_reuse_calls =
g_stream_expert_timing_prepare_batch_reuse_calls,
.prepare_batch_reuse_ms =
g_stream_expert_timing_prepare_batch_reuse_ms,
.prepare_buffer_calls =
g_stream_expert_timing_prepare_buffer_calls,
.prepare_buffer_ms =
g_stream_expert_timing_prepare_buffer_ms,
.prepare_task_experts =
g_stream_expert_timing_prepare_task_experts,
.prepare_task_ms =
g_stream_expert_timing_prepare_task_ms,
.reuse_scan_calls =
g_stream_expert_timing_reuse_scan_calls,
.reuse_scan_entries =
g_stream_expert_timing_reuse_scan_entries,
.reuse_scan_ms =
g_stream_expert_timing_reuse_scan_ms,
.reuse_clear_ms =
g_stream_expert_timing_reuse_clear_ms,
.readahead_calls =
g_stream_expert_timing_readahead_calls,
.readahead_bytes =
g_stream_expert_timing_readahead_bytes,
.readahead_ms =
g_stream_expert_timing_readahead_ms,
.cache_all_resident_layers =
g_stream_expert_timing_cache_all_resident_layers,
.cache_all_missing_layers =
g_stream_expert_timing_cache_all_missing_layers,
.cache_mixed_layers = g_stream_expert_timing_cache_mixed_layers,
.cache_resident_experts =
g_stream_expert_timing_cache_resident_experts,
.cache_missing_experts =
g_stream_expert_timing_cache_missing_experts,
};
}
static uint64_t ds4_gpu_stream_expert_timing_delta_u64(
uint64_t current,
uint64_t previous) {
return current >= previous ? current - previous : current;
}
static double ds4_gpu_stream_expert_timing_delta_f64(
double current,
double previous) {
return current >= previous ? current - previous : current;
}
static ds4_gpu_stream_expert_timing_snapshot
ds4_gpu_stream_expert_timing_delta(
ds4_gpu_stream_expert_timing_snapshot current,
ds4_gpu_stream_expert_timing_snapshot previous) {
return (ds4_gpu_stream_expert_timing_snapshot) {
.selected_calls =
ds4_gpu_stream_expert_timing_delta_u64(current.selected_calls,
previous.selected_calls),
.selected_read_ms =
ds4_gpu_stream_expert_timing_delta_f64(current.selected_read_ms,
previous.selected_read_ms),
.selected_sync_ms =
ds4_gpu_stream_expert_timing_delta_f64(current.selected_sync_ms,
previous.selected_sync_ms),
.selected_copy_ms =
ds4_gpu_stream_expert_timing_delta_f64(current.selected_copy_ms,
previous.selected_copy_ms),
.selected_bind_ms =
ds4_gpu_stream_expert_timing_delta_f64(current.selected_bind_ms,
previous.selected_bind_ms),
.split_layers =
ds4_gpu_stream_expert_timing_delta_u64(current.split_layers,
previous.split_layers),
.split_resident_experts =
ds4_gpu_stream_expert_timing_delta_u64(current.split_resident_experts,
previous.split_resident_experts),
.split_missing_experts =
ds4_gpu_stream_expert_timing_delta_u64(current.split_missing_experts,
previous.split_missing_experts),
.split_resident_ms =
ds4_gpu_stream_expert_timing_delta_f64(current.split_resident_ms,
previous.split_resident_ms),
.split_missing_ms =
ds4_gpu_stream_expert_timing_delta_f64(current.split_missing_ms,
previous.split_missing_ms),
.split_missing_load_ms =
ds4_gpu_stream_expert_timing_delta_f64(
current.split_missing_load_ms,
previous.split_missing_load_ms),
.split_missing_slot_ms =
ds4_gpu_stream_expert_timing_delta_f64(
current.split_missing_slot_ms,
previous.split_missing_slot_ms),
.split_missing_prune_ms =
ds4_gpu_stream_expert_timing_delta_f64(
current.split_missing_prune_ms,
previous.split_missing_prune_ms),
.split_missing_addr_ms =
ds4_gpu_stream_expert_timing_delta_f64(
current.split_missing_addr_ms,
previous.split_missing_addr_ms),
.split_missing_wait_ms =
ds4_gpu_stream_expert_timing_delta_f64(
current.split_missing_wait_ms,
previous.split_missing_wait_ms),
.load_calls =
ds4_gpu_stream_expert_timing_delta_u64(current.load_calls,
previous.load_calls),
.load_prepare_ms =
ds4_gpu_stream_expert_timing_delta_f64(current.load_prepare_ms,
previous.load_prepare_ms),
.load_pread_ms =
ds4_gpu_stream_expert_timing_delta_f64(current.load_pread_ms,
previous.load_pread_ms),
.load_install_ms =
ds4_gpu_stream_expert_timing_delta_f64(current.load_install_ms,
previous.load_install_ms),
.prepare_batch_reuse_calls =
ds4_gpu_stream_expert_timing_delta_u64(
current.prepare_batch_reuse_calls,
previous.prepare_batch_reuse_calls),
.prepare_batch_reuse_ms =
ds4_gpu_stream_expert_timing_delta_f64(
current.prepare_batch_reuse_ms,
previous.prepare_batch_reuse_ms),
.prepare_buffer_calls =
ds4_gpu_stream_expert_timing_delta_u64(
current.prepare_buffer_calls,
previous.prepare_buffer_calls),
.prepare_buffer_ms =
ds4_gpu_stream_expert_timing_delta_f64(
current.prepare_buffer_ms,
previous.prepare_buffer_ms),
.prepare_task_experts =
ds4_gpu_stream_expert_timing_delta_u64(
current.prepare_task_experts,
previous.prepare_task_experts),
.prepare_task_ms =
ds4_gpu_stream_expert_timing_delta_f64(
current.prepare_task_ms,
previous.prepare_task_ms),
.reuse_scan_calls =
ds4_gpu_stream_expert_timing_delta_u64(
current.reuse_scan_calls,
previous.reuse_scan_calls),
.reuse_scan_entries =
ds4_gpu_stream_expert_timing_delta_u64(
current.reuse_scan_entries,
previous.reuse_scan_entries),
.reuse_scan_ms =
ds4_gpu_stream_expert_timing_delta_f64(
current.reuse_scan_ms,
previous.reuse_scan_ms),
.reuse_clear_ms =
ds4_gpu_stream_expert_timing_delta_f64(
current.reuse_clear_ms,
previous.reuse_clear_ms),
.readahead_calls =
ds4_gpu_stream_expert_timing_delta_u64(
current.readahead_calls,
previous.readahead_calls),
.readahead_bytes =
ds4_gpu_stream_expert_timing_delta_u64(
current.readahead_bytes,
previous.readahead_bytes),
.readahead_ms =
ds4_gpu_stream_expert_timing_delta_f64(
current.readahead_ms,
previous.readahead_ms),
.cache_all_resident_layers =
ds4_gpu_stream_expert_timing_delta_u64(
current.cache_all_resident_layers,
previous.cache_all_resident_layers),
.cache_all_missing_layers =
ds4_gpu_stream_expert_timing_delta_u64(
current.cache_all_missing_layers,
previous.cache_all_missing_layers),
.cache_mixed_layers =
ds4_gpu_stream_expert_timing_delta_u64(current.cache_mixed_layers,
previous.cache_mixed_layers),
.cache_resident_experts =
ds4_gpu_stream_expert_timing_delta_u64(
current.cache_resident_experts,
previous.cache_resident_experts),
.cache_missing_experts =
ds4_gpu_stream_expert_timing_delta_u64(
current.cache_missing_experts,
previous.cache_missing_experts),
};
}
static int ds4_gpu_stream_expert_timing_has_data(
ds4_gpu_stream_expert_timing_snapshot s) {
return s.selected_calls != 0 ||
s.split_layers != 0 ||
s.load_calls != 0 ||
s.cache_all_resident_layers != 0 ||
s.cache_all_missing_layers != 0 ||
s.cache_mixed_layers != 0;
}
static void ds4_gpu_stream_expert_timing_print(
const char *scope,
ds4_gpu_stream_expert_timing_snapshot s) {
if (!ds4_gpu_stream_expert_timing_has_data(s)) return;
const double selected_calls = (double)s.selected_calls;
const double split_layers = (double)s.split_layers;
const double selected_read_avg =
selected_calls != 0.0 ? s.selected_read_ms / selected_calls : 0.0;
const double selected_sync_avg =
selected_calls != 0.0 ? s.selected_sync_ms / selected_calls : 0.0;
const double selected_copy_avg =
selected_calls != 0.0 ? s.selected_copy_ms / selected_calls : 0.0;
const double selected_bind_avg =
selected_calls != 0.0 ? s.selected_bind_ms / selected_calls : 0.0;
const double split_resident_avg =
split_layers != 0.0 ? s.split_resident_ms / split_layers : 0.0;
const double split_missing_avg =
split_layers != 0.0 ? s.split_missing_ms / split_layers : 0.0;
const double split_missing_load_avg =
split_layers != 0.0 ?
s.split_missing_load_ms / split_layers : 0.0;
const double split_missing_slot_avg =
split_layers != 0.0 ?
s.split_missing_slot_ms / split_layers : 0.0;
const double split_missing_prune_avg =
split_layers != 0.0 ?
s.split_missing_prune_ms / split_layers : 0.0;
const double split_missing_addr_avg =
split_layers != 0.0 ?
s.split_missing_addr_ms / split_layers : 0.0;
const double split_missing_wait_avg =
split_layers != 0.0 ?
s.split_missing_wait_ms / split_layers : 0.0;
const double load_calls = (double)s.load_calls;
const double load_prepare_avg =
load_calls != 0.0 ? s.load_prepare_ms / load_calls : 0.0;
const double load_pread_avg =
load_calls != 0.0 ? s.load_pread_ms / load_calls : 0.0;
const double load_install_avg =
load_calls != 0.0 ? s.load_install_ms / load_calls : 0.0;
const double prepare_batch_reuse_avg =
s.prepare_batch_reuse_calls != 0 ?
s.prepare_batch_reuse_ms /
(double)s.prepare_batch_reuse_calls : 0.0;
const double prepare_buffer_avg =
s.prepare_buffer_calls != 0 ?
s.prepare_buffer_ms / (double)s.prepare_buffer_calls : 0.0;
const double prepare_task_avg =
s.prepare_task_experts != 0 ?
s.prepare_task_ms / (double)s.prepare_task_experts : 0.0;
const double reuse_scan_avg =
s.reuse_scan_calls != 0 ?
s.reuse_scan_ms / (double)s.reuse_scan_calls : 0.0;
const double reuse_scan_entries_avg =
s.reuse_scan_calls != 0 ?
(double)s.reuse_scan_entries / (double)s.reuse_scan_calls : 0.0;
const double readahead_avg =
s.readahead_calls != 0 ?
s.readahead_ms / (double)s.readahead_calls : 0.0;
const double split_resident_experts_avg =
split_layers != 0.0 ?
(double)s.split_resident_experts / split_layers : 0.0;
const double split_missing_experts_avg =
split_layers != 0.0 ?
(double)s.split_missing_experts / split_layers : 0.0;
const uint64_t cache_layers =
s.cache_all_resident_layers +
s.cache_all_missing_layers +
s.cache_mixed_layers;
const double cache_layer_count = (double)cache_layers;
const double cache_resident_experts_avg =
cache_layer_count != 0.0 ?
(double)s.cache_resident_experts / cache_layer_count : 0.0;
const double cache_missing_experts_avg =
cache_layer_count != 0.0 ?
(double)s.cache_missing_experts / cache_layer_count : 0.0;
fprintf(stderr,
"ds4: streaming expert timing %s selected_calls=%llu read_avg=%.3f ms sync_avg=%.3f ms copy_avg=%.3f ms bind_avg=%.3f ms read_total=%.3f ms sync_total=%.3f ms copy_total=%.3f ms bind_total=%.3f ms split_layers=%llu resident_experts_avg=%.2f missing_experts_avg=%.2f resident_submit_avg=%.3f ms missing_bind_avg=%.3f ms resident_submit_total=%.3f ms missing_bind_total=%.3f ms missing_load_avg=%.3f ms missing_slot_avg=%.3f ms missing_prune_avg=%.3f ms missing_addr_avg=%.3f ms missing_wait_avg=%.3f ms missing_wait_total=%.3f ms load_calls=%llu load_prepare_avg=%.3f ms load_pread_avg=%.3f ms load_install_avg=%.3f ms prepare_batch_reuse_calls=%llu prepare_batch_reuse_avg=%.3f ms prepare_batch_reuse_total=%.3f ms prepare_buffer_calls=%llu prepare_buffer_avg=%.3f ms prepare_buffer_total=%.3f ms prepare_task_experts=%llu prepare_task_avg=%.3f ms prepare_task_total=%.3f ms reuse_scan_calls=%llu reuse_scan_entries_avg=%.1f reuse_scan_avg=%.3f ms reuse_scan_total=%.3f ms reuse_clear_total=%.3f ms readahead_calls=%llu readahead_avg=%.3f ms readahead_total=%.3f ms readahead_gib=%.2f cache_all_resident=%llu cache_all_missing=%llu cache_mixed=%llu cache_resident_avg=%.2f cache_missing_avg=%.2f\n",
scope ? scope : "total",
(unsigned long long)s.selected_calls,
selected_read_avg,
selected_sync_avg,
selected_copy_avg,
selected_bind_avg,
s.selected_read_ms,
s.selected_sync_ms,
s.selected_copy_ms,
s.selected_bind_ms,
(unsigned long long)s.split_layers,
split_resident_experts_avg,
split_missing_experts_avg,
split_resident_avg,
split_missing_avg,
s.split_resident_ms,
s.split_missing_ms,
split_missing_load_avg,
split_missing_slot_avg,
split_missing_prune_avg,
split_missing_addr_avg,
split_missing_wait_avg,
s.split_missing_wait_ms,
(unsigned long long)s.load_calls,
load_prepare_avg,
load_pread_avg,
load_install_avg,
(unsigned long long)s.prepare_batch_reuse_calls,
prepare_batch_reuse_avg,
s.prepare_batch_reuse_ms,
(unsigned long long)s.prepare_buffer_calls,
prepare_buffer_avg,
s.prepare_buffer_ms,
(unsigned long long)s.prepare_task_experts,
prepare_task_avg,
s.prepare_task_ms,
(unsigned long long)s.reuse_scan_calls,
reuse_scan_entries_avg,
reuse_scan_avg,
s.reuse_scan_ms,
s.reuse_clear_ms,
(unsigned long long)s.readahead_calls,
readahead_avg,
s.readahead_ms,
ds4_gpu_gib(s.readahead_bytes),
(unsigned long long)s.cache_all_resident_layers,
(unsigned long long)s.cache_all_missing_layers,
(unsigned long long)s.cache_mixed_layers,
cache_resident_experts_avg,
cache_missing_experts_avg);
}
static void ds4_gpu_print_task_memory_report(void) {
task_vm_info_data_t info;
mach_msg_type_number_t count = TASK_VM_INFO_COUNT;
const kern_return_t kr = task_info(mach_task_self(),
TASK_VM_INFO,
(task_info_t)&info,
&count);
if (kr != KERN_SUCCESS) return;
fprintf(stderr,
"ds4: macOS task memory footprint %.2f GiB, resident %.2f GiB, virtual %.2f GiB\n",
ds4_gpu_gib((uint64_t)info.phys_footprint),
ds4_gpu_gib((uint64_t)info.resident_size),
ds4_gpu_gib((uint64_t)info.virtual_size));
}
void ds4_gpu_print_memory_report(const char *label) {
uint64_t cached_prefill_mask_bytes = 0;
uint64_t cached_prefill_blk_bytes = 0;
for (uint32_t i = 0; i < DS4_GPU_PREFILL_MASK_CACHE_SLOTS; i++) {
const ds4_gpu_zero_prefix_prefill_mask_cache_entry *entry =
&g_zero_prefix_prefill_mask_cache[i];
if (entry->mask) cached_prefill_mask_bytes += entry->mask_bytes;
if (entry->blk) cached_prefill_blk_bytes += entry->blk_bytes;
}
const uint64_t scratch =
(uint64_t)g_flash_attn_mask_bytes +
(uint64_t)g_flash_attn_zero_mask_bytes +
cached_prefill_mask_bytes +
(uint64_t)g_flash_attn_pad_bytes +
(uint64_t)g_flash_attn_tmp_bytes +
(uint64_t)g_flash_attn_blk_bytes +
cached_prefill_blk_bytes +
(uint64_t)g_flash_attn_ring_bytes +
(uint64_t)g_flash_attn_kv_bytes +
(uint64_t)g_glm_flash_attn_mask_bytes +
(uint64_t)g_compressor_pool_kv_bytes +
(uint64_t)g_compressor_pool_score_bytes +
(uint64_t)g_compressor_pool_score_cont_bytes +
(uint64_t)g_compressor_pool_softmax_bytes +
(uint64_t)g_compressor_pool_product_bytes +
(uint64_t)g_compressor_store_ape_bytes +
(uint64_t)g_compressor_store_score_bytes +
(uint64_t)g_embed_rows_bytes +
(uint64_t)g_router_selection_bytes +
(uint64_t)g_router_weight_sum_bytes +
(uint64_t)g_indexer_head_scores_bytes +
(uint64_t)g_indexer_topk_bytes +
(uint64_t)g_indexed_topk_bytes +
(uint64_t)g_f16_round_scratch_bytes +
(uint64_t)g_q8_prefill_scratch_bytes + (uint64_t)g_qwen4_conv_halo_bytes +
(uint64_t)g_raw_store_round_bytes +
(uint64_t)g_moe_gate_scratch_bytes +
(uint64_t)g_moe_down_scratch_bytes +
(uint64_t)g_moe_id_map_bytes + (uint64_t)g_moe_packed_rhs_bytes +
(uint64_t)g_moe_q4_gate_slots_bytes +
(uint64_t)g_moe_q4_up_slots_bytes +
(uint64_t)g_moe_q4_down_slots_bytes;
pthread_mutex_lock(&g_tensor_mu);
const uint64_t tensor_live_snap = g_tensor_alloc_live_bytes;
const uint64_t tensor_peak_snap = g_tensor_alloc_peak_bytes;
pthread_mutex_unlock(&g_tensor_mu);
uint64_t tracked_live = tensor_live_snap;
if (tracked_live > UINT64_MAX - g_stream_expert_cache_bytes) {
tracked_live = UINT64_MAX;
} else {
tracked_live += g_stream_expert_cache_bytes;
}
const bool color = ds4_log_is_tty(stderr);
const char *green = color ? "\x1b[32m" : "";
const char *bright_green = color ? "\x1b[1;32m" : "";
const char *reset = color ? "\x1b[0m" : "";
fprintf(stderr,
"%sds4: Metal memory%s%s: runtime %.2f GiB + streaming experts %.2f GiB = %s%.2f GiB tracked live%s\n",
green,
label && label[0] ? " " : "",
label && label[0] ? label : "",
ds4_gpu_gib(tensor_live_snap),
ds4_gpu_gib(g_stream_expert_cache_bytes),
bright_green,
ds4_gpu_gib(tracked_live),
reset);
if (color) fputs(green, stderr);
fprintf(stderr,
"ds4: runtime tensors live %.2f MiB peak %.2f MiB\n",
ds4_gpu_mib(tensor_live_snap),
ds4_gpu_mib(tensor_peak_snap));
ds4_gpu_print_task_memory_report();
fprintf(stderr,
"ds4: mmap model wrapper creations %llu buffers %.2f GiB cumulative, %.2f GiB max (not copied)\n",
(unsigned long long)g_model_wrap_count,
ds4_gpu_gib(g_model_wrap_bytes),
ds4_gpu_gib(g_model_wrap_max_bytes));
if (g_model_buffer_cache && [g_model_buffer_cache count] != 0) {
const uint64_t limit = ds4_gpu_exact_view_cache_limit_bytes();
if (limit == 0) {
fprintf(stderr,
"ds4: exact model view cache %lu buffers %.2f GiB unlimited, %llu evictions (not copied)\n",
(unsigned long)[g_model_buffer_cache count],
ds4_gpu_gib(g_model_buffer_cache_bytes),
(unsigned long long)g_model_buffer_cache_evictions);
} else {
fprintf(stderr,
"ds4: exact model view cache %lu buffers %.2f GiB / %.2f GiB, %llu evictions (not copied)\n",
(unsigned long)[g_model_buffer_cache count],
ds4_gpu_gib(g_model_buffer_cache_bytes),
ds4_gpu_gib(limit),
(unsigned long long)g_model_buffer_cache_evictions);
}
}
if (g_stream_expert_cache_hits != 0 ||
g_stream_expert_cache_misses != 0 ||
g_stream_expert_cache_bytes != 0) {
const uint64_t budget = ds4_gpu_stream_expert_cache_configured_budget();
uint64_t target_bytes = 0;
if (budget != 0 && g_stream_expert_cache_expert_bytes != 0) {
target_bytes =
budget > UINT64_MAX / g_stream_expert_cache_expert_bytes ?
UINT64_MAX :
budget * g_stream_expert_cache_expert_bytes;
}
const uint64_t lookups = g_stream_expert_cache_hits + g_stream_expert_cache_misses;
const double hit_rate = lookups ?
(double)g_stream_expert_cache_hits / (double)lookups : 0.0;
if (g_stream_expert_cache_evict_advise_bytes != 0 ||
g_stream_expert_cache_willneed_advise_bytes != 0 ||
g_stream_expert_cache_pread_bytes != 0) {
fprintf(stderr,
"ds4: streaming expert cache budget=%llu experts entries=%u expert=%.2f MiB target=%.2f GiB live=%.2f GiB, hits=%llu misses=%llu hit_rate=%.3f wraps=%llu evictions=%llu buffer_allocs=%llu buffer_reuses=%llu evict_dontneed=%.2f GiB miss_willneed=%.2f GiB miss_pread=%.2f GiB pread_ms=%.3f\n",
(unsigned long long)budget,
g_stream_expert_cache_entry_count,
ds4_gpu_mib(g_stream_expert_cache_expert_bytes),
ds4_gpu_gib(target_bytes),
ds4_gpu_gib(g_stream_expert_cache_bytes),
(unsigned long long)g_stream_expert_cache_hits,
(unsigned long long)g_stream_expert_cache_misses,
hit_rate,
(unsigned long long)g_stream_expert_cache_wraps,
(unsigned long long)g_stream_expert_cache_evictions,
(unsigned long long)g_stream_expert_cache_buffer_allocs,
(unsigned long long)g_stream_expert_cache_buffer_reuses,
ds4_gpu_gib(g_stream_expert_cache_evict_advise_bytes),
ds4_gpu_gib(g_stream_expert_cache_willneed_advise_bytes),
ds4_gpu_gib(g_stream_expert_cache_pread_bytes),
g_stream_expert_cache_pread_ms);
} else {
fprintf(stderr,
"ds4: streaming expert cache budget=%llu experts entries=%u expert=%.2f MiB target=%.2f GiB live=%.2f GiB, hits=%llu misses=%llu hit_rate=%.3f wraps=%llu evictions=%llu buffer_allocs=%llu buffer_reuses=%llu\n",
(unsigned long long)budget,
g_stream_expert_cache_entry_count,
ds4_gpu_mib(g_stream_expert_cache_expert_bytes),
ds4_gpu_gib(target_bytes),
ds4_gpu_gib(g_stream_expert_cache_bytes),
(unsigned long long)g_stream_expert_cache_hits,
(unsigned long long)g_stream_expert_cache_misses,
hit_rate,
(unsigned long long)g_stream_expert_cache_wraps,
(unsigned long long)g_stream_expert_cache_evictions,
(unsigned long long)g_stream_expert_cache_buffer_allocs,
(unsigned long long)g_stream_expert_cache_buffer_reuses);
}
if (g_stream_expert_cache_mlock_bytes != 0 ||
g_stream_expert_cache_mlock_failures != 0) {
fprintf(stderr,
"ds4: streaming expert buffer mlock locked=%.2f GiB failed=%.2f GiB failures=%llu time=%.3f ms\n",
ds4_gpu_gib(g_stream_expert_cache_mlock_bytes),
ds4_gpu_gib(g_stream_expert_cache_mlock_fail_bytes),
(unsigned long long)g_stream_expert_cache_mlock_failures,
g_stream_expert_cache_mlock_ms);
}
if (ds4_gpu_stream_expert_timing_summary_enabled()) {
fprintf(stderr,
"ds4: streaming cache aging tokens=%llu first_layer=%u decay_token=%llu\n",
(unsigned long long)g_stream_expert_cache_decode_tokens,
g_stream_expert_cache_first_decode_layer,
(unsigned long long)g_stream_expert_cache_hotness_decay_token);
const ds4_gpu_stream_expert_timing_snapshot total =
ds4_gpu_stream_expert_timing_current();
if (ds4_gpu_stream_expert_timing_has_data(total)) {
const ds4_gpu_stream_expert_timing_snapshot delta =
ds4_gpu_stream_expert_timing_delta(
total,
g_stream_expert_timing_last_report);
ds4_gpu_stream_expert_timing_print("total", total);
ds4_gpu_stream_expert_timing_print("delta", delta);
g_stream_expert_timing_last_report = total;
}
}
if (getenv("DS4_METAL_STREAMING_EXPERT_LAYER_STATS") != NULL) {
fprintf(stderr, "ds4: streaming expert cache per-layer stats:\n");
for (uint32_t layer = 0;
layer < DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER;
layer++) {
const uint64_t hits = g_stream_expert_cache_layer_hits[layer];
const uint64_t misses = g_stream_expert_cache_layer_misses[layer];
const uint64_t lookups = hits + misses;
const uint64_t evictions =
g_stream_expert_cache_layer_evictions[layer];
const uint64_t pread_bytes =
g_stream_expert_cache_layer_pread_bytes[layer];
const double pread_ms =
g_stream_expert_cache_layer_pread_ms[layer];
const uint32_t cached =
g_stream_expert_cache_layer_count[layer];
const uint32_t layer_slots =
ds4_gpu_stream_expert_cache_configured_count() != 0 ?
DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT : 0;
if (lookups == 0 && evictions == 0 && pread_bytes == 0 &&
cached == 0) {
continue;
}
const double layer_hit_rate = lookups ?
(double)hits / (double)lookups : 0.0;
fprintf(stderr,
"ds4: layer=%u layer_slots=%u cached=%u hits=%llu misses=%llu hit_rate=%.3f evictions=%llu miss_pread=%.2f GiB pread_ms=%.3f\n",
layer,
layer_slots,
cached,
(unsigned long long)hits,
(unsigned long long)misses,
layer_hit_rate,
(unsigned long long)evictions,
ds4_gpu_gib(pread_bytes),
pread_ms);
}
if (getenv("DS4_METAL_STREAMING_EXPERT_LAYER_STATS_DELTA") != NULL) {
fprintf(stderr, "ds4: streaming expert cache per-layer delta:\n");
for (uint32_t layer = 0;
layer < DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER;
layer++) {
const uint64_t hits =
g_stream_expert_cache_layer_hits[layer];
const uint64_t misses =
g_stream_expert_cache_layer_misses[layer];
const uint64_t evictions =
g_stream_expert_cache_layer_evictions[layer];
const uint64_t pread_bytes =
g_stream_expert_cache_layer_pread_bytes[layer];
const double pread_ms =
g_stream_expert_cache_layer_pread_ms[layer];
const uint64_t delta_hits =
hits - g_stream_expert_cache_layer_last_hits[layer];
const uint64_t delta_misses =
misses - g_stream_expert_cache_layer_last_misses[layer];
const uint64_t delta_evictions =
evictions -
g_stream_expert_cache_layer_last_evictions[layer];
const uint64_t delta_pread_bytes =
pread_bytes -
g_stream_expert_cache_layer_last_pread_bytes[layer];
const double delta_pread_ms =
pread_ms -
g_stream_expert_cache_layer_last_pread_ms[layer];
const uint64_t lookups = delta_hits + delta_misses;
if (lookups == 0 && delta_evictions == 0 &&
delta_pread_bytes == 0) {
continue;
}
const double hit_rate = lookups ?
(double)delta_hits / (double)lookups : 0.0;
fprintf(stderr,
"ds4: layer=%u cached=%u hits=%llu misses=%llu hit_rate=%.3f evictions=%llu miss_pread=%.2f GiB pread_ms=%.3f\n",
layer,
g_stream_expert_cache_layer_count[layer],
(unsigned long long)delta_hits,
(unsigned long long)delta_misses,
hit_rate,
(unsigned long long)delta_evictions,
ds4_gpu_gib(delta_pread_bytes),
delta_pread_ms);
g_stream_expert_cache_layer_last_hits[layer] = hits;
g_stream_expert_cache_layer_last_misses[layer] = misses;
g_stream_expert_cache_layer_last_evictions[layer] =
evictions;
g_stream_expert_cache_layer_last_pread_bytes[layer] =
pread_bytes;
g_stream_expert_cache_layer_last_pread_ms[layer] =
pread_ms;
}
}
}
}
fprintf(stderr,
"ds4: model residency requests %llu%s\n",
(unsigned long long)g_model_residency_count,
g_ssd_streaming_mode ? " (ssd-streaming)" :
(getenv("DS4_METAL_NO_RESIDENCY") != NULL ? " (disabled)" : ""));
fprintf(stderr,
"ds4: device %s, Metal 4 runtime %s, family %s, MTL4 queue %s, tensor API %s, M5 neural accelerators %s\n",
g_metal_device_name[0] ? g_metal_device_name : "(unknown)",
g_metal4_runtime_available ? "yes" : "no",
g_metal4_family_supported ? "yes" : "no",
g_metal4_queue_supported ? "yes" : "no",
g_metal4_tensor_api_enabled ? "enabled" :
(g_metal4_tensor_api_compile_supported ? "available" : "disabled"),
g_metal4_m5_neural_accelerators_hint ? "likely" : "not detected");
fprintf(stderr,
"ds4: accelerated Metal path %s%s\n",
ds4_gpu_mpp_available() ? "enabled" : "disabled",
g_quality_mode ? " by --quality" :
(!g_metal4_tensor_api_enabled ? " (tensor API unavailable)" : ""));
fprintf(stderr,
"ds4: device %s, Metal 4 runtime %s, family %s, MTL4 queue %s, tensor API %s, M5 neural accelerators %s\n",
g_metal_device_name[0] ? g_metal_device_name : "(unknown)",
g_metal4_runtime_available ? "yes" : "no",
g_metal4_family_supported ? "yes" : "no",
g_metal4_queue_supported ? "yes" : "no",
g_metal4_tensor_api_enabled ? "enabled" :
(g_metal4_tensor_api_compile_supported ? "available" : "disabled"),
g_metal4_m5_neural_accelerators_hint ? "likely" : "not detected");
fprintf(stderr,
"ds4: scratch %.2f MiB (flash mask %.2f, pad %.2f, tmp %.2f, blk %.2f, ring %.2f, kv %.2f, compressor %.2f, router %.2f, indexer %.2f, moe %.2f, f16 %.2f, raw-store %.2f)\n",
ds4_gpu_mib(scratch),
ds4_gpu_mib((uint64_t)g_flash_attn_mask_bytes +
(uint64_t)g_glm_flash_attn_mask_bytes +
(uint64_t)g_flash_attn_zero_mask_bytes +
cached_prefill_mask_bytes),
ds4_gpu_mib((uint64_t)g_flash_attn_pad_bytes),
ds4_gpu_mib((uint64_t)g_flash_attn_tmp_bytes),
ds4_gpu_mib((uint64_t)g_flash_attn_blk_bytes +
cached_prefill_blk_bytes),
ds4_gpu_mib((uint64_t)g_flash_attn_ring_bytes),
ds4_gpu_mib((uint64_t)g_flash_attn_kv_bytes),
ds4_gpu_mib((uint64_t)g_compressor_pool_kv_bytes +
(uint64_t)g_compressor_pool_score_bytes +
(uint64_t)g_compressor_pool_score_cont_bytes +
(uint64_t)g_compressor_pool_softmax_bytes +
(uint64_t)g_compressor_pool_product_bytes +
(uint64_t)g_compressor_store_ape_bytes +
(uint64_t)g_compressor_store_score_bytes +
(uint64_t)g_embed_rows_bytes),
ds4_gpu_mib((uint64_t)g_router_selection_bytes +
(uint64_t)g_router_weight_sum_bytes),
ds4_gpu_mib((uint64_t)g_indexer_head_scores_bytes +
(uint64_t)g_indexer_topk_bytes +
(uint64_t)g_indexed_topk_bytes),
ds4_gpu_mib((uint64_t)g_moe_gate_scratch_bytes +
(uint64_t)g_moe_down_scratch_bytes +
(uint64_t)g_moe_id_map_bytes + (uint64_t)g_moe_packed_rhs_bytes +
(uint64_t)g_moe_q4_gate_slots_bytes +
(uint64_t)g_moe_q4_up_slots_bytes +
(uint64_t)g_moe_q4_down_slots_bytes),
ds4_gpu_mib((uint64_t)g_f16_round_scratch_bytes),
ds4_gpu_mib((uint64_t)g_raw_store_round_bytes));
if (color) fputs(reset, stderr);
}
void ds4_gpu_set_quality(bool quality) {
g_quality_mode = quality ? 1 : 0;
}
void ds4_gpu_set_glm_model(bool enabled) {
g_glm_model_mode = enabled ? 1 : 0;
}
void ds4_gpu_set_ssd_streaming(bool enabled) {
g_ssd_streaming_mode = enabled ? 1 : 0;
ds4_gpu_stream_expert_cache_clear_all(1);
if (g_ssd_streaming_mode) {
fprintf(stderr,
"ds4: Metal SSD streaming mode enabled; full model residency and warmup are skipped\n");
}
}
void ds4_gpu_set_glm_streaming_prefill_full_layer(bool enabled) {
g_glm_streaming_prefill_full_layer_runtime = enabled ? 1 : 0;
}
void ds4_gpu_set_streaming_expert_cache_budget(uint32_t experts) {
if (experts > DS4_METAL_STREAM_EXPERT_CACHE_MAX_ENTRIES) {
experts = DS4_METAL_STREAM_EXPERT_CACHE_MAX_ENTRIES;
}
g_stream_expert_cache_budget_override = experts;
ds4_gpu_stream_expert_cache_clear_all(1);
}
void ds4_gpu_set_streaming_expert_cache_expert_bytes(uint64_t bytes) {
/*
* Pre-seed the cache's single slab size class with the model's uniform
* per-expert bytes (first routed layer). With a mixed-precision GGUF this
* pins the class to the majority layers so the boosted ones are rejected
* deterministically from startup, instead of depending on which layer
* happens to touch the cache first.
*/
g_stream_expert_cache_expert_bytes = bytes;
}
uint64_t ds4_gpu_recommended_working_set_size(void) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!g_device) return 0;
return (uint64_t)[g_device recommendedMaxWorkingSetSize];
}
static int ds4_gpu_model_map_log_enabled(void) {
if (!g_ssd_streaming_mode) return 1;
const char *trace = getenv("DS4_METAL_STREAMING_MAP_TRACE");
return trace && trace[0] && strcmp(trace, "0") != 0;
}
static id<MTLBuffer> ds4_gpu_wrap_model_range(
const void *model_map,
uint64_t model_size,
uint64_t offset,
uint64_t len,
uint64_t *inner_offset);
static id<MTLBuffer> ds4_gpu_wrap_model_exact_range(
const void *model_map,
uint64_t model_size,
uint64_t offset,
uint64_t len,
uint64_t *inner_offset);
static const char *ds4_gpu_source =
"#include <metal_stdlib>\n"
"#ifdef DS4_METAL_HAS_TENSOR\n"
"#include <metal_tensor>\n"
"#include <MetalPerformancePrimitives/MetalPerformancePrimitives.h>\n"
"#endif\n"
"using namespace metal;\n"
"#ifdef DS4_METAL_HAS_TENSOR\n"
"using namespace mpp::tensor_ops;\n"
"#endif\n"
"\n"
"#define MAX(x, y) ((x) > (y) ? (x) : (y))\n"
"#define MIN(x, y) ((x) < (y) ? (x) : (y))\n"
"#define SWAP(x, y) { auto tmp = (x); (x) = (y); (y) = tmp; }\n"
"#define QK8_0 32\n"
"#ifndef QK_K\n"
"#define QK_K 256\n"
"#endif\n"
"#define N_SIMDWIDTH 32\n"
"#define N_R0_Q8_0 2\n"
"#define N_SG_Q8_0 4\n"
"#define FC_MUL_MV 600\n"
"#define FC_MUL_MM 700\n"
"#define FC_BIN 1300\n"
"#define FOR_UNROLL(x) _Pragma(\"clang loop unroll(full)\") for (x)\n"
"#define M_PI_F 3.14159265358979323846f\n"
"\n"
"// Reads one byte per stride to warm model-backed pages without copying the\n"
"// model. This is outside inference and exists only to reduce first-use stalls.\n"
"kernel void kernel_touch_u8_stride(\n"
" device const uchar *src [[buffer(0)]],\n"
" device uchar *dst [[buffer(1)]],\n"
" constant ulong &stride [[buffer(2)]],\n"
" constant ulong &bytes [[buffer(3)]],\n"
" constant ulong &dst_offset [[buffer(4)]],\n"
" uint gid [[thread_position_in_grid]]) {\n"
" ulong off = (ulong)gid * stride;\n"
" if (off >= bytes) return;\n"
" dst[dst_offset + (ulong)gid] = src[off];\n"
"}\n"
"\n"
"// Gate-time prefetch: one dispatch over up to 8 ranges given as GPU\n"
"// addresses (bindless views over the model), 128 B per thread, ranges\n"
"// assigned by cumulative line counts. One launch instead of one per\n"
"// range: the fixed cost per dispatch is what a rank pays when its peer's\n"
"// partial is already there.\n"
"kernel void kernel_touch_u8_stride_table(\n"
" constant ulong *addr [[buffer(0)]],\n"
" constant uint *lines [[buffer(1)]],\n"
" constant uint &count [[buffer(2)]],\n"
" device uchar *dst [[buffer(3)]],\n"
" uint gid [[thread_position_in_grid]]) {\n"
" uint line = gid;\n"
" uint i = 0;\n"
" while (i < count && line >= lines[i]) { line -= lines[i]; i++; }\n"
" if (i >= count) return;\n"
" device const uchar *src = (device const uchar *)addr[i];\n"
" dst[gid] = src[(ulong)line * 128ul];\n"
"}\n"
"\n"
"enum ds4_sort_order {\n"
" DS4_SORT_ORDER_ASC,\n"
" DS4_SORT_ORDER_DESC,\n"
"};\n"
"\n"
"struct block_q8_0 {\n"
" half d;\n"
" int8_t qs[QK8_0];\n"
"};\n"
"\n"
"struct block_q8_K {\n"
" float d;\n"
" int8_t qs[QK_K];\n"
" int16_t bsums[QK_K / 16];\n"
"};\n"
"\n"
"\n";
static NSString *ds4_gpu_full_source(void) {
NSString *base = [NSString stringWithUTF8String:ds4_gpu_source];
NSFileManager *fm = [NSFileManager defaultManager];
/*
* Kernels are kept as separate files for review, then concatenated into one
* Metal library. Environment overrides are still honored so a diagnostic
* run can swap one source file without changing the executable.
*/
NSArray<NSArray<NSString *> *> *required_sources = @[
@[@"DS4_METAL_FLASH_ATTN_SOURCE", @"metal/flash_attn.metal"],
@[@"DS4_METAL_DENSE_SOURCE", @"metal/dense.metal"],
@[@"DS4_METAL_GLM53_BF16_SOURCE", @"metal/glm53_bf16.metal"],
@[@"DS4_METAL_GLM53_VISION_SOURCE", @"metal/glm53_vision.metal"],
@[@"DS4_METAL_DEEPSEEK4_VISION_SOURCE", @"metal/deepseek4_vision.metal"],
@[@"DS4_METAL_GLM53_KDA_SOURCE", @"metal/glm53_kda.metal"],
@[@"DS4_METAL_MOE_SOURCE", @"metal/moe.metal"],
@[@"DS4_METAL_DSV4_HC_SOURCE", @"metal/dsv4_hc.metal"],
@[@"DS4_METAL_UNARY_SOURCE", @"metal/unary.metal"],
@[@"DS4_METAL_DSV4_KV_SOURCE", @"metal/dsv4_kv.metal"],
@[@"DS4_METAL_DSV41_SOURCE", @"metal/dsv41.metal"],
@[@"DS4_METAL_DSV4_ROPE_SOURCE", @"metal/dsv4_rope.metal"],
@[@"DS4_METAL_DSV4_MISC_SOURCE", @"metal/dsv4_misc.metal"],
@[@"DS4_METAL_ARGSORT_SOURCE", @"metal/argsort.metal"],
@[@"DS4_METAL_CPY_SOURCE", @"metal/cpy.metal"],
@[@"DS4_METAL_CONCAT_SOURCE", @"metal/concat.metal"],
@[@"DS4_METAL_GET_ROWS_SOURCE", @"metal/get_rows.metal"],
@[@"DS4_METAL_SUM_ROWS_SOURCE", @"metal/sum_rows.metal"],
@[@"DS4_METAL_SOFTMAX_SOURCE", @"metal/softmax.metal"],
@[@"DS4_METAL_REPEAT_SOURCE", @"metal/repeat.metal"],
@[@"DS4_METAL_GLU_SOURCE", @"metal/glu.metal"],
@[@"DS4_METAL_NORM_SOURCE", @"metal/norm.metal"],
@[@"DS4_METAL_BIN_SOURCE", @"metal/bin.metal"],
@[@"DS4_METAL_SET_ROWS_SOURCE", @"metal/set_rows.metal"],
@[@"DS4_METAL_QWEN4_SOURCE", @"metal/qwen4.metal"],
@[@"DS4_METAL_QWEN4_VISION_SOURCE", @"metal/qwen4_vision.metal"],
];
NSMutableString *source = [NSMutableString stringWithString:base];
for (NSArray<NSString *> *spec in required_sources) {
const char *override_path = getenv([spec[0] UTF8String]);
NSMutableArray<NSString *> *paths = [NSMutableArray array];
if (override_path && override_path[0]) {
[paths addObject:[NSString stringWithUTF8String:override_path]];
}
[paths addObject:spec[1]];
[paths addObject:[@"./" stringByAppendingString:spec[1]]];
NSString *loaded = nil;
NSString *loaded_path = nil;
for (NSString *path in paths) {
if (![fm fileExistsAtPath:path]) continue;
NSError *error = nil;
loaded = [NSString stringWithContentsOfFile:path
encoding:NSUTF8StringEncoding
error:&error];
if (!loaded) {
fprintf(stderr, "ds4: failed to read Metal source %s: %s\n",
[path UTF8String], [[error localizedDescription] UTF8String]);
return nil;
}
loaded_path = path;
break;
}
if (!loaded) {
fprintf(stderr,
"ds4: Metal source %s not found (set %s to override)\n",
[spec[1] UTF8String], [spec[0] UTF8String]);
return nil;
}
[source appendFormat:@"\n// appended %@\n%@\n", loaded_path, loaded];
}
return source;
}
typedef struct {
int32_t ne00t;
int32_t ne00;
uint64_t nb01;
uint64_t nb02;
uint64_t nb03;
int32_t ne10;
uint64_t nb10;
uint64_t nb11;
uint64_t nb12;
uint64_t nb1;
uint64_t nb2;
uint64_t nb3;
} ds4_gpu_get_rows_args;
typedef struct {
int32_t n_embd;
int32_t n_vocab;
int32_t n_tokens;
uint64_t src_row_bytes;
uint64_t dst_row_bytes;
uint64_t token_stride;
} ds4_gpu_get_rows_q8_0_args;
typedef struct {
int32_t ne00;
int32_t ne01;
int32_t ne02;
int32_t ne03;
uint64_t nb00;
uint64_t nb01;
uint64_t nb02;
uint64_t nb03;
int32_t ne0;
int32_t ne1;
int32_t ne2;
int32_t ne3;
uint64_t nb0;
uint64_t nb1;
uint64_t nb2;
uint64_t nb3;
} ds4_gpu_repeat_args;
typedef struct {
int32_t nk0;
int32_t ne01;
uint64_t nb01;
uint64_t nb02;
uint64_t nb03;
int32_t ne11;
int32_t ne12;
uint64_t nb10;
uint64_t nb11;
uint64_t nb12;
uint64_t nb1;
uint64_t nb2;
uint64_t nb3;
} ds4_gpu_set_rows_args;
typedef struct {
int32_t ne00;
int32_t ne01;
int32_t ne02;
int32_t ne03;
uint64_t nb00;
uint64_t nb01;
uint64_t nb02;
uint64_t nb03;
int32_t ne10;
int32_t ne11;
int32_t ne12;
int32_t ne13;
uint64_t nb10;
uint64_t nb11;
uint64_t nb12;
uint64_t nb13;
int32_t ne0;
int32_t ne1;
int32_t ne2;
int32_t ne3;
uint64_t nb0;
uint64_t nb1;
uint64_t nb2;
uint64_t nb3;
int32_t dim;
} ds4_gpu_concat_args;
typedef struct {
int64_t nk0;
int64_t ne00;
int64_t ne01;
int64_t ne02;
int64_t ne03;
uint64_t nb00;
uint64_t nb01;
uint64_t nb02;
uint64_t nb03;
int64_t ne0;
int64_t ne1;
int64_t ne2;
int64_t ne3;
uint64_t nb0;
uint64_t nb1;
uint64_t nb2;
uint64_t nb3;
} ds4_gpu_cpy_args;
static ds4_gpu_cpy_args ds4_gpu_make_cpy_1d_args(
uint32_t n,
uint64_t src_elem,
uint64_t dst_elem) {
return (ds4_gpu_cpy_args) {
.nk0 = (int64_t)n,
.ne00 = (int64_t)n,
.ne01 = 1,
.ne02 = 1,
.ne03 = 1,
.nb00 = src_elem,
.nb01 = (uint64_t)n * src_elem,
.nb02 = (uint64_t)n * src_elem,
.nb03 = (uint64_t)n * src_elem,
.ne0 = (int64_t)n,
.ne1 = 1,
.ne2 = 1,
.ne3 = 1,
.nb0 = dst_elem,
.nb1 = (uint64_t)n * dst_elem,
.nb2 = (uint64_t)n * dst_elem,
.nb3 = (uint64_t)n * dst_elem,
};
}
static NSUInteger ds4_gpu_cpy_threads(uint32_t n, id<MTLComputePipelineState> pipeline) {
NSUInteger nth = 32u;
const NSUInteger max_threads = pipeline.maxTotalThreadsPerThreadgroup;
while (nth < (NSUInteger)n && nth < max_threads) nth *= 2u;
if (nth > max_threads) nth = max_threads;
if (nth > (NSUInteger)n) nth = (NSUInteger)n;
return nth ? nth : 1u;
}
static float ds4_gpu_negative_infinity(void) {
union { uint32_t u; float f; } v = { 0xff800000u };
return v.f;
}
static float ds4_gpu_positive_infinity(void) {
union { uint32_t u; float f; } v = { 0x7f800000u };
return v.f;
}
static int ds4_gpu_encode_cpy_f32_f32_3d(
id<MTLCommandBuffer> cb,
id<MTLBuffer> src,
NSUInteger src_off,
id<MTLBuffer> dst,
NSUInteger dst_off,
uint32_t cols,
uint32_t rows,
uint32_t planes,
uint64_t src_row_stride,
uint64_t src_plane_stride,
uint64_t dst_row_stride,
uint64_t dst_plane_stride);
static int ds4_gpu_encode_cpy_f32_f32_3d_src_strided(
id<MTLCommandBuffer> cb,
id<MTLBuffer> src,
NSUInteger src_off,
id<MTLBuffer> dst,
NSUInteger dst_off,
uint32_t cols,
uint32_t rows,
uint32_t planes,
uint64_t src_col_stride,
uint64_t src_row_stride,
uint64_t src_plane_stride,
uint64_t dst_row_stride,
uint64_t dst_plane_stride);
static int ds4_gpu_encode_cpy_f32_f16_1d(
id<MTLCommandBuffer> cb,
id<MTLBuffer> src,
NSUInteger src_off,
id<MTLBuffer> dst,
NSUInteger dst_off,
uint32_t n);
static int ds4_gpu_encode_cpy_f32_f16_2d(
id<MTLCommandBuffer> cb,
id<MTLBuffer> src,
NSUInteger src_off,
id<MTLBuffer> dst,
NSUInteger dst_off,
uint32_t cols,
uint32_t rows,
uint64_t src_row_stride,
uint64_t dst_row_stride);
static int ds4_gpu_encode_cpy_f16_f32_1d(
id<MTLCommandBuffer> cb,
id<MTLBuffer> src,
NSUInteger src_off,
id<MTLBuffer> dst,
NSUInteger dst_off,
uint32_t n);
static int ds4_gpu_encode_fill_f32_rows(
id<MTLCommandBuffer> cb,
id<MTLBuffer> buf,
NSUInteger offset,
uint32_t width,
uint32_t rows,
float value);
static int ds4_gpu_encode_add_f32_1d(
id<MTLCommandBuffer> cb,
id<MTLBuffer> a,
NSUInteger a_off,
id<MTLBuffer> b,
NSUInteger b_off,
id<MTLBuffer> out,
NSUInteger out_off,
uint32_t n);
typedef struct {
int32_t ne00;
uint64_t nb01;
int32_t ne10;
uint64_t nb11;
int32_t ne0;
uint64_t nb1;
int32_t i00;
int32_t i10;
float alpha;
float limit;
} ds4_gpu_glu_args;
typedef struct {
uint32_t n;
} ds4_gpu_add_flat_args;
typedef struct {
int32_t ne00;
int32_t ne01;
int32_t ne02;
int32_t ne03;
uint64_t nb00;
uint64_t nb01;
uint64_t nb02;
uint64_t nb03;
int32_t ne10;
int32_t ne11;
int32_t ne12;
int32_t ne13;
uint64_t nb10;
uint64_t nb11;
uint64_t nb12;
uint64_t nb13;
int32_t ne0;
int32_t ne1;
int32_t ne2;
int32_t ne3;
uint64_t nb0;
uint64_t nb1;
uint64_t nb2;
uint64_t nb3;
uint64_t offs;
uint64_t o1[8];
} ds4_gpu_bin_args;
typedef struct {
int32_t ne00;
int32_t ne01;
int32_t ne02;
int32_t ne03;
uint64_t nb00;
uint64_t nb01;
uint64_t nb02;
uint64_t nb03;
int32_t ne0;
int32_t ne1;
int32_t ne2;
int32_t ne3;
uint64_t nb0;
uint64_t nb1;
uint64_t nb2;
uint64_t nb3;
float slope;
float scale;
float bias;
float val;
float min;
float max;
} ds4_gpu_unary_args;
static ds4_gpu_bin_args ds4_gpu_make_bin_rows_args(uint32_t n, uint32_t rows, uint32_t rhs_n) {
const uint64_t row_bytes = (uint64_t)n * sizeof(float);
const uint64_t rhs_row_bytes = (uint64_t)rhs_n * sizeof(float);
return (ds4_gpu_bin_args) {
.ne00 = (int32_t)n,
.ne01 = (int32_t)rows,
.ne02 = 1,
.ne03 = 1,
.nb00 = sizeof(float),
.nb01 = row_bytes,
.nb02 = row_bytes,
.nb03 = row_bytes,
.ne10 = (int32_t)rhs_n,
.ne11 = 1,
.ne12 = 1,
.ne13 = 1,
.nb10 = sizeof(float),
.nb11 = rhs_row_bytes,
.nb12 = rhs_row_bytes,
.nb13 = rhs_row_bytes,
.ne0 = (int32_t)n,
.ne1 = (int32_t)rows,
.ne2 = 1,
.ne3 = 1,
.nb0 = sizeof(float),
.nb1 = row_bytes,
.nb2 = row_bytes,
.nb3 = row_bytes,
.offs = 0,
.o1 = { 0 },
};
}
static ds4_gpu_unary_args ds4_gpu_make_unary_rows_args(
uint32_t n,
uint32_t rows,
int c4,
float scale,
float bias) {
const uint64_t row_bytes = (uint64_t)n * sizeof(float);
const uint32_t n_kernel = c4 ? n / 4u : n;
return (ds4_gpu_unary_args) {
.ne00 = (int32_t)n_kernel,
.ne01 = (int32_t)rows,
.ne02 = 1,
.ne03 = 1,
.nb00 = sizeof(float),
.nb01 = row_bytes,
.nb02 = row_bytes,
.nb03 = row_bytes,
.ne0 = (int32_t)n_kernel,
.ne1 = (int32_t)rows,
.ne2 = 1,
.ne3 = 1,
.nb0 = sizeof(float),
.nb1 = row_bytes,
.nb2 = row_bytes,
.nb3 = row_bytes,
.slope = 0.0f,
.scale = scale,
.bias = bias,
.val = 0.0f,
.min = 0.0f,
.max = 0.0f,
};
}
static ds4_gpu_bin_args ds4_gpu_make_bin_same_rows_args(uint32_t n, uint32_t rows) {
const uint64_t row_bytes = (uint64_t)n * sizeof(float);
return (ds4_gpu_bin_args) {
.ne00 = (int32_t)n,
.ne01 = (int32_t)rows,
.ne02 = 1,
.ne03 = 1,
.nb00 = sizeof(float),
.nb01 = row_bytes,
.nb02 = (uint64_t)rows * row_bytes,
.nb03 = (uint64_t)rows * row_bytes,
.ne10 = (int32_t)n,
.ne11 = (int32_t)rows,
.ne12 = 1,
.ne13 = 1,
.nb10 = sizeof(float),
.nb11 = row_bytes,
.nb12 = (uint64_t)rows * row_bytes,
.nb13 = (uint64_t)rows * row_bytes,
.ne0 = (int32_t)n,
.ne1 = (int32_t)rows,
.ne2 = 1,
.ne3 = 1,
.nb0 = sizeof(float),
.nb1 = row_bytes,
.nb2 = (uint64_t)rows * row_bytes,
.nb3 = (uint64_t)rows * row_bytes,
.offs = 0,
.o1 = { 0 },
};
}
static int ds4_gpu_encode_bin_f32_rows(
id<MTLCommandBuffer> cb,
id<MTLComputePipelineState> pipeline,
const ds4_gpu_bin_args *args,
id<MTLBuffer> a,
NSUInteger a_off,
id<MTLBuffer> b,
NSUInteger b_off,
id<MTLBuffer> out,
NSUInteger out_off);
static int ds4_gpu_encode_sum_rows_f32(
id<MTLCommandBuffer> cb,
id<MTLBuffer> src,
NSUInteger src_off,
id<MTLBuffer> dst,
NSUInteger dst_off,
uint32_t width,
uint32_t rows);
typedef struct {
int32_t ne00;
int32_t ne01;
int32_t ne02;
uint64_t nb00;
uint64_t nb01;
uint64_t nb02;
uint64_t nb03;
int32_t ne10;
int32_t ne11;
int32_t ne12;
uint64_t nb10;
uint64_t nb11;
uint64_t nb12;
uint64_t nb13;
int32_t ne0;
int32_t ne1;
int32_t nr0;
int16_t r2;
int16_t r3;
} ds4_gpu_q8_0_matvec_args;
typedef struct {
int32_t ne00;
int32_t ne02;
uint64_t nb01;
uint64_t nb02;
uint64_t nb03;
int32_t ne12;
uint64_t nb10;
uint64_t nb11;
uint64_t nb12;
uint64_t nb13;
int32_t ne0;
int32_t ne1;
int16_t r2;
int16_t r3;
} ds4_gpu_mul_mm_args;
typedef struct {
int32_t ne00;
int32_t ne01;
int32_t ne02;
uint64_t nb00;
uint64_t nb01;
uint64_t nb02;
uint64_t nb03;
int32_t ne10;
int32_t ne11;
int32_t ne12;
uint64_t nb10;
uint64_t nb11;
uint64_t nb12;
uint64_t nb13;
int32_t ne0;
int32_t ne1;
int16_t r2;
int16_t r3;
} ds4_gpu_mul_mv_ext_args;
typedef ds4_gpu_q8_0_matvec_args ds4_gpu_f16_matvec_args;
static ds4_gpu_q8_0_matvec_args ds4_gpu_make_q8_0_mv_args(uint64_t in_dim, uint64_t out_dim) {
const uint64_t row_bytes = (in_dim / 32u) * 34u;
return (ds4_gpu_q8_0_matvec_args) {
.ne00 = (int32_t)in_dim,
.ne01 = (int32_t)out_dim,
.ne02 = 1,
.nb00 = 34,
.nb01 = row_bytes,
.nb02 = row_bytes * out_dim,
.nb03 = row_bytes * out_dim,
.ne10 = (int32_t)in_dim,
.ne11 = 1,
.ne12 = 1,
.nb10 = sizeof(float),
.nb11 = in_dim * sizeof(float),
.nb12 = in_dim * sizeof(float),
.nb13 = in_dim * sizeof(float),
.ne0 = (int32_t)out_dim,
.ne1 = 1,
.nr0 = 2,
.r2 = 1,
.r3 = 1,
};
}
static ds4_gpu_f16_matvec_args ds4_gpu_make_f16_mv_args(uint64_t in_dim, uint64_t out_dim) {
const uint64_t row_bytes = in_dim * sizeof(uint16_t);
return (ds4_gpu_f16_matvec_args) {
.ne00 = (int32_t)in_dim,
.ne01 = (int32_t)out_dim,
.ne02 = 1,
.nb00 = sizeof(uint16_t),
.nb01 = row_bytes,
.nb02 = row_bytes * out_dim,
.nb03 = row_bytes * out_dim,
.ne10 = (int32_t)in_dim,
.ne11 = 1,
.ne12 = 1,
.nb10 = sizeof(float),
.nb11 = in_dim * sizeof(float),
.nb12 = in_dim * sizeof(float),
.nb13 = in_dim * sizeof(float),
.ne0 = (int32_t)out_dim,
.ne1 = 1,
.nr0 = 2,
.r2 = 1,
.r3 = 1,
};
}
static ds4_gpu_q8_0_matvec_args ds4_gpu_make_f32_mv_args(
uint64_t in_dim,
uint64_t out_dim,
uint64_t n_vec) {
const uint64_t row_bytes = in_dim * sizeof(float);
return (ds4_gpu_q8_0_matvec_args) {
.ne00 = (int32_t)in_dim,
.ne01 = (int32_t)out_dim,
.ne02 = 1,
.nb00 = sizeof(float),
.nb01 = row_bytes,
.nb02 = row_bytes * out_dim,
.nb03 = row_bytes * out_dim,
.ne10 = (int32_t)in_dim,
.ne11 = (int32_t)n_vec,
.ne12 = 1,
.nb10 = sizeof(float),
.nb11 = in_dim * sizeof(float),
.nb12 = in_dim * n_vec * sizeof(float),
.nb13 = in_dim * n_vec * sizeof(float),
.ne0 = (int32_t)out_dim,
.ne1 = (int32_t)n_vec,
.nr0 = 2,
.r2 = 1,
.r3 = 1,
};
}
typedef struct {
const char *function_name;
int16_t nsg;
int32_t nr0;
NSUInteger smem;
} ds4_gpu_mv_dispatch;
static int ds4_gpu_tp_world_is_two(void);
static ds4_gpu_mv_dispatch ds4_gpu_make_q8_0_mv_dispatch(void) {
const uint64_t default_nsg = ds4_gpu_tp_world_is_two() ? 2u : 4u;
const int16_t nsg =
(int16_t)ds4_gpu_env_u64("DS4_METAL_Q8_MV_NSG", default_nsg, 1u, 8u);
return (ds4_gpu_mv_dispatch) {
.function_name = "kernel_mul_mv_q8_0_f32",
.nsg = nsg,
.nr0 = 2,
.smem = 32u * 2u * sizeof(float),
};
}
/* Single-token F16/F32 matvecs with few output rows (the Qwen hyper-connection
* low-rank down projections and routers) launch one row per SIMD group on M5,
* where two-row tiles leave most of the 40 cores idle. The per-row K walk,
* simdgroup count and reduction tree are unchanged. DS4_METAL_PLAIN_MV_NR0
* forces 1 or 2 rows on any device. */
static bool ds4_gpu_plain_mv_single_row(uint64_t out_dim) {
const uint64_t override = ds4_gpu_env_u64("DS4_METAL_PLAIN_MV_NR0", 0u, 0u, 2u);
if (override) return override == 1u;
return out_dim <= 1024u && ds4_gpu_device_is_m5_apple_silicon();
}
static ds4_gpu_mv_dispatch ds4_gpu_make_plain_mv_dispatch(
uint64_t in_dim,
int f32_weights) {
if (in_dim < 32) {
return (ds4_gpu_mv_dispatch) {
.function_name = f32_weights ? "kernel_mul_mv_f32_f32_short" : "kernel_mul_mv_f16_f32_short",
.nsg = 1,
.nr0 = 32,
.smem = 0,
};
}
const int16_t nsg = (int16_t)((in_dim + 127u) / 128u > 8u ? 8u : (in_dim + 127u) / 128u);
const int use_4 = (in_dim % 4u) == 0;
return (ds4_gpu_mv_dispatch) {
.function_name = f32_weights
? (use_4 ? "kernel_mul_mv_f32_f32_4" : "kernel_mul_mv_f32_f32")
: (use_4 ? "kernel_mul_mv_f16_f32_4" : "kernel_mul_mv_f16_f32"),
.nsg = nsg,
.nr0 = 2,
.smem = 32u * 2u * sizeof(float),
};
}
static ds4_gpu_mul_mm_args ds4_gpu_make_mm_args(
uint64_t in_dim,
uint64_t out_dim,
uint64_t n_tok,
uint64_t row_bytes) {
return (ds4_gpu_mul_mm_args) {
.ne00 = (int32_t)in_dim,
.ne02 = 1,
.nb01 = row_bytes,
.nb02 = row_bytes * out_dim,
.nb03 = row_bytes * out_dim,
.ne12 = 1,
.nb10 = sizeof(float),
.nb11 = in_dim * sizeof(float),
.nb12 = in_dim * n_tok * sizeof(float),
.nb13 = in_dim * n_tok * sizeof(float),
.ne0 = (int32_t)out_dim,
.ne1 = (int32_t)n_tok,
.r2 = 1,
.r3 = 1,
};
}
static ds4_gpu_mul_mv_ext_args ds4_gpu_make_mv_ext_args(
uint64_t in_dim,
uint64_t out_dim,
uint64_t n_tok,
uint64_t elem_bytes,
uint64_t row_bytes) {
return (ds4_gpu_mul_mv_ext_args) {
.ne00 = (int32_t)in_dim,
.ne01 = (int32_t)out_dim,
.ne02 = 1,
.nb00 = elem_bytes,
.nb01 = row_bytes,
.nb02 = row_bytes * out_dim,
.nb03 = row_bytes * out_dim,
.ne10 = (int32_t)in_dim,
.ne11 = (int32_t)n_tok,
.ne12 = 1,
.nb10 = sizeof(float),
.nb11 = in_dim * sizeof(float),
.nb12 = in_dim * n_tok * sizeof(float),
.nb13 = in_dim * n_tok * sizeof(float),
.ne0 = (int32_t)out_dim,
.ne1 = (int32_t)n_tok,
.r2 = 1,
.r3 = 1,
};
}
/* Simdgroups per threadgroup for the few-row (2..16 tokens) Q8/F16 matvecs
* that carry the MTP verify rows. Rows per threadgroup only: each row keeps
* its lane walk and shuffle tree, so outputs are byte-identical at any
* value (tests/test_qwen4_kernels.c pins 1/2/4/8). */
/* Row-exact geometry for the 3-row speculative verify: the few-row matvec
* and routed-expert dispatches keep the lane maps and kernel choices of the
* T <= 2 decode paths, so every committed row stays byte-identical. */
static bool g_qwen4_verify_rows_exact;
void ds4_gpu_qwen4_set_verify_rows_exact(bool on) {
g_qwen4_verify_rows_exact = on;
}
static int16_t ds4_gpu_mv_ext_nsg(void) {
return (int16_t)ds4_gpu_env_u64("DS4_METAL_MV_EXT_NSG", 2u, 1u, 8u);
}
static int16_t ds4_gpu_mv_ext_nxpsg(uint64_t in_dim, uint64_t n_tok) {
if ((in_dim % 256u) == 0 && (n_tok < 3 || (n_tok == 3 && g_qwen4_verify_rows_exact))) return 16;
if ((in_dim % 128u) == 0) return 8;
return 4;
}
static int16_t ds4_gpu_mv_ext_r1ptg(uint64_t n_tok) {
switch (n_tok) {
case 2: return 2;
case 3:
case 6: return 3;
case 4:
case 7:
case 8: return 4;
case 5: return 5;
default: return n_tok > 8 ? 4 : 0;
}
}
static const char *ds4_gpu_mv_ext_name(int q8, int16_t r1ptg) {
if (q8) {
switch (r1ptg) {
case 2: return "kernel_mul_mv_ext_q8_0_f32_r1_2";
case 3: return "kernel_mul_mv_ext_q8_0_f32_r1_3";
case 4: return "kernel_mul_mv_ext_q8_0_f32_r1_4";
case 5: return "kernel_mul_mv_ext_q8_0_f32_r1_5";
default: return NULL;
}
}
switch (r1ptg) {
case 2: return "kernel_mul_mv_ext_f16_f32_r1_2";
case 3: return "kernel_mul_mv_ext_f16_f32_r1_3";
case 4: return "kernel_mul_mv_ext_f16_f32_r1_4";
case 5: return "kernel_mul_mv_ext_f16_f32_r1_5";
default: return NULL;
}
}
static const char *ds4_gpu_mv_ext_f32_name(int16_t r1ptg) {
switch (r1ptg) {
case 2: return "kernel_mul_mv_ext_f32_f32_r1_2";
case 3: return "kernel_mul_mv_ext_f32_f32_r1_3";
case 4: return "kernel_mul_mv_ext_f32_f32_r1_4";
case 5: return "kernel_mul_mv_ext_f32_f32_r1_5";
default: return NULL;
}
}
static const char *ds4_gpu_mv_ext_q8_pair_swiglu_name(int16_t r1ptg) {
switch (r1ptg) {
case 2: return "kernel_mul_mv_ext_q8_0_pair_swiglu_f32_r1_2";
case 3: return "kernel_mul_mv_ext_q8_0_pair_swiglu_f32_r1_3";
case 4: return "kernel_mul_mv_ext_q8_0_pair_swiglu_f32_r1_4";
case 5: return "kernel_mul_mv_ext_q8_0_pair_swiglu_f32_r1_5";
default: return NULL;
}
}
typedef struct {
int32_t ne00;
int32_t ne00_t;
uint64_t nb1;
uint64_t nb2;
uint64_t nb3;
float eps;
int32_t nef1[3];
int32_t nef2[3];
int32_t nef3[3];
uint64_t nbf1[3];
uint64_t nbf2[3];
uint64_t nbf3[3];
} ds4_gpu_rms_norm_args;
typedef struct {
int32_t q_n;
int32_t q_n4;
int32_t kv_n;
int32_t kv_n4;
uint64_t q_row_stride;
uint64_t kv_row_stride;
float eps;
uint32_t tasks;
} ds4_gpu_qkv_rms_norm_args;
static ds4_gpu_rms_norm_args ds4_gpu_make_rms_norm_args(uint32_t n, uint32_t rows, float eps) {
const uint64_t row_bytes = (uint64_t)n * sizeof(float);
return (ds4_gpu_rms_norm_args) {
.ne00 = (int32_t)n,
.ne00_t = (int32_t)(n / 4u),
.nb1 = row_bytes,
.nb2 = row_bytes * rows,
.nb3 = row_bytes * rows,
.eps = eps,
.nef1 = { (int32_t)rows, 1, 1 },
.nef2 = { 1, 1, 1 },
.nef3 = { 1, 1, 1 },
.nbf1 = { row_bytes, row_bytes, row_bytes },
.nbf2 = { row_bytes * rows, row_bytes, row_bytes },
.nbf3 = { row_bytes * rows, row_bytes, row_bytes },
};
}
static ds4_gpu_rms_norm_args ds4_gpu_make_rms_norm_3d_args(
uint32_t n0,
uint32_t n1,
uint32_t n2,
float eps) {
const uint64_t row_bytes = (uint64_t)n0 * sizeof(float);
const uint64_t plane_bytes = row_bytes * n1;
return (ds4_gpu_rms_norm_args) {
.ne00 = (int32_t)n0,
.ne00_t = (int32_t)(n0 / 4u),
.nb1 = row_bytes,
.nb2 = plane_bytes,
.nb3 = plane_bytes * n2,
.eps = eps,
.nef1 = { (int32_t)n1, 1, 1 },
.nef2 = { (int32_t)n2, 1, 1 },
.nef3 = { 1, 1, 1 },
.nbf1 = { row_bytes, row_bytes, row_bytes },
.nbf2 = { plane_bytes, row_bytes, row_bytes },
.nbf3 = { plane_bytes * n2, row_bytes, row_bytes },
};
}
static NSUInteger ds4_gpu_rms_norm_threads(uint32_t n) {
NSUInteger ne00_t = n / 4u;
NSUInteger nth = 32u;
while (nth < ne00_t && nth < 1024u) nth *= 2u;
if (nth > ne00_t) nth = ne00_t;
return nth ? nth : 1u;
}
static NSUInteger ds4_gpu_rms_norm_pipeline_threads(
uint32_t n,
id<MTLComputePipelineState> pipeline) {
NSUInteger ne00_t = n / 4u;
NSUInteger max_threads = pipeline ? [pipeline maxTotalThreadsPerThreadgroup] : 1024u;
NSUInteger nth = 32u;
while (nth < ne00_t && nth < max_threads) nth *= 2u;
if (nth > max_threads) nth = max_threads;
if (nth > ne00_t) nth = ne00_t;
return nth ? nth : 1u;
}
typedef struct {
int32_t n_hc;
int32_t sinkhorn_iters;
int64_t n_rows;
int64_t mix_hc;
uint64_t nb01;
uint64_t nb1;
float eps;
} ds4_gpu_hc_split_args;
typedef struct {
int64_t n_embd;
int64_t n_hc;
int64_t n_tokens;
uint64_t nb_x0;
uint64_t nb_x1;
uint64_t nb_x2;
uint64_t nb_w0;
uint64_t nb_w1;
uint64_t nb0;
uint64_t nb1;
uint64_t round_bf16;
} ds4_gpu_hc_weighted_sum_args;
typedef struct {
float post_scale;
float eps;
} ds4_gpu_output_hc_weights4_args;
typedef struct {
int64_t n_embd;
int32_t n_hc;
int32_t sinkhorn_iters;
int64_t n_rows;
int64_t mix_hc;
uint64_t nb_mix1;
uint64_t nb_split1;
uint64_t nb_x0;
uint64_t nb_x1;
uint64_t nb_x2;
uint64_t nb0;
uint64_t nb1;
float eps;
} ds4_gpu_hc_split_weighted_sum_args;
typedef struct {
int64_t n_embd;
int32_t n_hc;
int32_t sinkhorn_iters;
int64_t n_rows;
int64_t mix_hc;
uint64_t nb_mix1;
uint64_t nb_split1;
uint64_t nb_x0;
uint64_t nb_x1;
uint64_t nb_x2;
uint64_t nb0;
uint64_t nb1;
uint64_t nb_norm1;
float eps;
float norm_eps;
} ds4_gpu_hc_split_weighted_sum_norm_args;
typedef struct {
int64_t n_embd;
int64_t n_hc;
int64_t n_tokens;
uint64_t nb_block0;
uint64_t nb_block1;
uint64_t nb_add0;
uint64_t nb_add1;
uint64_t nb_res0;
uint64_t nb_res1;
uint64_t nb_res2;
uint64_t nb_post0;
uint64_t nb_post1;
uint64_t nb_comb0;
uint64_t nb_comb1;
uint64_t nb_comb2;
uint64_t nb0;
uint64_t nb1;
uint64_t nb2;
int32_t has_add;
int32_t round_bf16;
int32_t round_block_in;
} ds4_gpu_hc_expand_args;
typedef struct {
int32_t nei0;
int32_t nei1;
uint64_t nbi1;
int32_t ne00;
int32_t ne01;
int32_t ne02;
uint64_t nb00;
uint64_t nb01;
uint64_t nb02;
int32_t ne10;
int32_t ne11;
int32_t ne12;
int32_t ne13;
uint64_t nb10;
uint64_t nb11;
uint64_t nb12;
int32_t ne0;
int32_t ne1;
uint64_t nb1;
int32_t nr0;
/* Tensor-parallel expert ownership; see ds4_metal_args_mul_mv_id in
* metal/moe.metal. Zero (from struct literals) means no split. */
int32_t tp_rank;
int32_t tp_world;
int32_t tp_addend;
int32_t tp_expert_base;
} ds4_gpu_mul_mv_id_args;
typedef struct {
uint32_t n_total_expert;
uint32_t n_expert;
} ds4_gpu_stream_expert_validate_args;
typedef struct {
uint32_t active_mask;
uint32_t accumulate;
} ds4_gpu_stream_expert_split_args;
typedef struct {
int32_t ne02;
int32_t ne10;
int32_t ne11;
uint64_t nb11;
uint64_t nb12;
int32_t ne21;
int32_t ne20;
uint64_t nb21;
} ds4_gpu_mul_mm_id_map_args;
typedef struct {
int32_t ne00;
int32_t ne02;
uint64_t nb01;
uint64_t nb02;
uint64_t nb03;
int32_t ne11;
uint64_t nb10;
uint64_t nb11;
uint64_t nb12;
uint64_t nb13;
int32_t ne20;
int32_t ne21;
int32_t ne0;
int32_t ne1;
int16_t r2;
int16_t r3;
int32_t tp_rank;
int32_t tp_world;
int32_t tp_expert_base;
} ds4_gpu_mul_mm_id_args;
static int ds4_gpu_encode_mul_mv_id(
id<MTLCommandBuffer> cb,
id<MTLComputePipelineState> pipeline,
const ds4_gpu_mul_mv_id_args *args,
id<MTLBuffer> src0,
NSUInteger src0_off,
id<MTLBuffer> src1,
NSUInteger src1_off,
id<MTLBuffer> dst,
NSUInteger dst_off,
id<MTLBuffer> ids,
NSUInteger ids_off,
NSUInteger threadgroup_bytes,
NSUInteger nsg,
bool rows_per_group_is_nr0);
static int ds4_gpu_encode_attn_out_low_q8_direct(
id<MTLCommandBuffer> cb,
id<MTLComputePipelineState> pipeline,
const ds4_gpu_mul_mv_id_args *args,
id<MTLBuffer> src0,
NSUInteger src0_off,
id<MTLBuffer> src1,
NSUInteger src1_off,
id<MTLBuffer> dst,
NSUInteger dst_off,
NSUInteger threadgroup_bytes,
NSUInteger nsg,
bool rows_per_group_is_nr0);
static int ds4_gpu_encode_attn_out_low_mpp(
id<MTLCommandBuffer> cb,
id<MTLComputePipelineState> pipeline,
const ds4_gpu_mul_mm_id_args *mm_args,
id<MTLBuffer> src0,
NSUInteger src0_off,
id<MTLBuffer> src1,
NSUInteger src1_off,
id<MTLBuffer> dst,
NSUInteger dst_off);
static ds4_gpu_mul_mm_id_map_args ds4_gpu_make_mul_mm_id_map_args(
uint32_t src0_cols,
uint32_t src0_experts,
uint32_t src1_expert_rows,
uint32_t selected_experts,
uint32_t n_tokens);
static ds4_gpu_mul_mm_id_args ds4_gpu_make_mul_mm_id_args(
uint32_t src0_cols,
uint32_t src0_rows,
uint32_t src0_experts,
uint64_t src0_row_bytes,
uint64_t src0_expert_bytes,
uint32_t src1_expert_rows,
uint32_t selected_experts,
uint32_t n_tokens);
static ds4_gpu_mul_mm_id_args ds4_gpu_make_mul_mm_id_args_src1_size(
uint32_t src0_cols,
uint32_t src0_rows,
uint32_t src0_experts,
uint64_t src0_row_bytes,
uint64_t src0_expert_bytes,
uint32_t src1_expert_rows,
uint32_t selected_experts,
uint32_t n_tokens,
uint32_t src1_elem_size);
static int ds4_gpu_encode_mul_mm_id(
id<MTLCommandBuffer> cb,
id<MTLComputePipelineState> map_pipeline,
id<MTLComputePipelineState> mm_pipeline,
const ds4_gpu_mul_mm_id_map_args *map_args,
const ds4_gpu_mul_mm_id_args *mm_args,
id<MTLBuffer> src0,
NSUInteger src0_off,
id<MTLBuffer> src1,
NSUInteger src1_off,
id<MTLBuffer> dst,
NSUInteger dst_off,
id<MTLBuffer> ids,
NSUInteger ids_off);
static int ds4_gpu_encode_mul_mm_id_map(
id<MTLCommandBuffer> cb,
id<MTLComputePipelineState> map_pipeline,
const ds4_gpu_mul_mm_id_map_args *map_args,
const ds4_gpu_mul_mm_id_args *mm_args,
id<MTLBuffer> ids,
NSUInteger ids_off);
static int ds4_gpu_encode_mul_mm_id_mapped(
id<MTLCommandBuffer> cb,
id<MTLComputePipelineState> mm_pipeline,
const ds4_gpu_mul_mm_id_args *mm_args,
id<MTLBuffer> src0,
NSUInteger src0_off,
id<MTLBuffer> src1,
NSUInteger src1_off,
id<MTLBuffer> dst,
NSUInteger dst_off);
static int ds4_gpu_encode_mul_mm_id_mapped_tile(
id<MTLCommandBuffer> cb,
id<MTLComputePipelineState> mm_pipeline,
const ds4_gpu_mul_mm_id_args *mm_args,
id<MTLBuffer> src0,
NSUInteger src0_off,
id<MTLBuffer> src1,
NSUInteger src1_off,
id<MTLBuffer> dst,
NSUInteger dst_off,
NSUInteger threadgroup_bytes);
static int ds4_gpu_encode_mul_mm_id_addr_mapped_tile(
id<MTLCommandBuffer> cb,
id<MTLComputePipelineState> mm_pipeline,
const ds4_gpu_mul_mm_id_args *mm_args,
id<MTLBuffer> src0_addrs,
id<MTLBuffer> src1,
NSUInteger src1_off,
id<MTLBuffer> dst,
NSUInteger dst_off,
NSUInteger threadgroup_bytes,
ds4_gpu_stream_expert_cache_entry * const *resources,
uint32_t resource_count,
uint32_t resource_kind,
id<MTLBuffer> overflow_resource);
typedef struct {
int32_t ne11;
int32_t ne_12_2;
int32_t ne_12_3;
uint64_t nb11;
uint64_t nb12;
uint64_t nb13;
uint64_t nb21;
uint64_t nb22;
uint64_t nb23;
int32_t ne31;
int32_t ne32;
int32_t ne33;
uint64_t nb31;
uint64_t nb32;
uint64_t nb33;
} ds4_gpu_flash_attn_pad_args;
typedef struct {
uint32_t raw_cap;
uint32_t raw_start;
uint32_t n_raw;
uint32_t n_comp;
uint32_t pad_rows;
uint32_t shared_pad;
} ds4_gpu_flash_kv_stage_f16_args;
typedef struct {
int32_t ne01;
int32_t ne30;
int32_t ne31;
int32_t ne32;
int32_t ne33;
uint64_t nb31;
uint64_t nb32;
uint64_t nb33;
} ds4_gpu_flash_attn_blk_args;
typedef struct {
int32_t ne01;
int32_t ne02;
int32_t ne03;
uint64_t nb01;
uint64_t nb02;
uint64_t nb03;
int32_t ne11;
int32_t ne_12_2;
int32_t ne_12_3;
int32_t ns10;
uint64_t nb11;
uint64_t nb12;
uint64_t nb13;
int32_t ns20;
uint64_t nb21;
uint64_t nb22;
uint64_t nb23;
int32_t ne31;
int32_t ne32;
int32_t ne33;
uint64_t nb31;
uint64_t nb32;
uint64_t nb33;
int32_t ne1;
int32_t ne2;
int32_t ne3;
float scale;
float max_bias;
float m0;
float m1;
int32_t n_head_log2;
float logit_softcap;
} ds4_gpu_flash_attn_vec_args;
typedef struct {
int32_t nrows;
} ds4_gpu_flash_attn_reduce_args;
typedef struct {
int64_t ne00;
int64_t ne01;
int64_t ne02;
int64_t ne03;
uint64_t nb00;
uint64_t nb01;
uint64_t nb02;
uint64_t nb03;
uint64_t nb0;
uint64_t nb1;
uint64_t nb2;
uint64_t nb3;
int32_t n_dims;
int32_t mode;
int32_t n_ctx_orig;
int32_t inverse;
float freq_base;
float freq_scale;
float ext_factor;
float attn_factor;
float beta_fast;
float beta_slow;
bool src2;
} ds4_gpu_rope_tail_batch_args;
typedef struct {
uint64_t row_bytes;
uint64_t token_bytes;
int32_t head_dim;
int32_t n_dims;
int32_t n_ctx_orig;
int32_t inverse;
uint32_t pos0;
uint32_t pos_step;
float freq_base;
float freq_scale;
float ext_factor;
float attn_factor;
float beta_fast;
float beta_slow;
} ds4_gpu_rope_affine_pair_args;
/* Matches ds4_metal_args_dsv4_comp_finalize in dsv4_rope.metal. */
typedef struct {
ds4_gpu_rope_affine_pair_args rope;
float rms_eps;
uint32_t pad0;
} ds4_gpu_comp_finalize_args;
/* Set by ds4.c immediately before the decode attention call when the inverse
* RoPE tail is deferred into the reduce kernel. Cleared by the encoder. */
static ds4_gpu_rope_affine_pair_args g_decode_attn_rope_args;
static int g_decode_attn_rope_fuse;
/* Set only by the encoder that actually applied the deferred rotation, so ds4.c
* can fall back to the standalone RoPE on any attention path that does not
* consume it (for example ratio-0 layers that take the raw-heads encoder). */
static int g_decode_attn_rope_fuse_used;
void ds4_gpu_set_decode_attn_rope_fuse(
uint32_t head_dim, uint32_t n_rot, uint32_t pos0, uint32_t n_ctx_orig,
bool inverse, float freq_base, float freq_scale, float ext_factor,
float attn_factor, float beta_fast, float beta_slow) {
const uint64_t row_bytes = (uint64_t)head_dim * sizeof(float);
g_decode_attn_rope_args = (ds4_gpu_rope_affine_pair_args) {
.row_bytes = row_bytes,
.token_bytes = row_bytes,
.head_dim = (int32_t)head_dim,
.n_dims = (int32_t)n_rot,
.n_ctx_orig = (int32_t)n_ctx_orig,
.inverse = inverse ? 1 : 0,
.pos0 = pos0,
.pos_step = 1,
.freq_base = freq_base,
.freq_scale = freq_scale,
.ext_factor = ext_factor,
.attn_factor = attn_factor,
.beta_fast = beta_fast,
.beta_slow = beta_slow,
};
g_decode_attn_rope_fuse = 1;
g_decode_attn_rope_fuse_used = 0;
}
int ds4_gpu_decode_attn_rope_fuse_used(void) {
return g_decode_attn_rope_fuse_used;
}
_Static_assert(sizeof(ds4_gpu_rope_affine_pair_args) == 64,
"Metal affine RoPE argument ABI changed");
static ds4_gpu_rope_tail_batch_args ds4_gpu_make_rope_tail_args(
uint32_t n_tok,
uint32_t n_head,
uint32_t head_dim,
uint32_t n_rot,
uint32_t n_ctx_orig,
bool inverse,
float freq_base,
float freq_scale,
float ext_factor,
float attn_factor,
float beta_fast,
float beta_slow) {
const uint64_t row_bytes = (uint64_t)head_dim * sizeof(float);
const uint64_t tok_bytes = (uint64_t)n_head * row_bytes;
return (ds4_gpu_rope_tail_batch_args) {
.ne00 = head_dim,
.ne01 = n_head,
.ne02 = n_tok,
.ne03 = 1,
.nb00 = sizeof(float),
.nb01 = row_bytes,
.nb02 = tok_bytes,
.nb03 = (uint64_t)n_tok * tok_bytes,
.nb0 = sizeof(float),
.nb1 = row_bytes,
.nb2 = tok_bytes,
.nb3 = (uint64_t)n_tok * tok_bytes,
.n_dims = (int32_t)n_rot,
.mode = 0,
.n_ctx_orig = (int32_t)n_ctx_orig,
.inverse = inverse ? 1 : 0,
.freq_base = freq_base,
.freq_scale = freq_scale,
.ext_factor = ext_factor,
.attn_factor = attn_factor,
.beta_fast = beta_fast,
.beta_slow = beta_slow,
.src2 = false,
};
}
static int ds4_gpu_encode_rope_tail_inplace(
id<MTLCommandBuffer> cb,
id<MTLBuffer> xbuf,
NSUInteger xoff,
const ds4_gpu_rope_tail_batch_args *args,
uint32_t n_tok,
uint32_t n_head,
uint32_t head_dim,
uint32_t pos0,
uint32_t pos_step) {
const uint32_t tail_threads = args->n_dims > 0 ? (uint32_t)args->n_dims : 0u;
const bool lane_compatible =
tail_threads <= head_dim && ((head_dim - tail_threads) & 31u) == 0u;
const bool use_inplace_pair =
g_rope_tail_inplace_pair_pipeline != nil &&
getenv("DS4_METAL_DISABLE_INPLACE_ROPE_PAIR") == NULL &&
args->mode == 0 && !args->src2 &&
lane_compatible &&
(ds4_gpu_device_name_contains("M3") ||
(ds4_gpu_device_name_contains("M5") && n_tok == 1u));
const bool use_shared_coeff =
use_inplace_pair &&
g_rope_tail_inplace_pair_shared4_pipeline != nil &&
getenv("DS4_METAL_DISABLE_SHARED_ROPE_COEFF") == NULL &&
/* The 256-thread grouped schedule helps long prefill, but reduces the
* per-head parallelism that short batches and decode rely on. */
tail_threads == 64u && n_head >= 4u && n_tok >= 32u;
/* Keep long prefill on the proven shared4 kernel. Reconstructing affine
* positions inside its coefficient cohort perturbs YaRN fast-math codegen;
* the compact affine specialization is exact for the decode pair schedule. */
const bool use_affine_position =
use_inplace_pair && !use_shared_coeff &&
g_rope_tail_inplace_pair_affine_pipeline != nil &&
getenv("DS4_METAL_DISABLE_AFFINE_ROPE_PAIR") == NULL &&
n_tok == 1u &&
(ds4_gpu_device_name_contains("M3") ||
ds4_gpu_device_name_contains("M5"));
int32_t pos_stack[256];
int32_t *pos = NULL;
id<MTLBuffer> posbuf = nil;
const NSUInteger pos_bytes = (NSUInteger)n_tok * sizeof(int32_t);
if (!use_affine_position) {
pos = pos_stack;
if (n_tok > (uint32_t)(sizeof(pos_stack) / sizeof(pos_stack[0]))) {
pos = malloc((size_t)n_tok * sizeof(*pos));
if (!pos) {
fprintf(stderr, "ds4: failed to allocate Metal RoPE position buffer\n");
return 0;
}
}
for (uint32_t t = 0; t < n_tok; t++) {
pos[t] = (int32_t)(pos0 + t * pos_step);
}
if (pos_bytes > 4096u) {
/*
* Metal inline setBytes data is meant for small constants. Long
* prefill RoPE calls need thousands of positions; passing that much
* inline can make the Apple driver abort the process.
*/
posbuf = ds4_gpu_new_transient_buffer(
pos_bytes, "ds4_rope_positions");
if (!posbuf) {
if (pos != pos_stack) free(pos);
return 0;
}
memcpy([posbuf contents], pos, pos_bytes);
}
}
ds4_gpu_rope_affine_pair_args affine_args;
if (use_affine_position) {
const uint64_t row_bytes = (uint64_t)head_dim * sizeof(float);
affine_args = (ds4_gpu_rope_affine_pair_args) {
.row_bytes = row_bytes,
.token_bytes = (uint64_t)n_head * row_bytes,
.head_dim = (int32_t)head_dim,
.n_dims = args->n_dims,
.n_ctx_orig = args->n_ctx_orig,
.inverse = args->inverse,
.pos0 = pos0,
.pos_step = pos_step,
.freq_base = args->freq_base,
.freq_scale = args->freq_scale,
.ext_factor = args->ext_factor,
.attn_factor = args->attn_factor,
.beta_fast = args->beta_fast,
.beta_slow = args->beta_slow,
};
}
const NSUInteger reference_nth =
(NSUInteger)(head_dim < 256u ? head_dim : 256u);
const NSUInteger pair_nth =
(NSUInteger)(tail_threads < 256u ? tail_threads : 256u);
const NSUInteger nth = use_shared_coeff ? 256u :
(use_inplace_pair ? pair_nth : reference_nth);
const NSUInteger head_groups = use_shared_coeff ?
(NSUInteger)((n_head + 3u) / 4u) : (NSUInteger)n_head;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
id<MTLComputePipelineState> pipeline = use_affine_position ?
g_rope_tail_inplace_pair_affine_pipeline :
(use_shared_coeff ?
g_rope_tail_inplace_pair_shared4_pipeline :
(use_inplace_pair ?
g_rope_tail_inplace_pair_pipeline : g_rope_tail_batch_pipeline));
[enc setComputePipelineState:pipeline];
if (use_affine_position) {
[enc setBytes:&affine_args length:sizeof(affine_args) atIndex:0];
} else {
[enc setBytes:args length:sizeof(*args) atIndex:0];
}
[enc setBuffer:xbuf offset:xoff atIndex:1];
if (!use_affine_position) {
if (posbuf) {
[enc setBuffer:posbuf offset:0 atIndex:2];
} else {
[enc setBytes:pos length:pos_bytes atIndex:2];
}
[enc setBuffer:xbuf offset:xoff atIndex:3];
}
[enc setBuffer:xbuf offset:xoff atIndex:4];
[enc dispatchThreadgroups:MTLSizeMake(head_groups, n_tok, 1)
threadsPerThreadgroup:MTLSizeMake(nth ? nth : 1u, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (pos && pos != pos_stack) free(pos);
return 1;
}
typedef struct {
int64_t ne00;
int64_t ne01;
int64_t ne02;
int64_t ne03;
uint64_t nb00;
uint64_t nb01;
uint64_t nb02;
uint64_t nb03;
uint64_t nb0;
uint64_t nb1;
uint64_t nb2;
uint64_t nb3;
int32_t n_rot;
} ds4_gpu_dsv4_fp8_kv_quantize_args;
typedef struct {
int32_t head_dim;
int32_t n_rot;
int32_t raw_row;
} ds4_gpu_dsv4_kv_fp8_store_args;
typedef struct {
uint32_t n_rows;
uint32_t head_dim;
uint64_t row_stride;
} ds4_gpu_dsv4_indexer_qat_args;
typedef struct {
uint32_t width;
} ds4_gpu_dsv4_ratio4_shift_args;
typedef struct {
uint32_t head_dim;
uint32_t n_comp;
uint32_t replay;
uint32_t n_threads;
} ds4_gpu_dsv4_compressor_pack_ratio4_args;
typedef struct {
int64_t n_rows;
uint32_t head_dim;
uint32_t n_comp;
uint32_t replay;
uint32_t pad;
} ds4_gpu_dsv4_softmax_pool_ratio4_direct_args;
typedef struct {
uint32_t width;
uint32_t ratio;
uint32_t pos;
uint32_t ape_type;
} ds4_gpu_dsv4_compressor_store_one_args;
typedef struct {
int64_t ne00;
int64_t ne01;
int64_t ne02;
uint64_t nb00;
uint64_t nb01;
uint64_t nb02;
uint64_t nb10;
uint64_t nb11;
uint64_t nb12;
int64_t ne0;
int64_t ne1;
uint64_t nb0;
uint64_t nb1;
} ds4_gpu_dsv4_softmax_pool_args;
typedef struct {
uint32_t width;
uint32_t ratio;
uint32_t pos0;
uint32_t n_tokens;
} ds4_gpu_dsv4_compressor_score_ape_args;
typedef struct {
int32_t ne00;
int32_t ne01;
int32_t ne02;
int32_t ne03;
uint64_t nb00;
uint64_t nb01;
uint64_t nb02;
uint64_t nb03;
int32_t ne0;
int32_t ne1;
int32_t ne2;
int32_t ne3;
int32_t top_k;
uint32_t causal_start;
uint32_t causal_ratio;
} ds4_gpu_kargs_argsort;
typedef struct {
int64_t ne00;
int64_t ne01;
int64_t ne02;
int64_t ne03;
uint64_t nb00;
uint64_t nb01;
uint64_t nb02;
uint64_t nb03;
int32_t ne0;
int32_t ne1;
int32_t ne2;
int32_t ne3;
int32_t top_k;
int32_t len;
uint32_t causal_start;
uint32_t causal_ratio;
uint32_t block_width;
uint32_t block_top_k;
} ds4_gpu_kargs_argsort_merge;
typedef struct {
uint32_t n;
uint32_t rows;
uint32_t top_k;
uint32_t pass;
uint32_t parity;
uint32_t n_tg;
} ds4_gpu_kargs_topk_select;
typedef struct {
int64_t ne00;
int64_t ne01;
int64_t ne02;
int64_t ne03;
uint64_t nb00;
uint64_t nb01;
uint64_t nb02;
uint64_t nb03;
int64_t ne0;
int64_t ne1;
int64_t ne2;
int64_t ne3;
uint64_t nb0;
uint64_t nb1;
uint64_t nb2;
uint64_t nb3;
} ds4_gpu_kargs_sum_rows;
typedef struct {
int32_t ne00;
int32_t ne01;
int32_t ne02;
uint64_t nb01;
uint64_t nb02;
uint64_t nb03;
int32_t ne11;
int32_t ne12;
int32_t ne13;
uint64_t nb11;
uint64_t nb12;
uint64_t nb13;
uint64_t nb1;
uint64_t nb2;
uint64_t nb3;
float scale;
float max_bias;
float m0;
float m1;
int32_t n_head_log2;
} ds4_gpu_softmax_args;
typedef struct {
int64_t ne00;
int64_t ne01;
uint64_t nb00;
uint64_t nb01;
int64_t ne0;
int64_t ne1;
uint64_t nb0;
uint64_t nb1;
} ds4_gpu_dsv4_topk_mask_args;
typedef struct {
int64_t ne00;
int64_t ne01;
int64_t ne02;
uint64_t nb00;
uint64_t nb01;
uint64_t nb02;
int64_t ne10;
int64_t ne11;
uint64_t nb10;
uint64_t nb11;
int64_t ne0;
int64_t ne1;
uint64_t nb0;
uint64_t nb1;
float scale;
} ds4_gpu_dsv4_indexer_weighted_sum_args;
typedef struct {
uint32_t has_bias;
uint32_t hash_mode;
uint32_t use_token_buffer;
uint32_t token;
uint32_t hash_rows;
} ds4_gpu_dsv4_router_select_one_args;
typedef struct {
uint32_t hash_rows;
uint32_t vocab_size;
uint32_t n_tokens;
uint32_t has_bias;
uint32_t hash_mode;
} ds4_gpu_dsv4_router_select_visual_args;
typedef struct {
uint32_t n_expert;
uint32_t n_expert_used;
float expert_weight_scale;
uint32_t pad0;
} ds4_gpu_glm_router_select_one_args;
typedef struct {
uint32_t n_tokens;
uint32_t kv_raw_dim;
uint32_t kv_lora_dim;
float eps;
} ds4_gpu_glm_kv_lora_rms_norm_args;
typedef struct {
uint32_t n_tokens;
uint32_t kv_lora_dim;
uint32_t qk_nope;
uint32_t n_head;
uint32_t row_bytes;
uint32_t weight_type;
uint32_t pad1;
uint32_t pad2;
} ds4_gpu_glm_k_b_project_args;
typedef struct {
uint32_t pos0;
uint32_t n_tokens;
uint32_t cache_cap;
uint32_t kv_raw_dim;
uint32_t kv_lora_dim;
uint32_t qk_rope;
uint32_t cache_f16;
uint32_t pad1;
} ds4_gpu_glm_store_compact_kv_args;
typedef struct {
uint32_t pos0;
uint32_t n_tokens;
uint32_t cache_cap;
uint32_t q_n;
uint32_t q_n4;
uint32_t kv_raw_dim;
uint32_t kv_lora_dim;
uint32_t kv_lora_n4;
uint32_t qk_rope;
uint32_t cache_f16;
float eps;
uint32_t pad0;
} ds4_gpu_glm_qkv_norm_store_compact_kv_args;
typedef struct {
uint32_t pos0;
uint32_t n_tokens;
uint32_t cache_cap;
uint32_t head_dim;
uint32_t rot_dim;
uint32_t n_ctx_orig;
uint32_t cache_f16;
uint32_t pad0;
float eps;
float freq_base;
float freq_scale;
float ext_factor;
float attn_factor;
float beta_fast;
float beta_slow;
float pad1;
} ds4_gpu_glm_store_indexer_k_args;
typedef struct {
uint32_t pos0;
uint32_t n_tokens;
uint32_t cache_cap;
uint32_t head_dim;
uint32_t pool_size;
uint32_t cache_f16;
float eps;
uint32_t pad0;
} ds4_gpu_glm53_indexer_pool_update_args;
typedef struct {
uint32_t pos0;
uint32_t n_tokens;
uint32_t cache_cap;
uint32_t n_head;
uint32_t kv_raw_dim;
uint32_t kv_lora_dim;
uint32_t qk_nope;
uint32_t qk_rope;
uint32_t value_dim;
uint32_t n_ctx_orig;
uint32_t cache_f16;
uint32_t pad0;
float freq_base;
float freq_scale;
float ext_factor;
float attn_factor;
float beta_fast;
float beta_slow;
} ds4_gpu_glm_build_kv_cache_args;
typedef struct {
uint32_t pos0;
uint32_t n_tokens;
uint32_t cache_len;
uint32_t cache_cap;
uint32_t n_head;
uint32_t qk_dim;
uint32_t value_dim;
uint32_t pad0;
uint32_t cache_f16;
uint32_t pad1;
uint32_t pad2;
float scale;
} ds4_gpu_glm_attention_full_args;
typedef struct {
uint32_t n_selected;
} ds4_gpu_glm_fill_selected_range_args;
typedef struct {
uint32_t n_tokens;
uint32_t pos0;
uint32_t n_selected;
uint32_t pad_row;
} ds4_gpu_glm_fill_selected_range_batch_args;
typedef struct {
uint32_t n_tokens;
uint32_t pos0;
uint32_t selected_pools;
uint32_t index_topk;
uint32_t pool_size;
uint32_t output_width;
} ds4_gpu_glm53_expand_pool_selection_args;
typedef struct {
uint32_t n_tokens;
uint32_t n_head;
uint32_t head_dim;
uint32_t rot_dim;
uint32_t rot_offset;
uint32_t pos0;
uint32_t n_ctx_orig;
float freq_base;
float freq_scale;
float ext_factor;
float attn_factor;
float beta_fast;
float beta_slow;
} ds4_gpu_glm_rope_tail_args;
typedef struct {
uint32_t n_rows;
uint32_t n_head;
uint32_t head_dim;
uint32_t cache_f16;
float scale;
} ds4_gpu_glm_indexer_score_one_args;
typedef struct {
uint32_t n_rows;
uint32_t n_tokens;
uint32_t n_head;
uint32_t head_dim;
uint32_t pos0;
uint32_t cache_f16;
uint32_t row_group_size;
uint32_t pad0;
uint64_t q_token_stride;
uint64_t q_head_stride;
uint64_t weights_token_stride;
uint64_t score_token_stride;
float scale;
} ds4_gpu_glm_indexer_scores_batch_args;
typedef struct {
uint32_t n_head;
uint32_t kv_lora_dim;
uint32_t qk_nope;
uint32_t qk_dim;
uint32_t row_bytes;
uint32_t weight_type;
uint32_t pad1;
uint32_t pad2;
} ds4_gpu_glm_qk_lowrank_args;
typedef struct {
uint32_t n_tokens;
uint32_t n_head;
uint32_t kv_lora_dim;
uint32_t qk_nope;
uint32_t qk_dim;
uint32_t row_bytes;
uint32_t weight_type;
uint32_t head_base;
} ds4_gpu_glm_qk_lowrank_batch_args;
typedef struct {
uint32_t n_selected;
uint32_t cache_cap;
uint32_t cache_f16;
uint32_t n_head;
uint32_t kv_lora_dim;
uint32_t qk_nope;
uint32_t qk_rope;
uint32_t value_dim;
uint32_t n_ctx_orig;
uint32_t value_row_bytes;
float scale;
float freq_base;
float freq_scale;
float ext_factor;
float attn_factor;
float beta_fast;
float beta_slow;
uint32_t value_type;
} ds4_gpu_glm_attention_indexed_decode_args;
typedef struct {
uint32_t n_selected;
uint32_t cache_cap;
uint32_t cache_f16;
uint32_t n_head;
uint32_t kv_lora_dim;
uint32_t qk_nope;
uint32_t qk_rope;
uint32_t value_dim;
uint32_t n_ctx_orig;
uint32_t value_row_bytes;
uint32_t block_rows;
uint32_t n_blocks;
float scale;
float freq_base;
float freq_scale;
float ext_factor;
float attn_factor;
float beta_fast;
float beta_slow;
uint32_t value_type;
} ds4_gpu_glm_attention_indexed_decode_split_args;
typedef struct {
uint32_t n_tokens;
uint32_t n_selected;
uint32_t cache_cap;
uint32_t cache_f16;
uint32_t n_head;
uint32_t kv_lora_dim;
uint32_t qk_nope;
uint32_t qk_rope;
uint32_t value_dim;
uint32_t n_ctx_orig;
uint32_t value_row_bytes;
uint32_t value_type;
uint32_t pos0;
float scale;
float freq_base;
float freq_scale;
float ext_factor;
float attn_factor;
float beta_fast;
float beta_slow;
uint32_t head_base;
} ds4_gpu_glm_attention_indexed_batch_args;
typedef struct {
uint32_t in_dim;
uint32_t mid_dim;
uint32_t out_dim;
uint32_t n_total_expert;
uint32_t n_expert_used;
uint32_t n_tokens;
uint32_t mid_token_stride;
uint32_t down_type;
float swiglu_clamp;
int32_t tp_rank;
int32_t tp_world;
int32_t tp_expert_base;
uint64_t gate_expert_bytes;
uint64_t gate_row_bytes;
uint64_t up_expert_bytes;
uint64_t up_row_bytes;
uint64_t down_expert_bytes;
uint64_t down_row_bytes;
} ds4_gpu_glm_routed_moe_args;
typedef struct {
uint32_t n_tokens;
uint32_t n_head;
uint32_t n_raw;
uint32_t raw_cap;
uint32_t raw_start;
uint32_t n_comp;
uint32_t top_k;
uint32_t pos0;
uint32_t window;
uint32_t ratio;
uint32_t comp_kv_f16;
uint32_t n_splits;
uint64_t q_token_stride;
uint64_t q_head_stride;
uint64_t raw_row_stride;
uint64_t comp_row_stride;
uint64_t topk_token_stride;
uint64_t dst_token_stride;
uint64_t dst_head_stride;
float scale;
} ds4_gpu_dsv4_indexed_attention_args;
typedef struct {
uint32_t n_comp;
uint32_t n_tokens;
uint32_t n_head;
uint32_t head_dim;
uint32_t pos0;
uint32_t ratio;
uint64_t q_token_stride;
uint64_t q_head_stride;
uint64_t weights_token_stride;
uint64_t index_row_stride;
uint64_t score_token_stride;
float scale;
} ds4_gpu_dsv4_indexer_scores_fused_args;
typedef struct {
uint32_t width;
uint32_t rows;
uint64_t gate_row_stride;
uint64_t up_row_stride;
uint64_t mid_row_stride;
uint64_t weight_stride;
uint32_t write_clamped;
float clamp_value;
} ds4_gpu_dsv4_moe_swiglu_weight_args;
typedef struct {
uint32_t expert_base;
uint32_t expert_count;
uint32_t accumulate;
uint32_t pad0;
} ds4_gpu_moe_expert_group_args;
typedef struct {
uint64_t expert_bytes;
uint32_t group_size;
uint32_t n_slots;
} ds4_gpu_q4_gather_slots6_args;
typedef struct {
uint32_t width;
uint32_t tokens;
uint64_t src_token_stride;
uint64_t dst_token_stride;
} ds4_gpu_dsv4_moe_sum6_args;
/* Compile the single in-repo Metal source and create the pipelines that every
* session uses. Shape-dependent kernels with function constants are built
* lazily by the small ds4_gpu_get_* caches, so startup stays predictable
* while long-context prefill and decode can still pick specialized variants. */
int ds4_gpu_init(void) {
if (g_initialized) return 1;
@autoreleasepool {
ds4_gpu_decode_pipeline_fast_cache_reset();
g_device = MTLCreateSystemDefaultDevice();
if (!g_device) {
fprintf(stderr, "ds4: Metal device not available\n");
return 0;
}
ds4_gpu_timeline_probe(g_device);
ds4_gpu_print_device_summary();
ds4_gpu_detect_metal4_features();
g_queue = [g_device newCommandQueueWithMaxCommandBufferCount:256];
if (!g_queue) {
fprintf(stderr, "ds4: failed to create Metal command queue\n");
g_device = nil;
return 0;
}
g_model_buffer_cache = [NSMutableDictionary dictionary];
g_model_buffer_cache_bytes = 0;
g_model_buffer_cache_evictions = 0;
g_model_buffer_cache_over_limit = 0;
g_q4_expert_table_cache = [NSMutableDictionary dictionary];
g_q4_expert_layer_residency_cache = [NSMutableDictionary dictionary];
g_pipeline_cache = [NSMutableDictionary dictionary];
g_dsv4_completion_cache = [NSCache new];
g_dsv4_completion_cache.countLimit = 256u;
g_transient_buffers = [NSMutableArray array];
g_pending_cbs = [NSMutableArray array];
if (!g_model_buffer_cache || !g_q4_expert_table_cache ||
!g_q4_expert_layer_residency_cache ||
!g_pipeline_cache || !g_dsv4_completion_cache ||
!g_transient_buffers || !g_pending_cbs) {
fprintf(stderr, "ds4: Metal bookkeeping allocation failed\n");
g_pending_cbs = nil;
g_transient_buffers = nil;
g_dsv4_completion_cache = nil;
g_pipeline_cache = nil;
g_q4_expert_layer_residency_cache = nil;
g_q4_expert_table_cache = nil;
g_model_buffer_cache = nil;
g_queue = nil;
g_device = nil;
return 0;
}
NSError *error = nil;
NSString *source = ds4_gpu_full_source();
if (!source) {
g_queue = nil;
g_device = nil;
return 0;
}
MTLCompileOptions *options = [MTLCompileOptions new];
NSMutableDictionary *macros = [NSMutableDictionary new];
if (g_metal4_tensor_api_enabled) {
macros[@"DS4_METAL_HAS_TENSOR"] = @"1";
fprintf(stderr, "ds4: Metal 4 tensor API enabled for Tensor kernels\n");
}
const int drift_hc_stable = ds4_gpu_env_bool("DS4_METAL_HC_STABLE") != 0; // default ON
const int drift_norm_unify = ds4_gpu_env_bool("DS4_METAL_NORM_RSQRT_DISABLE") != 0; // default ON
const int drift_kv_raw_f32 = ds4_gpu_env_bool("DS4_METAL_KV_RAW_F32") > 0; // default OFF
const int drift_rope_exp2_log2 = ds4_gpu_env_bool("DS4_METAL_ROPE_EXP2_LOG2") > 0; // default OFF
const int drift_math_safe = ds4_gpu_env_bool("DS4_METAL_MATH_SAFE") > 0; // default OFF
if (drift_math_safe) {
// MTLCompileOptions.fastMathEnabled defaults to YES and Apple's
// headers explicitly say this "may violate the IEEE 754 standard".
// Different fast-math optimizations get applied across the
// matmul2d cooperative-tensor path and the legacy
// simdgroup_multiply_accumulate path on M5, amplifying the
// mismatch. MTLMathModeSafe pins the entire library to strict
// IEEE-754 semantics. Diagnostic-only: useful to localize drift
// sources but not to ship as a default.
if (@available(macOS 15.0, *)) {
options.mathMode = MTLMathModeSafe;
fprintf(stderr, "ds4: Metal shader library math mode = safe (strict IEEE-754) by DS4_METAL_MATH_SAFE\n");
} else {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
options.fastMathEnabled = NO;
#pragma clang diagnostic pop
fprintf(stderr, "ds4: Metal shader library fast-math disabled by DS4_METAL_MATH_SAFE (pre-macOS 15)\n");
}
}
if (drift_hc_stable) macros[@"DS4_METAL_HC_STABLE"] = @"1";
if (drift_norm_unify) macros[@"DS4_METAL_NORM_RSQRT_DISABLE"] = @"1";
if (drift_kv_raw_f32) macros[@"DS4_METAL_KV_RAW_F32"] = @"1";
if (drift_rope_exp2_log2) macros[@"DS4_METAL_ROPE_EXP2_LOG2"] = @"1";
fprintf(stderr,
"ds4: drift-patch flags hc_stable=%s norm_unify=%s kv_raw_f32=%s rope_exp2_log2=%s math_safe=%s tensor_matmul=%s\n",
drift_hc_stable ? "on" : "off",
drift_norm_unify ? "on" : "off",
drift_kv_raw_f32 ? "on" : "off",
drift_rope_exp2_log2 ? "on" : "off",
drift_math_safe ? "on" : "off",
g_metal4_tensor_api_enabled ? "on" : "off");
options.preprocessorMacros = macros;
id<MTLLibrary> library = [g_device newLibraryWithSource:source options:options error:&error];
if (!library) {
fprintf(stderr, "ds4: Metal shader compilation failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
g_library = library;
id<MTLFunction> fn = [library newFunctionWithName:@"kernel_get_rows_f32"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_get_rows_f32 function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
g_get_rows_f32_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_get_rows_f32_pipeline) {
fprintf(stderr, "ds4: Metal kernel_get_rows_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
fn = [library newFunctionWithName:@"kernel_get_rows_f16"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_get_rows_f16 function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
g_get_rows_f16_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_get_rows_f16_pipeline) {
fprintf(stderr, "ds4: Metal kernel_get_rows_f16 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
fn = [library newFunctionWithName:@"kernel_get_rows_i32"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_get_rows_i32 function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
g_get_rows_i32_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_get_rows_i32_pipeline) {
fprintf(stderr, "ds4: Metal kernel_get_rows_i32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
fn = [library newFunctionWithName:@"kernel_get_rows_q8_0_f32"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_get_rows_q8_0_f32 function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
g_get_rows_q8_0_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_get_rows_q8_0_pipeline) {
fprintf(stderr, "ds4: Metal kernel_get_rows_q8_0_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
fn = [library newFunctionWithName:@"kernel_get_rows_q4_0_f32"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_get_rows_q4_0_f32 function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
g_get_rows_q4_0_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_get_rows_q4_0_pipeline) {
fprintf(stderr, "ds4: Metal kernel_get_rows_q4_0_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
fn = [library newFunctionWithName:@"kernel_get_rows_q4_K_f32"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_get_rows_q4_K_f32 function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
g_get_rows_q4_K_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_get_rows_q4_K_pipeline) {
fprintf(stderr, "ds4: Metal kernel_get_rows_q4_K_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
fn = [library newFunctionWithName:@"kernel_repeat_f32"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_repeat_f32 function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
g_repeat_f32_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_repeat_f32_pipeline) {
fprintf(stderr, "ds4: Metal kernel_repeat_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
fn = [library newFunctionWithName:@"kernel_set_rows_f32_i32"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_set_rows_f32_i32 function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
g_set_rows_f32_i32_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_set_rows_f32_i32_pipeline) {
fprintf(stderr, "ds4: Metal kernel_set_rows_f32_i32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
fn = [library newFunctionWithName:@"kernel_concat"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_concat function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
g_concat_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_concat_pipeline) {
fprintf(stderr, "ds4: Metal kernel_concat pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
fn = [library newFunctionWithName:@"kernel_cpy_f32_f32"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_cpy_f32_f32 function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
g_cpy_f32_f32_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_cpy_f32_f32_pipeline) {
fprintf(stderr, "ds4: Metal kernel_cpy_f32_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
fn = [library newFunctionWithName:@"kernel_cpy_f32_f16"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_cpy_f32_f16 function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
g_cpy_f32_f16_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_cpy_f32_f16_pipeline) {
fprintf(stderr, "ds4: Metal kernel_cpy_f32_f16 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
fn = [library newFunctionWithName:@"kernel_cpy_contig_f32_f16_4"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_cpy_contig_f32_f16_4 function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
g_cpy_contig_f32_f16_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_cpy_contig_f32_f16_pipeline) {
fprintf(stderr, "ds4: Metal kernel_cpy_contig_f32_f16_4 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
fn = [library newFunctionWithName:@"kernel_cpy_f16_f32"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_cpy_f16_f32 function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
g_cpy_f16_f32_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_cpy_f16_f32_pipeline) {
fprintf(stderr, "ds4: Metal kernel_cpy_f16_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
fn = [library newFunctionWithName:@"kernel_cpy_f16_f16"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_cpy_f16_f16 function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
g_cpy_f16_f16_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_cpy_f16_f16_pipeline) {
fprintf(stderr, "ds4: Metal kernel_cpy_f16_f16 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
fn = [library newFunctionWithName:@"kernel_cpy_contig_f16_f32_4"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_cpy_contig_f16_f32_4 function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
g_cpy_contig_f16_f32_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_cpy_contig_f16_f32_pipeline) {
fprintf(stderr, "ds4: Metal kernel_cpy_contig_f16_f32_4 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
fn = [library newFunctionWithName:@"kernel_cpy_contig_f16_f16_bits_4"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_cpy_contig_f16_f16_bits_4 function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
g_cpy_contig_f16_f16_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_cpy_contig_f16_f16_pipeline) {
fprintf(stderr, "ds4: Metal kernel_cpy_contig_f16_f16_bits_4 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
error = nil;
fn = [library newFunctionWithName:@"kernel_dsv4_flash_kv_stage_f16"];
if (fn) {
g_flash_kv_stage_f16_pipeline =
[g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_flash_kv_stage_f16_pipeline) {
fprintf(stderr,
"ds4: optional Metal gathered KV staging pipeline unavailable: %s\n",
[[error localizedDescription] UTF8String]);
}
} else {
fprintf(stderr,
"ds4: optional Metal gathered KV staging kernel unavailable\n");
}
fn = [library newFunctionWithName:@"kernel_dsv4_fp8_kv_quantize_f32"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_dsv4_fp8_kv_quantize_f32 function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
g_dsv4_fp8_kv_quantize_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_dsv4_fp8_kv_quantize_pipeline) {
fprintf(stderr, "ds4: Metal kernel_dsv4_fp8_kv_quantize_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
fn = [library newFunctionWithName:@"kernel_dsv4_indexer_hadamard_fp4_f32"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_dsv4_indexer_hadamard_fp4_f32 function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
g_dsv4_indexer_qat_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_dsv4_indexer_qat_pipeline) {
fprintf(stderr, "ds4: Metal kernel_dsv4_indexer_hadamard_fp4_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
fn = [library newFunctionWithName:@"kernel_dsv4_kv_fp8_store_f32"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_dsv4_kv_fp8_store_f32 function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
g_dsv4_kv_fp8_store_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_dsv4_kv_fp8_store_pipeline) {
fprintf(stderr, "ds4: Metal kernel_dsv4_kv_fp8_store_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
fn = [library newFunctionWithName:@"kernel_dsv4_kv_rope_fp8_store_f32"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_dsv4_kv_rope_fp8_store_f32 function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
error = nil;
g_dsv4_kv_rope_fp8_store_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_dsv4_kv_rope_fp8_store_pipeline) {
fprintf(stderr, "ds4: Metal kernel_dsv4_kv_rope_fp8_store_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
fn = [library newFunctionWithName:@"kernel_dsv4_ratio4_shift_f32"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_dsv4_ratio4_shift_f32 function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
g_dsv4_ratio4_shift_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_dsv4_ratio4_shift_pipeline) {
fprintf(stderr, "ds4: Metal kernel_dsv4_ratio4_shift_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
fn = [library newFunctionWithName:@"kernel_swiglu_f32"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_swiglu_f32 function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
g_swiglu_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_swiglu_pipeline) {
fprintf(stderr, "ds4: Metal kernel_swiglu_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
fn = [library newFunctionWithName:@"kernel_swiglu_flat_f32"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_swiglu_flat_f32 function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
error = nil;
g_swiglu_flat_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_swiglu_flat_pipeline) {
fprintf(stderr, "ds4: Metal kernel_swiglu_flat_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
fn = [library newFunctionWithName:@"kernel_dsv4_moe_sum6_f32"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_dsv4_moe_sum6_f32 function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
g_moe_sum6_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_moe_sum6_pipeline) {
fprintf(stderr, "ds4: Metal kernel_dsv4_moe_sum6_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
fn = [library newFunctionWithName:@"kernel_dsv4_moe_sum8_f32"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_dsv4_moe_sum8_f32 function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
g_moe_sum8_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_moe_sum8_pipeline) {
fprintf(stderr, "ds4: Metal kernel_dsv4_moe_sum8_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
MTLFunctionConstantValues *bin_constants = [[MTLFunctionConstantValues alloc] init];
int16_t bin_op = 0;
int16_t bin_f = 1;
bool bin_rb = false;
bool bin_cb = false;
[bin_constants setConstantValue:&bin_op type:MTLDataTypeShort atIndex:1300];
[bin_constants setConstantValue:&bin_f type:MTLDataTypeShort atIndex:1301];
[bin_constants setConstantValue:&bin_rb type:MTLDataTypeBool atIndex:1302];
[bin_constants setConstantValue:&bin_cb type:MTLDataTypeBool atIndex:1303];
error = nil;
fn = [library newFunctionWithName:@"kernel_bin_fuse_f32_f32_f32"
constantValues:bin_constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_bin_fuse_f32_f32_f32 function not found: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
g_add_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_add_pipeline) {
fprintf(stderr, "ds4: Metal kernel_bin_fuse_f32_f32_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
fn = [library newFunctionWithName:@"kernel_add2_f32"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_add2_f32 function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
error = nil;
g_add2_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_add2_pipeline) {
fprintf(stderr, "ds4: Metal kernel_add2_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
fn = [library newFunctionWithName:@"kernel_add3_f32"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_add3_f32 function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
error = nil;
g_add3_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_add3_pipeline) {
fprintf(stderr, "ds4: Metal kernel_add3_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
MTLFunctionConstantValues *bin_mul_constants = [[MTLFunctionConstantValues alloc] init];
int16_t bin_mul_plain_op = 2;
int16_t bin_mul_plain_f = 1;
bool bin_mul_plain_rb = false;
bool bin_mul_plain_cb = false;
[bin_mul_constants setConstantValue:&bin_mul_plain_op type:MTLDataTypeShort atIndex:1300];
[bin_mul_constants setConstantValue:&bin_mul_plain_f type:MTLDataTypeShort atIndex:1301];
[bin_mul_constants setConstantValue:&bin_mul_plain_rb type:MTLDataTypeBool atIndex:1302];
[bin_mul_constants setConstantValue:&bin_mul_plain_cb type:MTLDataTypeBool atIndex:1303];
error = nil;
fn = [library newFunctionWithName:@"kernel_bin_fuse_f32_f32_f32"
constantValues:bin_mul_constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_bin_fuse_f32_f32_f32 mul function not found: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
g_mul_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_mul_pipeline) {
fprintf(stderr, "ds4: Metal kernel_bin_fuse_f32_f32_f32 mul pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
MTLFunctionConstantValues *bin_mul_scalar_constants = [[MTLFunctionConstantValues alloc] init];
int16_t bin_mul_op = 2;
int16_t bin_mul_f = 1;
bool bin_mul_rb = false;
bool bin_mul_cb = true;
[bin_mul_scalar_constants setConstantValue:&bin_mul_op type:MTLDataTypeShort atIndex:1300];
[bin_mul_scalar_constants setConstantValue:&bin_mul_f type:MTLDataTypeShort atIndex:1301];
[bin_mul_scalar_constants setConstantValue:&bin_mul_rb type:MTLDataTypeBool atIndex:1302];
[bin_mul_scalar_constants setConstantValue:&bin_mul_cb type:MTLDataTypeBool atIndex:1303];
error = nil;
fn = [library newFunctionWithName:@"kernel_bin_fuse_f32_f32_f32"
constantValues:bin_mul_scalar_constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_bin_fuse_f32_f32_f32 mul-scalar function not found: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
g_bin_mul_scalar_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_bin_mul_scalar_pipeline) {
fprintf(stderr, "ds4: Metal kernel_bin_fuse_f32_f32_f32 mul-scalar pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
MTLFunctionConstantValues *bin_div_row_constants = [[MTLFunctionConstantValues alloc] init];
int16_t bin_div_op = 3;
int16_t bin_div_f = 1;
bool bin_div_rb = false;
bool bin_div_cb = true;
[bin_div_row_constants setConstantValue:&bin_div_op type:MTLDataTypeShort atIndex:1300];
[bin_div_row_constants setConstantValue:&bin_div_f type:MTLDataTypeShort atIndex:1301];
[bin_div_row_constants setConstantValue:&bin_div_rb type:MTLDataTypeBool atIndex:1302];
[bin_div_row_constants setConstantValue:&bin_div_cb type:MTLDataTypeBool atIndex:1303];
error = nil;
fn = [library newFunctionWithName:@"kernel_bin_fuse_f32_f32_f32"
constantValues:bin_div_row_constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_bin_fuse_f32_f32_f32 div-row function not found: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
g_bin_div_row_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_bin_div_row_pipeline) {
fprintf(stderr, "ds4: Metal kernel_bin_fuse_f32_f32_f32 div-row pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
fn = [library newFunctionWithName:@"kernel_rms_norm_mul_f32_4"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_rms_norm_mul_f32_4 function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
g_rms_norm_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_rms_norm_pipeline) {
fprintf(stderr, "ds4: Metal kernel_rms_norm_mul_f32_4 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
fn = [library newFunctionWithName:@"kernel_rms_norm_f32_4"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_rms_norm_f32_4 function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
g_rms_norm_plain_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_rms_norm_plain_pipeline) {
fprintf(stderr, "ds4: Metal kernel_rms_norm_f32_4 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
fn = [library newFunctionWithName:@"kernel_add_rms_norm_mul_f32_4"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_add_rms_norm_mul_f32_4 function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
g_add_rms_norm_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_add_rms_norm_pipeline) {
fprintf(stderr, "ds4: Metal kernel_add_rms_norm_mul_f32_4 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
fn = [library newFunctionWithName:@"kernel_dsv4_qkv_rms_norm_f32_4"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_dsv4_qkv_rms_norm_f32_4 function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
g_dsv4_qkv_rms_norm_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_dsv4_qkv_rms_norm_pipeline) {
fprintf(stderr, "ds4: Metal kernel_dsv4_qkv_rms_norm_f32_4 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
fn = [library newFunctionWithName:@"kernel_dsv4_head_rms_norm_rope_tail_f32"];
if (!fn) {
fprintf(stderr,
"ds4: Metal kernel_dsv4_head_rms_norm_rope_tail_f32 function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
error = nil;
g_dsv4_head_rms_norm_rope_tail_pipeline =
[g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_dsv4_head_rms_norm_rope_tail_pipeline) {
fprintf(stderr,
"ds4: Metal kernel_dsv4_head_rms_norm_rope_tail_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
g_use_dsv4_head_rms_norm_rope_tail_pipeline =
ds4_gpu_device_is_pre_m5_apple_silicon() &&
getenv("DS4_METAL_DISABLE_PRE_M5_HEAD_RMS_ROPE_PIPELINE_STATIC") == NULL;
MTLFunctionConstantValues *moe_mv_id_constants = [[MTLFunctionConstantValues alloc] init];
int16_t moe_mv_id_nsg = 2;
[moe_mv_id_constants setConstantValue:&moe_mv_id_nsg type:MTLDataTypeShort atIndex:600];
error = nil;
fn = [library newFunctionWithName:@"kernel_mul_mv_id_iq2_xxs_f32"
constantValues:moe_mv_id_constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_id_iq2_xxs_f32 function not found: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
g_moe_mul_mv_id_iq2_xxs_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_moe_mul_mv_id_iq2_xxs_pipeline) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_id_iq2_xxs_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
error = nil;
fn = [library newFunctionWithName:@"kernel_mul_mv_id_iq2_xxs_pair_f32"
constantValues:moe_mv_id_constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_id_iq2_xxs_pair_f32 function not found: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
g_moe_mul_mv_id_iq2_xxs_pair_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_moe_mul_mv_id_iq2_xxs_pair_pipeline) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_id_iq2_xxs_pair_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
error = nil;
fn = [library newFunctionWithName:@"kernel_mul_mv_id_iq2_xxs_pair_swiglu_f32"
constantValues:moe_mv_id_constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_id_iq2_xxs_pair_swiglu_f32 function not found: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
g_moe_mul_mv_id_iq2_xxs_pair_swiglu_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_moe_mul_mv_id_iq2_xxs_pair_swiglu_pipeline) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_id_iq2_xxs_pair_swiglu_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
error = nil;
fn = [library newFunctionWithName:@"kernel_mul_mv_id_iq2_xxs_pair_swiglu_pack2_overlap_f32"
constantValues:moe_mv_id_constants
error:&error];
if (fn) {
g_moe_mul_mv_id_iq2_xxs_pair_swiglu_pack2_pipeline =
[g_device newComputePipelineStateWithFunction:fn error:&error];
}
if (!g_moe_mul_mv_id_iq2_xxs_pair_swiglu_pack2_pipeline) {
fprintf(stderr,
"ds4: optional Metal IQ2 pair pack2 pipeline unavailable: %s\n",
error ? [[error localizedDescription] UTF8String] :
"function not found");
}
error = nil;
fn = [library newFunctionWithName:@"kernel_mul_mv_id_q2_K_f32"
constantValues:moe_mv_id_constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_id_q2_K_f32 function not found: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
g_moe_mul_mv_id_q2_k_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_moe_mul_mv_id_q2_k_pipeline) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_id_q2_K_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
error = nil;
fn = [library newFunctionWithName:@"kernel_mul_mv_id_q2_K_sum6_f32"
constantValues:moe_mv_id_constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_id_q2_K_sum6_f32 function not found: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
g_moe_mul_mv_id_q2_k_sum6_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_moe_mul_mv_id_q2_k_sum6_pipeline) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_id_q2_K_sum6_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
error = nil;
fn = [library newFunctionWithName:@"kernel_mul_mv_id_iq2_xxs_sum6_f32"
constantValues:moe_mv_id_constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_id_iq2_xxs_sum6_f32 function not found: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
g_moe_mul_mv_id_iq2_xxs_sum6_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_moe_mul_mv_id_iq2_xxs_sum6_pipeline) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_id_iq2_xxs_sum6_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
error = nil;
fn = [library newFunctionWithName:@"kernel_mul_mv_slots6_iq2_xxs_pair_swiglu_f32"
constantValues:moe_mv_id_constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_slots6_iq2_xxs_pair_swiglu_f32 function not found: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
g_moe_mul_mv_slots6_iq2_xxs_pair_swiglu_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_moe_mul_mv_slots6_iq2_xxs_pair_swiglu_pipeline) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_slots6_iq2_xxs_pair_swiglu_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
error = nil;
fn = [library newFunctionWithName:@"kernel_mul_mv_slots6_q2_K_sum6_f32"
constantValues:moe_mv_id_constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_slots6_q2_K_sum6_f32 function not found: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
g_moe_mul_mv_slots6_q2_k_sum6_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_moe_mul_mv_slots6_q2_k_sum6_pipeline) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_slots6_q2_K_sum6_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
error = nil;
fn = [library newFunctionWithName:@"kernel_mul_mv_addr_iq2_xxs_pair_swiglu_f32"
constantValues:moe_mv_id_constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_addr_iq2_xxs_pair_swiglu_f32 function not found: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
g_moe_mul_mv_addr_iq2_xxs_pair_swiglu_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_moe_mul_mv_addr_iq2_xxs_pair_swiglu_pipeline) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_addr_iq2_xxs_pair_swiglu_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
error = nil;
fn = [library newFunctionWithName:@"kernel_mul_mv_addr_iq2_xxs_f32"
constantValues:moe_mv_id_constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_addr_iq2_xxs_f32 function not found: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
g_moe_mul_mv_addr_iq2_xxs_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_moe_mul_mv_addr_iq2_xxs_pipeline) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_addr_iq2_xxs_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
error = nil;
fn = [library newFunctionWithName:@"kernel_mul_mv_addr_q2_K_sum6_f32"
constantValues:moe_mv_id_constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_addr_q2_K_sum6_f32 function not found: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
g_moe_mul_mv_addr_q2_k_sum6_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_moe_mul_mv_addr_q2_k_sum6_pipeline) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_addr_q2_K_sum6_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
error = nil;
fn = [library newFunctionWithName:@"kernel_mul_mv_addr_iq2_xxs_pair_swiglu_masked_f32"
constantValues:moe_mv_id_constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_addr_iq2_xxs_pair_swiglu_masked_f32 function not found: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
g_moe_mul_mv_addr_iq2_xxs_pair_swiglu_masked_pipeline =
[g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_moe_mul_mv_addr_iq2_xxs_pair_swiglu_masked_pipeline) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_addr_iq2_xxs_pair_swiglu_masked_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
error = nil;
fn = [library newFunctionWithName:@"kernel_mul_mv_addr_q2_K_sum6_masked_f32"
constantValues:moe_mv_id_constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_addr_q2_K_sum6_masked_f32 function not found: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
g_moe_mul_mv_addr_q2_k_sum6_masked_pipeline =
[g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_moe_mul_mv_addr_q2_k_sum6_masked_pipeline) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_addr_q2_K_sum6_masked_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
error = nil;
fn = [library newFunctionWithName:@"kernel_stream_expert_cache_validate"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_stream_expert_cache_validate function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
g_moe_stream_expert_cache_validate_pipeline =
[g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_moe_stream_expert_cache_validate_pipeline) {
fprintf(stderr, "ds4: Metal kernel_stream_expert_cache_validate pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
error = nil;
fn = [library newFunctionWithName:@"kernel_mul_mv_id_q4_K_f32"
constantValues:moe_mv_id_constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_id_q4_K_f32 function not found: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
g_moe_mul_mv_id_q4_k_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_moe_mul_mv_id_q4_k_pipeline) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_id_q4_K_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
error = nil;
fn = [library newFunctionWithName:@"kernel_mul_mv_id_q4_K_pair_f32"
constantValues:moe_mv_id_constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_id_q4_K_pair_f32 function not found: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
g_moe_mul_mv_id_q4_k_pair_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_moe_mul_mv_id_q4_k_pair_pipeline) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_id_q4_K_pair_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
error = nil;
fn = [library newFunctionWithName:@"kernel_mul_mv_id_q4_K_pair_swiglu_f32"
constantValues:moe_mv_id_constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_id_q4_K_pair_swiglu_f32 function not found: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
g_moe_mul_mv_id_q4_k_pair_swiglu_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_moe_mul_mv_id_q4_k_pair_swiglu_pipeline) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_id_q4_K_pair_swiglu_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
error = nil;
fn = [library newFunctionWithName:@"kernel_mul_mv_id_q4_K_sum6_f32"
constantValues:moe_mv_id_constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_id_q4_K_sum6_f32 function not found: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
g_moe_mul_mv_id_q4_k_sum6_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_moe_mul_mv_id_q4_k_sum6_pipeline) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_id_q4_K_sum6_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
error = nil;
fn = [library newFunctionWithName:@"kernel_mul_mv_group_q4_K_pair_swiglu_f32"
constantValues:moe_mv_id_constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_group_q4_K_pair_swiglu_f32 function not found: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
g_moe_mul_mv_group_q4_k_pair_swiglu_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_moe_mul_mv_group_q4_k_pair_swiglu_pipeline) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_group_q4_K_pair_swiglu_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
error = nil;
fn = [library newFunctionWithName:@"kernel_mul_mv_group_q4_K_sum6_f32"
constantValues:moe_mv_id_constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_group_q4_K_sum6_f32 function not found: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
g_moe_mul_mv_group_q4_k_sum6_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_moe_mul_mv_group_q4_k_sum6_pipeline) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_group_q4_K_sum6_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
error = nil;
fn = [library newFunctionWithName:@"kernel_mul_mv_group6_q4_K_pair_swiglu_f32"
constantValues:moe_mv_id_constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_group6_q4_K_pair_swiglu_f32 function not found: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
g_moe_mul_mv_group6_q4_k_pair_swiglu_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_moe_mul_mv_group6_q4_k_pair_swiglu_pipeline) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_group6_q4_K_pair_swiglu_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
error = nil;
fn = [library newFunctionWithName:@"kernel_mul_mv_group6_q4_K_sum6_f32"
constantValues:moe_mv_id_constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_group6_q4_K_sum6_f32 function not found: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
g_moe_mul_mv_group6_q4_k_sum6_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_moe_mul_mv_group6_q4_k_sum6_pipeline) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_group6_q4_K_sum6_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
error = nil;
fn = [library newFunctionWithName:@"kernel_mul_mv_group8_q4_K_pair_swiglu_f32"
constantValues:moe_mv_id_constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_group8_q4_K_pair_swiglu_f32 function not found: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
g_moe_mul_mv_group8_q4_k_pair_swiglu_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_moe_mul_mv_group8_q4_k_pair_swiglu_pipeline) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_group8_q4_K_pair_swiglu_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
error = nil;
fn = [library newFunctionWithName:@"kernel_mul_mv_group8_q4_K_sum6_f32"
constantValues:moe_mv_id_constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_group8_q4_K_sum6_f32 function not found: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
g_moe_mul_mv_group8_q4_k_sum6_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_moe_mul_mv_group8_q4_k_sum6_pipeline) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_group8_q4_K_sum6_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
error = nil;
fn = [library newFunctionWithName:@"kernel_mul_mv_group24_q4_K_id_f32"
constantValues:moe_mv_id_constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_group24_q4_K_id_f32 function not found: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
g_moe_mul_mv_group24_q4_k_id_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_moe_mul_mv_group24_q4_k_id_pipeline) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_group24_q4_K_id_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
error = nil;
fn = [library newFunctionWithName:@"kernel_mul_mv_group24_q4_K_sum6_f32"
constantValues:moe_mv_id_constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_group24_q4_K_sum6_f32 function not found: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
g_moe_mul_mv_group24_q4_k_sum6_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_moe_mul_mv_group24_q4_k_sum6_pipeline) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_group24_q4_K_sum6_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
error = nil;
fn = [library newFunctionWithName:@"kernel_mul_mv_slots6_q4_K_pair_swiglu_f32"
constantValues:moe_mv_id_constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_slots6_q4_K_pair_swiglu_f32 function not found: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
g_moe_mul_mv_slots6_q4_k_pair_swiglu_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_moe_mul_mv_slots6_q4_k_pair_swiglu_pipeline) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_slots6_q4_K_pair_swiglu_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
error = nil;
fn = [library newFunctionWithName:@"kernel_mul_mv_slots6_q4_K_sum6_f32"
constantValues:moe_mv_id_constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_slots6_q4_K_sum6_f32 function not found: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
g_moe_mul_mv_slots6_q4_k_sum6_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_moe_mul_mv_slots6_q4_k_sum6_pipeline) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_slots6_q4_K_sum6_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
g_moe_mul_mv_id_mxfp4_pipeline =
ds4_gpu_get_mul_mv_pipeline("kernel_mul_mv_id_mxfp4_f32", 2);
g_moe_mul_mv_id_mxfp4_pair_swiglu_pipeline =
ds4_gpu_get_mul_mv_pipeline("kernel_mul_mv_id_mxfp4_pair_swiglu_f32", 2);
g_moe_mul_mv_id_mxfp4_sum6_pipeline =
ds4_gpu_get_mul_mv_pipeline("kernel_mul_mv_id_mxfp4_sum6_f32", 2);
/* Single-simdgroup variants of the MXFP4 decode MoE kernels. The
* per-row per-lane arithmetic is identical; only the row-to-simdgroup
* mapping changes, so outputs remain bit-exact. Smaller threadgroups
* measurably improve GPU scheduling granularity on these
* latency-bound small-grid matvecs. Pipelines are built
* unconditionally; the dispatch-time selection gate reads the
* environment so A/B harnesses can toggle it after engine creation. */
g_moe_mul_mv_id_mxfp4_pair_swiglu_pipeline_nsg1 =
ds4_gpu_get_mul_mv_pipeline("kernel_mul_mv_id_mxfp4_pair_swiglu_f32", 1);
g_moe_mul_mv_id_mxfp4_sum6_pipeline_nsg1 =
ds4_gpu_get_mul_mv_pipeline("kernel_mul_mv_id_mxfp4_sum6_f32", 1);
g_moe_mul_mv_id_mxfp4_pair_swiglu_pipeline_nsg1_tg_multiple =
ds4_gpu_new_mul_mv_tg_multiple_pipeline(
"kernel_mul_mv_id_mxfp4_pair_swiglu_f32", 1);
g_moe_mul_mv_id_mxfp4_sum6_pipeline_nsg1_tg_multiple =
ds4_gpu_new_mul_mv_tg_multiple_pipeline(
"kernel_mul_mv_id_mxfp4_sum6_f32", 1);
g_moe_mul_mv_id_mxfp4_pair_swiglu_fixed_route_pipeline_nsg1 =
ds4_gpu_new_mul_mv_tg_multiple_pipeline(
"kernel_mul_mv_id_mxfp4_pair_swiglu_fixed_route_f32", 1);
g_moe_mul_mv_id_mxfp4_sum6_fixed_route_pipeline_nsg1 =
ds4_gpu_new_mul_mv_tg_multiple_pipeline(
"kernel_mul_mv_id_mxfp4_sum6_fixed_route_f32", 1);
g_moe_mul_mv_id_mxfp4_sum6_fixed_route_full_rows_pipeline_nsg1 =
ds4_gpu_new_mul_mv_tg_multiple_pipeline(
"kernel_mul_mv_id_mxfp4_sum6_fixed_route_full_rows_f32", 1);
g_moe_mul_mv_id_mxfp4_pair_swiglu_fixed_route_static_pipeline_nsg1 =
ds4_gpu_new_mul_mv_tg_multiple_pipeline(
"kernel_mul_mv_id_mxfp4_pair_swiglu_fixed_route_static_f32", 1);
g_moe_mul_mv_id_mxfp4_sum6_fixed_route_full_rows_static_pipeline_nsg1 =
ds4_gpu_new_mul_mv_tg_multiple_pipeline(
"kernel_mul_mv_id_mxfp4_sum6_fixed_route_full_rows_static_f32", 1);
g_moe_mul_mv_id_mxfp4_pair_swiglu_tp_static_pipeline_nsg1 =
ds4_gpu_new_mul_mv_tg_multiple_pipeline(
"kernel_mul_mv_id_mxfp4_pair_swiglu_tp_static_f32", 1);
g_moe_mul_mv_id_mxfp4_sum6_tp_full_rows_static_pipeline_nsg1 =
ds4_gpu_new_mul_mv_tg_multiple_pipeline(
"kernel_mul_mv_id_mxfp4_sum6_tp_full_rows_static_f32", 1);
if (!g_moe_mul_mv_id_mxfp4_pair_swiglu_pipeline_nsg1 ||
!g_moe_mul_mv_id_mxfp4_sum6_pipeline_nsg1 ||
!g_moe_mul_mv_id_mxfp4_pair_swiglu_pipeline_nsg1_tg_multiple ||
!g_moe_mul_mv_id_mxfp4_sum6_pipeline_nsg1_tg_multiple ||
!g_moe_mul_mv_id_mxfp4_pair_swiglu_fixed_route_pipeline_nsg1 ||
!g_moe_mul_mv_id_mxfp4_sum6_fixed_route_pipeline_nsg1 ||
!g_moe_mul_mv_id_mxfp4_sum6_fixed_route_full_rows_pipeline_nsg1 ||
!g_moe_mul_mv_id_mxfp4_pair_swiglu_fixed_route_static_pipeline_nsg1 ||
!g_moe_mul_mv_id_mxfp4_sum6_fixed_route_full_rows_static_pipeline_nsg1 ||
!g_moe_mul_mv_id_mxfp4_pair_swiglu_tp_static_pipeline_nsg1 ||
!g_moe_mul_mv_id_mxfp4_sum6_tp_full_rows_static_pipeline_nsg1) {
g_queue = nil;
g_device = nil;
return 0;
}
g_moe_mul_mv_slots6_mxfp4_pair_swiglu_pipeline =
ds4_gpu_get_mul_mv_pipeline("kernel_mul_mv_slots6_mxfp4_pair_swiglu_f32", 2);
g_moe_mul_mv_slots6_mxfp4_sum6_pipeline =
ds4_gpu_get_mul_mv_pipeline("kernel_mul_mv_slots6_mxfp4_sum6_f32", 2);
if (!g_moe_mul_mv_id_mxfp4_pipeline ||
!g_moe_mul_mv_id_mxfp4_pair_swiglu_pipeline ||
!g_moe_mul_mv_id_mxfp4_sum6_pipeline ||
!g_moe_mul_mv_slots6_mxfp4_pair_swiglu_pipeline ||
!g_moe_mul_mv_slots6_mxfp4_sum6_pipeline) {
g_queue = nil;
g_device = nil;
return 0;
}
error = nil;
fn = [library newFunctionWithName:@"kernel_q4_gather_slots6"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_q4_gather_slots6 function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
g_moe_q4_gather_slots6_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_moe_q4_gather_slots6_pipeline) {
fprintf(stderr, "ds4: Metal kernel_q4_gather_slots6 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
error = nil;
fn = [library newFunctionWithName:@"kernel_mul_mv_table_q4_K_pair_swiglu_f32"
constantValues:moe_mv_id_constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_table_q4_K_pair_swiglu_f32 function not found: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
g_moe_table_q4_pair_gate_encoder = [fn newArgumentEncoderWithBufferIndex:2];
g_moe_table_q4_pair_up_encoder = [fn newArgumentEncoderWithBufferIndex:3];
if (!g_moe_table_q4_pair_gate_encoder || !g_moe_table_q4_pair_up_encoder) {
fprintf(stderr, "ds4: Metal Q4 expert-table pair argument encoder creation failed\n");
g_queue = nil;
g_device = nil;
return 0;
}
g_moe_mul_mv_table_q4_k_pair_swiglu_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_moe_mul_mv_table_q4_k_pair_swiglu_pipeline) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_table_q4_K_pair_swiglu_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
error = nil;
fn = [library newFunctionWithName:@"kernel_mul_mv_table_q4_K_sum6_f32"
constantValues:moe_mv_id_constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_table_q4_K_sum6_f32 function not found: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
g_moe_table_q4_sum_down_encoder = [fn newArgumentEncoderWithBufferIndex:1];
if (!g_moe_table_q4_sum_down_encoder) {
fprintf(stderr, "ds4: Metal Q4 expert-table down argument encoder creation failed\n");
g_queue = nil;
g_device = nil;
return 0;
}
g_moe_mul_mv_table_q4_k_sum6_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_moe_mul_mv_table_q4_k_sum6_pipeline) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_table_q4_K_sum6_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
error = nil;
fn = [library newFunctionWithName:@"kernel_mul_mv_addr_q4_K_pair_swiglu_f32"
constantValues:moe_mv_id_constants
error:&error];
if (fn) {
g_moe_mul_mv_addr_q4_k_pair_swiglu_pipeline =
[g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_moe_mul_mv_addr_q4_k_pair_swiglu_pipeline) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_addr_q4_K_pair_swiglu_f32 pipeline unavailable: %s\n",
[[error localizedDescription] UTF8String]);
}
} else {
fprintf(stderr, "ds4: Metal kernel_mul_mv_addr_q4_K_pair_swiglu_f32 function unavailable: %s\n",
[[error localizedDescription] UTF8String]);
}
error = nil;
fn = [library newFunctionWithName:@"kernel_mul_mv_addr_q4_K_sum6_f32"
constantValues:moe_mv_id_constants
error:&error];
if (fn) {
g_moe_mul_mv_addr_q4_k_sum6_pipeline =
[g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_moe_mul_mv_addr_q4_k_sum6_pipeline) {
fprintf(stderr, "ds4: Metal kernel_mul_mv_addr_q4_K_sum6_f32 pipeline unavailable: %s\n",
[[error localizedDescription] UTF8String]);
}
} else {
fprintf(stderr, "ds4: Metal kernel_mul_mv_addr_q4_K_sum6_f32 function unavailable: %s\n",
[[error localizedDescription] UTF8String]);
}
fn = [library newFunctionWithName:@"kernel_dsv4_rope_tail_f32"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_dsv4_rope_tail_f32 function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
g_rope_tail_batch_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_rope_tail_batch_pipeline) {
fprintf(stderr, "ds4: Metal kernel_dsv4_rope_tail_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
error = nil;
fn = [library newFunctionWithName:@"kernel_dsv4_rope_tail_f32_inplace_pair"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_dsv4_rope_tail_f32_inplace_pair function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
g_rope_tail_inplace_pair_pipeline =
[g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_rope_tail_inplace_pair_pipeline) {
fprintf(stderr,
"ds4: Metal kernel_dsv4_rope_tail_f32_inplace_pair pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
error = nil;
fn = [library newFunctionWithName:@"kernel_dsv4_rope_tail_f32_inplace_pair_shared4"];
if (!fn) {
fprintf(stderr,
"ds4: optional Metal shared-head RoPE kernel unavailable; using per-head path\n");
} else {
g_rope_tail_inplace_pair_shared4_pipeline =
[g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_rope_tail_inplace_pair_shared4_pipeline) {
fprintf(stderr,
"ds4: optional Metal shared-head RoPE pipeline unavailable; using per-head path: %s\n",
[[error localizedDescription] UTF8String]);
}
}
error = nil;
fn = [library newFunctionWithName:@"kernel_dsv4_rope_tail_f32_inplace_pair_affine"];
if (fn) {
g_rope_tail_inplace_pair_affine_pipeline =
[g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_rope_tail_inplace_pair_affine_pipeline) {
fprintf(stderr,
"ds4: optional Metal affine-position RoPE pair pipeline unavailable: %s\n",
[[error localizedDescription] UTF8String]);
}
} else {
fprintf(stderr,
"ds4: optional Metal affine-position RoPE pair kernel unavailable\n");
}
error = nil;
fn = [library newFunctionWithName:@"kernel_dsv4_softmax_pool"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_dsv4_softmax_pool function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
g_dsv4_softmax_pool_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_dsv4_softmax_pool_pipeline) {
fprintf(stderr, "ds4: Metal kernel_dsv4_softmax_pool pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
fn = [library newFunctionWithName:@"kernel_soft_max_f32"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_soft_max_f32 function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
g_soft_max_f32_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_soft_max_f32_pipeline) {
fprintf(stderr, "ds4: Metal kernel_soft_max_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
fn = [library newFunctionWithName:@"kernel_soft_max_f32_4"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_soft_max_f32_4 function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
g_soft_max_f32_4_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_soft_max_f32_4_pipeline) {
fprintf(stderr, "ds4: Metal kernel_soft_max_f32_4 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
fn = [library newFunctionWithName:@"kernel_argsort_f32_i32_desc"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_argsort_f32_i32_desc function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
g_argsort_f32_i32_desc_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_argsort_f32_i32_desc_pipeline) {
fprintf(stderr, "ds4: Metal kernel_argsort_f32_i32_desc pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
fn = [library newFunctionWithName:@"kernel_argsort_merge_f32_i32_desc"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_argsort_merge_f32_i32_desc function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
g_argsort_merge_f32_i32_desc_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_argsort_merge_f32_i32_desc_pipeline) {
fprintf(stderr, "ds4: Metal kernel_argsort_merge_f32_i32_desc pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
MTLFunctionConstantValues *sum_rows_constants = [[MTLFunctionConstantValues alloc] init];
int16_t sum_rows_op = 10;
[sum_rows_constants setConstantValue:&sum_rows_op type:MTLDataTypeShort atIndex:1400];
error = nil;
fn = [library newFunctionWithName:@"kernel_sum_rows_f32_f32"
constantValues:sum_rows_constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_sum_rows_f32_f32 function not found: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
g_sum_rows_f32_f32_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_sum_rows_f32_f32_pipeline) {
fprintf(stderr, "ds4: Metal kernel_sum_rows_f32_f32 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
fn = [library newFunctionWithName:@"kernel_dsv4_topk_mask"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_dsv4_topk_mask function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
g_dsv4_topk_mask_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_dsv4_topk_mask_pipeline) {
fprintf(stderr, "ds4: Metal kernel_dsv4_topk_mask pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
fn = [library newFunctionWithName:@"kernel_dsv4_topk_mask_scatter"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_dsv4_topk_mask_scatter function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
g_dsv4_topk_mask_scatter_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_dsv4_topk_mask_scatter_pipeline) {
fprintf(stderr, "ds4: Metal kernel_dsv4_topk_mask_scatter pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
fn = [library newFunctionWithName:@"kernel_dsv4_indexer_weighted_sum"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_dsv4_indexer_weighted_sum function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
g_dsv4_indexer_weighted_sum_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_dsv4_indexer_weighted_sum_pipeline) {
fprintf(stderr, "ds4: Metal kernel_dsv4_indexer_weighted_sum pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
fn = [library newFunctionWithName:@"kernel_dsv4_hc_split_sinkhorn"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_dsv4_hc_split_sinkhorn function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
g_hc_split_sinkhorn_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_hc_split_sinkhorn_pipeline) {
fprintf(stderr, "ds4: Metal kernel_dsv4_hc_split_sinkhorn pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
fn = [library newFunctionWithName:@"kernel_dsv4_hc_split_weighted_sum"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_dsv4_hc_split_weighted_sum function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
g_hc_split_weighted_sum_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_hc_split_weighted_sum_pipeline) {
fprintf(stderr, "ds4: Metal kernel_dsv4_hc_split_weighted_sum pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
fn = [library newFunctionWithName:@"kernel_dsv4_hc_split_weighted_sum_norm4"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_dsv4_hc_split_weighted_sum_norm4 function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
g_hc_split_weighted_sum_norm_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_hc_split_weighted_sum_norm_pipeline) {
fprintf(stderr, "ds4: Metal kernel_dsv4_hc_split_weighted_sum_norm4 pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
fn = [library newFunctionWithName:@"kernel_dsv4_hc_weighted_sum"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_dsv4_hc_weighted_sum function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
g_hc_weighted_sum_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_hc_weighted_sum_pipeline) {
fprintf(stderr, "ds4: Metal kernel_dsv4_hc_weighted_sum pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
error = nil;
fn = [library newFunctionWithName:@"kernel_dsv4_output_hc_weights4"];
if (fn) {
g_output_hc_weights4_pipeline =
[g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_output_hc_weights4_pipeline) {
fprintf(stderr,
"ds4: optional Metal output HC weights4 pipeline unavailable: %s\n",
[[error localizedDescription] UTF8String]);
}
} else {
fprintf(stderr,
"ds4: optional Metal output HC weights4 kernel unavailable\n");
}
MTLFunctionConstantValues *unary_sigmoid_constants = [[MTLFunctionConstantValues alloc] init];
int16_t unary_sigmoid_op = 102;
bool unary_cnt = false;
[unary_sigmoid_constants setConstantValue:&unary_sigmoid_op type:MTLDataTypeShort atIndex:1200];
[unary_sigmoid_constants setConstantValue:&unary_cnt type:MTLDataTypeBool atIndex:1201];
error = nil;
fn = [library newFunctionWithName:@"kernel_unary_f32_f32_4"
constantValues:unary_sigmoid_constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_unary_f32_f32_4 sigmoid function not found: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
g_unary_sigmoid_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_unary_sigmoid_pipeline) {
fprintf(stderr, "ds4: Metal kernel_unary_f32_f32_4 sigmoid pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
MTLFunctionConstantValues *unary_silu_constants = [[MTLFunctionConstantValues alloc] init];
int16_t unary_silu_op = 106;
[unary_silu_constants setConstantValue:&unary_silu_op type:MTLDataTypeShort atIndex:1200];
[unary_silu_constants setConstantValue:&unary_cnt type:MTLDataTypeBool atIndex:1201];
error = nil;
fn = [library newFunctionWithName:@"kernel_unary_f32_f32_4"
constantValues:unary_silu_constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_unary_f32_f32_4 silu function not found: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
g_unary_silu_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_unary_silu_pipeline) {
fprintf(stderr, "ds4: Metal kernel_unary_f32_f32_4 silu pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
MTLFunctionConstantValues *unary_softplus_constants = [[MTLFunctionConstantValues alloc] init];
int16_t unary_softplus_op = 115;
[unary_softplus_constants setConstantValue:&unary_softplus_op type:MTLDataTypeShort atIndex:1200];
[unary_softplus_constants setConstantValue:&unary_cnt type:MTLDataTypeBool atIndex:1201];
error = nil;
fn = [library newFunctionWithName:@"kernel_unary_f32_f32_4"
constantValues:unary_softplus_constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_unary_f32_f32_4 softplus function not found: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
g_unary_softplus_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_unary_softplus_pipeline) {
fprintf(stderr, "ds4: Metal kernel_unary_f32_f32_4 softplus pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
MTLFunctionConstantValues *unary_sqrt_constants = [[MTLFunctionConstantValues alloc] init];
int16_t unary_sqrt_op = 14;
[unary_sqrt_constants setConstantValue:&unary_sqrt_op type:MTLDataTypeShort atIndex:1200];
[unary_sqrt_constants setConstantValue:&unary_cnt type:MTLDataTypeBool atIndex:1201];
error = nil;
fn = [library newFunctionWithName:@"kernel_unary_f32_f32_4"
constantValues:unary_sqrt_constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_unary_f32_f32_4 sqrt function not found: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
g_unary_sqrt_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_unary_sqrt_pipeline) {
fprintf(stderr, "ds4: Metal kernel_unary_f32_f32_4 sqrt pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
MTLFunctionConstantValues *unary_clamp_constants = [[MTLFunctionConstantValues alloc] init];
int16_t unary_clamp_op = 12;
[unary_clamp_constants setConstantValue:&unary_clamp_op type:MTLDataTypeShort atIndex:1200];
[unary_clamp_constants setConstantValue:&unary_cnt type:MTLDataTypeBool atIndex:1201];
error = nil;
fn = [library newFunctionWithName:@"kernel_unary_f32_f32"
constantValues:unary_clamp_constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_unary_f32_f32 clamp function not found: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
g_unary_clamp_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_unary_clamp_pipeline) {
fprintf(stderr, "ds4: Metal kernel_unary_f32_f32 clamp pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
MTLFunctionConstantValues *unary_scale_constants = [[MTLFunctionConstantValues alloc] init];
int16_t unary_scale_op = 10;
[unary_scale_constants setConstantValue:&unary_scale_op type:MTLDataTypeShort atIndex:1200];
[unary_scale_constants setConstantValue:&unary_cnt type:MTLDataTypeBool atIndex:1201];
error = nil;
fn = [library newFunctionWithName:@"kernel_unary_f32_f32_4"
constantValues:unary_scale_constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_unary_f32_f32_4 scale function not found: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
g_unary_scale_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_unary_scale_pipeline) {
fprintf(stderr, "ds4: Metal kernel_unary_f32_f32_4 scale pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
MTLFunctionConstantValues *unary_fill_constants = [[MTLFunctionConstantValues alloc] init];
int16_t unary_fill_op = 11;
[unary_fill_constants setConstantValue:&unary_fill_op type:MTLDataTypeShort atIndex:1200];
[unary_fill_constants setConstantValue:&unary_cnt type:MTLDataTypeBool atIndex:1201];
error = nil;
fn = [library newFunctionWithName:@"kernel_unary_f32_f32_4"
constantValues:unary_fill_constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_unary_f32_f32_4 fill function not found: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
g_unary_fill_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_unary_fill_pipeline) {
fprintf(stderr, "ds4: Metal kernel_unary_f32_f32_4 fill pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
error = nil;
fn = [library newFunctionWithName:@"kernel_unary_f16_f16"
constantValues:unary_fill_constants
error:&error];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_unary_f16_f16 fill function not found: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
g_unary_fill_f16_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_unary_fill_f16_pipeline) {
fprintf(stderr, "ds4: Metal kernel_unary_f16_f16 fill pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
fn = [library newFunctionWithName:@"kernel_dsv4_hc_expand"];
if (!fn) {
fprintf(stderr, "ds4: Metal kernel_dsv4_hc_expand function not found\n");
g_queue = nil;
g_device = nil;
return 0;
}
g_hc_expand_pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!g_hc_expand_pipeline) {
fprintf(stderr, "ds4: Metal kernel_dsv4_hc_expand pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
g_queue = nil;
g_device = nil;
return 0;
}
g_dsv4_indexer_score_one_direct_pipeline =
ds4_gpu_get_pipeline("kernel_dsv4_indexer_score_one_direct");
g_dsv4_compressor_store_one_pipeline =
ds4_gpu_get_pipeline("kernel_dsv4_compressor_store_one");
g_dsv4_compressor_pack_ratio4_pipeline =
ds4_gpu_get_pipeline("kernel_dsv4_compressor_pack_ratio4");
g_dsv4_compressor_pack_ratio4_decode_ggml_pipeline =
ds4_gpu_get_pipeline(
"kernel_dsv4_compressor_pack_ratio4_decode_ggml");
g_dsv4_compressor_exact_softmax_product_pipeline =
ds4_gpu_get_pipeline(
"kernel_dsv4_compressor_exact_softmax_product_ratio4");
g_dsv4_compressor_exact_pool_ratio4_pipeline =
ds4_gpu_get_pipeline(
"kernel_dsv4_compressor_exact_pool_ratio4_decode_ggml");
g_dsv4_softmax_pool_ratio4_direct_pipeline =
ds4_gpu_get_pipeline("kernel_dsv4_softmax_pool_ratio4_direct");
g_rms_norm_scale_pipeline =
ds4_gpu_get_pipeline("kernel_rms_norm_scale_f32_4");
g_dsv4_sort_i32_rows_asc_pipeline =
ds4_gpu_get_pipeline("kernel_dsv4_sort_i32_rows_asc");
g_dsv4_indexed_attention_heads8_pipeline =
ds4_gpu_get_pipeline("kernel_dsv4_indexed_mixed_attention_heads8");
g_dsv4_indexed_attention_heads8_rb16_pipeline =
ds4_gpu_get_pipeline("kernel_dsv4_indexed_mixed_attention_heads8_rb16");
g_dsv4_indexed_attention_heads16_dual_pipeline =
ds4_gpu_get_pipeline("kernel_dsv4_indexed_mixed_attention_heads16_dual");
g_dsv4_indexed_attention_heads8_split_pipeline =
ds4_gpu_get_pipeline("kernel_dsv4_indexed_mixed_attention_heads8_split");
g_dsv4_indexed_attention_heads8_split_reduce_pipeline =
ds4_gpu_get_pipeline("kernel_dsv4_indexed_mixed_attention_heads8_split_reduce");
g_dsv4_softplus_sqrt_pipeline =
ds4_gpu_get_pipeline("kernel_dsv4_softplus_sqrt_f32_4");
g_dsv4_router_finalize_one_pipeline =
ds4_gpu_get_pipeline("kernel_dsv4_router_finalize_one");
g_dsv4_router_finalize_one_simd_pipeline =
ds4_gpu_get_pipeline("kernel_dsv4_router_finalize_one_simd");
g_dsv4_router_finalize_weights_one_simd_pipeline =
ds4_gpu_get_pipeline("kernel_dsv4_router_finalize_weights_one_simd");
g_dsv4_router_transform_finalize_weights_one_simd_pipeline =
ds4_gpu_get_pipeline(
"kernel_dsv4_router_transform_finalize_weights_one_simd");
g_dsv4_router_select_visual_batch_pipeline =
ds4_gpu_get_pipeline("kernel_dsv4_router_select_visual_batch");
g_dsv4_router_weights_one_pipeline =
ds4_gpu_get_pipeline("kernel_dsv4_router_weights_one");
g_glm_router_select_one_pipeline =
ds4_gpu_get_pipeline("kernel_glm_router_select_one");
g_glm_kv_lora_rms_norm_pipeline =
ds4_gpu_get_pipeline("kernel_glm_kv_lora_rms_norm");
g_glm_k_b_project_pipeline =
ds4_gpu_get_pipeline("kernel_glm_k_b_project_q8_0");
g_glm_store_compact_kv_pipeline =
ds4_gpu_get_pipeline("kernel_glm_store_compact_kv");
g_glm_qkv_norm_store_compact_kv_pipeline =
ds4_gpu_get_pipeline("kernel_glm_qkv_norm_store_compact_kv");
g_glm_store_indexer_k_pipeline =
ds4_gpu_get_pipeline("kernel_glm_store_indexer_k");
g_glm53_indexer_pool_update_pipeline =
ds4_gpu_get_pipeline("kernel_glm53_indexer_pool_update");
g_glm_build_kv_cache_pipeline =
ds4_gpu_get_pipeline("kernel_glm_build_kv_cache");
g_glm_build_kv_cache_decode_group4_pipeline =
ds4_gpu_get_pipeline("kernel_glm_build_kv_cache_decode_group4");
g_glm_build_kv_cache_flash_pipeline =
ds4_gpu_get_pipeline("kernel_glm_build_kv_cache_flash");
g_glm_attention_full_pipeline =
ds4_gpu_get_pipeline("kernel_glm_attention_full");
g_glm_fill_selected_range_pipeline =
ds4_gpu_get_pipeline("kernel_glm_fill_selected_range");
g_glm_fill_selected_range_batch_pipeline =
ds4_gpu_get_pipeline("kernel_glm_fill_selected_range_batch");
g_glm53_expand_pool_selection_pipeline =
ds4_gpu_get_pipeline("kernel_glm53_expand_pool_selection");
g_glm_indexer_rope_tail_pipeline =
ds4_gpu_get_pipeline("kernel_glm_indexer_rope_tail_f32");
g_glm_indexer_score_one_pipeline =
ds4_gpu_get_pipeline("kernel_glm_indexer_score_one");
g_glm_indexer_score_one_direct_pipeline =
ds4_gpu_get_pipeline("kernel_glm_indexer_score_one_direct");
g_glm_indexer_score_one_wide_pipeline =
ds4_gpu_get_pipeline("kernel_glm_indexer_score_one_wide");
g_glm_indexer_scores_batch_pipeline =
ds4_gpu_get_pipeline("kernel_glm_indexer_scores_batch");
g_glm_indexer_scores_tiled_pipeline =
ds4_gpu_get_pipeline("kernel_glm_indexer_scores_tiled");
g_glm_indexer_scores_tiled_f32_pipeline =
ds4_gpu_get_pipeline("kernel_glm_indexer_scores_tiled_f32");
g_glm_qk_lowrank_pipeline =
ds4_gpu_get_pipeline("kernel_glm_qk_lowrank_q8_0");
g_glm_qk_lowrank_glm52_pipeline =
ds4_gpu_get_pipeline("kernel_glm_qk_lowrank_q8_0_glm52");
g_glm_qk_lowrank_glm52_sg_pipeline =
ds4_gpu_get_pipeline("kernel_glm_qk_lowrank_q8_0_glm52_sg");
g_glm_qk_lowrank_batch_pipeline =
ds4_gpu_get_pipeline("kernel_glm_qk_lowrank_q8_0_batch");
g_glm_qk_lowrank_batch_glm52_t4_pipeline =
ds4_gpu_get_pipeline("kernel_glm_qk_lowrank_q8_0_batch_glm52_t4");
g_glm_value_project_q8_0_pipeline =
ds4_gpu_get_pipeline("kernel_glm_value_project_q8_0");
g_glm_value_project_q8_0_batch_heads_pipeline =
ds4_gpu_get_pipeline("kernel_glm_value_project_q8_0_batch_heads");
g_glm_value_project_q8_0_batch_heads_mma_pipeline =
ds4_gpu_get_pipeline("kernel_glm_value_project_q8_0_batch_heads_mma");
g_glm_attention_indexed_decode_pipeline =
ds4_gpu_get_pipeline("kernel_glm_attention_indexed_decode");
g_glm_attention_indexed_decode_split_group8_partial_pipeline =
ds4_gpu_get_pipeline("kernel_glm_attention_indexed_decode_split_group8_partial");
g_glm_attention_indexed_decode_split_group8_partial_valid_fullheads_pipeline =
ds4_gpu_get_pipeline("kernel_glm_attention_indexed_decode_split_group8_partial_valid_fullheads");
g_glm_attention_indexed_decode_split_group8_reduce_pipeline =
ds4_gpu_get_pipeline("kernel_glm_attention_indexed_decode_split_group8_reduce");
g_glm_attention_indexed_decode_split_group8_reduce16_pipeline =
ds4_gpu_get_pipeline("kernel_glm_attention_indexed_decode_split_group8_reduce16");
g_glm_attention_indexed_batch_pipeline =
ds4_gpu_get_pipeline("kernel_glm_attention_indexed_batch");
g_glm_attention_indexed_batch_group2_pipeline =
ds4_gpu_get_pipeline("kernel_glm_attention_indexed_batch_group2");
g_glm_attention_indexed_batch_q2_group4_pipeline =
ds4_gpu_get_pipeline("kernel_glm_attention_indexed_batch_q2_group4");
g_glm_attention_indexed_batch_group8_pipeline =
ds4_gpu_get_pipeline("kernel_glm_attention_indexed_batch_group8");
g_glm_attention_indexed_batch_lora_group8_vec_pipeline =
ds4_gpu_get_pipeline("kernel_glm_attention_indexed_batch_lora_group8_vec");
g_glm_attention_indexed_batch_lora_group8_vec_valid_pipeline =
ds4_gpu_get_pipeline("kernel_glm_attention_indexed_batch_lora_group8_vec_valid");
g_glm_attention_indexed_batch_lora_group8_vec_valid_fullheads_pipeline =
ds4_gpu_get_pipeline("kernel_glm_attention_indexed_batch_lora_group8_vec_valid_fullheads");
g_glm_attention_indexed_batch_lora_group8_vec_causal_pipeline =
ds4_gpu_get_pipeline("kernel_glm_attention_indexed_batch_lora_group8_vec_causal");
g_glm_attention_indexed_batch_lora_group8_vec_causal_fullheads_pipeline =
ds4_gpu_get_pipeline("kernel_glm_attention_indexed_batch_lora_group8_vec_causal_fullheads");
g_glm_q4_k_pair_swiglu_f32_pipeline =
ds4_gpu_get_pipeline("kernel_glm_q4_K_pair_swiglu_f32");
g_glm_q4_k_pair_swiglu2_f32_pipeline =
ds4_gpu_get_pipeline("kernel_glm_q4_K_pair_swiglu2_f32");
g_glm_q4_k_pair_swiglu4_f32_pipeline =
ds4_gpu_get_pipeline("kernel_glm_q4_K_pair_swiglu4_f32");
g_glm_q4_k_pair_swiglu2_mapped_f32_pipeline =
ds4_gpu_get_pipeline("kernel_glm_q4_K_pair_swiglu2_mapped_f32");
g_glm_q4_k_pair_swiglu2_mapped_row_f32_pipeline =
ds4_gpu_get_pipeline("kernel_glm_q4_K_pair_swiglu2_mapped_row_f32");
g_glm_q2_k_pair_swiglu_f32_pipeline =
ds4_gpu_get_pipeline("kernel_glm_q2_K_pair_swiglu_f32");
g_glm_q2_k_addr_pair_swiglu2_f32_pipeline =
ds4_gpu_get_pipeline("kernel_glm_q2_K_addr_pair_swiglu2_f32");
g_glm_q2_k_addr_pair_swiglu2_masked_f32_pipeline =
ds4_gpu_get_pipeline("kernel_glm_q2_K_addr_pair_swiglu2_f32_masked");
g_glm_q4_k_addr_pair_swiglu_f32_pipeline =
ds4_gpu_get_pipeline("kernel_glm_q4_K_addr_pair_swiglu_f32");
g_glm_q4_k_addr_pair_swiglu_masked_f32_pipeline =
ds4_gpu_get_pipeline("kernel_glm_q4_K_addr_pair_swiglu_f32_masked");
g_glm_q2_k_down_f32_pipeline =
ds4_gpu_get_pipeline("kernel_glm_q2_K_down_f32");
g_glm_q4_k_down_f32_pipeline =
ds4_gpu_get_pipeline("kernel_glm_q4_K_down_simd_f32");
g_glm_q2_k_addr_down_f32_pipeline =
ds4_gpu_get_pipeline("kernel_glm_q2_K_addr_down_f32");
g_glm_q4_k_addr_down_f32_pipeline =
ds4_gpu_get_pipeline("kernel_glm_q4_K_addr_down_simd_f32");
g_glm_q5_k_pair_swiglu_f32_pipeline =
ds4_gpu_get_pipeline("kernel_glm_q5_K_pair_swiglu_f32");
g_glm_q5_k_pair_swiglu_mapped_f32_pipeline =
ds4_gpu_get_pipeline("kernel_glm_q5_K_pair_swiglu_mapped_f32");
g_glm_q5_k_pair_swiglu_mapped_row_f32_pipeline =
ds4_gpu_get_pipeline("kernel_glm_q5_K_pair_swiglu_mapped_row_f32");
g_glm_q5_k_down_f32_pipeline =
ds4_gpu_get_pipeline("kernel_glm_q5_K_down_f32");
g_glm_q6_k_down_f32_pipeline =
ds4_gpu_get_pipeline("kernel_glm_q6_K_down_f32");
g_dsv4_router_weights_batch_pipeline =
ds4_gpu_get_pipeline("kernel_dsv4_router_weights_batch");
g_dsv4_hc_expand4_pipeline =
ds4_gpu_get_pipeline("kernel_dsv4_hc_expand4");
if (!g_dsv4_indexer_score_one_direct_pipeline ||
!g_dsv4_compressor_store_one_pipeline ||
!g_dsv4_sort_i32_rows_asc_pipeline ||
!g_dsv4_indexed_attention_heads8_pipeline ||
!g_dsv4_indexed_attention_heads8_rb16_pipeline ||
!g_dsv4_indexed_attention_heads16_dual_pipeline ||
!g_dsv4_indexed_attention_heads8_split_pipeline ||
!g_dsv4_indexed_attention_heads8_split_reduce_pipeline ||
!g_dsv4_softplus_sqrt_pipeline ||
!g_dsv4_router_finalize_one_pipeline ||
!g_dsv4_router_select_visual_batch_pipeline ||
!g_dsv4_router_weights_one_pipeline ||
!g_glm_router_select_one_pipeline ||
!g_glm_kv_lora_rms_norm_pipeline ||
!g_glm_k_b_project_pipeline ||
!g_glm_store_compact_kv_pipeline ||
!g_glm_qkv_norm_store_compact_kv_pipeline ||
!g_glm_store_indexer_k_pipeline ||
!g_glm53_indexer_pool_update_pipeline ||
!g_glm_build_kv_cache_pipeline ||
!g_glm_build_kv_cache_decode_group4_pipeline ||
!g_glm_build_kv_cache_flash_pipeline ||
!g_glm_attention_full_pipeline ||
!g_glm_fill_selected_range_pipeline ||
!g_glm_fill_selected_range_batch_pipeline ||
!g_glm53_expand_pool_selection_pipeline ||
!g_glm_indexer_rope_tail_pipeline ||
!g_glm_indexer_score_one_pipeline ||
!g_glm_indexer_score_one_direct_pipeline ||
!g_glm_indexer_score_one_wide_pipeline ||
!g_glm_indexer_scores_batch_pipeline ||
!g_glm_indexer_scores_tiled_pipeline ||
!g_glm_indexer_scores_tiled_f32_pipeline ||
!g_glm_qk_lowrank_pipeline ||
!g_glm_qk_lowrank_glm52_pipeline ||
!g_glm_qk_lowrank_glm52_sg_pipeline ||
!g_glm_qk_lowrank_batch_pipeline ||
!g_glm_qk_lowrank_batch_glm52_t4_pipeline ||
!g_glm_value_project_q8_0_pipeline ||
!g_glm_value_project_q8_0_batch_heads_pipeline ||
!g_glm_value_project_q8_0_batch_heads_mma_pipeline ||
!g_glm_attention_indexed_decode_pipeline ||
!g_glm_attention_indexed_decode_split_group8_partial_pipeline ||
!g_glm_attention_indexed_decode_split_group8_partial_valid_fullheads_pipeline ||
!g_glm_attention_indexed_decode_split_group8_reduce_pipeline ||
!g_glm_attention_indexed_decode_split_group8_reduce16_pipeline ||
!g_glm_attention_indexed_batch_pipeline ||
!g_glm_attention_indexed_batch_group2_pipeline ||
!g_glm_attention_indexed_batch_q2_group4_pipeline ||
!g_glm_attention_indexed_batch_group8_pipeline ||
!g_glm_attention_indexed_batch_lora_group8_vec_pipeline ||
!g_glm_attention_indexed_batch_lora_group8_vec_valid_pipeline ||
!g_glm_attention_indexed_batch_lora_group8_vec_valid_fullheads_pipeline ||
!g_glm_attention_indexed_batch_lora_group8_vec_causal_pipeline ||
!g_glm_attention_indexed_batch_lora_group8_vec_causal_fullheads_pipeline ||
!g_glm_q4_k_pair_swiglu_f32_pipeline ||
!g_glm_q4_k_pair_swiglu2_f32_pipeline ||
!g_glm_q4_k_pair_swiglu4_f32_pipeline ||
!g_glm_q4_k_pair_swiglu2_mapped_f32_pipeline ||
!g_glm_q4_k_pair_swiglu2_mapped_row_f32_pipeline ||
!g_glm_q2_k_pair_swiglu_f32_pipeline ||
!g_glm_q2_k_addr_pair_swiglu2_f32_pipeline ||
!g_glm_q2_k_addr_pair_swiglu2_masked_f32_pipeline ||
!g_glm_q4_k_addr_pair_swiglu_f32_pipeline ||
!g_glm_q4_k_addr_pair_swiglu_masked_f32_pipeline ||
!g_glm_q2_k_down_f32_pipeline ||
!g_glm_q4_k_down_f32_pipeline ||
!g_glm_q2_k_addr_down_f32_pipeline ||
!g_glm_q4_k_addr_down_f32_pipeline ||
!g_glm_q5_k_pair_swiglu_f32_pipeline ||
!g_glm_q5_k_pair_swiglu_mapped_f32_pipeline ||
!g_glm_q5_k_pair_swiglu_mapped_row_f32_pipeline ||
!g_glm_q5_k_down_f32_pipeline ||
!g_glm_q6_k_down_f32_pipeline ||
!g_dsv4_hc_expand4_pipeline) {
g_queue = nil;
g_device = nil;
return 0;
}
g_initialized = 1;
}
return 1;
}
int ds4_gpu_test_mxfp4_down_half_lut(uint16_t *legacy_bits,
uint16_t *lut_bits) {
if (!legacy_bits || !lut_bits) return 0;
if (!g_initialized && !ds4_gpu_init()) return 0;
@autoreleasepool {
const NSUInteger count = 4096u;
const NSUInteger bytes = count * sizeof(uint16_t);
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_pipeline("kernel_test_mxfp4_down_half_lut");
id<MTLBuffer> legacy = [g_device newBufferWithLength:bytes
options:MTLResourceStorageModeShared];
id<MTLBuffer> lut = [g_device newBufferWithLength:bytes
options:MTLResourceStorageModeShared];
id<MTLCommandBuffer> cb = ds4_gpu_new_command_buffer();
if (!pipeline || !legacy || !lut || !cb) {
fprintf(stderr, "ds4: Metal MXFP4 half-LUT test setup failed\n");
return 0;
}
id<MTLComputeCommandEncoder> enc = [cb computeCommandEncoder];
if (!enc) return 0;
[enc setComputePipelineState:pipeline];
[enc setBuffer:legacy offset:0 atIndex:0];
[enc setBuffer:lut offset:0 atIndex:1];
[enc dispatchThreadgroups:MTLSizeMake((count + 255u) / 256u, 1, 1)
threadsPerThreadgroup:MTLSizeMake(256u, 1, 1)];
[enc endEncoding];
[cb commit];
if (!ds4_gpu_wait_command_buffer(cb, "MXFP4 half-LUT raw-bit test")) {
return 0;
}
memcpy(legacy_bits, [legacy contents], bytes);
memcpy(lut_bits, [lut contents], bytes);
}
return 1;
}
void ds4_gpu_test_set_flags(uint32_t flags) {
g_test_flags = flags;
}
ds4_gpu_tensor *ds4_gpu_tensor_alloc(uint64_t bytes) {
if (!g_initialized && !ds4_gpu_init()) return NULL;
if (bytes == 0 || bytes > (uint64_t)NSUIntegerMax) return NULL;
@autoreleasepool {
DS4MetalTensor *tensor = [DS4MetalTensor new];
tensor.buffer = [g_device newBufferWithLength:(NSUInteger)bytes
options:MTLResourceStorageModeShared];
if (!tensor.buffer) {
return NULL;
}
tensor.offset = 0;
tensor.bytes = bytes;
tensor.owner = 1;
uint64_t live_snap = 0;
uint64_t peak_snap = 0;
pthread_mutex_lock(&g_tensor_mu);
const int tracked = ds4_gpu_tensor_track_alloc_locked(
(__bridge const void *)tensor,
bytes,
&live_snap,
&peak_snap);
pthread_mutex_unlock(&g_tensor_mu);
if (!tracked) {
fprintf(stderr, "ds4: failed to track Metal tensor allocation\n");
tensor.buffer = nil;
return NULL;
}
if (ds4_gpu_trace_allocs()) {
fprintf(stderr,
"ds4: Metal tensor alloc %.3f MiB live %.3f MiB peak %.3f MiB\n",
(double)bytes / (1024.0 * 1024.0),
(double)live_snap / (1024.0 * 1024.0),
(double)peak_snap / (1024.0 * 1024.0));
}
return (__bridge_retained ds4_gpu_tensor *)tensor;
}
}
ds4_gpu_tensor *ds4_gpu_tensor_alloc_managed(uint64_t bytes) {
return ds4_gpu_tensor_alloc(bytes);
}
int ds4_gpu_should_use_managed_kv_cache(uint64_t kv_cache_bytes, uint64_t context_bytes) {
(void)kv_cache_bytes;
(void)context_bytes;
return 0;
}
ds4_gpu_tensor *ds4_gpu_tensor_view(const ds4_gpu_tensor *base, uint64_t offset, uint64_t bytes) {
if (!base) return NULL;
const DS4MetalTensor *base_obj = ds4_gpu_tensor_const_obj(base);
if (offset > base_obj.bytes || bytes > base_obj.bytes - offset) return NULL;
if (base_obj.offset > UINT64_MAX - offset) return NULL;
const uint64_t absolute_offset = base_obj.offset + offset;
if (absolute_offset > (uint64_t)NSUIntegerMax) return NULL;
@autoreleasepool {
DS4MetalTensor *view = [DS4MetalTensor new];
view.buffer = base_obj.buffer;
view.offset = absolute_offset;
view.bytes = bytes;
view.owner = 0;
pthread_mutex_lock(&g_tensor_mu);
const int tracked = ds4_gpu_tensor_track_view_locked((__bridge const void *)view);
pthread_mutex_unlock(&g_tensor_mu);
if (!tracked) {
fprintf(stderr, "ds4: failed to track Metal tensor view\n");
view.buffer = nil;
return NULL;
}
return (__bridge_retained ds4_gpu_tensor *)view;
}
}
void ds4_gpu_tensor_free(ds4_gpu_tensor *tensor) {
if (!tensor) return;
@autoreleasepool {
uint8_t owner = 0;
uint64_t bytes = 0;
uint64_t live_snap = 0;
uint64_t peak_snap = 0;
if (!ds4_gpu_tensor_prepare_free(tensor,
&owner,
&bytes,
&live_snap,
&peak_snap)) {
return;
}
DS4MetalTensor *obj = (__bridge_transfer DS4MetalTensor *)tensor;
if (owner) {
if (ds4_gpu_trace_allocs()) {
fprintf(stderr,
"ds4: Metal tensor free %.3f MiB live %.3f MiB peak %.3f MiB\n",
(double)bytes / (1024.0 * 1024.0),
(double)live_snap / (1024.0 * 1024.0),
(double)peak_snap / (1024.0 * 1024.0));
}
}
obj.buffer = nil;
obj.offset = 0;
obj.bytes = 0;
obj.owner = 0;
}
}
uint64_t ds4_gpu_tensor_bytes(const ds4_gpu_tensor *tensor) {
if (!tensor) return 0;
const DS4MetalTensor *obj = ds4_gpu_tensor_const_obj(tensor);
return obj.bytes;
}
void *ds4_gpu_tensor_contents(ds4_gpu_tensor *tensor) {
if (!tensor) return NULL;
DS4MetalTensor *obj = ds4_gpu_tensor_obj(tensor);
return (uint8_t *)[obj.buffer contents] + obj.offset;
}
int ds4_gpu_tensor_fill_f32(ds4_gpu_tensor *tensor, float value, uint64_t count) {
if (!tensor || count > ds4_gpu_tensor_bytes(tensor) / sizeof(float)) return 0;
float *p = ds4_gpu_tensor_contents(tensor);
if (!p && count != 0) return 0;
for (uint64_t i = 0; i < count; i++) p[i] = value;
return 1;
}
int ds4_gpu_tensor_write(ds4_gpu_tensor *tensor, uint64_t offset, const void *data, uint64_t bytes) {
if (!tensor || (!data && bytes != 0)) return 0;
DS4MetalTensor *obj = ds4_gpu_tensor_obj(tensor);
if (offset > obj.bytes || bytes > obj.bytes - offset) return 0;
if (bytes != 0) {
memcpy((uint8_t *)[obj.buffer contents] + obj.offset + offset, data, (size_t)bytes);
}
return 1;
}
int ds4_gpu_tensor_read(const ds4_gpu_tensor *tensor, uint64_t offset, void *data, uint64_t bytes) {
if (!tensor || (!data && bytes != 0)) return 0;
const DS4MetalTensor *obj = ds4_gpu_tensor_const_obj(tensor);
if (offset > obj.bytes || bytes > obj.bytes - offset) return 0;
if (bytes != 0) {
memcpy(data, (const uint8_t *)[obj.buffer contents] + obj.offset + offset, (size_t)bytes);
}
return 1;
}
int ds4_gpu_tensor_copy(ds4_gpu_tensor *dst, uint64_t dst_offset,
const ds4_gpu_tensor *src, uint64_t src_offset,
uint64_t bytes) {
if (!dst || !src) return 0;
if (!g_initialized && !ds4_gpu_init()) return 0;
DS4MetalTensor *d = ds4_gpu_tensor_obj(dst);
const DS4MetalTensor *s = ds4_gpu_tensor_const_obj(src);
if (dst_offset > d.bytes || bytes > d.bytes - dst_offset) return 0;
if (src_offset > s.bytes || bytes > s.bytes - src_offset) return 0;
if (bytes == 0) return 1;
if (!g_batch_cb) return 0;
/* C inference callers have no surrounding autorelease pool. In particular,
* validation-layer blit bookkeeping otherwise accumulates across tokens. */
@autoreleasepool {
ds4_gpu_close_batch_encoder();
g_batch_has_work = YES;
id<MTLBlitCommandEncoder> blit = ds4_gpu_blit_encoder(g_batch_cb, "tensor_copy", 1u);
if (!blit) return 0;
[blit copyFromBuffer:s.buffer
sourceOffset:(NSUInteger)(s.offset + src_offset)
toBuffer:d.buffer
destinationOffset:(NSUInteger)(d.offset + dst_offset)
size:(NSUInteger)bytes];
[blit endEncoding];
}
return 1;
}
int ds4_gpu_tensor_copy_f32_to_f16(ds4_gpu_tensor *dst, uint64_t dst_offset,
const ds4_gpu_tensor *src, uint64_t src_offset,
uint64_t count) {
if (!dst || !src) return 0;
if (!g_initialized && !ds4_gpu_init()) return 0;
DS4MetalTensor *d = ds4_gpu_tensor_obj(dst);
const DS4MetalTensor *s = ds4_gpu_tensor_const_obj(src);
if (count == 0) return 1;
if (count > UINT64_MAX / sizeof(float) ||
count > UINT64_MAX / sizeof(uint16_t)) {
return 0;
}
const uint64_t src_bytes = count * sizeof(float);
const uint64_t dst_bytes = count * sizeof(uint16_t);
if (src_offset > s.bytes || src_bytes > s.bytes - src_offset ||
dst_offset > d.bytes || dst_bytes > d.bytes - dst_offset) {
return 0;
}
@autoreleasepool {
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
uint64_t done = 0;
int ok = 1;
while (done < count && ok) {
uint64_t chunk64 = count - done;
if (chunk64 > UINT32_MAX) chunk64 = UINT32_MAX;
const uint32_t chunk = (uint32_t)chunk64;
ok = ds4_gpu_encode_cpy_f32_f16_1d(
cb,
s.buffer,
(NSUInteger)(s.offset + src_offset + done * sizeof(float)),
d.buffer,
(NSUInteger)(d.offset + dst_offset + done * sizeof(uint16_t)),
chunk);
done += chunk;
}
if (ok) ok = ds4_gpu_finish_command_buffer(cb, owned, "tensor f32 to f16 copy");
return ok;
}
}
int ds4_gpu_pack_slot_rows_f32_tensor(
ds4_gpu_tensor *out,
const ds4_gpu_tensor *slots,
uint32_t n_rows,
uint32_t width,
uint32_t n_slots,
uint32_t slot_cap) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!out || !slots || n_rows == 0 || width == 0 || n_slots == 0 ||
slot_cap == 0 || n_rows > slot_cap) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> slotsbuf = ds4_gpu_tensor_buffer(slots);
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out);
uint64_t row_bytes = 0;
uint64_t slot_plane_bytes = 0;
uint64_t slots_bytes = 0;
uint64_t out_rows = 0;
uint64_t out_bytes = 0;
if ((uint64_t)width > UINT64_MAX / sizeof(float)) return 0;
row_bytes = (uint64_t)width * sizeof(float);
if ((uint64_t)slot_cap > UINT64_MAX / row_bytes) return 0;
slot_plane_bytes = (uint64_t)slot_cap * row_bytes;
if ((uint64_t)n_slots > UINT64_MAX / slot_plane_bytes) return 0;
slots_bytes = (uint64_t)n_slots * slot_plane_bytes;
if ((uint64_t)n_rows > UINT64_MAX / n_slots) return 0;
out_rows = (uint64_t)n_rows * n_slots;
if (out_rows > UINT64_MAX / row_bytes) return 0;
out_bytes = out_rows * row_bytes;
if (!slotsbuf || !outbuf ||
ds4_gpu_tensor_bytes(slots) < slots_bytes ||
ds4_gpu_tensor_bytes(out) < out_bytes) {
fprintf(stderr, "ds4: Metal slot-row pack received undersized buffers\n");
return 0;
}
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
if (!ds4_gpu_encode_cpy_f32_f32_3d_src_strided(cb,
slotsbuf,
ds4_gpu_tensor_offset(slots),
outbuf,
ds4_gpu_tensor_offset(out),
width,
n_slots,
n_rows,
sizeof(float),
slot_plane_bytes,
row_bytes,
row_bytes,
(uint64_t)n_slots * row_bytes)) {
return 0;
}
if (!ds4_gpu_finish_command_buffer(cb, owned, "slot-row pack")) return 0;
}
return 1;
}
int ds4_gpu_begin_commands(void) {
if (!g_initialized && !ds4_gpu_init()) return 0;
/* A failed concurrent FFN must never affect the next command batch. */
ds4_gpu_parallel_ffn_reset_state(YES);
if (g_batch_cb) return 0;
/* Refresh once per command batch so same-engine A/B runs can toggle the
* static PSO without paying for environment lookups in every layer. */
g_use_dsv4_head_rms_norm_rope_tail_pipeline =
!g_ssd_streaming_mode &&
ds4_gpu_device_is_pre_m5_apple_silicon() &&
getenv("DS4_METAL_DISABLE_PRE_M5_HEAD_RMS_ROPE_PIPELINE_STATIC") == NULL;
g_batch_cb_created_ms = ds4_gpu_now_ms();
g_batch_cb = ds4_gpu_new_command_buffer();
g_batch_has_work = NO;
if (g_batch_cb) ds4_gpu_stream_expert_cache_note_batch_created();
ds4_gpu_timeline_attach(g_batch_cb);
return g_batch_cb != nil;
}
int ds4_gpu_flush_encoder(void) {
if (!g_initialized && !ds4_gpu_init()) return 0;
ds4_gpu_parallel_ffn_reset_state(YES);
if (!g_batch_cb) return 0;
ds4_gpu_close_batch_encoder();
return 1;
}
int ds4_gpu_flush_commands(void) {
if (!g_initialized && !ds4_gpu_init()) return 0;
ds4_gpu_parallel_ffn_reset_state(YES);
if (!g_batch_cb) return 0;
ds4_gpu_close_batch_encoder();
id<MTLCommandBuffer> cb = g_batch_cb;
g_batch_cb = nil;
g_batch_has_work = NO;
[cb commit];
[g_pending_cbs addObject:cb];
ds4_gpu_stream_expert_cache_note_batch_committed();
g_batch_cb = ds4_gpu_new_command_buffer();
g_batch_has_work = NO;
if (g_batch_cb) ds4_gpu_stream_expert_cache_note_batch_created();
ds4_gpu_timeline_attach(g_batch_cb);
if (!g_batch_cb) {
(void)ds4_gpu_wait_pending_command_buffers("command batch");
[g_transient_buffers removeAllObjects];
return 0;
}
return 1;
}
int ds4_gpu_commands_active(void) {
return g_batch_cb != nil;
}
/* Exact M5 full-FFN overlap inside one concurrent compute encoder. Shared
* gate/up and routed IQ2 pair-SwiGLU launch together; explicit level barriers
* precede the routed Q2 and shared Q8 down consumers. */
static id<MTLComputePipelineState> g_parallel_q8_pipeline;
static id<MTLBuffer> g_parallel_q8_weight;
static id<MTLBuffer> g_parallel_q8_x;
static id<MTLBuffer> g_parallel_q8_out;
static NSUInteger g_parallel_q8_weight_offset;
static NSUInteger g_parallel_q8_x_offset;
static NSUInteger g_parallel_q8_out_offset;
static ds4_gpu_q8_0_matvec_args g_parallel_q8_args;
static id<MTLComputePipelineState> g_parallel_gate_up_pipeline;
static id<MTLBuffer> g_parallel_gate_weight;
static id<MTLBuffer> g_parallel_up_weight;
static id<MTLBuffer> g_parallel_gate_x;
static id<MTLBuffer> g_parallel_gate_out;
static id<MTLBuffer> g_parallel_up_out;
static id<MTLBuffer> g_parallel_mid_out;
static NSUInteger g_parallel_gate_weight_offset;
static NSUInteger g_parallel_up_weight_offset;
static NSUInteger g_parallel_gate_x_offset;
static NSUInteger g_parallel_gate_out_offset;
static NSUInteger g_parallel_up_out_offset;
static NSUInteger g_parallel_mid_out_offset;
static ds4_gpu_q8_0_matvec_args g_parallel_gate_up_args;
static float g_parallel_gate_up_clamp;
static NSUInteger g_parallel_gate_up_nsg;
static NSUInteger g_parallel_gate_up_nr0;
static NSUInteger g_parallel_gate_up_smem;
static int g_parallel_ffn_mode; /* 2: gate/up + down */
static int g_parallel_ffn_stage;
static BOOL g_parallel_q8_pending;
static BOOL g_parallel_q8_encoded;
/* GPU-decided shared-expert lane split (see ds4_shared_split_range in
* metal/dense.metal): the split kernels read the selected expert ids and
* take a complementary lane range per rank sized to balance the bytes each
* rank streams; the host binds full weight ranges and the ids buffer. */
typedef struct {
int32_t tp_rank;
int32_t tp_world;
int32_t n_expert;
int32_t n_expert_used;
int32_t shared_dim;
int32_t lane_granule;
int32_t shift_q16;
} ds4_gpu_shared_split_args;
static BOOL g_parallel_split_active;
static ds4_gpu_shared_split_args g_parallel_split_args;
static id<MTLBuffer> g_parallel_split_ids;
static NSUInteger g_parallel_split_ids_offset;
/* Reset is deliberately idempotent. Closing the concurrent encoder preserves
* work already encoded, while clearing every admission/reference field keeps
* a failed FFN path from turning later ordinary dispatches concurrent. */
static void ds4_gpu_parallel_ffn_reset_state(BOOL close_encoder) {
if (close_encoder &&
(g_batch_encoder_concurrent || g_parallel_q8_pending ||
g_parallel_q8_encoded || g_parallel_ffn_mode != 0 ||
g_parallel_ffn_stage != 0)) {
ds4_gpu_close_batch_encoder();
}
g_batch_encoder_concurrent = NO;
g_parallel_q8_pending = NO;
g_parallel_q8_encoded = NO;
g_parallel_ffn_mode = 0;
g_parallel_ffn_stage = 0;
g_parallel_q8_pipeline = nil;
g_parallel_q8_weight = nil;
g_parallel_q8_x = nil;
g_parallel_q8_out = nil;
g_parallel_q8_weight_offset = 0;
g_parallel_q8_x_offset = 0;
g_parallel_q8_out_offset = 0;
g_parallel_q8_args = (ds4_gpu_q8_0_matvec_args){0};
g_parallel_gate_up_pipeline = nil;
g_parallel_gate_weight = nil;
g_parallel_up_weight = nil;
g_parallel_gate_x = nil;
g_parallel_gate_out = nil;
g_parallel_up_out = nil;
g_parallel_mid_out = nil;
g_parallel_gate_weight_offset = 0;
g_parallel_up_weight_offset = 0;
g_parallel_gate_x_offset = 0;
g_parallel_gate_out_offset = 0;
g_parallel_up_out_offset = 0;
g_parallel_mid_out_offset = 0;
g_parallel_gate_up_args = (ds4_gpu_q8_0_matvec_args){0};
g_parallel_gate_up_clamp = 0.0f;
g_parallel_gate_up_nsg = 0;
g_parallel_gate_up_nr0 = 0;
g_parallel_split_active = NO;
g_parallel_split_args = (ds4_gpu_shared_split_args){0};
g_parallel_split_ids = nil;
g_parallel_split_ids_offset = 0;
g_parallel_gate_up_smem = 0;
}
void ds4_gpu_parallel_ffn_abort(void) {
ds4_gpu_parallel_ffn_reset_state(YES);
}
/* Clang cleanup makes every early return from the large generic routed-MoE
* wrapper abort an armed concurrent FFN without touching its many
* established fallback branches. */
static void ds4_gpu_parallel_ffn_scope_cleanup(BOOL *armed) {
if (armed && *armed && g_parallel_q8_pending) {
ds4_gpu_parallel_ffn_abort();
}
}
/* DeepSeek V4.1 keeps the shared expert's bf16 boundaries inside the
* concurrent encoder: mid rounded by the fused gate/up, bf16 in and out on
* the down projection. */
static BOOL g_parallel_ffn_bf16;
static int ds4_gpu_parallel_ffn_start_range(
ds4_gpu_tensor *gate,
ds4_gpu_tensor *up,
ds4_gpu_tensor *mid,
ds4_gpu_tensor *shared_out,
const void *model_map,
uint64_t model_size,
uint64_t gate_offset,
uint64_t up_offset,
uint64_t down_offset,
uint32_t model_dim,
uint32_t shared_dim,
uint32_t shared_lane_offset,
uint32_t shared_lane_count,
const ds4_gpu_tensor *x,
float clamp) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!g_batch_cb || g_parallel_q8_pending || g_batch_encoder_concurrent ||
!gate || !up || !mid || !shared_out || !x || !model_map ||
model_dim == 0 || shared_dim == 0 || shared_lane_count == 0 ||
shared_lane_offset > shared_dim ||
shared_lane_count > shared_dim - shared_lane_offset ||
(model_dim & 31u) != 0 || (shared_dim & 31u) != 0 ||
(shared_lane_offset & 31u) != 0 ||
(shared_lane_count & 31u) != 0 ||
!isfinite(clamp) || clamp < 0.0f) {
return 0;
}
id<MTLBuffer> xbuf = ds4_gpu_tensor_buffer(x);
id<MTLBuffer> gatebuf = ds4_gpu_tensor_buffer(gate);
id<MTLBuffer> upbuf = ds4_gpu_tensor_buffer(up);
id<MTLBuffer> midbuf = ds4_gpu_tensor_buffer(mid);
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(shared_out);
if (!xbuf || !gatebuf || !upbuf || !midbuf || !outbuf ||
ds4_gpu_tensor_bytes(x) < (uint64_t)model_dim * sizeof(float) ||
ds4_gpu_tensor_bytes(gate) < (uint64_t)shared_lane_count * sizeof(float) ||
ds4_gpu_tensor_bytes(up) < (uint64_t)shared_lane_count * sizeof(float) ||
ds4_gpu_tensor_bytes(mid) < (uint64_t)shared_lane_count * sizeof(float) ||
ds4_gpu_tensor_bytes(shared_out) < (uint64_t)model_dim * sizeof(float)) {
return 0;
}
const uint64_t gate_row_bytes = ((uint64_t)model_dim / 32u) * 34u;
const uint64_t gate_weight_bytes =
(uint64_t)shared_lane_count * gate_row_bytes;
const uint64_t gate_lane_bytes =
(uint64_t)shared_lane_offset * gate_row_bytes;
const uint64_t down_row_bytes = ((uint64_t)shared_dim / 32u) * 34u;
const uint64_t down_weight_bytes = (uint64_t)model_dim * down_row_bytes;
if (gate_offset > model_size || gate_lane_bytes > model_size - gate_offset ||
gate_weight_bytes > model_size - gate_offset - gate_lane_bytes ||
up_offset > model_size || gate_lane_bytes > model_size - up_offset ||
gate_weight_bytes > model_size - up_offset - gate_lane_bytes ||
down_offset > model_size || down_weight_bytes > model_size - down_offset) {
return 0;
}
uint64_t gate_inner = 0, up_inner = 0, down_inner = 0;
id<MTLBuffer> gate_wbuf = ds4_gpu_wrap_model_range(
model_map, model_size, gate_offset + gate_lane_bytes,
gate_weight_bytes, &gate_inner);
id<MTLBuffer> up_wbuf = ds4_gpu_wrap_model_range(
model_map, model_size, up_offset + gate_lane_bytes,
gate_weight_bytes, &up_inner);
id<MTLBuffer> down_wbuf = ds4_gpu_wrap_model_range(
model_map, model_size, down_offset, down_weight_bytes, &down_inner);
if (!gate_wbuf || !up_wbuf || !down_wbuf) return 0;
ds4_gpu_mv_dispatch gate_dispatch = ds4_gpu_make_q8_0_mv_dispatch();
const char *gate_fn = g_parallel_ffn_bf16 ?
"kernel_dsv4_shared_mid_swiglu_q8_0_bf16" : "kernel_dsv4_shared_gate_up_swiglu_q8_0";
id<MTLComputePipelineState> gate_pipeline =
ds4_gpu_get_mul_mv_pipeline(gate_fn, gate_dispatch.nsg);
id<MTLComputePipelineState> down_pipeline =
ds4_gpu_get_mul_mv_pipeline(g_parallel_ffn_bf16 ?
"kernel_mul_mv_q8_0_f32_bf16io" : "kernel_mul_mv_q8_0_f32", 4);
if (!gate_pipeline || !down_pipeline ||
down_pipeline.maxTotalThreadsPerThreadgroup < 128u) {
return 0;
}
ds4_gpu_close_batch_encoder();
g_batch_encoder_concurrent = YES;
g_parallel_gate_up_pipeline = gate_pipeline;
g_parallel_gate_weight = gate_wbuf;
g_parallel_up_weight = up_wbuf;
g_parallel_gate_x = xbuf;
g_parallel_gate_out = gatebuf;
g_parallel_up_out = upbuf;
g_parallel_mid_out = midbuf;
g_parallel_gate_weight_offset = (NSUInteger)gate_inner;
g_parallel_up_weight_offset = (NSUInteger)up_inner;
g_parallel_gate_x_offset = ds4_gpu_tensor_offset(x);
g_parallel_gate_out_offset = ds4_gpu_tensor_offset(gate);
g_parallel_up_out_offset = ds4_gpu_tensor_offset(up);
g_parallel_mid_out_offset = ds4_gpu_tensor_offset(mid);
g_parallel_gate_up_args =
ds4_gpu_make_q8_0_mv_args(model_dim, shared_lane_count);
g_parallel_gate_up_args.nr0 = gate_dispatch.nr0;
g_parallel_gate_up_clamp = clamp;
g_parallel_gate_up_nsg = gate_dispatch.nsg;
g_parallel_gate_up_nr0 = gate_dispatch.nr0;
g_parallel_gate_up_smem = gate_dispatch.smem;
g_parallel_q8_pipeline = down_pipeline;
g_parallel_q8_weight = down_wbuf;
g_parallel_q8_x = midbuf;
g_parallel_q8_out = outbuf;
g_parallel_q8_weight_offset =
(NSUInteger)(down_inner +
((uint64_t)shared_lane_offset / 32u) * 34u);
g_parallel_q8_x_offset = ds4_gpu_tensor_offset(mid);
g_parallel_q8_out_offset = ds4_gpu_tensor_offset(shared_out);
g_parallel_q8_args =
ds4_gpu_make_q8_0_mv_args(shared_dim, model_dim);
g_parallel_q8_args.ne00 = (int32_t)shared_lane_count;
g_parallel_q8_args.ne10 = (int32_t)shared_lane_count;
g_parallel_q8_args.nr0 = 2;
g_parallel_ffn_mode = 2;
g_parallel_ffn_stage = 0;
g_parallel_q8_pending = YES;
g_parallel_q8_encoded = NO;
return 1;
}
int ds4_gpu_parallel_ffn_start(
ds4_gpu_tensor *gate,
ds4_gpu_tensor *up,
ds4_gpu_tensor *mid,
ds4_gpu_tensor *shared_out,
const void *model_map,
uint64_t model_size,
uint64_t gate_offset,
uint64_t up_offset,
uint64_t down_offset,
uint32_t model_dim,
uint32_t shared_dim,
const ds4_gpu_tensor *x,
float clamp) {
return ds4_gpu_parallel_ffn_start_range(
gate, up, mid, shared_out, model_map, model_size,
gate_offset, up_offset, down_offset, model_dim, shared_dim,
0, shared_dim, x, clamp);
}
int ds4_gpu_parallel_ffn_start_bf16(
ds4_gpu_tensor *gate,
ds4_gpu_tensor *up,
ds4_gpu_tensor *mid,
ds4_gpu_tensor *shared_out,
const void *model_map,
uint64_t model_size,
uint64_t gate_offset,
uint64_t up_offset,
uint64_t down_offset,
uint32_t model_dim,
uint32_t shared_dim,
const ds4_gpu_tensor *x,
float clamp) {
g_parallel_ffn_bf16 = YES;
const int ok = ds4_gpu_parallel_ffn_start_range(
gate, up, mid, shared_out, model_map, model_size,
gate_offset, up_offset, down_offset, model_dim, shared_dim,
0, shared_dim, x, clamp);
g_parallel_ffn_bf16 = NO;
return ok;
}
int ds4_gpu_parallel_ffn_start_sliced(
ds4_gpu_tensor *gate,
ds4_gpu_tensor *up,
ds4_gpu_tensor *mid,
ds4_gpu_tensor *shared_out,
const void *model_map,
uint64_t model_size,
uint64_t gate_offset,
uint64_t up_offset,
uint64_t down_offset,
uint32_t model_dim,
uint32_t shared_dim,
uint32_t shared_lane_offset,
uint32_t shared_lane_count,
const ds4_gpu_tensor *x,
float clamp) {
return ds4_gpu_parallel_ffn_start_range(
gate, up, mid, shared_out, model_map, model_size,
gate_offset, up_offset, down_offset, model_dim, shared_dim,
shared_lane_offset, shared_lane_count, x, clamp);
}
int ds4_gpu_parallel_ffn_start_split(
ds4_gpu_tensor *gate,
ds4_gpu_tensor *up,
ds4_gpu_tensor *mid,
ds4_gpu_tensor *shared_out,
const void *model_map,
uint64_t model_size,
uint64_t gate_offset,
uint64_t up_offset,
uint64_t down_offset,
uint32_t model_dim,
uint32_t shared_dim,
const ds4_gpu_tensor *x,
float clamp,
const ds4_gpu_tensor *selected,
uint32_t tp_rank,
uint32_t tp_world,
uint32_t n_expert,
uint32_t n_expert_used,
uint32_t shift_q16) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!g_batch_cb || g_parallel_q8_pending || g_batch_encoder_concurrent ||
!gate || !up || !mid || !shared_out || !x || !selected || !model_map ||
model_dim == 0 || shared_dim == 0 ||
tp_world != 2 || tp_rank >= tp_world ||
n_expert == 0 || (n_expert % tp_world) != 0 || n_expert_used == 0 ||
n_expert_used > 64 || shift_q16 > 65536u ||
(model_dim & 31u) != 0 || (shared_dim & 63u) != 0 ||
!isfinite(clamp) || clamp < 0.0f) {
return 0;
}
id<MTLBuffer> xbuf = ds4_gpu_tensor_buffer(x);
id<MTLBuffer> gatebuf = ds4_gpu_tensor_buffer(gate);
id<MTLBuffer> upbuf = ds4_gpu_tensor_buffer(up);
id<MTLBuffer> midbuf = ds4_gpu_tensor_buffer(mid);
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(shared_out);
id<MTLBuffer> idsbuf = ds4_gpu_tensor_buffer(selected);
if (!xbuf || !gatebuf || !upbuf || !midbuf || !outbuf || !idsbuf ||
ds4_gpu_tensor_bytes(x) < (uint64_t)model_dim * sizeof(float) ||
ds4_gpu_tensor_bytes(gate) < (uint64_t)shared_dim * sizeof(float) ||
ds4_gpu_tensor_bytes(up) < (uint64_t)shared_dim * sizeof(float) ||
ds4_gpu_tensor_bytes(mid) < (uint64_t)shared_dim * sizeof(float) ||
ds4_gpu_tensor_bytes(shared_out) < (uint64_t)model_dim * sizeof(float) ||
ds4_gpu_tensor_bytes(selected) < (uint64_t)n_expert_used * sizeof(int32_t)) {
return 0;
}
const uint64_t gate_row_bytes = ((uint64_t)model_dim / 32u) * 34u;
const uint64_t gate_weight_bytes = (uint64_t)shared_dim * gate_row_bytes;
const uint64_t down_row_bytes = ((uint64_t)shared_dim / 32u) * 34u;
const uint64_t down_weight_bytes = (uint64_t)model_dim * down_row_bytes;
if (gate_offset > model_size || gate_weight_bytes > model_size - gate_offset ||
up_offset > model_size || gate_weight_bytes > model_size - up_offset ||
down_offset > model_size || down_weight_bytes > model_size - down_offset) {
return 0;
}
uint64_t gate_inner = 0, up_inner = 0, down_inner = 0;
id<MTLBuffer> gate_wbuf = ds4_gpu_wrap_model_range(
model_map, model_size, gate_offset, gate_weight_bytes, &gate_inner);
id<MTLBuffer> up_wbuf = ds4_gpu_wrap_model_range(
model_map, model_size, up_offset, gate_weight_bytes, &up_inner);
id<MTLBuffer> down_wbuf = ds4_gpu_wrap_model_range(
model_map, model_size, down_offset, down_weight_bytes, &down_inner);
if (!gate_wbuf || !up_wbuf || !down_wbuf) return 0;
ds4_gpu_mv_dispatch gate_dispatch = ds4_gpu_make_q8_0_mv_dispatch();
id<MTLComputePipelineState> gate_pipeline =
ds4_gpu_get_mul_mv_pipeline(
"kernel_dsv4_shared_gate_up_swiglu_q8_0_split", gate_dispatch.nsg);
id<MTLComputePipelineState> down_pipeline =
ds4_gpu_get_mul_mv_pipeline("kernel_dsv4_shared_down_q8_0_split", 4);
if (!gate_pipeline || !down_pipeline ||
down_pipeline.maxTotalThreadsPerThreadgroup < 128u) {
return 0;
}
ds4_gpu_close_batch_encoder();
g_batch_encoder_concurrent = YES;
g_parallel_gate_up_pipeline = gate_pipeline;
g_parallel_gate_weight = gate_wbuf;
g_parallel_up_weight = up_wbuf;
g_parallel_gate_x = xbuf;
g_parallel_gate_out = gatebuf;
g_parallel_up_out = upbuf;
g_parallel_mid_out = midbuf;
g_parallel_gate_weight_offset = (NSUInteger)gate_inner;
g_parallel_up_weight_offset = (NSUInteger)up_inner;
g_parallel_gate_x_offset = ds4_gpu_tensor_offset(x);
g_parallel_gate_out_offset = ds4_gpu_tensor_offset(gate);
g_parallel_up_out_offset = ds4_gpu_tensor_offset(up);
g_parallel_mid_out_offset = ds4_gpu_tensor_offset(mid);
/* Full width: the grid covers every shared row and the kernels trim to
* the rank's lane range. */
g_parallel_gate_up_args = ds4_gpu_make_q8_0_mv_args(model_dim, shared_dim);
g_parallel_gate_up_args.nr0 = gate_dispatch.nr0;
g_parallel_gate_up_clamp = clamp;
g_parallel_gate_up_nsg = gate_dispatch.nsg;
g_parallel_gate_up_nr0 = gate_dispatch.nr0;
g_parallel_gate_up_smem = gate_dispatch.smem;
g_parallel_q8_pipeline = down_pipeline;
g_parallel_q8_weight = down_wbuf;
g_parallel_q8_x = midbuf;
g_parallel_q8_out = outbuf;
g_parallel_q8_weight_offset = (NSUInteger)down_inner;
g_parallel_q8_x_offset = ds4_gpu_tensor_offset(mid);
g_parallel_q8_out_offset = ds4_gpu_tensor_offset(shared_out);
g_parallel_q8_args = ds4_gpu_make_q8_0_mv_args(shared_dim, model_dim);
g_parallel_q8_args.nr0 = 2;
g_parallel_split_active = YES;
g_parallel_split_args = (ds4_gpu_shared_split_args) {
.tp_rank = (int32_t)tp_rank,
.tp_world = (int32_t)tp_world,
.n_expert = (int32_t)n_expert,
.n_expert_used = (int32_t)n_expert_used,
.shared_dim = (int32_t)shared_dim,
.lane_granule = 64,
.shift_q16 = (int32_t)shift_q16,
};
g_parallel_split_ids = idsbuf;
g_parallel_split_ids_offset = ds4_gpu_tensor_offset(selected);
g_parallel_ffn_mode = 2;
g_parallel_ffn_stage = 0;
g_parallel_q8_pending = YES;
g_parallel_q8_encoded = NO;
return 1;
}
static void ds4_gpu_encode_parallel_q8_down(
id<MTLComputeCommandEncoder> enc) {
[enc setComputePipelineState:g_parallel_q8_pipeline];
[enc setBytes:&g_parallel_q8_args
length:sizeof(g_parallel_q8_args)
atIndex:0];
if (g_parallel_split_active) {
[enc setBytes:&g_parallel_split_args
length:sizeof(g_parallel_split_args)
atIndex:1];
[enc setBuffer:g_parallel_q8_weight
offset:g_parallel_q8_weight_offset
atIndex:2];
[enc setBuffer:g_parallel_q8_x offset:g_parallel_q8_x_offset atIndex:3];
[enc setBuffer:g_parallel_q8_out offset:g_parallel_q8_out_offset atIndex:4];
[enc setBuffer:g_parallel_split_ids
offset:g_parallel_split_ids_offset
atIndex:5];
} else {
[enc setBuffer:g_parallel_q8_weight
offset:g_parallel_q8_weight_offset
atIndex:1];
[enc setBuffer:g_parallel_q8_x offset:g_parallel_q8_x_offset atIndex:2];
[enc setBuffer:g_parallel_q8_out offset:g_parallel_q8_out_offset atIndex:3];
}
[enc setThreadgroupMemoryLength:32u * 2u * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(
((NSUInteger)g_parallel_q8_args.ne0 + 1u) / 2u, 1, 1)
threadsPerThreadgroup:MTLSizeMake(32, 4, 1)];
}
static int ds4_gpu_parallel_q8_matvec_encode_pending(
id<MTLCommandBuffer> cb,
id<MTLBuffer> routed_mid) {
if (!g_parallel_q8_pending || g_parallel_ffn_mode != 2 ||
g_parallel_ffn_stage != 0 || !g_batch_encoder_concurrent ||
!g_batch_cb || cb != g_batch_cb) {
return 0;
}
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
if (!enc || enc.dispatchType != MTLDispatchTypeConcurrent) return 0;
[enc setComputePipelineState:g_parallel_gate_up_pipeline];
if (g_parallel_split_active) {
[enc setBytes:&g_parallel_gate_up_args
length:sizeof(g_parallel_gate_up_args)
atIndex:0];
[enc setBytes:&g_parallel_split_args
length:sizeof(g_parallel_split_args)
atIndex:1];
[enc setBuffer:g_parallel_gate_weight
offset:g_parallel_gate_weight_offset
atIndex:2];
[enc setBuffer:g_parallel_up_weight
offset:g_parallel_up_weight_offset
atIndex:3];
[enc setBuffer:g_parallel_gate_x
offset:g_parallel_gate_x_offset
atIndex:4];
[enc setBuffer:g_parallel_gate_out
offset:g_parallel_gate_out_offset
atIndex:5];
[enc setBuffer:g_parallel_up_out
offset:g_parallel_up_out_offset
atIndex:6];
[enc setBuffer:g_parallel_mid_out
offset:g_parallel_mid_out_offset
atIndex:7];
[enc setBytes:&g_parallel_gate_up_clamp
length:sizeof(g_parallel_gate_up_clamp)
atIndex:8];
[enc setBuffer:g_parallel_split_ids
offset:g_parallel_split_ids_offset
atIndex:9];
} else {
[enc setBytes:&g_parallel_gate_up_args
length:sizeof(g_parallel_gate_up_args)
atIndex:0];
[enc setBuffer:g_parallel_gate_weight
offset:g_parallel_gate_weight_offset
atIndex:1];
[enc setBuffer:g_parallel_up_weight
offset:g_parallel_up_weight_offset
atIndex:2];
[enc setBuffer:g_parallel_gate_x
offset:g_parallel_gate_x_offset
atIndex:3];
[enc setBuffer:g_parallel_gate_out
offset:g_parallel_gate_out_offset
atIndex:4];
[enc setBuffer:g_parallel_up_out
offset:g_parallel_up_out_offset
atIndex:5];
[enc setBuffer:g_parallel_mid_out
offset:g_parallel_mid_out_offset
atIndex:6];
[enc setBytes:&g_parallel_gate_up_clamp
length:sizeof(g_parallel_gate_up_clamp)
atIndex:7];
}
[enc setThreadgroupMemoryLength:2u * g_parallel_gate_up_smem atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(
((NSUInteger)g_parallel_gate_up_args.ne0 +
g_parallel_gate_up_nr0 - 1u) / g_parallel_gate_up_nr0,
1, 1)
threadsPerThreadgroup:MTLSizeMake(32, g_parallel_gate_up_nsg, 1)];
g_parallel_ffn_stage = 1;
/* Metal defines either barrier form as an execution barrier for every
* earlier dispatch in this concurrent encoder. The resource list narrows
* visibility, not completion. This is therefore the explicit level break:
* routed pair-SwiGLU and shared gate/up must finish before
* the routed Q2 sum6 consumer can start. */
id<MTLResource> barrier_resources[2] = {
routed_mid,
g_parallel_mid_out,
};
[enc memoryBarrierWithResources:barrier_resources count:2u];
return 1;
}
static int ds4_gpu_parallel_ffn_encode_second_stage(
id<MTLCommandBuffer> cb) {
if (!g_parallel_q8_pending || g_parallel_ffn_mode != 2 ||
g_parallel_ffn_stage != 1 || !g_batch_encoder_concurrent ||
!g_batch_cb || cb != g_batch_cb) {
return 0;
}
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
if (!enc || enc.dispatchType != MTLDispatchTypeConcurrent) return 0;
ds4_gpu_encode_parallel_q8_down(enc);
/* No final memory barrier is required: finish() ends this concurrent
* encoder before the serial HC encoder is created, and a Metal command
* buffer is an ordered list of command encoders. */
g_parallel_ffn_stage = 2;
g_parallel_q8_encoded = YES;
return 1;
}
int ds4_gpu_parallel_ffn_finish(void) {
const int completed =
g_parallel_q8_pending && g_parallel_q8_encoded &&
g_parallel_ffn_stage == 2 && g_batch_encoder_concurrent;
/* Reset even on an incomplete join. This makes the error path just as
* safe and idempotent as an explicit abort. */
ds4_gpu_parallel_ffn_reset_state(YES);
return completed;
}
static int ds4_gpu_stream_expert_cache_wait_inflight(const char *label) {
const char *what = label ? label : "streaming expert cache in-flight";
if (g_batch_cb && ds4_gpu_flush_commands() == 0) return 0;
if ([g_pending_cbs count] != 0 &&
ds4_gpu_wait_pending_command_buffers(what) == 0) {
return 0;
}
return 1;
}
int ds4_gpu_signal_selected_readback_ready(uint64_t *event_value) {
if (!event_value) return 0;
*event_value = 0;
if (!g_initialized && !ds4_gpu_init()) return 0;
ds4_gpu_parallel_ffn_reset_state(YES);
if (!g_batch_cb) return 0;
if (@available(macOS 12.0, *)) {
if (!g_selected_readback_event) {
g_selected_readback_event = [g_device newSharedEvent];
if (!g_selected_readback_event) {
fprintf(stderr, "ds4: failed to create Metal shared event for selected-id overlap\n");
return 0;
}
}
ds4_gpu_close_batch_encoder();
const uint64_t value = ++g_selected_readback_event_value;
[g_batch_cb encodeSignalEvent:g_selected_readback_event value:value];
g_batch_has_work = YES;
*event_value = value;
return 1;
}
fprintf(stderr, "ds4: selected-id overlap requires MTLSharedEvent support\n");
return 0;
}
int ds4_gpu_commit_and_wait_selected_readback(uint64_t event_value, const char *label) {
if (!g_initialized && !ds4_gpu_init()) return 0;
ds4_gpu_parallel_ffn_reset_state(YES);
if (!g_batch_cb || event_value == 0) return 0;
if (@available(macOS 12.0, *)) {
if (!g_selected_readback_event) return 0;
ds4_gpu_close_batch_encoder();
id<MTLCommandBuffer> cb = g_batch_cb;
g_batch_cb = nil;
g_batch_has_work = NO;
[cb commit];
ds4_gpu_stream_expert_cache_note_batch_committed();
const char *what = label ? label : "selected-id overlap";
const BOOL signaled =
[g_selected_readback_event waitUntilSignaledValue:event_value timeoutMS:60000];
[g_pending_cbs addObject:cb];
if (!signaled) {
fprintf(stderr, "ds4: timeout waiting for Metal shared event in %s\n", what);
(void)ds4_gpu_wait_pending_command_buffers(what);
[g_transient_buffers removeAllObjects];
return 0;
}
if (cb.status == MTLCommandBufferStatusError) {
fprintf(stderr, "ds4: Metal %s failed: %s\n",
what,
[[cb.error localizedDescription] UTF8String]);
(void)ds4_gpu_wait_pending_command_buffers(what);
[g_transient_buffers removeAllObjects];
return 0;
}
g_batch_cb = ds4_gpu_new_command_buffer();
g_batch_has_work = NO;
if (g_batch_cb) ds4_gpu_stream_expert_cache_note_batch_created();
if (!g_batch_cb) {
(void)ds4_gpu_wait_pending_command_buffers(what);
[g_transient_buffers removeAllObjects];
return 0;
}
return 1;
}
fprintf(stderr, "ds4: selected-id overlap requires MTLSharedEvent support\n");
return 0;
}
/*
* Tensor-parallel gates.
*
* A TP gate is a mid-command-stream rendezvous with the peer machine: the
* kernels ahead of the gate leave a partial block output in a slab slot,
* the GPU signals g_tp_gpu_event, and the pre-encoded combine kernel waits
* on g_tp_cpu_event. A dedicated service thread bridges the two: it spins
* until the GPU reaches the gate, runs the transport exchange (RDMA WRITE
* plus flag poll, or a TCP write/read pair — behind the callback), and
* CPU-signals the release. On exchange failure the release is signaled
* anyway so the GPU never deadlocks; the failure latches in g_tp_failed
* and the eval aborts at the next command-buffer boundary.
*
* Gate sequence values increase monotonically per encoded gate. Both ranks
* encode the identical graph, so the values agree by construction and slots
* never need resetting between tokens.
*/
typedef struct {
uint32_t layer;
uint32_t gate;
uint32_t rows; /* 0 = row gate; >0 = verify-block batch gate */
uint32_t batch_slot; /* slab batch slot of a verify-block gate */
uint32_t event_arrival;
uint64_t seq;
const volatile uint32_t *check; /* batch gate payload behind a checked flag */
uint32_t check_words;
/* Big batch gates (prefill): exchange big_bytes from big_out into
* big_in directly (CPU-visible bounce buffers), bypassing the slab. */
const void *big_out;
void *big_in;
uint64_t big_bytes;
uint32_t poll; /* release through the poll region */
uint32_t spin; /* inline gate: release through the coherent spin word */
} ds4_gpu_tp_request;
enum { DS4_GPU_TP_QUEUE = 1024 };
static id<MTLSharedEvent> g_tp_gpu_event; /* GPU -> service thread */
static id<MTLSharedEvent> g_tp_cpu_event; /* service thread -> GPU */
/* Batch (verify-block) gates run on their own sequence space and release
* event: the row-gate seq feeds the RDMA pre-posted recv accounting, which
* requires consecutive values, and a shared release event would make a
* small batch value satisfy waits armed against the larger row seq. */
static id<MTLSharedEvent> g_tp_batch_gpu_event;
static id<MTLSharedEvent> g_tp_batch_cpu_event;
static uint64_t g_tp_batch_seq;
/* Batch flag values are tagged so a stale row-gate seq in the reused FFN
* flag word can never satisfy a batch arrival spin (and vice versa). */
#define DS4_TP_BATCH_FLAG_TAG 0x80000000u
/* Expert-ownership split parameters for routed kernels. World 1 means TP is
* not bound; world 2 assigns each rank one contiguous expert range. */
static int32_t g_tp_split_rank;
static int32_t g_tp_split_world = 1;
static int32_t g_tp_session_batch_mode;
static int ds4_gpu_tp_world_is_two(void) {
return g_tp_split_world == 2;
}
/* Decode balances the routed experts by list position instead of by id
* range: every expert is bound and the kernels mask nothing. */
static int g_tp_full_expert_bind;
void ds4_gpu_tp_full_expert_bind(int on) { g_tp_full_expert_bind = on; }
/* Return the contiguous routed-expert range backed by this process. Rank 1
* owns the high range and receives any odd-count remainder. */
static void ds4_gpu_tp_expert_range(uint32_t n_total_expert,
uint32_t *first_expert,
uint32_t *n_expert) {
*first_expert = 0;
*n_expert = n_total_expert;
if (g_tp_split_world != 2 || g_tp_full_expert_bind) return;
const uint32_t low_experts = n_total_expert / 2u;
if (g_tp_split_rank == 1) {
*first_expert = low_experts;
*n_expert = n_total_expert - low_experts;
} else {
*n_expert = low_experts;
}
}
/* Attention head split for GLM batch prefill: each rank computes a
* contiguous half of the heads in the qk-low / attention-lora /
* value-project batch kernels; the caller zeroes the unowned head range
* of the heads buffer and combines the attn-output partials over the
* TP big-gate exchange. */
static int32_t g_tp_attn_head_split;
void ds4_gpu_tp_set_attn_head_split(int enabled) {
g_tp_attn_head_split = enabled ? 1 : 0;
}
static void ds4_gpu_tp_attn_head_range(uint32_t n_head,
uint32_t group,
uint32_t *head_base,
uint32_t *head_count) {
*head_base = 0;
*head_count = n_head;
if (!g_tp_attn_head_split || g_tp_split_world != 2) return;
const uint32_t half = n_head / 2u;
if (half == 0u || (half % group) != 0u || (n_head % 2u) != 0u) return;
*head_count = half;
*head_base = g_tp_split_rank == 1 ? half : 0u;
}
/* Flag gates (DS4_TP_FLAG_GATES): the GPU publishes gate arrival by storing
* the sequence number into a slab word instead of signaling the shared
* event; the service thread spin-reads it from shared memory, which wakes
* hundreds of microseconds earlier than signaledValue polling. The
* CPU->GPU release direction stays on the shared event. */
static bool g_tp_flag_gates;
/* Poll gates (default with flag gates): the GPU waits for a gate release by
* probing fresh cache lines at the head of a new command buffer instead of
* parking on g_tp_cpu_event. See kernel_dsv4_tp_poll_release. */
#define DS4_TP_POLL_LINES 8192u
#define DS4_TP_POLL_LINE_BYTES 128u
#define DS4_TP_POLL_MAX_SLOTS 1024u
#define DS4_TP_POLL_RING 16u /* regions cycled per gate so no line is re-read while cached */
static bool g_tp_poll_gates;
static id<MTLBuffer> g_tp_poll_buffer; /* release region + status words */
static volatile uint32_t *g_tp_poll_region; /* CPU view of the release lines */
static volatile uint32_t *g_tp_poll_status; /* per-slot poll result, GPU-written */
/* Inline decode gates: the partial and the flag are published with
* system-coherent stores and kernel_dsv4_tp_release_wait spins inside the open
* command buffer on [0] of this buffer ([1] spin count of the last wait, [2]
* timeouts), so a token ends no command buffer per gate.
* DS4_TP_DISABLE_DECODE_POLL_GATES keeps the flushing poll gates;
* DS4_TP_INLINE_SPIN_ITERS bounds the spin (default 8M). */
static id<MTLBuffer> g_tp_release_buffer;
static volatile uint32_t *g_tp_release_ptr;
static uint32_t g_tp_spin_max_iters;
static uint64_t g_tp_stat_spin_iters;
static int g_tp_poll_prev_valid;
static uint32_t g_tp_poll_prev_slot;
static uint64_t g_tp_poll_prev_seq;
/* Gate-time prefetch: the poll wait leaves the memory bus idle, so the next
* phase's weights are streamed into the cache first (see the notebook). */
#define DS4_TP_PREFETCH_MAX 8u
#define DS4_TP_PREFETCH_BYTES_PER_US 450000ull /* ~450 GB/s streaming */
#define DS4_TP_PREFETCH_MARGIN_US 8.0 /* flush + wire + pickup */
static double g_tp_exchange_ewma_us[2]; /* per gate kind, always on */
typedef struct { uint64_t offset; uint64_t bytes; } ds4_tp_prefetch_range;
static ds4_tp_prefetch_range g_tp_prefetch_plan[2][DS4_TP_PREFETCH_MAX]; /* per gate kind */
/* Gate flag folded into the payload producer (see
* kernel_dsv4_add2_f32_tp_flag_checked): the producer publishes the checked
* flag for the sequence number the next gate encode will use, and the gate
* encode skips its own flag kernel. */
static uint64_t g_tp_flag_prepublished_seq;
static id<MTLBuffer> g_tp_fold_ctl;
static uint32_t g_tp_prefetch_count[2];
static const void *g_tp_prefetch_map;
static uint64_t g_tp_prefetch_map_size;
static id<MTLBuffer> g_tp_prefetch_scratch;
static bool g_tp_gate_prefetch;
static uint64_t g_tp_stat_poll_lines; /* sum of poll hit lines (profile) */
static uint64_t g_tp_stat_poll_max_line;
static uint64_t g_tp_stat_poll_count;
static uint64_t g_tp_stat_encode_lead; /* sum of (encoded seq - gate seq) at arrival */
static double g_tp_stat_release_ms; /* time spent writing poll releases */
static double g_tp_stat_cbwait_ms; /* flag seen -> payload verified in memory */
static uint64_t g_tp_out_off; /* slab offset of out slot 0 */
static uint64_t g_tp_vec_bytes; /* partial vector bytes per slot */
static id<MTLBuffer> g_tp_check_buffer; /* per-slot payload checksums */
static volatile uint32_t *g_tp_check_words;
static volatile uint8_t *g_tp_slab_cpu; /* CPU view of the slab base */
/* Bound on gate command buffers in flight: the GPU only needs a few
* segments of runway and a deep backlog costs command-buffer memory. */
#define DS4_TP_POLL_MAX_INFLIGHT 32u
static id<MTLBuffer> g_tp_slab_buffer;
static NSUInteger g_tp_slab_buffer_off;
static volatile uint32_t *g_tp_gpu_flags; /* CPU view of the flag words */
static uint64_t g_tp_gpu_flags_off;
static uint64_t g_tp_seq;
static ds4_gpu_tp_exchange_fn g_tp_exchange_fn;
static ds4_gpu_tp_batch_exchange_fn g_tp_batch_exchange_fn;
static ds4_gpu_tp_big_exchange_fn g_tp_big_exchange_fn;
void ds4_gpu_tp_set_big_exchange(ds4_gpu_tp_big_exchange_fn fn) {
g_tp_big_exchange_fn = fn;
}
static void *g_tp_exchange_ud;
static pthread_t g_tp_thread;
static int g_tp_thread_running;
static int g_tp_shutdown;
static int g_tp_failed_flag;
static pthread_mutex_t g_tp_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t g_tp_cond = PTHREAD_COND_INITIALIZER;
static ds4_gpu_tp_request g_tp_queue[DS4_GPU_TP_QUEUE];
static uint32_t g_tp_queue_head;
static uint32_t g_tp_queue_count;
static uint64_t g_tp_stat_gates;
static uint64_t g_tp_stat_batch_gates; /* verify-block batch gates */
static double g_tp_stat_batch_gpu_wait_ms; /* arrival spin (GPU compute + park) */
static double g_tp_stat_batch_exchange_ms; /* exchange callback (peer wait + wire) */
static double g_tp_stat_batch_release_ms; /* exchange end -> cpu event signaled */
static double g_tp_stat_gpu_wait_ms;
static double g_tp_stat_exchange_ms;
enum { DS4_GPU_TP_GATE_ATTN = 0, DS4_GPU_TP_GATE_FFN = 1,
DS4_GPU_TP_GATES_PER_LAYER = 2 };
static uint64_t g_tp_stat_gate_count[DS4_GPU_TP_GATES_PER_LAYER];
static double g_tp_stat_gate_gpu_wait_ms[DS4_GPU_TP_GATES_PER_LAYER];
static double g_tp_stat_gate_exchange_ms[DS4_GPU_TP_GATES_PER_LAYER];
/* GPU keep-alive (see kernel_dsv4_tp_keepalive): its own queue and thread,
* alive exactly as long as the TP gate machinery. */
static id<MTLCommandQueue> g_tp_keepalive_queue;
static id<MTLBuffer> g_tp_keepalive_buffer;
static pthread_t g_tp_keepalive_thread;
static int g_tp_keepalive_running;
/* Nonzero while a verify block runs: the GPU is genuinely busy
* there, so the keep-alive is a pure parasite (~2.3ms per 5-row block
* measured against the single-machine verify). */
static volatile int g_tp_keepalive_paused;
void ds4_gpu_tp_keepalive_pause(int paused) {
g_tp_keepalive_paused = paused;
}
void ds4_gpu_tp_set_session_batch_mode(int enabled) {
g_tp_session_batch_mode = enabled ? 1 : 0;
}
int ds4_gpu_tp_decode_split_flush_safe(void) {
return g_tp_thread_running && g_tp_flag_gates &&
!g_tp_session_batch_mode;
}
int ds4_gpu_tp_gate_prefetch_plan(uint32_t gate,
const void *model_map, uint64_t model_size,
const uint64_t *offsets, const uint64_t *bytes,
uint32_t count) {
if (gate >= 2u) return 0;
if (!g_tp_gate_prefetch || !g_tp_poll_gates || !model_map) {
g_tp_prefetch_count[gate] = 0;
return 1;
}
if (count > DS4_TP_PREFETCH_MAX) count = DS4_TP_PREFETCH_MAX;
uint32_t n = 0;
for (uint32_t i = 0; i < count; i++) {
if (bytes[i] == 0 || offsets[i] > model_size ||
bytes[i] > model_size - offsets[i]) continue;
g_tp_prefetch_plan[gate][n].offset = offsets[i];
g_tp_prefetch_plan[gate][n].bytes = bytes[i];
n++;
}
g_tp_prefetch_map = model_map;
g_tp_prefetch_map_size = model_size;
g_tp_prefetch_count[gate] = n;
return 1;
}
/* Encode the touch dispatches of one gate's plan into the open batch. */
static uint64_t ds4_gpu_buffer_address(id<MTLBuffer> buffer, NSUInteger inner);
/* One dispatch of kernel_touch_u8_stride_table over up to DS4_TP_PREFETCH_MAX
* model ranges (priority order, cut at `budget` bytes), 128 B per thread. */
static void ds4_gpu_encode_touch_table(id<MTLComputeCommandEncoder> enc,
id<MTLComputePipelineState> touch,
const ds4_tp_prefetch_range *plan,
uint32_t n,
uint64_t budget) {
const uint64_t stride = 128;
uint64_t addr[DS4_TP_PREFETCH_MAX] = {0};
uint32_t lines[DS4_TP_PREFETCH_MAX] = {0};
id<MTLResource> views[DS4_TP_PREFETCH_MAX];
uint32_t count = 0;
uint64_t total_lines = 0;
for (uint32_t i = 0; i < n && budget != 0u; i++) {
uint64_t inner = 0;
uint64_t bytes = plan[i].bytes;
if (bytes > budget) bytes = budget;
budget -= bytes;
if (bytes == 0u) continue;
id<MTLBuffer> src = ds4_gpu_wrap_model_range(
g_tp_prefetch_map, g_tp_prefetch_map_size, plan[i].offset, bytes, &inner);
if (!src) continue;
const uint64_t range_lines = (bytes + stride - 1u) / stride;
if (total_lines + range_lines > (1u << 20)) break;
const uint64_t address = ds4_gpu_buffer_address(src, (NSUInteger)inner);
if (address == 0u) continue;
addr[count] = address;
lines[count] = (uint32_t)range_lines;
views[count] = src;
total_lines += range_lines;
count++;
}
if (count == 0u) return;
[enc setComputePipelineState:touch];
[enc useResources:views count:count usage:MTLResourceUsageRead];
[enc setBytes:addr length:sizeof(addr) atIndex:0];
[enc setBytes:lines length:sizeof(lines) atIndex:1];
[enc setBytes:&count length:sizeof(count) atIndex:2];
[enc setBuffer:g_tp_prefetch_scratch offset:0 atIndex:3];
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)((total_lines + 255u) / 256u), 1, 1)
threadsPerThreadgroup:MTLSizeMake(256, 1, 1)];
}
static int ds4_gpu_tp_encode_gate_prefetch(uint32_t gate) {
const uint32_t n = g_tp_prefetch_count[gate];
g_tp_prefetch_count[gate] = 0;
if (n == 0 || !g_batch_cb) return 1;
if (!g_tp_prefetch_scratch) {
g_tp_prefetch_scratch =
[g_device newBufferWithLength:(NSUInteger)(1u << 20)
options:MTLResourceStorageModePrivate];
if (!g_tp_prefetch_scratch) return 1;
}
id<MTLComputePipelineState> touch = ds4_gpu_get_pipeline("kernel_touch_u8_stride_table");
if (!touch) return 1;
/* Budget: what fits in this rank's typical wait at this gate kind. The
* first observations use a small default so a slow rank never delays
* its own release pickup. */
const double wait_us = g_tp_exchange_ewma_us[gate];
/* Diagnostics: DS4_TP_PREFETCH_MARGIN_US and DS4_TP_PREFETCH_BYTES_PER_US
* override the sizing constants for experiments. */
static double margin_us = -1.0;
static double bytes_per_us = -1.0;
if (margin_us < 0.0) {
margin_us = (double)ds4_gpu_env_u64("DS4_TP_PREFETCH_MARGIN_US",
(uint64_t)DS4_TP_PREFETCH_MARGIN_US, 0u, 1000u);
bytes_per_us = (double)ds4_gpu_env_u64("DS4_TP_PREFETCH_BYTES_PER_US",
DS4_TP_PREFETCH_BYTES_PER_US, 1000u, 10000000ull);
}
uint64_t budget = wait_us > margin_us
? (uint64_t)((wait_us - margin_us) * bytes_per_us)
: 0u;
if (wait_us == 0.0) budget = 4u << 20;
if (budget == 0u) return 1;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(g_batch_cb);
ds4_gpu_encode_touch_table(enc, touch, g_tp_prefetch_plan[gate], n, budget);
ds4_gpu_end_compute_encoder(g_batch_cb, enc);
return 1;
}
static uint32_t ds4_gpu_tp_keepalive_tgs_from_env(void) {
uint32_t ka_tgs = 1;
const char *tgs_env = getenv("DS4_TP_KEEPALIVE_TGS");
if (tgs_env) {
int v = atoi(tgs_env);
if (v > 0 && v <= 2048) ka_tgs = (uint32_t)v;
}
return ka_tgs;
}
static void *ds4_gpu_tp_keepalive_thread(void *arg) {
(void)arg;
/* Swept on the M5 Max pair: too few iterations lets clocks sag. Current
* TP split-resident Flash runs show 1.2M is a small Q4/Q2 decode win over
* 800k, while two threadgroups waste work. */
uint32_t iters = 1200000;
const char *env = getenv("DS4_TP_KEEPALIVE_ITERS");
if (env) iters = (uint32_t)atoi(env);
/* One ALU-only threadgroup keeps the GPU from power-gating but does
* not push the frequency governor; solo-vs-engine kernel gaps
* (~1.7x) suggest decode runs well below max clocks. More TGs raise
* apparent utilization without eating memory bandwidth. */
uint32_t ka_tgs = ds4_gpu_tp_keepalive_tgs_from_env();
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_pipeline("kernel_dsv4_tp_keepalive");
if (!pipeline) {
fprintf(stderr, "ds4: TP keep-alive pipeline missing\n");
return NULL;
}
while (!g_tp_shutdown) {
if (g_tp_keepalive_paused) {
usleep(200);
continue;
}
@autoreleasepool {
id<MTLCommandBuffer> cb = [g_tp_keepalive_queue commandBuffer];
id<MTLComputeCommandEncoder> enc = [cb computeCommandEncoder];
[enc setComputePipelineState:pipeline];
[enc setBuffer:g_tp_keepalive_buffer offset:0 atIndex:0];
[enc setBytes:&iters length:sizeof(iters) atIndex:1];
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)ka_tgs, 1, 1)
threadsPerThreadgroup:MTLSizeMake(256, 1, 1)];
[enc endEncoding];
[cb commit];
[cb waitUntilCompleted];
}
}
return NULL;
}
static void *ds4_gpu_tp_service_thread(void *arg) {
(void)arg;
const bool profile = getenv("DS4_TP_GATE_PROFILE") != NULL;
while (1) {
pthread_mutex_lock(&g_tp_mutex);
while (g_tp_queue_count == 0 && !g_tp_shutdown)
pthread_cond_wait(&g_tp_cond, &g_tp_mutex);
if (g_tp_shutdown && g_tp_queue_count == 0) {
pthread_mutex_unlock(&g_tp_mutex);
break;
}
ds4_gpu_tp_request req = g_tp_queue[g_tp_queue_head];
g_tp_queue_head = (g_tp_queue_head + 1) % DS4_GPU_TP_QUEUE;
g_tp_queue_count--;
pthread_mutex_unlock(&g_tp_mutex);
/* Wait for the GPU to reach this gate. Tight spin: gate arrival is
* on the decode critical path and normally tens of microseconds
* out; yielding here measurably delays the release wake-up. */
const double t0 = profile ? ds4_gpu_now_ms() : 0.0;
uint32_t spins = 0;
if (req.big_bytes > 0) {
/* Big gates always signal arrival through the batch shared
* event (see ds4_gpu_tp_big_gate_kick): the event completion
* semantics are what guarantee the bounce payload is visible
* before the exchange reads it. */
/* A big-gate exchange takes milliseconds, so a tight spin on
* the shared event buys nothing; each signaledValue read is a
* driver round trip. Sleep-poll unless asked otherwise
* (DS4_TP_BIG_GATE_SPIN_US=0 restores the tight spin). */
static int64_t big_spin_us = -1;
if (big_spin_us < 0)
big_spin_us = (int64_t)ds4_gpu_env_u64("DS4_TP_BIG_GATE_SPIN_US", 20u, 0u, 5000u);
while (g_tp_batch_gpu_event.signaledValue < req.seq) {
if (g_tp_shutdown) break;
if (big_spin_us > 0) usleep((useconds_t)big_spin_us);
else if (++spins > (1u << 16)) sched_yield();
}
} else if (!req.event_arrival) {
const uint32_t slot =
req.layer * DS4_GPU_TP_GATES_PER_LAYER + req.gate;
uint32_t want = (uint32_t)req.seq;
if (req.rows > 0)
want = DS4_TP_BATCH_FLAG_TAG | (uint32_t)req.seq;
while (__atomic_load_n(&g_tp_gpu_flags[slot], __ATOMIC_ACQUIRE) != want) {
if (g_tp_shutdown) break;
if (++spins > (1u << 20)) {
sched_yield();
spins = 0;
}
}
} else if (req.rows > 0) {
while (g_tp_batch_gpu_event.signaledValue < req.seq) {
if (g_tp_shutdown) break;
if (++spins > (1u << 16)) sched_yield();
}
} else {
while (g_tp_gpu_event.signaledValue < req.seq) {
if (g_tp_shutdown) break;
if (++spins > (1u << 16)) sched_yield();
}
}
const double t1 = ds4_gpu_now_ms();
if (profile) g_tp_stat_encode_lead += g_tp_seq - req.seq;
if (req.poll && g_tp_check_words && g_tp_slab_cpu && (req.rows == 0 || req.check) &&
req.big_bytes == 0) {
/* The flag line may land before the payload lines: verify the
* partial against the checksum the flag kernel published. */
const uint32_t slot = req.layer * DS4_GPU_TP_GATES_PER_LAYER + req.gate;
const uint32_t words = req.rows ? req.check_words : (uint32_t)(g_tp_vec_bytes / 4u);
const volatile uint32_t *payload = req.rows ? req.check :
(const volatile uint32_t *)(g_tp_slab_cpu + g_tp_out_off + (uint64_t)slot * g_tp_vec_bytes);
const uint32_t mix = (req.rows ? DS4_TP_BATCH_FLAG_TAG | (uint32_t)req.seq :
(uint32_t)req.seq) * 0x9E3779B9u;
uint64_t tries = 0;
for (;;) {
const uint32_t want = __atomic_load_n(&g_tp_check_words[slot], __ATOMIC_ACQUIRE) ^ mix;
uint32_t sum = 0;
for (uint32_t i = 0; i < words; i++) sum += payload[i];
if (sum == want) break;
if (++tries > 20000000ull || g_tp_shutdown) {
if (!g_tp_failed_flag)
fprintf(stderr, "ds4: TP gate payload never matched its checksum (seq %llu)\n",
(unsigned long long)req.seq);
g_tp_failed_flag = 1;
break;
}
}
if (profile) g_tp_stat_cbwait_ms += ds4_gpu_now_ms() - t1;
}
int ok = 0;
if (!g_tp_shutdown && !g_tp_failed_flag) {
if (req.big_bytes > 0) {
if (g_tp_big_exchange_fn)
ok = g_tp_big_exchange_fn(g_tp_exchange_ud, req.layer,
req.seq, req.big_out,
req.big_in, req.big_bytes);
} else if (req.rows > 0) {
const double bx0 = profile ? ds4_gpu_now_ms() : 0.0;
if (g_tp_batch_exchange_fn)
ok = g_tp_batch_exchange_fn(g_tp_exchange_ud, req.batch_slot,
req.rows, req.seq);
if (profile) {
g_tp_stat_batch_gates++;
g_tp_stat_batch_gpu_wait_ms += t1 - t0;
g_tp_stat_batch_exchange_ms += ds4_gpu_now_ms() - bx0;
}
} else if (g_tp_exchange_fn) {
ok = g_tp_exchange_fn(g_tp_exchange_ud, req.layer, req.gate,
req.seq);
}
}
if (!ok && !g_tp_shutdown) {
if (!g_tp_failed_flag)
fprintf(stderr, "ds4: TP gate exchange failed (layer %u gate %u seq %llu)\n",
req.layer, req.gate, (unsigned long long)req.seq);
g_tp_failed_flag = 1;
}
const double t_ex = ds4_gpu_now_ms();
if (req.rows == 0 && req.big_bytes == 0 && req.gate < 2u) {
/* Sized the gate-time prefetch: how long this rank typically
* waits for the peer at this gate kind (flag seen -> exchange done). */
const double ex_us = (t_ex - t1) * 1000.0;
g_tp_exchange_ewma_us[req.gate] =
g_tp_exchange_ewma_us[req.gate] == 0.0 ? ex_us :
0.9 * g_tp_exchange_ewma_us[req.gate] + 0.1 * ex_us;
}
/* Release the GPU even on failure so end_commands can drain. */
if (req.spin && g_tp_release_ptr) {
const uint32_t prev_spin = g_tp_release_ptr[1];
__atomic_store_n(&g_tp_release_ptr[0], (uint32_t)req.seq, __ATOMIC_SEQ_CST);
/* a timed-out wait opened its gate without the peer partial */
if (g_tp_release_ptr[2] != 0 && !g_tp_shutdown) {
if (!g_tp_failed_flag)
fprintf(stderr, "ds4: TP release spin timed out before seq %llu (%u gates); failing the eval\n",
(unsigned long long)req.seq, g_tp_release_ptr[2]);
g_tp_failed_flag = 1;
}
if (profile) g_tp_stat_spin_iters += prev_spin;
} else if (req.rows > 0 && !req.poll) {
const double br0 = profile ? ds4_gpu_now_ms() : 0.0;
g_tp_batch_cpu_event.signaledValue = req.seq;
if (profile) g_tp_stat_batch_release_ms += ds4_gpu_now_ms() - br0;
} else if (req.poll) {
/* The flag we just consumed was published by the command buffer
* that ran the previous poll, so that poll's result is visible:
* a timeout there means the GPU has already run on stale data. */
const uint32_t slot = req.layer * DS4_GPU_TP_GATES_PER_LAYER + req.gate;
if (profile && g_tp_poll_prev_valid) {
const uint32_t hit = g_tp_poll_status[g_tp_poll_prev_slot * 2u];
if (hit != 0xffffffffu) {
g_tp_stat_poll_lines += hit;
if (hit > g_tp_stat_poll_max_line) g_tp_stat_poll_max_line = hit;
g_tp_stat_poll_count++;
}
}
if (g_tp_poll_prev_valid &&
g_tp_poll_status[g_tp_poll_prev_slot * 2u] == 0xffffffffu &&
!g_tp_shutdown) {
if (!g_tp_failed_flag)
fprintf(stderr,
"ds4: TP poll gate timed out (seq %llu, slot %u)\n",
(unsigned long long)g_tp_poll_prev_seq,
g_tp_poll_prev_slot);
g_tp_failed_flag = 1;
}
const uint32_t v = req.rows ? DS4_TP_BATCH_FLAG_TAG | (uint32_t)req.seq : (uint32_t)req.seq;
const double rel_t0 = profile ? ds4_gpu_now_ms() : 0.0;
volatile uint32_t *region =
g_tp_poll_region +
(size_t)(req.seq % DS4_TP_POLL_RING) *
DS4_TP_POLL_LINES * (DS4_TP_POLL_LINE_BYTES / 4u);
/* the poll kernel probes 32 lines per round and releases on any
* match: one marker per round is enough */
for (uint32_t j = 0; j < DS4_TP_POLL_LINES; j += 32u) {
__atomic_store_n(&region[j * (DS4_TP_POLL_LINE_BYTES / 4u)],
v, __ATOMIC_RELAXED);
}
__atomic_thread_fence(__ATOMIC_SEQ_CST);
if (profile) g_tp_stat_release_ms += ds4_gpu_now_ms() - rel_t0;
g_tp_poll_prev_valid = 1;
g_tp_poll_prev_slot = slot;
g_tp_poll_prev_seq = req.seq;
} else {
g_tp_cpu_event.signaledValue = req.seq;
}
if (!req.poll) g_tp_poll_prev_valid = 0;
if (profile) {
const double gpu_wait_ms = t1 - t0;
const double exchange_ms = t_ex - t1;
g_tp_stat_gpu_wait_ms += gpu_wait_ms;
g_tp_stat_exchange_ms += exchange_ms;
if (req.gate < DS4_GPU_TP_GATES_PER_LAYER) {
g_tp_stat_gate_count[req.gate]++;
g_tp_stat_gate_gpu_wait_ms[req.gate] += gpu_wait_ms;
g_tp_stat_gate_exchange_ms[req.gate] += exchange_ms;
}
if (++g_tp_stat_gates % 860 == 0) {
const double attn_count =
(double)(g_tp_stat_gate_count[DS4_GPU_TP_GATE_ATTN]
? g_tp_stat_gate_count[DS4_GPU_TP_GATE_ATTN] : 1u);
const double ffn_count =
(double)(g_tp_stat_gate_count[DS4_GPU_TP_GATE_FFN]
? g_tp_stat_gate_count[DS4_GPU_TP_GATE_FFN] : 1u);
fprintf(stderr,
"ds4: TP gates %llu: avg gpu-wait %.1f us, exchange %.1f us; "
"attn %.1f/%.1f us, ffn %.1f/%.1f us\n",
(unsigned long long)g_tp_stat_gates,
g_tp_stat_gpu_wait_ms / (double)g_tp_stat_gates * 1000.0,
g_tp_stat_exchange_ms / (double)g_tp_stat_gates * 1000.0,
g_tp_stat_gate_gpu_wait_ms[DS4_GPU_TP_GATE_ATTN] /
attn_count * 1000.0,
g_tp_stat_gate_exchange_ms[DS4_GPU_TP_GATE_ATTN] /
attn_count * 1000.0,
g_tp_stat_gate_gpu_wait_ms[DS4_GPU_TP_GATE_FFN] /
ffn_count * 1000.0,
g_tp_stat_gate_exchange_ms[DS4_GPU_TP_GATE_FFN] /
ffn_count * 1000.0);
if (g_tp_stat_batch_gates)
fprintf(stderr, "ds4: TP batch gates: %llu, arrival wait %.1f us, exchange %.1f us, release %.1f us per gate\n",
(unsigned long long)g_tp_stat_batch_gates,
g_tp_stat_batch_gpu_wait_ms * 1e3 / (double)g_tp_stat_batch_gates,
g_tp_stat_batch_exchange_ms * 1e3 / (double)g_tp_stat_batch_gates,
g_tp_stat_batch_release_ms * 1e3 / (double)g_tp_stat_batch_gates);
fprintf(stderr,
"ds4: TP gates: encode lead %.2f gates; verify %.1f us; release %.1f us; poll hit line avg %.1f max %llu over %llu\n",
(double)g_tp_stat_encode_lead / (double)g_tp_stat_gates,
g_tp_stat_cbwait_ms / (double)g_tp_stat_gates * 1000.0,
g_tp_stat_release_ms / (double)g_tp_stat_gates * 1000.0,
g_tp_stat_poll_count ?
(double)g_tp_stat_poll_lines / (double)g_tp_stat_poll_count : 0.0,
(unsigned long long)g_tp_stat_poll_max_line,
(unsigned long long)g_tp_stat_poll_count);
}
}
}
return NULL;
}
int ds4_gpu_tp_init(uint32_t rank,
ds4_gpu_tensor *slab, uint64_t gpu_flags_off,
uint64_t out_off, uint64_t vec_bytes,
ds4_gpu_tp_exchange_fn fn, void *ud) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (g_tp_thread_running || rank > 1) return 0;
g_tp_split_rank = (int32_t)rank;
g_tp_split_world = 2;
g_tp_slab_buffer = slab ? ds4_gpu_tensor_buffer(slab) : nil;
g_tp_slab_buffer_off = slab ? ds4_gpu_tensor_offset(slab) : 0;
g_tp_gpu_flags_off = gpu_flags_off;
g_tp_out_off = out_off;
g_tp_vec_bytes = vec_bytes;
g_tp_slab_cpu = slab ? (volatile uint8_t *)ds4_gpu_tensor_contents(slab) : NULL;
g_tp_check_buffer = [g_device newBufferWithLength:(NSUInteger)DS4_TP_POLL_MAX_SLOTS * sizeof(uint32_t)
options:MTLResourceStorageModeShared];
if (g_tp_check_buffer) {
memset([g_tp_check_buffer contents], 0, (size_t)DS4_TP_POLL_MAX_SLOTS * sizeof(uint32_t));
g_tp_check_words = (volatile uint32_t *)[g_tp_check_buffer contents];
} else {
g_tp_check_words = NULL;
}
g_tp_gpu_flags = slab ?
(volatile uint32_t *)((uint8_t *)ds4_gpu_tensor_contents(slab) + gpu_flags_off) : NULL;
/* Flag arrival is the default: the slab-word publish detects in ~1-3us
* where signaledValue polling costs 10-20, worth +2.3 t/s on the pair
* (A/B 2026-07-06, byte-identical output). DS4_TP_EVENT_GATES falls
* back to the shared-event arrival path. */
g_tp_flag_gates = g_tp_gpu_flags != NULL && getenv("DS4_TP_EVENT_GATES") == NULL;
g_tp_poll_gates = false;
g_tp_poll_buffer = nil;
g_tp_poll_region = NULL;
g_tp_poll_status = NULL;
g_tp_poll_prev_valid = 0;
g_tp_gate_prefetch = getenv("DS4_TP_DISABLE_GATE_PREFETCH") == NULL;
memset(g_tp_prefetch_count, 0, sizeof(g_tp_prefetch_count));
/* Default with flag gates: byte-identical to the event wait and about
* 20 us cheaper per gate on the M5 Max pair (A/B 2026-09-01, README
* ctx 128: 46.6-47.2 vs 43.6 t/s). DS4_TP_DISABLE_POLL_GATES restores
* the shared-event wait for diagnosis. */
if (g_tp_flag_gates && getenv("DS4_TP_DISABLE_POLL_GATES") == NULL) {
const NSUInteger region_bytes =
(NSUInteger)DS4_TP_POLL_LINES * DS4_TP_POLL_LINE_BYTES * DS4_TP_POLL_RING;
const NSUInteger status_bytes =
(NSUInteger)DS4_TP_POLL_MAX_SLOTS * 2u * sizeof(uint32_t);
g_tp_poll_buffer =
[g_device newBufferWithLength:region_bytes + status_bytes
options:MTLResourceStorageModeShared];
if (g_tp_poll_buffer) {
memset([g_tp_poll_buffer contents], 0, region_bytes + status_bytes);
g_tp_poll_region = (volatile uint32_t *)[g_tp_poll_buffer contents];
g_tp_poll_status =
(volatile uint32_t *)((uint8_t *)[g_tp_poll_buffer contents] + region_bytes);
g_tp_poll_gates = true;
} else {
fprintf(stderr, "ds4: TP poll gate buffer failed; using event gates\n");
}
}
g_tp_release_buffer = nil;
g_tp_release_ptr = NULL;
g_tp_stat_spin_iters = 0;
if (g_tp_flag_gates && getenv("DS4_TP_DISABLE_DECODE_POLL_GATES") == NULL &&
ds4_gpu_get_pipeline("kernel_dsv4_tp_release_wait") != nil &&
ds4_gpu_get_pipeline("kernel_dsv4_tp_flag_publish") != nil &&
ds4_gpu_get_pipeline("kernel_dsv4_tp_payload_publish") != nil) {
g_tp_release_buffer = [g_device newBufferWithLength:8 * sizeof(uint32_t)
options:MTLResourceStorageModeShared];
if (g_tp_release_buffer) {
memset(g_tp_release_buffer.contents, 0, 8 * sizeof(uint32_t));
g_tp_release_ptr = (volatile uint32_t *)g_tp_release_buffer.contents;
g_tp_spin_max_iters =
(uint32_t)ds4_gpu_env_u64("DS4_TP_INLINE_SPIN_ITERS", 8000000u, 100000u, 200000000u);
}
}
g_tp_gpu_event = [g_device newSharedEvent];
g_tp_cpu_event = [g_device newSharedEvent];
g_tp_batch_gpu_event = [g_device newSharedEvent];
g_tp_batch_cpu_event = [g_device newSharedEvent];
if (!g_tp_gpu_event || !g_tp_cpu_event ||
!g_tp_batch_gpu_event || !g_tp_batch_cpu_event) {
fprintf(stderr, "ds4: failed to create TP shared events\n");
return 0;
}
g_tp_exchange_fn = fn;
g_tp_exchange_ud = ud;
g_tp_seq = 0;
g_tp_batch_seq = 0;
g_tp_shutdown = 0;
g_tp_failed_flag = 0;
g_tp_queue_head = 0;
g_tp_queue_count = 0;
g_tp_stat_gates = 0;
g_tp_stat_gpu_wait_ms = 0.0;
g_tp_stat_exchange_ms = 0.0;
memset(g_tp_stat_gate_count, 0, sizeof(g_tp_stat_gate_count));
memset(g_tp_stat_gate_gpu_wait_ms, 0,
sizeof(g_tp_stat_gate_gpu_wait_ms));
memset(g_tp_stat_gate_exchange_ms, 0,
sizeof(g_tp_stat_gate_exchange_ms));
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_set_qos_class_np(&attr, QOS_CLASS_USER_INTERACTIVE, 0);
if (pthread_create(&g_tp_thread, &attr, ds4_gpu_tp_service_thread, NULL) != 0) {
pthread_attr_destroy(&attr);
fprintf(stderr, "ds4: failed to start TP gate service thread\n");
return 0;
}
pthread_attr_destroy(&attr);
g_tp_thread_running = 1;
/* The spin kernel keeps the GPU busy; the keep-alive kernels would
* timeslice against it. */
if (getenv("DS4_TP_NO_KEEPALIVE") == NULL && !ds4_gpu_tp_decode_inline_gates()) {
uint32_t ka_tgs = ds4_gpu_tp_keepalive_tgs_from_env();
g_tp_keepalive_queue = [g_device newCommandQueue];
g_tp_keepalive_buffer = [g_device newBufferWithLength:(NSUInteger)ka_tgs * 256u * sizeof(float)
options:MTLResourceStorageModeShared];
if (g_tp_keepalive_queue && g_tp_keepalive_buffer &&
pthread_create(&g_tp_keepalive_thread, NULL,
ds4_gpu_tp_keepalive_thread, NULL) == 0) {
g_tp_keepalive_running = 1;
} else {
fprintf(stderr, "ds4: TP keep-alive setup failed (continuing without)\n");
}
}
return 1;
}
void ds4_gpu_tp_shutdown(void) {
if (!g_tp_thread_running) return;
pthread_mutex_lock(&g_tp_mutex);
g_tp_shutdown = 1;
pthread_cond_broadcast(&g_tp_cond);
pthread_mutex_unlock(&g_tp_mutex);
pthread_join(g_tp_thread, NULL);
g_tp_thread_running = 0;
if (g_tp_keepalive_running) {
pthread_join(g_tp_keepalive_thread, NULL);
g_tp_keepalive_running = 0;
g_tp_keepalive_queue = nil;
g_tp_keepalive_buffer = nil;
}
g_tp_exchange_fn = NULL;
g_tp_batch_exchange_fn = NULL;
g_tp_exchange_ud = NULL;
g_tp_split_rank = 0;
g_tp_split_world = 1;
g_tp_session_batch_mode = 0;
g_tp_poll_gates = false;
g_tp_poll_buffer = nil;
g_tp_poll_region = NULL;
g_tp_poll_status = NULL;
g_tp_release_buffer = nil;
g_tp_release_ptr = NULL;
}
void ds4_gpu_tp_suspend_expert_sharding(int suspend) {
if (!g_tp_thread_running) return;
g_tp_split_world = suspend ? 1 : 2;
}
/* Fold request for producers that write the TP partial with a generic matvec
* (the attention output K-slice): the layer code registers (layer, gate)
* right before the producer call; the producer takes the request when its
* output is that slot and publishes the checked flag itself. */
static int g_tp_fold_req_active;
static uint32_t g_tp_fold_req_layer;
static uint32_t g_tp_fold_req_gate;
static bool ds4_gpu_tp_flag_fold_ok(uint32_t slot, uint64_t bytes) {
return g_initialized && g_batch_cb && g_tp_thread_running &&
!g_tp_session_batch_mode && g_tp_flag_gates && g_tp_poll_gates &&
!g_ssd_streaming_mode && g_tp_check_words != NULL &&
slot < DS4_TP_POLL_MAX_SLOTS && g_tp_vec_bytes == bytes &&
getenv("DS4_TP_DISABLE_FLAG_FOLD") == NULL;
}
static bool ds4_gpu_tp_fold_ctl_ready(void) {
if (g_tp_fold_ctl) return true;
g_tp_fold_ctl = [g_device newBufferWithLength:(NSUInteger)DS4_TP_POLL_MAX_SLOTS * 8u
options:MTLResourceStorageModeShared];
if (!g_tp_fold_ctl) return false;
memset(g_tp_fold_ctl.contents, 0, (size_t)DS4_TP_POLL_MAX_SLOTS * 8u);
return true;
}
int ds4_gpu_tp_decode_inline_gates(void) {
return g_tp_flag_gates && !g_tp_session_batch_mode && g_tp_release_ptr != NULL;
}
void ds4_gpu_tp_flag_fold_request(uint32_t layer, uint32_t gate) {
g_tp_fold_req_active = 0;
if (ds4_gpu_tp_decode_inline_gates()) return; /* the inline gate publishes coherently */
const uint32_t slot = layer * DS4_GPU_TP_GATES_PER_LAYER + gate;
static int debug_left = -1;
if (debug_left < 0) debug_left = getenv("DS4_TP_FOLD_DEBUG") ? 8 : 0;
if (debug_left > 0) {
debug_left--;
fprintf(stderr, "ds4: fold request slot %u ok=%d init=%d cb=%d thr=%d batch=%d flag=%d poll=%d ssd=%d chk=%d vec=%llu\n",
slot, (int)ds4_gpu_tp_flag_fold_ok(slot, g_tp_vec_bytes), (int)g_initialized,
(int)(g_batch_cb != nil), (int)g_tp_thread_running, (int)g_tp_session_batch_mode,
(int)g_tp_flag_gates, (int)g_tp_poll_gates, (int)g_ssd_streaming_mode,
(int)(g_tp_check_words != NULL), (unsigned long long)g_tp_vec_bytes);
}
if (!ds4_gpu_tp_flag_fold_ok(slot, g_tp_vec_bytes) || g_tp_vec_bytes == 0) return;
g_tp_fold_req_active = 1;
g_tp_fold_req_layer = layer;
g_tp_fold_req_gate = gate;
}
/* Returns the slot when `out` (out_bytes long) is the requested partial. */
static int ds4_gpu_tp_flag_fold_take(const ds4_gpu_tensor *out, uint64_t out_bytes,
uint32_t *slot_out, uint32_t *value_out) {
if (!g_tp_fold_req_active || !out) return 0;
const uint32_t slot = g_tp_fold_req_layer * DS4_GPU_TP_GATES_PER_LAYER + g_tp_fold_req_gate;
static int debug_left = -1;
if (debug_left < 0) debug_left = getenv("DS4_TP_FOLD_DEBUG") ? 8 : 0;
if (debug_left > 0) {
debug_left--;
fprintf(stderr, "ds4: fold take slot %u ok=%d buf_eq=%d off=%llu want=%llu bytes=%llu vec=%llu\n",
slot, (int)ds4_gpu_tp_flag_fold_ok(slot, out_bytes),
(int)(ds4_gpu_tensor_buffer(out) == g_tp_slab_buffer),
(unsigned long long)ds4_gpu_tensor_offset(out),
(unsigned long long)(g_tp_slab_buffer_off + g_tp_out_off + (uint64_t)slot * g_tp_vec_bytes),
(unsigned long long)out_bytes, (unsigned long long)g_tp_vec_bytes);
}
if (!ds4_gpu_tp_flag_fold_ok(slot, out_bytes) ||
ds4_gpu_tensor_buffer(out) != g_tp_slab_buffer ||
ds4_gpu_tensor_offset(out) !=
(uint64_t)(g_tp_slab_buffer_off + g_tp_out_off + (uint64_t)slot * g_tp_vec_bytes) ||
!ds4_gpu_tp_fold_ctl_ready()) {
return 0;
}
g_tp_fold_req_active = 0;
*slot_out = slot;
*value_out = (uint32_t)(g_tp_seq + 1u);
return 1;
}
int ds4_gpu_add_tensor_tp_flag(
ds4_gpu_tensor *out,
const ds4_gpu_tensor *a,
const ds4_gpu_tensor *b,
uint32_t n,
uint32_t layer,
uint32_t gate) {
const uint32_t slot = layer * DS4_GPU_TP_GATES_PER_LAYER + gate;
const bool fold =
g_initialized && g_batch_cb && g_tp_thread_running &&
!g_tp_session_batch_mode && g_tp_flag_gates && g_tp_poll_gates &&
!ds4_gpu_tp_decode_inline_gates() && /* the inline gate publishes coherently */
!g_ssd_streaming_mode && g_tp_check_words != NULL &&
slot < DS4_TP_POLL_MAX_SLOTS && g_tp_vec_bytes == (uint64_t)n * 4u &&
out && a && b && n != 0 && (n % 256u) == 0u &&
ds4_gpu_tensor_buffer(out) == g_tp_slab_buffer &&
ds4_gpu_tensor_offset(out) ==
(uint64_t)(g_tp_slab_buffer_off + g_tp_out_off + (uint64_t)slot * g_tp_vec_bytes) &&
getenv("DS4_TP_DISABLE_FLAG_FOLD") == NULL;
if (!fold) return ds4_gpu_add_tensor(out, a, b, n);
if (!ds4_gpu_tp_fold_ctl_ready()) return ds4_gpu_add_tensor(out, a, b, n);
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_pipeline("kernel_dsv4_add2_f32_tp_flag_checked");
if (!pipeline) return ds4_gpu_add_tensor(out, a, b, n);
id<MTLBuffer> abuf = ds4_gpu_tensor_buffer(a);
id<MTLBuffer> bbuf = ds4_gpu_tensor_buffer(b);
if (!abuf || !bbuf) return ds4_gpu_add_tensor(out, a, b, n);
const uint32_t value = (uint32_t)(g_tp_seq + 1u);
const uint32_t ntg = n / 256u;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(g_batch_cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&n length:sizeof(n) atIndex:0];
[enc setBuffer:abuf offset:ds4_gpu_tensor_offset(a) atIndex:1];
[enc setBuffer:bbuf offset:ds4_gpu_tensor_offset(b) atIndex:2];
[enc setBuffer:g_tp_slab_buffer offset:ds4_gpu_tensor_offset(out) atIndex:3];
[enc setBuffer:g_tp_slab_buffer
offset:(NSUInteger)(g_tp_slab_buffer_off + g_tp_gpu_flags_off + (uint64_t)slot * 4u)
atIndex:4];
[enc setBuffer:g_tp_check_buffer offset:(NSUInteger)slot * 4u atIndex:5];
[enc setBytes:&value length:sizeof(value) atIndex:6];
[enc setBuffer:g_tp_fold_ctl offset:(NSUInteger)slot * 8u atIndex:7];
[enc setBytes:&ntg length:sizeof(ntg) atIndex:8];
[enc setThreadgroupMemoryLength:8 * sizeof(uint32_t) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(ntg, 1, 1)
threadsPerThreadgroup:MTLSizeMake(256, 1, 1)];
g_tp_flag_prepublished_seq = g_tp_seq + 1u;
return 1;
}
/* Inline gate: publish the partial (n_words at slab offset out_off) and the
* flag word of `slot` coherently, stream the next phase's weights while the
* exchange runs, then spin on the release word inside this command buffer. */
static int ds4_gpu_tp_spin_gate_encode(uint32_t slot, uint32_t value,
uint64_t out_off, uint32_t n_words,
uint32_t prefetch_gate) {
id<MTLComputePipelineState> publish = ds4_gpu_get_pipeline("kernel_dsv4_tp_payload_publish");
id<MTLComputePipelineState> flag = ds4_gpu_get_pipeline("kernel_dsv4_tp_flag_publish");
id<MTLComputePipelineState> wait = ds4_gpu_get_pipeline("kernel_dsv4_tp_release_wait");
if (!publish || !flag || !wait || !g_tp_release_buffer) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(g_batch_cb);
if (g_batch_encoder_concurrent) [enc memoryBarrierWithScope:MTLBarrierScopeBuffers];
[enc setComputePipelineState:publish];
[enc setBuffer:g_tp_slab_buffer offset:(NSUInteger)(g_tp_slab_buffer_off + out_off) atIndex:0];
[enc setBytes:&n_words length:sizeof(n_words) atIndex:1];
[enc dispatchThreadgroups:MTLSizeMake((n_words + 255u) / 256u, 1, 1)
threadsPerThreadgroup:MTLSizeMake(256, 1, 1)];
[enc setComputePipelineState:flag];
[enc setBuffer:g_tp_slab_buffer
offset:(NSUInteger)(g_tp_slab_buffer_off + g_tp_gpu_flags_off + (uint64_t)slot * 4u)
atIndex:0];
[enc setBytes:&value length:sizeof(value) atIndex:1];
[enc dispatchThreadgroups:MTLSizeMake(1, 1, 1) threadsPerThreadgroup:MTLSizeMake(1, 1, 1)];
ds4_gpu_end_compute_encoder(g_batch_cb, enc);
ds4_gpu_close_batch_encoder();
if (prefetch_gate < 2u) (void)ds4_gpu_tp_encode_gate_prefetch(prefetch_gate);
enc = ds4_gpu_compute_encoder(g_batch_cb);
[enc setComputePipelineState:wait];
[enc setBuffer:g_tp_release_buffer offset:0 atIndex:0];
[enc setBytes:&value length:sizeof(value) atIndex:1];
[enc setBytes:&g_tp_spin_max_iters length:sizeof(g_tp_spin_max_iters) atIndex:2];
[enc dispatchThreadgroups:MTLSizeMake(1, 1, 1) threadsPerThreadgroup:MTLSizeMake(1, 1, 1)];
/* Hazard tracking cannot see the CPU-side dependency that orders the
* combine reads after the spin. */
[enc memoryBarrierWithScope:MTLBarrierScopeBuffers];
ds4_gpu_end_compute_encoder(g_batch_cb, enc);
return 1;
}
int ds4_gpu_tp_gate_encode(uint32_t layer, uint32_t gate) {
/* The C graph has no outer pool for flag/poll encoder temporaries. */
@autoreleasepool {
if (!g_batch_cb) {
fprintf(stderr, "ds4: TP gate encode without an open command batch (layer %u gate %u)\n",
layer, gate);
return 0;
}
if (!g_tp_thread_running) {
fprintf(stderr, "ds4: TP gate encode without the gate service (layer %u)\n", layer);
return 0;
}
g_tp_fold_req_active = 0;
const uint64_t seq = ++g_tp_seq;
const uint32_t slot = layer * DS4_GPU_TP_GATES_PER_LAYER + gate;
const bool spin_gate = ds4_gpu_tp_decode_inline_gates() && !g_ssd_streaming_mode &&
g_tp_vec_bytes != 0;
const bool event_arrival = !g_tp_flag_gates || (g_tp_session_batch_mode && !spin_gate);
if (spin_gate) {
if (!ds4_gpu_tp_spin_gate_encode(slot, (uint32_t)seq,
g_tp_out_off + (uint64_t)slot * g_tp_vec_bytes,
(uint32_t)(g_tp_vec_bytes / 4u), gate))
return 0;
g_tp_flag_prepublished_seq = 0;
} else if (!event_arrival) {
/* Publish arrival through the slab word; the buffer hazard against
* the partial-output kernels orders the store after the payload. */
const uint32_t value = (uint32_t)seq;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb || owned) return 0;
const bool checked =
g_tp_poll_gates && !g_ssd_streaming_mode && g_tp_check_words != NULL &&
slot < DS4_TP_POLL_MAX_SLOTS && g_tp_vec_bytes != 0;
/* The payload producer may already have published this flag. */
const bool prepublished = checked && g_tp_flag_prepublished_seq == seq;
g_tp_flag_prepublished_seq = 0;
if (prepublished) {
ds4_gpu_close_batch_encoder();
} else {
id<MTLComputePipelineState> pipeline = ds4_gpu_get_pipeline(
checked ? "kernel_dsv4_tp_flag_set_checked" : "kernel_dsv4_tp_flag_set");
if (!pipeline) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBuffer:g_tp_slab_buffer
offset:(NSUInteger)(g_tp_slab_buffer_off + g_tp_gpu_flags_off + (uint64_t)slot * 4u)
atIndex:0];
if (checked) {
const uint32_t words = (uint32_t)(g_tp_vec_bytes / 4u);
[enc setBuffer:g_tp_check_buffer offset:(NSUInteger)slot * 4u atIndex:1];
[enc setBytes:&value length:sizeof(value) atIndex:2];
[enc setBuffer:g_tp_slab_buffer
offset:(NSUInteger)(g_tp_slab_buffer_off + g_tp_out_off + (uint64_t)slot * g_tp_vec_bytes)
atIndex:3];
[enc setBytes:&words length:sizeof(words) atIndex:4];
[enc setThreadgroupMemoryLength:8 * sizeof(uint32_t) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(1, 1, 1)
threadsPerThreadgroup:MTLSizeMake(256, 1, 1)];
} else {
[enc setBytes:&value length:sizeof(value) atIndex:1];
[enc dispatchThreadgroups:MTLSizeMake(1, 1, 1)
threadsPerThreadgroup:MTLSizeMake(1, 1, 1)];
}
ds4_gpu_end_compute_encoder(cb, enc);
ds4_gpu_close_batch_encoder();
}
} else {
ds4_gpu_close_batch_encoder();
[g_batch_cb encodeSignalEvent:g_tp_gpu_event value:seq];
}
const bool poll_gate =
!spin_gate && !event_arrival && g_tp_poll_gates && !g_ssd_streaming_mode &&
slot < DS4_TP_POLL_MAX_SLOTS;
if (poll_gate) {
/* Ending the command buffer is what makes the flag and the partial
* CPU-visible; the next buffer, already queued, opens with the poll
* so the GPU resumes within a memory round trip of the release. */
if (!ds4_gpu_flush_commands()) return 0;
/* Keep the queue shallow: the firmware only chains what the driver
* has handed it, and a deep backlog of many-encoder buffers is fed
* from the CPU with scheduling latency. */
if ([g_pending_cbs count] > DS4_TP_POLL_MAX_INFLIGHT) {
id<MTLCommandBuffer> old_cb =
g_pending_cbs[[g_pending_cbs count] - 1u - DS4_TP_POLL_MAX_INFLIGHT];
[old_cb waitUntilCompleted];
}
/* The flag is already published by the flushed buffer; stream the
* next phase's weights into the cache while the exchange runs. */
(void)ds4_gpu_tp_encode_gate_prefetch(gate);
id<MTLComputePipelineState> poll_pipeline =
ds4_gpu_get_pipeline("kernel_dsv4_tp_poll_release");
if (!poll_pipeline) return 0;
const uint32_t value = (uint32_t)seq;
const uint32_t nlines = DS4_TP_POLL_LINES;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(g_batch_cb);
[enc setComputePipelineState:poll_pipeline];
[enc setBuffer:g_tp_poll_buffer
offset:(NSUInteger)(seq % DS4_TP_POLL_RING) *
DS4_TP_POLL_LINES * DS4_TP_POLL_LINE_BYTES
atIndex:0];
[enc setBytes:&value length:sizeof(value) atIndex:1];
[enc setBytes:&nlines length:sizeof(nlines) atIndex:2];
[enc setBuffer:g_tp_poll_buffer
offset:(NSUInteger)DS4_TP_POLL_LINES * DS4_TP_POLL_LINE_BYTES *
DS4_TP_POLL_RING +
(NSUInteger)slot * 2u * sizeof(uint32_t)
atIndex:3];
[enc dispatchThreadgroups:MTLSizeMake(1, 1, 1)
threadsPerThreadgroup:MTLSizeMake(32, 1, 1)];
ds4_gpu_end_compute_encoder(g_batch_cb, enc);
} else if (!spin_gate) {
[g_batch_cb encodeWaitForEvent:g_tp_cpu_event value:seq];
}
pthread_mutex_lock(&g_tp_mutex);
if (g_tp_queue_count >= DS4_GPU_TP_QUEUE) {
pthread_mutex_unlock(&g_tp_mutex);
fprintf(stderr, "ds4: TP gate queue overflow\n");
return 0;
}
uint32_t tail = (g_tp_queue_head + g_tp_queue_count) % DS4_GPU_TP_QUEUE;
g_tp_queue[tail].layer = layer;
g_tp_queue[tail].batch_slot = layer;
g_tp_queue[tail].gate = gate;
g_tp_queue[tail].rows = 0;
g_tp_queue[tail].poll = poll_gate ? 1u : 0u;
g_tp_queue[tail].spin = spin_gate ? 1u : 0u;
g_tp_queue[tail].event_arrival = event_arrival ? 1u : 0u;
g_tp_queue[tail].seq = seq;
g_tp_queue[tail].big_out = NULL;
g_tp_queue[tail].big_in = NULL;
g_tp_queue[tail].big_bytes = 0;
g_tp_queue_count++;
pthread_cond_signal(&g_tp_cond);
pthread_mutex_unlock(&g_tp_mutex);
return 1;
}
}
void ds4_gpu_tp_set_batch_exchange(ds4_gpu_tp_batch_exchange_fn fn) {
g_tp_batch_exchange_fn = fn;
}
/* Verify-block batch gate: same arrival/release machinery as the row gate
* (the FFN flag word and event pair are reused — a decode gate and a batch
* gate are never in flight together, and seq values stay globally unique),
* but the service thread runs the multi-row exchange callback. */
/* A verify-block gate publishes its arrival on the (layer, gate) flag word
* and exchanges the rows of slab batch slot `batch_slot`. */
static int ds4_gpu_tp_batch_gate_encode_slot(uint32_t layer, uint32_t gate,
uint32_t batch_slot, uint32_t rows,
const ds4_gpu_tensor *payload) {
@autoreleasepool {
if (!g_batch_cb) return 0;
if (!g_tp_thread_running || rows == 0) return 0;
const uint64_t seq = ++g_tp_batch_seq;
const bool event_arrival = g_tp_session_batch_mode || !g_tp_flag_gates;
const uint32_t slot = layer * DS4_GPU_TP_GATES_PER_LAYER + gate;
const uint32_t value = DS4_TP_BATCH_FLAG_TAG | (uint32_t)seq;
const uint32_t words = (uint32_t)(rows * g_tp_vec_bytes / 4u);
/* With the payload at hand the gate spins like a decode gate: a checked
* flag on arrival, kernel_dsv4_tp_poll_release instead of the event wake. */
static int poll_off = -1;
if (poll_off < 0) poll_off = getenv("DS4_TP_DISABLE_BATCH_POLL_GATES") != NULL;
const bool poll_gate = !event_arrival && !poll_off && payload && g_tp_poll_gates &&
!g_ssd_streaming_mode && g_tp_check_words != NULL &&
slot < DS4_TP_POLL_MAX_SLOTS && words != 0;
if (!event_arrival) {
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb || owned) return 0;
id<MTLComputePipelineState> pipeline = ds4_gpu_get_pipeline(
poll_gate ? "kernel_dsv4_tp_flag_set_checked" : "kernel_dsv4_tp_flag_set");
if (!pipeline) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBuffer:g_tp_slab_buffer
offset:(NSUInteger)(g_tp_slab_buffer_off + g_tp_gpu_flags_off + (uint64_t)slot * 4u)
atIndex:0];
if (poll_gate) {
[enc setBuffer:g_tp_check_buffer offset:(NSUInteger)slot * 4u atIndex:1];
[enc setBytes:&value length:sizeof(value) atIndex:2];
[enc setBuffer:ds4_gpu_tensor_buffer((ds4_gpu_tensor *)payload)
offset:ds4_gpu_tensor_offset((ds4_gpu_tensor *)payload) atIndex:3];
[enc setBytes:&words length:sizeof(words) atIndex:4];
[enc setThreadgroupMemoryLength:8 * sizeof(uint32_t) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(1, 1, 1)
threadsPerThreadgroup:MTLSizeMake(256, 1, 1)];
} else {
[enc setBytes:&value length:sizeof(value) atIndex:1];
[enc dispatchThreadgroups:MTLSizeMake(1, 1, 1)
threadsPerThreadgroup:MTLSizeMake(1, 1, 1)];
}
ds4_gpu_end_compute_encoder(cb, enc);
ds4_gpu_close_batch_encoder();
} else {
ds4_gpu_close_batch_encoder();
[g_batch_cb encodeSignalEvent:g_tp_batch_gpu_event value:seq];
}
if (poll_gate) {
if (!ds4_gpu_flush_commands()) return 0;
if ([g_pending_cbs count] > DS4_TP_POLL_MAX_INFLIGHT) {
id<MTLCommandBuffer> old_cb =
g_pending_cbs[[g_pending_cbs count] - 1u - DS4_TP_POLL_MAX_INFLIGHT];
[old_cb waitUntilCompleted];
}
id<MTLComputePipelineState> poll_pipeline =
ds4_gpu_get_pipeline("kernel_dsv4_tp_poll_release");
if (!poll_pipeline) return 0;
const uint32_t nlines = DS4_TP_POLL_LINES;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(g_batch_cb);
[enc setComputePipelineState:poll_pipeline];
[enc setBuffer:g_tp_poll_buffer
offset:(NSUInteger)(seq % DS4_TP_POLL_RING) *
DS4_TP_POLL_LINES * DS4_TP_POLL_LINE_BYTES
atIndex:0];
[enc setBytes:&value length:sizeof(value) atIndex:1];
[enc setBytes:&nlines length:sizeof(nlines) atIndex:2];
[enc setBuffer:g_tp_poll_buffer
offset:(NSUInteger)DS4_TP_POLL_LINES * DS4_TP_POLL_LINE_BYTES *
DS4_TP_POLL_RING +
(NSUInteger)slot * 2u * sizeof(uint32_t)
atIndex:3];
[enc dispatchThreadgroups:MTLSizeMake(1, 1, 1)
threadsPerThreadgroup:MTLSizeMake(32, 1, 1)];
ds4_gpu_end_compute_encoder(g_batch_cb, enc);
} else {
[g_batch_cb encodeWaitForEvent:g_tp_batch_cpu_event value:seq];
}
pthread_mutex_lock(&g_tp_mutex);
if (g_tp_queue_count >= DS4_GPU_TP_QUEUE) {
pthread_mutex_unlock(&g_tp_mutex);
fprintf(stderr, "ds4: TP gate queue overflow\n");
return 0;
}
uint32_t tail = (g_tp_queue_head + g_tp_queue_count) % DS4_GPU_TP_QUEUE;
g_tp_queue[tail].layer = layer;
g_tp_queue[tail].gate = gate;
g_tp_queue[tail].batch_slot = batch_slot;
g_tp_queue[tail].rows = rows;
g_tp_queue[tail].spin = 0;
g_tp_queue[tail].poll = poll_gate ? 1u : 0u;
g_tp_queue[tail].check = poll_gate ?
(const volatile uint32_t *)ds4_gpu_tensor_contents((ds4_gpu_tensor *)payload) : NULL;
g_tp_queue[tail].check_words = words;
g_tp_queue[tail].event_arrival = event_arrival ? 1u : 0u;
g_tp_queue[tail].seq = seq;
g_tp_queue[tail].big_out = NULL;
g_tp_queue[tail].big_in = NULL;
g_tp_queue[tail].big_bytes = 0;
g_tp_queue_count++;
pthread_cond_signal(&g_tp_cond);
pthread_mutex_unlock(&g_tp_mutex);
return 1;
}
}
int ds4_gpu_tp_batch_gate_encode(uint32_t layer, uint32_t rows) {
return ds4_gpu_tp_batch_gate_encode_slot(layer, DS4_GPU_TP_GATE_FFN, layer, rows, NULL);
}
/* Two gates per layer: slot layer*2+gate of the doubled batch region, whose
* rows `payload` (the slot's out view) holds. */
int ds4_gpu_tp_batch_gate_encode_kind(uint32_t layer, uint32_t gate, uint32_t rows,
const ds4_gpu_tensor *payload) {
return ds4_gpu_tp_batch_gate_encode_slot(layer, gate,
layer * DS4_GPU_TP_GATES_PER_LAYER + gate, rows, payload);
}
/* Prefill batch gate: the service thread exchanges big_bytes directly
* between two CPU-visible bounce buffers. The shared-event signal orders
* all preceding GPU stores before the service thread reads the payload. */
static uint64_t ds4_gpu_tp_big_gate_kick(uint32_t layer, uint32_t rows,
const ds4_gpu_tensor *out_t,
ds4_gpu_tensor *in_t,
uint64_t bytes) {
if (!g_batch_cb) return 0;
if (!g_tp_thread_running || rows == 0 || bytes == 0) return 0;
const void *out_ptr = ds4_gpu_tensor_contents((ds4_gpu_tensor *)out_t);
void *in_ptr = ds4_gpu_tensor_contents(in_t);
if (!out_ptr || !in_ptr) {
fprintf(stderr, "ds4: TP big gate needs CPU-visible bounce buffers\n");
return 0;
}
const uint64_t seq = ++g_tp_batch_seq;
ds4_gpu_close_batch_encoder();
[g_batch_cb encodeSignalEvent:g_tp_batch_gpu_event value:seq];
pthread_mutex_lock(&g_tp_mutex);
if (g_tp_queue_count >= DS4_GPU_TP_QUEUE) {
pthread_mutex_unlock(&g_tp_mutex);
fprintf(stderr, "ds4: TP gate queue overflow\n");
return 0;
}
uint32_t tail = (g_tp_queue_head + g_tp_queue_count) % DS4_GPU_TP_QUEUE;
g_tp_queue[tail].layer = layer;
g_tp_queue[tail].batch_slot = layer;
g_tp_queue[tail].gate = 1u;
g_tp_queue[tail].rows = rows;
g_tp_queue[tail].event_arrival = 1u;
g_tp_queue[tail].seq = seq;
g_tp_queue[tail].big_out = out_ptr;
g_tp_queue[tail].big_in = in_ptr;
g_tp_queue[tail].big_bytes = bytes;
g_tp_queue[tail].spin = 0;
g_tp_queue[tail].poll = 0;
g_tp_queue_count++;
pthread_cond_signal(&g_tp_cond);
pthread_mutex_unlock(&g_tp_mutex);
return seq;
}
static int ds4_gpu_tp_big_gate_wait(uint64_t seq) {
if (!g_batch_cb || seq == 0) return 0;
ds4_gpu_close_batch_encoder();
[g_batch_cb encodeWaitForEvent:g_tp_batch_cpu_event value:seq];
return 1;
}
int ds4_gpu_tp_big_gate_encode(uint32_t layer, uint32_t rows,
const ds4_gpu_tensor *out_t,
ds4_gpu_tensor *in_t,
uint64_t bytes) {
const uint64_t seq = ds4_gpu_tp_big_gate_kick(layer, rows, out_t, in_t, bytes);
if (seq == 0) return 0;
return ds4_gpu_tp_big_gate_wait(seq);
}
/* Run one trivial command buffer so the process's first-submission costs
* (command queue and GPU context setup, page-table validation of the
* mapped model views) are paid at load time rather than inside the first
* prefill. TP sharding skips the load-time model-view warm, which used to
* leave a 600-800 ms stall at the head of the first prompt on each rank. */
int ds4_gpu_warm_command_queue(void) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (g_batch_cb) return 1;
id<MTLComputePipelineState> pipeline = ds4_gpu_get_pipeline("kernel_dsv4_tp_keepalive");
if (!pipeline) return 0;
const double t0 = ds4_gpu_now_ms();
@autoreleasepool {
id<MTLBuffer> scratch = [g_device newBufferWithLength:256u * sizeof(float)
options:MTLResourceStorageModeShared];
if (!scratch) return 0;
memset(scratch.contents, 0, 256u * sizeof(float));
id<MTLCommandBuffer> cb = ds4_gpu_new_command_buffer();
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = [cb computeCommandEncoder];
const uint32_t iters = 1u;
[enc setComputePipelineState:pipeline];
[enc setBuffer:scratch offset:0 atIndex:0];
[enc setBytes:&iters length:sizeof(iters) atIndex:1];
[enc dispatchThreadgroups:MTLSizeMake(1, 1, 1) threadsPerThreadgroup:MTLSizeMake(256, 1, 1)];
[enc endEncoding];
[cb commit];
[cb waitUntilCompleted];
if (cb.status != MTLCommandBufferStatusCompleted) return 0;
}
if (getenv("DS4_METAL_CB_TIMES"))
fprintf(stderr, "ds4: command queue warm in %.1f ms\n", ds4_gpu_now_ms() - t0);
ds4_gpu_queue_keepalive_start();
return 1;
}
/* Main-queue idle keepalive. Measured on M5 Max (macOS 26): once the
* engine's command queue has been idle for about 3 s, the next command
* buffer on it starts 600-800 ms late (the driver drops and rebuilds the
* process's GPU mappings; 76-145 GB of no-copy model views here). Work on
* other queues does not prevent it. One 1 KB kernel per second on this
* queue keeps the mappings alive at no measurable cost.
* DS4_METAL_DISABLE_QUEUE_KEEPALIVE=1 opts out. */
static pthread_t g_queue_keepalive_thread;
static volatile int g_queue_keepalive_running;
static volatile int g_queue_keepalive_stop;
static void *ds4_gpu_queue_keepalive_thread(void *arg) {
(void)arg;
id<MTLComputePipelineState> pipeline = ds4_gpu_get_pipeline("kernel_dsv4_tp_keepalive");
id<MTLBuffer> scratch = [g_device newBufferWithLength:256u * sizeof(float)
options:MTLResourceStorageModeShared];
if (!pipeline || !scratch) return NULL;
memset(scratch.contents, 0, 256u * sizeof(float));
const uint32_t iters = 1u;
uint64_t period_ms = ds4_gpu_env_u64("DS4_METAL_QUEUE_KEEPALIVE_MS", 1000u, 50u, 10000u);
while (!g_queue_keepalive_stop) {
@autoreleasepool {
id<MTLCommandBuffer> cb = [g_queue commandBuffer];
id<MTLComputeCommandEncoder> enc = [cb computeCommandEncoder];
[enc setComputePipelineState:pipeline];
[enc setBuffer:scratch offset:0 atIndex:0];
[enc setBytes:&iters length:sizeof(iters) atIndex:1];
[enc dispatchThreadgroups:MTLSizeMake(1, 1, 1) threadsPerThreadgroup:MTLSizeMake(256, 1, 1)];
[enc endEncoding];
[cb commit];
}
for (uint64_t slept = 0; slept < period_ms && !g_queue_keepalive_stop; slept += 50)
usleep(50000);
}
return NULL;
}
static void ds4_gpu_queue_keepalive_start(void) {
if (g_queue_keepalive_running) return;
if (getenv("DS4_METAL_DISABLE_QUEUE_KEEPALIVE")) return;
g_queue_keepalive_stop = 0;
if (pthread_create(&g_queue_keepalive_thread, NULL, ds4_gpu_queue_keepalive_thread, NULL) == 0)
g_queue_keepalive_running = 1;
}
static void ds4_gpu_queue_keepalive_stop_thread(void) {
if (!g_queue_keepalive_running) return;
g_queue_keepalive_stop = 1;
pthread_join(g_queue_keepalive_thread, NULL);
g_queue_keepalive_running = 0;
}
int ds4_gpu_tp_failed(void) {
return g_tp_failed_flag;
}
int ds4_gpu_wait_selected_readback_ready(uint64_t event_value, const char *label) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (event_value == 0) return 0;
if (@available(macOS 12.0, *)) {
if (!g_selected_readback_event) return 0;
const char *what = label ? label : "selected-id readback";
const BOOL signaled =
[g_selected_readback_event waitUntilSignaledValue:event_value timeoutMS:60000];
if (!signaled) {
fprintf(stderr, "ds4: timeout waiting for Metal shared event in %s\n", what);
return 0;
}
return 1;
}
fprintf(stderr, "ds4: selected-id overlap requires MTLSharedEvent support\n");
return 0;
}
static int ds4_gpu_signal_batch_and_wait_event(const char *label) {
if (!g_initialized && !ds4_gpu_init()) return 0;
ds4_gpu_parallel_ffn_reset_state(YES);
if (!g_batch_cb) return 0;
if (@available(macOS 12.0, *)) {
if (!g_selected_readback_event) {
g_selected_readback_event = [g_device newSharedEvent];
if (!g_selected_readback_event) {
fprintf(stderr, "ds4: failed to create Metal shared event for %s\n",
label ? label : "selected-id readback");
return 0;
}
}
ds4_gpu_close_batch_encoder();
id<MTLCommandBuffer> cb = g_batch_cb;
g_batch_cb = nil;
const uint64_t value = ++g_selected_readback_event_value;
[cb encodeSignalEvent:g_selected_readback_event value:value];
g_batch_has_work = YES;
[cb commit];
ds4_gpu_stream_expert_cache_note_batch_committed();
const BOOL signaled = [g_selected_readback_event waitUntilSignaledValue:value timeoutMS:60000];
[g_pending_cbs addObject:cb];
if (!signaled) {
fprintf(stderr, "ds4: timeout waiting for Metal shared event in %s\n",
label ? label : "selected-id readback");
(void)ds4_gpu_wait_pending_command_buffers(label ? label : "selected-id readback");
[g_transient_buffers removeAllObjects];
return 0;
}
if (cb.status == MTLCommandBufferStatusError) {
fprintf(stderr, "ds4: Metal %s failed: %s\n",
label ? label : "selected-id readback",
[[cb.error localizedDescription] UTF8String]);
(void)ds4_gpu_wait_pending_command_buffers(label ? label : "selected-id readback");
[g_transient_buffers removeAllObjects];
return 0;
}
g_batch_cb = ds4_gpu_new_command_buffer();
g_batch_has_work = NO;
if (g_batch_cb) ds4_gpu_stream_expert_cache_note_batch_created();
if (!g_batch_cb) {
(void)ds4_gpu_wait_pending_command_buffers(label ? label : "selected-id readback");
[g_transient_buffers removeAllObjects];
return 0;
}
return 1;
} else {
ds4_gpu_close_batch_encoder();
id<MTLCommandBuffer> cb = g_batch_cb;
g_batch_cb = nil;
g_batch_has_work = NO;
if (ds4_gpu_finish_command_buffer(cb, 1, label ? label : "selected-id readback") == 0) {
return 0;
}
return ds4_gpu_begin_commands();
}
}
int ds4_gpu_end_commands(void) {
if (!g_batch_cb) {
ds4_gpu_parallel_ffn_reset_state(YES);
return 0;
}
ds4_gpu_parallel_ffn_reset_state(YES);
ds4_gpu_close_batch_encoder();
id<MTLCommandBuffer> cb = g_batch_cb;
g_batch_cb = nil;
g_batch_has_work = NO;
g_stream_expert_cache_owned_seq = g_stream_expert_cache_batch_seq;
g_stream_expert_cache_batch_seq = 0;
return ds4_gpu_finish_command_buffer(cb, 1, "command batch");
}
static int ds4_gpu_flash_attn_stage_profile_boundary(
id<MTLCommandBuffer> __strong *cbp,
const char *mode,
const char *stage,
uint32_t n_tokens,
uint32_t n_comp,
uint32_t n_keys,
uint32_t n_head,
uint32_t head_dim,
uint32_t window,
uint32_t ratio,
double *stage_t0) {
if (!cbp || !*cbp || !stage_t0 || !stage) return 0;
if (ds4_gpu_end_commands() == 0) return 0;
const double now_ms = ds4_gpu_now_ms();
const char *filter = getenv("DS4_METAL_FLASH_ATTN_STAGE_PROFILE_FILTER");
const int print_stage =
!filter || !filter[0] ||
strstr(stage, filter) != NULL ||
(mode && strstr(mode, filter) != NULL);
if (print_stage) {
fprintf(stderr,
"ds4: Metal FlashAttention prefill stage mode=%s tokens=%u comp=%u "
"keys=%u heads=%u dim=%u window=%u ratio=%u %s=%.3f ms\n",
mode ? mode : "unknown",
n_tokens,
n_comp,
n_keys,
n_head,
head_dim,
window,
ratio,
stage,
now_ms - *stage_t0);
}
*stage_t0 = now_ms;
if (ds4_gpu_begin_commands() == 0) return 0;
int owned = 0;
*cbp = ds4_gpu_command_buffer(&owned);
return *cbp != nil && owned == 0;
}
int ds4_gpu_synchronize(void) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (g_batch_cb) return ds4_gpu_end_commands();
ds4_gpu_parallel_ffn_reset_state(YES);
if ([g_pending_cbs count] != 0) {
int ok = ds4_gpu_wait_pending_command_buffers("synchronize");
[g_transient_buffers removeAllObjects];
ds4_gpu_model_buffer_cache_maybe_evict("synchronize");
return ok;
}
id<MTLCommandBuffer> cb = ds4_gpu_new_command_buffer();
if (!cb) return 0;
return ds4_gpu_finish_command_buffer(cb, 1, "synchronize");
}
static void qwen4_nax_release_scratch(void);
static void qwen4_batch_release_scratch(void);
void ds4_gpu_cleanup(void) {
if (!g_initialized) return;
ds4_gpu_queue_keepalive_stop_thread();
@autoreleasepool {
ds4_gpu_decode_pipeline_fast_cache_reset();
ds4_gpu_parallel_ffn_reset_state(YES);
if (g_batch_cb) {
ds4_gpu_close_batch_encoder();
[g_batch_cb commit];
[g_batch_cb waitUntilCompleted];
g_batch_cb = nil;
if (g_stream_expert_cache_batch_seq > g_stream_expert_cache_done_seq) {
g_stream_expert_cache_done_seq = g_stream_expert_cache_batch_seq;
}
g_stream_expert_cache_batch_seq = 0;
}
(void)ds4_gpu_wait_pending_command_buffers("cleanup");
if (ds4_gpu_stream_expert_timing_summary_enabled() &&
getenv("DS4_METAL_MEMORY_REPORT") == NULL) {
ds4_gpu_print_memory_report("at cleanup");
}
g_selected_readback_event = nil;
g_selected_readback_event_value = 0;
[g_transient_buffers removeAllObjects];
ds4_gpu_stream_expert_pread_pool_shutdown();
ds4_gpu_stream_expert_cache_clear_all(1);
for (uint32_t layer = 0; layer < DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER; layer++) {
g_stream_expert_cache_gate_addr_buffers[layer] = nil;
g_stream_expert_cache_up_addr_buffers[layer] = nil;
g_stream_expert_cache_down_addr_buffers[layer] = nil;
g_stream_compact_gate_addr_buffers[layer] = nil;
g_stream_compact_up_addr_buffers[layer] = nil;
g_stream_compact_down_addr_buffers[layer] = nil;
g_stream_compact_selected_buffers[layer] = nil;
g_stream_selected_id_buffers[layer] = nil;
}
g_set_rows_f32_i32_pipeline = nil;
g_get_rows_f32_pipeline = nil;
g_get_rows_f16_pipeline = nil;
g_get_rows_i32_pipeline = nil;
g_get_rows_q8_0_pipeline = nil;
g_get_rows_q4_0_pipeline = nil;
g_get_rows_q4_K_pipeline = nil;
g_repeat_f32_pipeline = nil;
g_concat_pipeline = nil;
g_cpy_f32_f32_pipeline = nil;
g_cpy_f32_f16_pipeline = nil;
g_cpy_contig_f32_f16_pipeline = nil;
g_cpy_f16_f32_pipeline = nil;
g_cpy_f16_f16_pipeline = nil;
g_cpy_contig_f16_f32_pipeline = nil;
g_cpy_contig_f16_f16_pipeline = nil;
g_flash_kv_stage_f16_pipeline = nil;
g_swiglu_pipeline = nil;
g_swiglu_flat_pipeline = nil;
g_add_pipeline = nil;
g_add2_pipeline = nil;
g_add3_pipeline = nil;
g_moe_sum6_pipeline = nil;
g_moe_sum8_pipeline = nil;
g_mul_pipeline = nil;
g_bin_mul_scalar_pipeline = nil;
g_bin_div_row_pipeline = nil;
g_unary_sigmoid_pipeline = nil;
g_unary_silu_pipeline = nil;
g_unary_softplus_pipeline = nil;
g_unary_sqrt_pipeline = nil;
g_unary_clamp_pipeline = nil;
g_unary_scale_pipeline = nil;
g_unary_fill_pipeline = nil;
g_unary_fill_f16_pipeline = nil;
g_rms_norm_pipeline = nil;
g_rms_norm_plain_pipeline = nil;
g_add_rms_norm_pipeline = nil;
g_rms_norm_scale_pipeline = nil;
g_dsv4_qkv_rms_norm_pipeline = nil;
g_dsv4_head_rms_norm_rope_tail_pipeline = nil;
g_use_dsv4_head_rms_norm_rope_tail_pipeline = false;
g_hc_split_sinkhorn_pipeline = nil;
g_hc_split_weighted_sum_pipeline = nil;
g_hc_split_weighted_sum_norm_pipeline = nil;
g_dsv4_hc_producer_pre_norm_pipeline = nil;
g_hc_weighted_sum_pipeline = nil;
g_output_hc_weights4_pipeline = nil;
g_hc_expand_pipeline = nil;
g_moe_mul_mv_id_iq2_xxs_pipeline = nil;
g_moe_mul_mv_id_iq2_xxs_pair_pipeline = nil;
g_moe_mul_mv_id_iq2_xxs_pair_swiglu_pipeline = nil;
g_moe_mul_mv_id_iq2_xxs_pair_swiglu_pack2_pipeline = nil;
g_moe_mul_mv_id_q2_k_pipeline = nil;
g_moe_mul_mv_id_q2_k_sum6_pipeline = nil;
g_moe_mul_mv_id_iq2_xxs_sum6_pipeline = nil;
g_moe_mul_mv_id_q4_k_pipeline = nil;
g_moe_mul_mv_id_q4_k_pair_pipeline = nil;
g_moe_mul_mv_id_q4_k_pair_swiglu_pipeline = nil;
g_moe_mul_mv_id_q4_k_sum6_pipeline = nil;
g_moe_mul_mv_group_q4_k_pair_swiglu_pipeline = nil;
g_moe_mul_mv_group_q4_k_sum6_pipeline = nil;
g_moe_mul_mv_group6_q4_k_pair_swiglu_pipeline = nil;
g_moe_mul_mv_group6_q4_k_sum6_pipeline = nil;
g_moe_mul_mv_group8_q4_k_pair_swiglu_pipeline = nil;
g_moe_mul_mv_group8_q4_k_sum6_pipeline = nil;
g_moe_mul_mv_group24_q4_k_id_pipeline = nil;
g_moe_mul_mv_group24_q4_k_sum6_pipeline = nil;
g_moe_mul_mv_slots6_iq2_xxs_pair_swiglu_pipeline = nil;
g_moe_mul_mv_slots6_q2_k_sum6_pipeline = nil;
g_moe_mul_mv_slots6_q4_k_pair_swiglu_pipeline = nil;
g_moe_mul_mv_slots6_q4_k_sum6_pipeline = nil;
g_moe_mul_mv_id_mxfp4_pipeline = nil;
g_moe_mul_mv_id_mxfp4_pair_swiglu_pipeline = nil;
g_moe_mul_mv_id_mxfp4_sum6_pipeline = nil;
g_moe_mul_mv_id_mxfp4_pair_swiglu_pipeline_nsg1 = nil;
g_moe_mul_mv_id_mxfp4_sum6_pipeline_nsg1 = nil;
g_moe_mul_mv_id_mxfp4_pair_swiglu_pipeline_nsg1_tg_multiple = nil;
g_moe_mul_mv_id_mxfp4_sum6_pipeline_nsg1_tg_multiple = nil;
g_moe_mul_mv_id_mxfp4_pair_swiglu_fixed_route_pipeline_nsg1 = nil;
g_moe_mul_mv_id_mxfp4_sum6_fixed_route_pipeline_nsg1 = nil;
g_moe_mul_mv_id_mxfp4_sum6_fixed_route_full_rows_pipeline_nsg1 = nil;
g_moe_mul_mv_id_mxfp4_pair_swiglu_fixed_route_static_pipeline_nsg1 = nil;
g_moe_mul_mv_id_mxfp4_sum6_fixed_route_full_rows_static_pipeline_nsg1 = nil;
g_moe_mul_mv_id_mxfp4_pair_swiglu_tp_static_pipeline_nsg1 = nil;
g_moe_mul_mv_id_mxfp4_sum6_tp_full_rows_static_pipeline_nsg1 = nil;
g_moe_mul_mv_slots6_mxfp4_pair_swiglu_pipeline = nil;
g_moe_mul_mv_slots6_mxfp4_sum6_pipeline = nil;
g_moe_mul_mv_addr_iq2_xxs_pair_swiglu_pipeline = nil;
g_moe_mul_mv_addr_iq2_xxs_pipeline = nil;
g_moe_mul_mv_addr_q2_k_sum6_pipeline = nil;
g_moe_mul_mv_addr_iq2_xxs_pair_swiglu_masked_pipeline = nil;
g_moe_mul_mv_addr_q2_k_sum6_masked_pipeline = nil;
g_moe_stream_expert_cache_validate_pipeline = nil;
g_moe_q4_gather_slots6_pipeline = nil;
g_moe_mul_mv_table_q4_k_pair_swiglu_pipeline = nil;
g_moe_mul_mv_table_q4_k_sum6_pipeline = nil;
g_moe_mul_mv_addr_q4_k_pair_swiglu_pipeline = nil;
g_moe_mul_mv_addr_q4_k_sum6_pipeline = nil;
g_moe_table_q4_pair_gate_encoder = nil;
g_moe_table_q4_pair_up_encoder = nil;
g_moe_table_q4_sum_down_encoder = nil;
g_rope_tail_batch_pipeline = nil;
g_rope_tail_inplace_pair_pipeline = nil;
g_rope_tail_inplace_pair_shared4_pipeline = nil;
g_rope_tail_inplace_pair_affine_pipeline = nil;
g_dsv4_fp8_kv_quantize_pipeline = nil;
g_dsv4_indexer_qat_pipeline = nil;
g_dsv4_kv_fp8_store_pipeline = nil;
g_dsv4_kv_rope_fp8_store_pipeline = nil;
g_dsv4_ratio4_shift_pipeline = nil;
g_dsv4_compressor_pack_ratio4_pipeline = nil;
g_dsv4_compressor_pack_ratio4_decode_ggml_pipeline = nil;
g_dsv4_compressor_exact_softmax_product_pipeline = nil;
g_dsv4_compressor_exact_pool_ratio4_pipeline = nil;
g_dsv4_softmax_pool_ratio4_direct_pipeline = nil;
g_dsv4_softmax_pool_pipeline = nil;
g_soft_max_f32_pipeline = nil;
g_soft_max_f32_4_pipeline = nil;
g_argsort_f32_i32_desc_pipeline = nil;
g_argsort_merge_f32_i32_desc_pipeline = nil;
g_sum_rows_f32_f32_pipeline = nil;
g_dsv4_topk_mask_pipeline = nil;
g_dsv4_topk_mask_scatter_pipeline = nil;
g_dsv4_indexer_weighted_sum_pipeline = nil;
g_dsv4_indexer_score_one_direct_pipeline = nil;
g_dsv4_compressor_store_one_pipeline = nil;
g_dsv4_sort_i32_rows_asc_pipeline = nil;
g_dsv4_indexed_attention_heads8_pipeline = nil;
g_dsv4_indexed_attention_heads8_rb16_pipeline = nil;
g_dsv4_indexed_attention_heads16_dual_pipeline = nil;
g_dsv4_indexed_attention_heads8_split_pipeline = nil;
g_dsv4_indexed_attention_heads8_split_reduce_pipeline = nil;
g_dsv4_softplus_sqrt_pipeline = nil;
g_dsv4_router_finalize_one_pipeline = nil;
g_dsv4_router_finalize_one_simd_pipeline = nil;
g_dsv4_router_finalize_weights_one_simd_pipeline = nil;
g_dsv4_router_transform_finalize_weights_one_simd_pipeline = nil;
g_dsv4_router_select_visual_batch_pipeline = nil;
g_dsv4_hc_producer_last_completion = nil;
g_dsv4_hc_producer_last_mix_buffer = nil;
g_dsv4_hc_producer_last_mix_offset = 0;
[g_dsv4_completion_cache removeAllObjects];
g_dsv4_completion_cache = nil;
g_dsv4_router_weights_one_pipeline = nil;
g_glm_router_select_one_pipeline = nil;
g_glm_kv_lora_rms_norm_pipeline = nil;
g_glm_k_b_project_pipeline = nil;
g_glm_store_compact_kv_pipeline = nil;
g_glm_qkv_norm_store_compact_kv_pipeline = nil;
g_glm_store_indexer_k_pipeline = nil;
g_glm53_indexer_pool_update_pipeline = nil;
g_glm_build_kv_cache_pipeline = nil;
g_glm_build_kv_cache_decode_group4_pipeline = nil;
g_glm_build_kv_cache_flash_pipeline = nil;
g_glm_attention_full_pipeline = nil;
g_glm_fill_selected_range_pipeline = nil;
g_glm_fill_selected_range_batch_pipeline = nil;
g_glm53_expand_pool_selection_pipeline = nil;
g_glm_indexer_rope_tail_pipeline = nil;
g_glm_indexer_score_one_pipeline = nil;
g_glm_indexer_score_one_direct_pipeline = nil;
g_glm_indexer_scores_batch_pipeline = nil;
g_glm_indexer_scores_tiled_pipeline = nil;
g_glm_indexer_scores_tiled_f32_pipeline = nil;
g_glm_qk_lowrank_pipeline = nil;
g_glm_qk_lowrank_glm52_pipeline = nil;
g_glm_qk_lowrank_glm52_sg_pipeline = nil;
g_glm_qk_lowrank_batch_pipeline = nil;
g_glm_qk_lowrank_batch_glm52_t4_pipeline = nil;
g_glm_value_project_q8_0_pipeline = nil;
g_glm_value_project_q8_0_batch_heads_pipeline = nil;
g_glm_value_project_q8_0_batch_heads_mma_pipeline = nil;
g_glm_attention_indexed_decode_pipeline = nil;
g_glm_attention_indexed_decode_split_group8_partial_pipeline = nil;
g_glm_attention_indexed_decode_split_group8_partial_valid_fullheads_pipeline = nil;
g_glm_attention_indexed_decode_split_group8_reduce_pipeline = nil;
g_glm_attention_indexed_decode_split_group8_reduce16_pipeline = nil;
g_glm_attention_indexed_batch_pipeline = nil;
g_glm_attention_indexed_batch_group2_pipeline = nil;
g_glm_attention_indexed_batch_q2_group4_pipeline = nil;
g_glm_attention_indexed_batch_group8_pipeline = nil;
g_glm_attention_indexed_batch_lora_group8_vec_pipeline = nil;
g_glm_attention_indexed_batch_lora_group8_vec_valid_pipeline = nil;
g_glm_attention_indexed_batch_lora_group8_vec_valid_fullheads_pipeline = nil;
g_glm_attention_indexed_batch_lora_group8_vec_causal_pipeline = nil;
g_glm_attention_indexed_batch_lora_group8_vec_causal_fullheads_pipeline = nil;
g_glm_q4_k_pair_swiglu_f32_pipeline = nil;
g_glm_q4_k_pair_swiglu2_f32_pipeline = nil;
g_glm_q4_k_pair_swiglu4_f32_pipeline = nil;
g_glm_q4_k_pair_swiglu2_mapped_f32_pipeline = nil;
g_glm_q4_k_pair_swiglu2_mapped_row_f32_pipeline = nil;
g_glm_q2_k_pair_swiglu_f32_pipeline = nil;
g_glm_q2_k_addr_pair_swiglu2_f32_pipeline = nil;
g_glm_q2_k_addr_pair_swiglu2_masked_f32_pipeline = nil;
g_glm_q4_k_addr_pair_swiglu_f32_pipeline = nil;
g_glm_q4_k_addr_pair_swiglu_masked_f32_pipeline = nil;
g_glm_q2_k_down_f32_pipeline = nil;
g_glm_q4_k_down_f32_pipeline = nil;
g_glm_q2_k_addr_down_f32_pipeline = nil;
g_glm_q4_k_addr_down_f32_pipeline = nil;
g_glm_q5_k_pair_swiglu_f32_pipeline = nil;
g_glm_q5_k_pair_swiglu_mapped_f32_pipeline = nil;
g_glm_q5_k_pair_swiglu_mapped_row_f32_pipeline = nil;
g_glm_q5_k_down_f32_pipeline = nil;
g_glm_q6_k_down_f32_pipeline = nil;
g_dsv4_router_weights_batch_pipeline = nil;
g_dsv4_hc_expand4_pipeline = nil;
g_flash_attn_mask_buffer = nil;
g_flash_attn_zero_mask_buffer = nil;
g_flash_attn_pad_buffer = nil;
g_flash_attn_tmp_buffer = nil;
g_flash_attn_blk_buffer = nil;
ds4_gpu_clear_zero_prefix_prefill_mask_cache();
g_flash_attn_ring_buffer = nil;
g_flash_attn_kv_buffer = nil;
g_glm_flash_attn_mask_buffer = nil;
g_compressor_pool_kv_buffer = nil;
g_compressor_pool_score_buffer = nil;
g_compressor_pool_score_cont_buffer = nil;
g_compressor_pool_softmax_buffer = nil;
g_compressor_pool_product_buffer = nil;
g_compressor_store_ape_buffer = nil;
g_compressor_store_score_buffer = nil;
g_embed_rows_buffer = nil;
g_router_selection_buffer = nil;
g_router_weight_sum_buffer = nil;
g_indexer_head_scores_buffer = nil;
g_indexer_topk_buffer = nil;
g_indexed_topk_buffer = nil;
g_stream_expert_validate_status_buffer = nil;
g_f16_round_scratch_buffer = nil;
g_q8_prefill_scratch_buffer = nil;
g_qwen4_conv_halo_buffer = nil;
g_qwen4_conv_halo_bytes = 0;
g_raw_store_round_buffer = nil;
g_moe_gate_scratch_buffer = nil;
g_moe_down_scratch_buffer = nil;
g_moe_id_map_buffer = nil;
g_moe_packed_rhs_buffer = nil;
g_moe_packed_rhs_bytes = 0;
g_moe_q4_gate_slots_buffer = nil;
g_moe_q4_up_slots_buffer = nil;
g_moe_q4_down_slots_buffer = nil;
g_attn_out_group_ids_buffer = nil;
g_model_fd = -1;
g_model_map_ptr = NULL;
g_model_map_size = 0;
g_model_mapped_offset = 0;
g_model_mapped_size = 0;
g_model_mapped_max_tensor_bytes = 0;
qwen4_nax_release_scratch();
qwen4_batch_release_scratch();
ds4_gpu_tensor_tracking_reset();
g_flash_attn_mask_bytes = 0;
g_flash_attn_zero_mask_bytes = 0;
g_flash_attn_pad_bytes = 0;
g_flash_attn_tmp_bytes = 0;
g_flash_attn_blk_bytes = 0;
g_flash_attn_ring_bytes = 0;
g_flash_attn_kv_bytes = 0;
g_glm_flash_attn_mask_bytes = 0;
g_glm_flash_attn_mask_valid = 0;
g_glm_flash_attn_mask_pos0 = 0;
g_glm_flash_attn_mask_tokens = 0;
g_glm_flash_attn_mask_cache_len = 0;
g_compressor_pool_kv_bytes = 0;
g_compressor_pool_score_bytes = 0;
g_compressor_pool_score_cont_bytes = 0;
g_compressor_pool_softmax_bytes = 0;
g_compressor_pool_product_bytes = 0;
g_compressor_store_ape_bytes = 0;
g_compressor_store_score_bytes = 0;
g_embed_rows_bytes = 0;
g_router_selection_bytes = 0;
g_router_weight_sum_bytes = 0;
g_indexer_head_scores_bytes = 0;
g_indexer_topk_bytes = 0;
g_indexed_topk_bytes = 0;
g_f16_round_scratch_bytes = 0;
g_q8_prefill_scratch_bytes = 0;
g_raw_store_round_bytes = 0;
g_moe_gate_scratch_bytes = 0;
g_moe_down_scratch_bytes = 0;
g_moe_id_map_bytes = 0;
g_moe_q4_gate_slots_bytes = 0;
g_moe_q4_up_slots_bytes = 0;
g_moe_q4_down_slots_bytes = 0;
g_attn_out_group_ids_bytes = 0;
g_model_wrap_count = 0;
g_model_wrap_bytes = 0;
g_model_wrap_max_bytes = 0;
g_model_buffer_cache_bytes = 0;
g_model_buffer_cache_evictions = 0;
g_model_buffer_cache_over_limit = 0;
ds4_gpu_model_residency_clear();
ds4_gpu_model_views_clear();
[g_pipeline_cache removeAllObjects];
g_pipeline_cache = nil;
[g_q4_expert_layer_residency_cache removeAllObjects];
g_q4_expert_layer_residency_cache = nil;
[g_q4_expert_table_cache removeAllObjects];
g_q4_expert_table_cache = nil;
[g_model_buffer_cache removeAllObjects];
g_model_buffer_cache = nil;
g_transient_buffers = nil;
g_pending_cbs = nil;
g_library = nil;
g_queue = nil;
g_device = nil;
g_initialized = 0;
}
}
static int ds4_gpu_encode_get_rows_f16(
id<MTLCommandBuffer> cb,
id<MTLBuffer> weight,
NSUInteger weight_offset,
id<MTLBuffer> tokens,
NSUInteger tokens_offset,
id<MTLBuffer> out,
NSUInteger out_offset,
uint32_t n_vocab,
uint32_t n_tokens,
uint32_t n_embd) {
if (!cb || !weight || !tokens || !out || n_vocab == 0 || n_tokens == 0 || n_embd == 0) {
return 0;
}
const uint64_t src_row_bytes = (uint64_t)n_embd * sizeof(uint16_t);
const uint64_t dst_row_bytes = (uint64_t)n_embd * sizeof(float);
const uint64_t token_bytes = (uint64_t)n_tokens * sizeof(int32_t);
ds4_gpu_get_rows_args args = {
.ne00t = (int32_t)n_embd,
.ne00 = (int32_t)n_embd,
.nb01 = src_row_bytes,
.nb02 = (uint64_t)n_vocab * src_row_bytes,
.nb03 = (uint64_t)n_vocab * src_row_bytes,
.ne10 = (int32_t)n_tokens,
.nb10 = sizeof(int32_t),
.nb11 = token_bytes,
.nb12 = token_bytes,
.nb1 = dst_row_bytes,
.nb2 = (uint64_t)n_tokens * dst_row_bytes,
.nb3 = (uint64_t)n_tokens * dst_row_bytes,
};
NSUInteger nth = (NSUInteger)n_embd;
const NSUInteger max_threads = g_get_rows_f16_pipeline.maxTotalThreadsPerThreadgroup;
if (nth > max_threads) nth = max_threads;
if (nth == 0) nth = 1;
const NSUInteger nw0 = ((NSUInteger)n_embd + nth - 1u) / nth;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:g_get_rows_f16_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:weight offset:weight_offset atIndex:1];
[enc setBuffer:tokens offset:tokens_offset atIndex:2];
[enc setBuffer:out offset:out_offset atIndex:3];
[enc dispatchThreadgroups:MTLSizeMake(nw0 * n_tokens, 1, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static uint64_t ds4_gpu_q8_0_row_bytes(uint32_t n_embd) {
return (((uint64_t)n_embd + 31u) / 32u) * 34u;
}
static int ds4_gpu_quant_row_bytes(
uint32_t type,
uint32_t n_embd,
uint64_t *row_bytes_out) {
if (!row_bytes_out || n_embd == 0) return 0;
switch (type) {
case DS4_METAL_TENSOR_Q8_0:
*row_bytes_out = (((uint64_t)n_embd + 31u) / 32u) * 34u;
return 1;
case DS4_METAL_TENSOR_Q4_0:
*row_bytes_out = (((uint64_t)n_embd + 31u) / 32u) * 18u;
return 1;
case DS4_METAL_TENSOR_Q4_K:
if ((n_embd % 256u) != 0) return 0;
*row_bytes_out = ((uint64_t)n_embd / 256u) * 144u;
return 1;
default:
return 0;
}
}
static int ds4_gpu_q8_0_table_bytes(
uint32_t n_vocab,
uint32_t n_embd,
uint64_t *bytes_out) {
if (!bytes_out || n_vocab == 0 || n_embd == 0) return 0;
const uint64_t row_bytes = ds4_gpu_q8_0_row_bytes(n_embd);
if (row_bytes != 0 && (uint64_t)n_vocab > UINT64_MAX / row_bytes) return 0;
*bytes_out = (uint64_t)n_vocab * row_bytes;
return 1;
}
static int ds4_gpu_quant_table_bytes(
uint32_t type,
uint32_t n_vocab,
uint32_t n_embd,
uint64_t *bytes_out) {
if (!bytes_out || n_vocab == 0 || n_embd == 0) return 0;
uint64_t row_bytes = 0;
if (!ds4_gpu_quant_row_bytes(type, n_embd, &row_bytes)) return 0;
if (row_bytes != 0 && (uint64_t)n_vocab > UINT64_MAX / row_bytes) return 0;
*bytes_out = (uint64_t)n_vocab * row_bytes;
return 1;
}
static int ds4_gpu_encode_get_rows_q8_0(
id<MTLCommandBuffer> cb,
id<MTLBuffer> weight,
NSUInteger weight_offset,
id<MTLBuffer> tokens,
NSUInteger tokens_offset,
const int32_t *single_token,
id<MTLBuffer> out,
NSUInteger out_offset,
uint32_t n_vocab,
uint32_t n_tokens,
uint32_t n_embd) {
if (!cb || !weight || !out || n_vocab == 0 || n_tokens == 0 || n_embd == 0) {
return 0;
}
if (!tokens && (!single_token || n_tokens != 1)) {
return 0;
}
ds4_gpu_get_rows_q8_0_args args = {
.n_embd = (int32_t)n_embd,
.n_vocab = (int32_t)n_vocab,
.n_tokens = (int32_t)n_tokens,
.src_row_bytes = ds4_gpu_q8_0_row_bytes(n_embd),
.dst_row_bytes = (uint64_t)n_embd * sizeof(float),
.token_stride = sizeof(int32_t),
};
NSUInteger nth = 32u;
const NSUInteger max_threads = g_get_rows_q8_0_pipeline.maxTotalThreadsPerThreadgroup;
if (nth > max_threads) nth = max_threads;
if (nth == 0) nth = 1;
const NSUInteger nblocks = ((NSUInteger)n_embd + 31u) / 32u;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:g_get_rows_q8_0_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:weight offset:weight_offset atIndex:1];
if (tokens) {
[enc setBuffer:tokens offset:tokens_offset atIndex:2];
} else {
[enc setBytes:single_token length:sizeof(*single_token) atIndex:2];
}
[enc setBuffer:out offset:out_offset atIndex:3];
[enc dispatchThreadgroups:MTLSizeMake(nblocks, n_tokens, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_get_rows_quant(
id<MTLCommandBuffer> cb,
id<MTLBuffer> weight,
NSUInteger weight_offset,
uint32_t weight_type,
id<MTLBuffer> tokens,
NSUInteger tokens_offset,
const int32_t *single_token,
id<MTLBuffer> out,
NSUInteger out_offset,
uint32_t n_vocab,
uint32_t n_tokens,
uint32_t n_embd) {
if (weight_type == DS4_METAL_TENSOR_Q8_0) {
return ds4_gpu_encode_get_rows_q8_0(cb,
weight,
weight_offset,
tokens,
tokens_offset,
single_token,
out,
out_offset,
n_vocab,
n_tokens,
n_embd);
}
if (!cb || !weight || !out || n_vocab == 0 || n_tokens == 0 || n_embd == 0) {
return 0;
}
if (!tokens && (!single_token || n_tokens != 1)) {
return 0;
}
uint64_t src_row_bytes = 0;
if (!ds4_gpu_quant_row_bytes(weight_type, n_embd, &src_row_bytes)) return 0;
ds4_gpu_get_rows_q8_0_args args = {
.n_embd = (int32_t)n_embd,
.n_vocab = (int32_t)n_vocab,
.n_tokens = (int32_t)n_tokens,
.src_row_bytes = src_row_bytes,
.dst_row_bytes = (uint64_t)n_embd * sizeof(float),
.token_stride = sizeof(int32_t),
};
id<MTLComputePipelineState> pipeline = nil;
NSUInteger block_width = 0;
if (weight_type == DS4_METAL_TENSOR_Q4_0) {
pipeline = g_get_rows_q4_0_pipeline;
block_width = 32u;
} else if (weight_type == DS4_METAL_TENSOR_Q4_K) {
pipeline = g_get_rows_q4_K_pipeline;
block_width = 256u;
}
if (!pipeline || block_width == 0) return 0;
NSUInteger nth = block_width;
const NSUInteger max_threads = pipeline.maxTotalThreadsPerThreadgroup;
if (nth > max_threads) nth = max_threads;
if (nth == 0) nth = 1;
const NSUInteger nblocks = ((NSUInteger)n_embd + block_width - 1u) / block_width;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:weight offset:weight_offset atIndex:1];
if (tokens) {
[enc setBuffer:tokens offset:tokens_offset atIndex:2];
} else {
[enc setBytes:single_token length:sizeof(*single_token) atIndex:2];
}
[enc setBuffer:out offset:out_offset atIndex:3];
[enc dispatchThreadgroups:MTLSizeMake(nblocks, n_tokens, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_repeat_hc_embedding(
id<MTLCommandBuffer> cb,
id<MTLBuffer> rows,
NSUInteger rows_offset,
id<MTLBuffer> out,
NSUInteger out_offset,
uint32_t n_tokens,
uint32_t n_embd,
uint32_t n_hc) {
if (!cb || !rows || !out || n_tokens == 0 || n_embd == 0 || n_hc == 0) return 0;
const uint64_t embd_bytes = (uint64_t)n_embd * sizeof(float);
ds4_gpu_repeat_args args = {
.ne00 = (int32_t)n_embd,
.ne01 = 1,
.ne02 = (int32_t)n_tokens,
.ne03 = 1,
.nb00 = sizeof(float),
.nb01 = embd_bytes,
.nb02 = embd_bytes,
.nb03 = (uint64_t)n_tokens * embd_bytes,
.ne0 = (int32_t)n_embd,
.ne1 = (int32_t)n_hc,
.ne2 = (int32_t)n_tokens,
.ne3 = 1,
.nb0 = sizeof(float),
.nb1 = embd_bytes,
.nb2 = (uint64_t)n_hc * embd_bytes,
.nb3 = (uint64_t)n_tokens * n_hc * embd_bytes,
};
NSUInteger nth = (NSUInteger)n_embd;
const NSUInteger max_threads = g_repeat_f32_pipeline.maxTotalThreadsPerThreadgroup;
if (nth > max_threads) nth = max_threads;
if (nth == 0) nth = 1;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:g_repeat_f32_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:rows offset:rows_offset atIndex:1];
[enc setBuffer:out offset:out_offset atIndex:2];
[enc dispatchThreadgroups:MTLSizeMake(n_hc, n_tokens, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
int ds4_gpu_embed_token_q8_0_tensor(
ds4_gpu_tensor *out,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint32_t n_vocab,
uint32_t token,
uint32_t n_embd) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!out || !model_map || n_vocab == 0 || token >= n_vocab || n_embd == 0) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out);
const uint64_t out_bytes = (uint64_t)n_embd * sizeof(float);
if (!outbuf || ds4_gpu_tensor_bytes(out) < out_bytes) {
fprintf(stderr, "ds4: Metal Q8_0 embedding received undersized output buffer\n");
return 0;
}
uint64_t weight_bytes = 0;
if (!ds4_gpu_q8_0_table_bytes(n_vocab, n_embd, &weight_bytes) ||
weight_offset > model_size ||
weight_bytes > model_size - weight_offset) {
fprintf(stderr, "ds4: Metal Q8_0 embedding range is outside the mapped model\n");
return 0;
}
uint64_t inner_offset = 0;
uint32_t token_for_kernel = token;
id<MTLBuffer> wbuf = nil;
const bool exact_token_row =
getenv("DS4_METAL_DISABLE_TOKEN_EMBED_EXACT_VIEW") == NULL;
if (exact_token_row) {
const uint64_t row_bytes = ds4_gpu_q8_0_row_bytes(n_embd);
const uint64_t token_rel = (uint64_t)token * row_bytes;
if (token_rel > weight_bytes || row_bytes > weight_bytes - token_rel) {
fprintf(stderr, "ds4: Metal Q8_0 embedding token row is outside the mapped table\n");
return 0;
}
wbuf = ds4_gpu_wrap_model_exact_range(model_map,
model_size,
weight_offset + token_rel,
row_bytes,
&inner_offset);
token_for_kernel = 0;
} else {
wbuf = ds4_gpu_wrap_model_range(model_map,
model_size,
weight_offset,
weight_bytes,
&inner_offset);
}
if (!wbuf) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
const int32_t token_i32 = (int32_t)token_for_kernel;
if (!ds4_gpu_encode_get_rows_q8_0(cb,
wbuf,
(NSUInteger)inner_offset,
nil,
0,
&token_i32,
outbuf,
ds4_gpu_tensor_offset(out),
n_vocab,
1,
n_embd)) {
return 0;
}
if (!ds4_gpu_finish_command_buffer(cb, owned, "q8_0 embed token")) return 0;
}
return 1;
}
int ds4_gpu_embed_tokens_q8_0_tensor(
ds4_gpu_tensor *out,
const ds4_gpu_tensor *tokens,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint32_t n_vocab,
uint32_t n_tokens,
uint32_t n_embd) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!out || !tokens || !model_map || n_vocab == 0 || n_tokens == 0 || n_embd == 0) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out);
id<MTLBuffer> tokbuf = ds4_gpu_tensor_buffer(tokens);
const uint64_t out_bytes = (uint64_t)n_tokens * n_embd * sizeof(float);
const uint64_t token_bytes = (uint64_t)n_tokens * sizeof(int32_t);
if (!outbuf || !tokbuf ||
ds4_gpu_tensor_bytes(out) < out_bytes ||
ds4_gpu_tensor_bytes(tokens) < token_bytes) {
fprintf(stderr, "ds4: Metal Q8_0 batched embedding received undersized buffers\n");
return 0;
}
uint64_t weight_bytes = 0;
if (!ds4_gpu_q8_0_table_bytes(n_vocab, n_embd, &weight_bytes) ||
weight_offset > model_size ||
weight_bytes > model_size - weight_offset) {
fprintf(stderr, "ds4: Metal Q8_0 batched embedding range is outside the mapped model\n");
return 0;
}
uint64_t inner_offset = 0;
id<MTLBuffer> wbuf =
ds4_gpu_wrap_model_range(model_map,
model_size,
weight_offset,
weight_bytes,
&inner_offset);
if (!wbuf) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
if (!ds4_gpu_encode_get_rows_q8_0(cb,
wbuf,
(NSUInteger)inner_offset,
tokbuf,
ds4_gpu_tensor_offset(tokens),
NULL,
outbuf,
ds4_gpu_tensor_offset(out),
n_vocab,
n_tokens,
n_embd)) {
return 0;
}
if (!ds4_gpu_finish_command_buffer(cb, owned, "q8_0 embed tokens")) return 0;
}
return 1;
}
int ds4_gpu_embed_token_quant_tensor(
ds4_gpu_tensor *out,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint32_t weight_type,
uint32_t n_vocab,
uint32_t token,
uint32_t n_embd) {
if (weight_type == DS4_METAL_TENSOR_Q8_0) {
return ds4_gpu_embed_token_q8_0_tensor(out,
model_map,
model_size,
weight_offset,
n_vocab,
token,
n_embd);
}
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!out || !model_map || n_vocab == 0 || token >= n_vocab || n_embd == 0) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out);
const uint64_t out_bytes = (uint64_t)n_embd * sizeof(float);
if (!outbuf || ds4_gpu_tensor_bytes(out) < out_bytes) {
fprintf(stderr, "ds4: Metal quant embedding received undersized output buffer\n");
return 0;
}
uint64_t weight_bytes = 0;
uint64_t row_bytes = 0;
if (!ds4_gpu_quant_table_bytes(weight_type, n_vocab, n_embd, &weight_bytes) ||
!ds4_gpu_quant_row_bytes(weight_type, n_embd, &row_bytes) ||
weight_offset > model_size ||
weight_bytes > model_size - weight_offset) {
fprintf(stderr, "ds4: Metal quant embedding range is outside the mapped model\n");
return 0;
}
const uint64_t token_rel = (uint64_t)token * row_bytes;
if (token_rel > weight_bytes || row_bytes > weight_bytes - token_rel) {
fprintf(stderr, "ds4: Metal quant embedding token row is outside the mapped table\n");
return 0;
}
uint64_t inner_offset = 0;
id<MTLBuffer> wbuf =
ds4_gpu_wrap_model_exact_range(model_map,
model_size,
weight_offset + token_rel,
row_bytes,
&inner_offset);
if (!wbuf) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
const int32_t token_i32 = 0;
if (!ds4_gpu_encode_get_rows_quant(cb,
wbuf,
(NSUInteger)inner_offset,
weight_type,
nil,
0,
&token_i32,
outbuf,
ds4_gpu_tensor_offset(out),
n_vocab,
1,
n_embd)) {
return 0;
}
if (!ds4_gpu_finish_command_buffer(cb, owned, "quant embed token")) return 0;
}
return 1;
}
int ds4_gpu_embed_tokens_quant_tensor(
ds4_gpu_tensor *out,
const ds4_gpu_tensor *tokens,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint32_t weight_type,
uint32_t n_vocab,
uint32_t n_tokens,
uint32_t n_embd) {
if (weight_type == DS4_METAL_TENSOR_Q8_0) {
return ds4_gpu_embed_tokens_q8_0_tensor(out,
tokens,
model_map,
model_size,
weight_offset,
n_vocab,
n_tokens,
n_embd);
}
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!out || !tokens || !model_map || n_vocab == 0 || n_tokens == 0 || n_embd == 0) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out);
id<MTLBuffer> tokbuf = ds4_gpu_tensor_buffer(tokens);
const uint64_t out_bytes = (uint64_t)n_tokens * n_embd * sizeof(float);
const uint64_t token_bytes = (uint64_t)n_tokens * sizeof(int32_t);
if (!outbuf || !tokbuf ||
ds4_gpu_tensor_bytes(out) < out_bytes ||
ds4_gpu_tensor_bytes(tokens) < token_bytes) {
fprintf(stderr, "ds4: Metal quant batched embedding received undersized buffers\n");
return 0;
}
uint64_t weight_bytes = 0;
if (!ds4_gpu_quant_table_bytes(weight_type, n_vocab, n_embd, &weight_bytes) ||
weight_offset > model_size ||
weight_bytes > model_size - weight_offset) {
fprintf(stderr, "ds4: Metal quant batched embedding range is outside the mapped model\n");
return 0;
}
uint64_t inner_offset = 0;
id<MTLBuffer> wbuf =
ds4_gpu_wrap_model_range(model_map,
model_size,
weight_offset,
weight_bytes,
&inner_offset);
if (!wbuf) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
if (!ds4_gpu_encode_get_rows_quant(cb,
wbuf,
(NSUInteger)inner_offset,
weight_type,
tokbuf,
ds4_gpu_tensor_offset(tokens),
NULL,
outbuf,
ds4_gpu_tensor_offset(out),
n_vocab,
n_tokens,
n_embd)) {
return 0;
}
if (!ds4_gpu_finish_command_buffer(cb, owned, "quant embed tokens")) return 0;
}
return 1;
}
int ds4_gpu_embed_token_hc_tensor(
ds4_gpu_tensor *out_hc,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint32_t n_vocab,
uint32_t token,
uint32_t n_embd,
uint32_t n_hc) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!out_hc || !model_map || n_vocab == 0 || token >= n_vocab || n_embd == 0 || n_hc == 0) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out_hc);
const uint64_t out_bytes = (uint64_t)n_embd * n_hc * sizeof(float);
if (!outbuf || ds4_gpu_tensor_bytes(out_hc) < out_bytes) {
fprintf(stderr, "ds4: Metal graph embedding received undersized HC output buffer\n");
return 0;
}
const uint64_t weight_bytes = (uint64_t)n_vocab * n_embd * sizeof(uint16_t);
if (weight_offset > model_size || weight_bytes > model_size - weight_offset) {
fprintf(stderr, "ds4: Metal graph embedding range is outside the mapped model\n");
return 0;
}
uint64_t inner_offset = 0;
id<MTLBuffer> wbuf =
ds4_gpu_wrap_model_range(model_map,
model_size,
weight_offset,
weight_bytes,
&inner_offset);
if (!wbuf) return 0;
const NSUInteger row_bytes = (NSUInteger)n_embd * sizeof(float);
if (!ds4_gpu_ensure_scratch_buffer(&g_embed_rows_buffer,
&g_embed_rows_bytes,
row_bytes,
"ds4_embed_rows")) {
return 0;
}
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
const int32_t token_i32 = (int32_t)token;
const uint64_t src_row_bytes = (uint64_t)n_embd * sizeof(uint16_t);
const uint64_t dst_row_bytes = (uint64_t)n_embd * sizeof(float);
ds4_gpu_get_rows_args args = {
.ne00t = (int32_t)n_embd,
.ne00 = (int32_t)n_embd,
.nb01 = src_row_bytes,
.nb02 = (uint64_t)n_vocab * src_row_bytes,
.nb03 = (uint64_t)n_vocab * src_row_bytes,
.ne10 = 1,
.nb10 = sizeof(int32_t),
.nb11 = sizeof(int32_t),
.nb12 = sizeof(int32_t),
.nb1 = dst_row_bytes,
.nb2 = dst_row_bytes,
.nb3 = dst_row_bytes,
};
NSUInteger nth = (NSUInteger)n_embd;
const NSUInteger max_threads = g_get_rows_f16_pipeline.maxTotalThreadsPerThreadgroup;
if (nth > max_threads) nth = max_threads;
if (nth == 0) nth = 1;
const NSUInteger nw0 = ((NSUInteger)n_embd + nth - 1u) / nth;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:g_get_rows_f16_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:wbuf offset:(NSUInteger)inner_offset atIndex:1];
[enc setBytes:&token_i32 length:sizeof(token_i32) atIndex:2];
[enc setBuffer:g_embed_rows_buffer offset:0 atIndex:3];
[enc dispatchThreadgroups:MTLSizeMake(nw0, 1, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_encode_repeat_hc_embedding(cb,
g_embed_rows_buffer,
0,
outbuf,
ds4_gpu_tensor_offset(out_hc),
1,
n_embd,
n_hc)) {
return 0;
}
if (!ds4_gpu_finish_command_buffer(cb, owned, "graph embed token")) return 0;
}
return 1;
}
int ds4_gpu_embed_tokens_hc_tensor(
ds4_gpu_tensor *out_hc,
const ds4_gpu_tensor *tokens,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint32_t n_vocab,
uint32_t n_tokens,
uint32_t n_embd,
uint32_t n_hc) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!out_hc || !tokens || !model_map || n_vocab == 0 || n_tokens == 0 || n_embd == 0 || n_hc == 0) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out_hc);
id<MTLBuffer> tokbuf = ds4_gpu_tensor_buffer(tokens);
const uint64_t out_bytes = (uint64_t)n_tokens * n_embd * n_hc * sizeof(float);
const uint64_t token_bytes = (uint64_t)n_tokens * sizeof(int32_t);
if (!outbuf || !tokbuf ||
ds4_gpu_tensor_bytes(out_hc) < out_bytes ||
ds4_gpu_tensor_bytes(tokens) < token_bytes) {
fprintf(stderr, "ds4: Metal graph batched embedding received undersized buffers\n");
return 0;
}
const uint64_t weight_bytes = (uint64_t)n_vocab * n_embd * sizeof(uint16_t);
if (weight_offset > model_size || weight_bytes > model_size - weight_offset) {
fprintf(stderr, "ds4: Metal graph batched embedding range is outside the mapped model\n");
return 0;
}
uint64_t inner_offset = 0;
id<MTLBuffer> wbuf =
ds4_gpu_wrap_model_range(model_map,
model_size,
weight_offset,
weight_bytes,
&inner_offset);
if (!wbuf) return 0;
const NSUInteger rows_bytes = (NSUInteger)n_tokens * n_embd * sizeof(float);
if (!ds4_gpu_ensure_scratch_buffer(&g_embed_rows_buffer,
&g_embed_rows_bytes,
rows_bytes,
"ds4_embed_rows")) {
return 0;
}
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
if (!ds4_gpu_encode_get_rows_f16(cb,
wbuf,
(NSUInteger)inner_offset,
tokbuf,
ds4_gpu_tensor_offset(tokens),
g_embed_rows_buffer,
0,
n_vocab,
n_tokens,
n_embd) ||
!ds4_gpu_encode_repeat_hc_embedding(cb,
g_embed_rows_buffer,
0,
outbuf,
ds4_gpu_tensor_offset(out_hc),
n_tokens,
n_embd,
n_hc)) {
return 0;
}
if (!ds4_gpu_finish_command_buffer(cb, owned, "graph embed tokens")) return 0;
}
return 1;
}
int ds4_gpu_set_model_map_range(const void *model_map, uint64_t model_size, uint64_t map_offset, uint64_t map_size, uint64_t max_tensor_bytes) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!model_map || model_size == 0) return 0;
if (map_offset > model_size || map_size == 0 || map_size > model_size - map_offset) return 0;
max_tensor_bytes = ds4_gpu_effective_model_max_tensor_bytes(map_size, max_tensor_bytes);
@autoreleasepool {
if (g_model_map_ptr == model_map &&
g_model_map_size == model_size &&
g_model_mapped_offset == map_offset &&
g_model_mapped_size == map_size &&
g_model_mapped_max_tensor_bytes == max_tensor_bytes) {
return 1;
}
for (uint32_t i = 0; i < g_model_view_count; i++) {
if (g_model_views[i].model_map == model_map &&
g_model_views[i].model_size == model_size &&
map_offset >= g_model_views[i].model_offset &&
map_offset + map_size <= g_model_views[i].model_offset + g_model_views[i].bytes) {
return 1;
}
}
ds4_gpu_model_residency_clear();
if (!ds4_gpu_map_model_views(model_map, model_size, map_offset, map_size, max_tensor_bytes)) {
ds4_gpu_model_residency_clear();
return 0;
}
g_model_map_ptr = model_map;
g_model_map_size = model_size;
g_model_mapped_offset = map_offset;
g_model_mapped_size = map_size;
g_model_mapped_max_tensor_bytes = max_tensor_bytes;
if (ds4_gpu_model_map_log_enabled()) {
fprintf(stderr,
"ds4: Metal mapped mmaped model as %u overlapping shared buffers\n",
g_model_view_count);
}
return 1;
}
}
static int ds4_gpu_model_views_cover_spans(
const void *model_map,
uint64_t model_size,
const uint64_t *offsets,
const uint64_t *sizes,
uint32_t count);
int ds4_gpu_set_model_map_spans(
const void *model_map,
uint64_t model_size,
const uint64_t *offsets,
const uint64_t *sizes,
uint32_t count,
uint64_t max_tensor_bytes) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!model_map || model_size == 0 || !offsets || !sizes || count == 0) return 0;
if (count == 1 && !g_ssd_streaming_mode) {
return ds4_gpu_set_model_map_range(model_map,
model_size,
offsets[0],
sizes[0],
max_tensor_bytes);
}
if (ds4_gpu_model_views_cover_spans(model_map, model_size, offsets, sizes, count)) {
return 1;
}
@autoreleasepool {
const double t0 = ds4_gpu_now_ms();
max_tensor_bytes = ds4_gpu_effective_model_max_tensor_bytes(model_size, max_tensor_bytes);
ds4_gpu_model_residency_clear();
/* Streaming changes the target's active layer, not the auxiliary
* vision/drafter models. Single spans need the same bounded lifetime
* as disjoint spans instead of accumulating every visited layer. */
if (g_ssd_streaming_mode) ds4_gpu_model_views_remove_map(model_map);
else ds4_gpu_model_views_clear();
uint64_t mapped_total = 0;
uint64_t first_offset = UINT64_MAX;
for (uint32_t i = 0; i < count; i++) {
if (offsets[i] > model_size || sizes[i] == 0 || sizes[i] > model_size - offsets[i]) {
fprintf(stderr, "ds4: Metal model span %u is outside the GGUF mapping\n", i);
ds4_gpu_model_residency_clear();
ds4_gpu_model_views_clear();
return 0;
}
if (offsets[i] < first_offset) first_offset = offsets[i];
uint64_t effective_max = max_tensor_bytes;
if (effective_max > sizes[i]) effective_max = sizes[i];
if (!ds4_gpu_add_model_view_range(model_map,
model_size,
offsets[i],
sizes[i],
effective_max,
true,
&mapped_total)) {
ds4_gpu_model_residency_clear();
ds4_gpu_model_views_clear();
return 0;
}
}
if (!ds4_gpu_finish_model_views(t0, mapped_total, first_offset)) {
ds4_gpu_model_residency_clear();
ds4_gpu_model_views_clear();
return 0;
}
g_model_map_ptr = model_map;
g_model_map_size = model_size;
g_model_mapped_offset = first_offset == UINT64_MAX ? 0 : first_offset;
g_model_mapped_size = mapped_total;
g_model_mapped_max_tensor_bytes = max_tensor_bytes;
if (ds4_gpu_model_map_log_enabled()) {
fprintf(stderr,
"ds4: Metal mapped mmaped model as %u disjoint shared buffers across %u tensor spans\n",
g_model_view_count,
count);
}
return 1;
}
}
int ds4_gpu_set_model_map(const void *model_map, uint64_t model_size) {
return ds4_gpu_set_model_map_range(model_map, model_size, 0, model_size, 0);
}
int ds4_gpu_set_model_fd(int fd) {
g_model_fd = fd;
return 1;
}
static int ds4_gpu_model_views_cover_range(
const void *model_map,
uint64_t model_size,
uint64_t offset,
uint64_t size) {
if (!model_map || model_size == 0 || size == 0 ||
offset > model_size || size > model_size - offset) {
return 0;
}
const uint64_t end = offset + size;
for (uint32_t i = 0; i < g_model_view_count; i++) {
if (g_model_views[i].model_map != model_map ||
g_model_views[i].model_size != model_size) {
continue;
}
const uint64_t view_start = g_model_views[i].model_offset;
const uint64_t view_end = view_start + g_model_views[i].bytes;
if (offset >= view_start && end <= view_end) return 1;
}
return 0;
}
static int ds4_gpu_model_views_cover_spans(
const void *model_map,
uint64_t model_size,
const uint64_t *offsets,
const uint64_t *sizes,
uint32_t count) {
if (!offsets || !sizes || count == 0) return 0;
for (uint32_t i = 0; i < count; i++) {
if (!ds4_gpu_model_views_cover_range(model_map,
model_size,
offsets[i],
sizes[i])) {
return 0;
}
}
return 1;
}
int ds4_gpu_set_model_fd_for_map(int fd, const void *model_map) {
(void)fd;
(void)model_map;
return 1;
}
static id<MTLBuffer> ds4_gpu_wrap_model_range(
const void *model_map,
uint64_t model_size,
uint64_t offset,
uint64_t len,
uint64_t *inner_offset) {
(void)model_map;
if (model_size == 0 || offset > model_size || len > model_size - offset) {
fprintf(stderr, "ds4: Metal model range is outside the mapped model\n");
return nil;
}
const uint64_t end = offset + len;
for (uint32_t i = 0; i < g_model_view_count; i++) {
if (g_model_views[i].model_map != model_map ||
g_model_views[i].model_size != model_size) {
continue;
}
const uint64_t view_start = g_model_views[i].model_offset;
const uint64_t view_end = view_start + g_model_views[i].bytes;
if (offset >= view_start && end <= view_end) {
*inner_offset = offset - view_start;
return g_model_views[i].buffer;
}
}
fprintf(stderr,
"ds4: Metal model range %.2f..%.2f GiB is not covered by mapped model views\n",
ds4_gpu_gib(offset),
ds4_gpu_gib(end));
return nil;
}
typedef enum {
DS4_GPU_EXACT_VIEW_CACHED,
DS4_GPU_EXACT_VIEW_TRANSIENT,
DS4_GPU_EXACT_VIEW_OWNED,
} ds4_gpu_exact_view_lifetime;
static id<MTLBuffer> ds4_gpu_wrap_model_exact_range_impl(
const void *model_map,
uint64_t model_size,
uint64_t offset,
uint64_t len,
uint64_t *inner_offset,
ds4_gpu_exact_view_lifetime lifetime) {
const bool cache_view = lifetime == DS4_GPU_EXACT_VIEW_CACHED;
const bool transient_view = lifetime == DS4_GPU_EXACT_VIEW_TRANSIENT;
if (!model_map || !g_device ||
(cache_view && !g_model_buffer_cache) ||
(transient_view && !g_transient_buffers) ||
model_size == 0 || offset > model_size || len > model_size - offset) {
fprintf(stderr, "ds4: Metal exact model range is outside the mapped model\n");
return nil;
}
const uint64_t page = (uint64_t)getpagesize();
const uint64_t page_offset = offset & ~(page - 1);
const uint64_t leading = offset - page_offset;
if (len > UINT64_MAX - leading ||
leading + len > UINT64_MAX - (page - 1)) {
fprintf(stderr, "ds4: Metal exact model range overflows page alignment\n");
return nil;
}
uint64_t view_bytes = round_up_u64(leading + len, page);
if (view_bytes > model_size - page_offset) view_bytes = model_size - page_offset;
if (leading + len > view_bytes) {
fprintf(stderr, "ds4: Metal exact model range alignment exceeds mapped model\n");
return nil;
}
if (view_bytes > (uint64_t)[g_device maxBufferLength]) {
fprintf(stderr,
"ds4: Metal exact model range %.2f GiB exceeds maxBufferLength %.2f GiB\n",
ds4_gpu_gib(view_bytes),
ds4_gpu_gib((uint64_t)[g_device maxBufferLength]));
return nil;
}
NSString *key = nil;
id<MTLBuffer> buffer = nil;
if (cache_view) {
key = [NSString stringWithFormat:@"%p:%llu:%llu:%llu",
model_map,
(unsigned long long)model_size,
(unsigned long long)page_offset,
(unsigned long long)view_bytes];
buffer = [g_model_buffer_cache objectForKey:key];
}
if (!buffer) {
const uintptr_t base = (uintptr_t)model_map;
buffer = [g_device newBufferWithBytesNoCopy:(void *)(base + page_offset)
length:(NSUInteger)view_bytes
options:ds4_gpu_model_resource_options()
deallocator:nil];
if (!buffer) {
fprintf(stderr,
"ds4: Metal could not wrap exact mmaped model range at %.2f GiB, size %.2f MiB\n",
ds4_gpu_gib(page_offset),
ds4_gpu_mib(view_bytes));
return nil;
}
if (cache_view) {
buffer.label = @"ds4_model_exact_view";
} else if (transient_view) {
buffer.label = @"ds4_model_exact_transient_view";
} else {
buffer.label = @"ds4_model_exact_owned_view";
}
if (cache_view) {
[g_model_buffer_cache setObject:buffer forKey:key];
ds4_gpu_model_buffer_cache_note_insert(view_bytes);
} else if (transient_view) {
[g_transient_buffers addObject:buffer];
}
}
if (inner_offset) *inner_offset = leading;
return buffer;
}
static id<MTLBuffer> ds4_gpu_wrap_model_exact_range(
const void *model_map,
uint64_t model_size,
uint64_t offset,
uint64_t len,
uint64_t *inner_offset) {
return ds4_gpu_wrap_model_exact_range_impl(model_map,
model_size,
offset,
len,
inner_offset,
DS4_GPU_EXACT_VIEW_CACHED);
}
static id<MTLBuffer> ds4_gpu_wrap_model_exact_range_transient(
const void *model_map,
uint64_t model_size,
uint64_t offset,
uint64_t len,
uint64_t *inner_offset) {
return ds4_gpu_wrap_model_exact_range_impl(model_map,
model_size,
offset,
len,
inner_offset,
DS4_GPU_EXACT_VIEW_TRANSIENT);
}
static id<MTLBuffer> ds4_gpu_wrap_model_exact_range_owned(
const void *model_map,
uint64_t model_size,
uint64_t offset,
uint64_t len,
uint64_t *inner_offset) {
return ds4_gpu_wrap_model_exact_range_impl(model_map,
model_size,
offset,
len,
inner_offset,
DS4_GPU_EXACT_VIEW_OWNED);
}
uint32_t ds4_gpu_stream_expert_cache_configured_count(void) {
uint32_t budget = ds4_gpu_stream_expert_cache_configured_budget();
if (budget > DS4_METAL_STREAM_EXPERT_CACHE_MAX_ENTRIES) {
budget = DS4_METAL_STREAM_EXPERT_CACHE_MAX_ENTRIES;
}
return budget;
}
uint32_t ds4_gpu_stream_expert_cache_current_count(void) {
return g_stream_expert_cache_entry_count;
}
uint32_t ds4_gpu_stream_expert_cache_budget_for_expert_size(
uint64_t gate_expert_bytes,
uint64_t down_expert_bytes) {
if (!ds4_gpu_stream_expert_cache_note_expert_size(gate_expert_bytes,
down_expert_bytes)) {
return 0;
}
return ds4_gpu_stream_expert_cache_configured_budget();
}
static int ds4_gpu_stream_expert_cache_note_expert_size(
uint64_t gate_expert_bytes,
uint64_t down_expert_bytes) {
if (gate_expert_bytes == 0 || down_expert_bytes == 0) return 0;
if (gate_expert_bytes > (UINT64_MAX - down_expert_bytes) / 2ull) {
fprintf(stderr, "ds4: Metal streaming expert cache byte size overflow\n");
return 0;
}
/*
* The cache is a single-size-class slab allocator: the expert byte size is
* frozen on first sight (or pre-seeded at startup from the model's slab
* class) and off-size layers are REJECTED rather than adopted. A rejected
* layer (mixed-precision boost: Q4_K experts among IQ2 layers) falls back
* to the mapped-model per-expert path; last-writer-wins here would instead
* poison the slab size class and deadlock slab reuse.
*/
const uint64_t bytes = gate_expert_bytes * 2ull + down_expert_bytes;
if (g_stream_expert_cache_expert_bytes == 0) {
g_stream_expert_cache_expert_bytes = bytes;
return 1;
}
return bytes == g_stream_expert_cache_expert_bytes;
}
static uint32_t ds4_gpu_stream_expert_cache_requested_budget(void) {
if (!g_ssd_streaming_mode) return 0;
if (g_stream_expert_cache_budget_override != 0) {
return g_stream_expert_cache_budget_override;
}
return 0;
}
static uint32_t ds4_gpu_stream_expert_cache_configured_budget(void) {
uint32_t budget = ds4_gpu_stream_expert_cache_requested_budget();
if (budget != 0 &&
g_stream_expert_cache_mlock_budget_cap != 0 &&
budget > g_stream_expert_cache_mlock_budget_cap) {
budget = g_stream_expert_cache_mlock_budget_cap;
}
return budget;
}
static uint32_t ds4_gpu_stream_expert_cache_effective_cap(
uint32_t layer,
uint32_t n_total_expert,
uint32_t n_selected) {
(void)layer;
if (ds4_gpu_stream_expert_cache_configured_budget() == 0) return 0;
/*
* The residency policy is global: every layer can use any expert slot it
* routes to, and global pruning decides which existing entry is least
* valuable. A per-layer cap made cache size depend on model depth rather
* than the actual byte budget.
*/
uint32_t cap = n_total_expert;
if (cap < n_selected) cap = n_selected;
if (cap > DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT) {
cap = DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT;
}
return cap;
}
static int ds4_gpu_stream_expert_timing_summary_enabled(void) {
static int checked = 0;
static int enabled = 0;
if (!checked) {
enabled =
(getenv("DS4_METAL_STREAMING_EXPERT_TIMING_SUMMARY") != NULL ||
getenv("DS4_METAL_STREAMING_EXPERT_PROFILE_SUMMARY") != NULL) &&
getenv("DS4_METAL_DISABLE_STREAMING_EXPERT_TIMING_SUMMARY") == NULL;
checked = 1;
}
return enabled;
}
static uint32_t ds4_gpu_stream_expert_popcount(uint32_t mask) {
return (uint32_t)__builtin_popcount(mask);
}
static int ds4_gpu_stream_expert_split_worthwhile(
uint32_t resident_mask,
uint32_t missing_mask) {
if (resident_mask == 0 || missing_mask == 0) return 0;
/*
* The split path pays an extra command stage and a second routed-expert
* bind. It is worthwhile when several experts are missing and their SSD
* reads can be hidden by resident expert work. With one or two misses,
* especially in large caches, a single unsplit routed pass is faster.
*/
return ds4_gpu_stream_expert_popcount(missing_mask) >= 3u;
}
static void ds4_gpu_stream_expert_timing_note_selected(
double sync_ms,
double copy_ms,
double bind_ms) {
if (!ds4_gpu_stream_expert_timing_summary_enabled()) return;
g_stream_expert_timing_selected_calls++;
g_stream_expert_timing_selected_sync_ms += sync_ms;
g_stream_expert_timing_selected_copy_ms += copy_ms;
g_stream_expert_timing_selected_read_ms += sync_ms + copy_ms;
g_stream_expert_timing_selected_bind_ms += bind_ms;
}
static void ds4_gpu_stream_expert_timing_note_split(
uint32_t resident_mask,
uint32_t missing_mask,
double resident_ms,
double missing_ms) {
if (!ds4_gpu_stream_expert_timing_summary_enabled()) return;
g_stream_expert_timing_split_layers++;
g_stream_expert_timing_split_resident_experts +=
ds4_gpu_stream_expert_popcount(resident_mask);
g_stream_expert_timing_split_missing_experts +=
ds4_gpu_stream_expert_popcount(missing_mask);
g_stream_expert_timing_split_resident_ms += resident_ms;
g_stream_expert_timing_split_missing_ms += missing_ms;
}
static void ds4_gpu_stream_expert_timing_note_split_missing_detail(
double load_ms,
double slot_ms,
double prune_ms,
double addr_ms,
double wait_ms) {
if (!ds4_gpu_stream_expert_timing_summary_enabled()) return;
g_stream_expert_timing_split_missing_load_ms += load_ms;
g_stream_expert_timing_split_missing_slot_ms += slot_ms;
g_stream_expert_timing_split_missing_prune_ms += prune_ms;
g_stream_expert_timing_split_missing_addr_ms += addr_ms;
g_stream_expert_timing_split_missing_wait_ms += wait_ms;
}
static void ds4_gpu_stream_expert_timing_note_load_detail(
double prepare_ms,
double pread_ms,
double install_ms) {
if (!ds4_gpu_stream_expert_timing_summary_enabled()) return;
g_stream_expert_timing_load_calls++;
g_stream_expert_timing_load_prepare_ms += prepare_ms;
g_stream_expert_timing_load_pread_ms += pread_ms;
g_stream_expert_timing_load_install_ms += install_ms;
}
static void ds4_gpu_stream_expert_timing_note_prepare_batch_reuse(double ms) {
if (!ds4_gpu_stream_expert_timing_summary_enabled()) return;
g_stream_expert_timing_prepare_batch_reuse_calls++;
g_stream_expert_timing_prepare_batch_reuse_ms += ms;
}
static void ds4_gpu_stream_expert_timing_note_prepare_buffer(double ms) {
if (!ds4_gpu_stream_expert_timing_summary_enabled()) return;
g_stream_expert_timing_prepare_buffer_calls++;
g_stream_expert_timing_prepare_buffer_ms += ms;
}
static void ds4_gpu_stream_expert_timing_note_prepare_task(
uint32_t experts,
double ms) {
if (!ds4_gpu_stream_expert_timing_summary_enabled()) return;
g_stream_expert_timing_prepare_task_experts += experts;
g_stream_expert_timing_prepare_task_ms += ms;
}
static void ds4_gpu_stream_expert_timing_note_reuse_scan(
uint64_t entries,
double ms) {
if (!ds4_gpu_stream_expert_timing_summary_enabled()) return;
g_stream_expert_timing_reuse_scan_calls++;
g_stream_expert_timing_reuse_scan_entries += entries;
g_stream_expert_timing_reuse_scan_ms += ms;
}
static void ds4_gpu_stream_expert_timing_note_reuse_clear(double ms) {
if (!ds4_gpu_stream_expert_timing_summary_enabled()) return;
g_stream_expert_timing_reuse_clear_ms += ms;
}
static void ds4_gpu_stream_expert_timing_note_cache_class(
uint32_t resident_mask,
uint32_t missing_mask) {
if (!ds4_gpu_stream_expert_timing_summary_enabled()) return;
const uint32_t resident = ds4_gpu_stream_expert_popcount(resident_mask);
const uint32_t missing = ds4_gpu_stream_expert_popcount(missing_mask);
if (missing == 0) {
g_stream_expert_timing_cache_all_resident_layers++;
} else if (resident == 0) {
g_stream_expert_timing_cache_all_missing_layers++;
} else {
g_stream_expert_timing_cache_mixed_layers++;
}
g_stream_expert_timing_cache_resident_experts += resident;
g_stream_expert_timing_cache_missing_experts += missing;
}
static int ds4_gpu_stream_expert_readahead_enabled(void) {
return g_ssd_streaming_mode &&
getenv("DS4_METAL_DISABLE_STREAMING_EXPERT_READAHEAD") == NULL;
}
static void ds4_gpu_stream_expert_readahead_range(uint64_t offset, uint64_t len) {
if (!ds4_gpu_stream_expert_readahead_enabled() || g_model_fd < 0 || len == 0) {
return;
}
#if defined(F_RDADVISE)
const int timing = ds4_gpu_stream_expert_timing_summary_enabled();
const double t0 = timing ? ds4_gpu_now_ms() : 0.0;
uint64_t pos = offset;
uint64_t rem = len;
while (rem > 0) {
const uint64_t chunk64 =
rem > (uint64_t)INT_MAX ? (uint64_t)INT_MAX : rem;
if (pos > (uint64_t)LLONG_MAX) break;
struct radvisory ra;
ra.ra_offset = (off_t)pos;
ra.ra_count = (int)chunk64;
(void)fcntl(g_model_fd, F_RDADVISE, &ra);
pos += chunk64;
rem -= chunk64;
}
if (timing) {
g_stream_expert_timing_readahead_calls++;
g_stream_expert_timing_readahead_bytes += len;
g_stream_expert_timing_readahead_ms += ds4_gpu_now_ms() - t0;
}
#else
(void)offset;
(void)len;
#endif
}
typedef struct {
uint64_t offset;
uint64_t len;
uint8_t *dst;
uint64_t read_bytes;
double ms;
int ok;
} ds4_gpu_stream_expert_pread_task;
typedef struct {
int active;
const void *model_map;
uint64_t model_size;
uint32_t layer;
uint32_t n_total_expert;
uint32_t n_selected;
uint32_t missing_mask;
uint32_t load_slots[DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED];
uint32_t source_slots[DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED];
uint32_t n_loads;
uint32_t n_tasks;
uint64_t gate_expert_bytes;
uint64_t down_expert_bytes;
int32_t selected_ids[DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED];
uint64_t gate_abs_offsets[DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED];
uint64_t up_abs_offsets[DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED];
uint64_t down_abs_offsets[DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED];
__strong id<MTLBuffer> gate_bufs[DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED];
__strong id<MTLBuffer> up_bufs[DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED];
__strong id<MTLBuffer> down_bufs[DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED];
NSUInteger gate_inners[DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED];
NSUInteger up_inners[DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED];
NSUInteger down_inners[DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED];
ds4_gpu_stream_expert_pread_task tasks[DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED * 3u];
double start_ms;
double prepare_ms;
} ds4_gpu_stream_expert_pending_load;
static ds4_gpu_stream_expert_pending_load g_stream_expert_pending_load;
typedef struct {
int active;
const void *model_map;
uint64_t model_size;
uint32_t layer;
uint32_t n_total_expert;
uint32_t n_selected;
uint64_t gate_offset;
uint64_t up_offset;
uint64_t down_offset;
uint64_t gate_expert_bytes;
uint64_t down_expert_bytes;
int32_t selected_ids[DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED];
} ds4_gpu_glm_stream_selected_prefetch;
static ds4_gpu_glm_stream_selected_prefetch g_glm_stream_selected_prefetch;
typedef struct {
ds4_gpu_stream_expert_pread_task *tasks;
uint32_t n_tasks;
uint32_t worker_index;
uint32_t n_workers;
} ds4_gpu_stream_expert_pread_worker_args;
static void ds4_gpu_stream_expert_cache_note_pread(
uint32_t layer,
uint64_t bytes,
double ms) {
if (g_stream_expert_cache_pread_bytes > UINT64_MAX - bytes) {
g_stream_expert_cache_pread_bytes = UINT64_MAX;
} else {
g_stream_expert_cache_pread_bytes += bytes;
}
g_stream_expert_cache_pread_ms += ms;
if (layer < DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER) {
if (g_stream_expert_cache_layer_pread_bytes[layer] > UINT64_MAX - bytes) {
g_stream_expert_cache_layer_pread_bytes[layer] = UINT64_MAX;
} else {
g_stream_expert_cache_layer_pread_bytes[layer] += bytes;
}
g_stream_expert_cache_layer_pread_ms[layer] += ms;
}
}
static uint32_t ds4_gpu_stream_expert_pread_thread_limit(void) {
uint32_t threads = 9;
const char *env = getenv("DS4_METAL_STREAMING_EXPERT_PREAD_THREADS");
if (env && env[0]) {
char *end = NULL;
unsigned long v = strtoul(env, &end, 10);
if (end != env && *end == '\0') {
threads = v > UINT32_MAX ? UINT32_MAX : (uint32_t)v;
}
}
if (threads == 0) threads = 1;
if (threads > 18) threads = 18;
return threads;
}
static uint32_t ds4_gpu_stream_expert_pread_thread_count(uint32_t n_tasks) {
if (n_tasks <= 1) return n_tasks;
uint32_t threads = ds4_gpu_stream_expert_pread_thread_limit();
if (threads > n_tasks) threads = n_tasks;
return threads;
}
static int ds4_gpu_stream_expert_pread_into(
uint64_t offset,
uint64_t len,
uint8_t *dst,
uint64_t *read_bytes,
double *ms_out) {
if (read_bytes) *read_bytes = 0;
if (ms_out) *ms_out = 0.0;
if (g_model_fd < 0 ||
!dst ||
len == 0 ||
offset > (uint64_t)LLONG_MAX ||
len > (uint64_t)LLONG_MAX - offset) {
return 0;
}
const double t0 = ds4_gpu_now_ms();
uint64_t pos = 0;
int ok = 1;
while (pos < len) {
const uint64_t rem = len - pos;
const size_t want = rem > (uint64_t)SSIZE_MAX ? (size_t)SSIZE_MAX : (size_t)rem;
ssize_t nread;
do {
nread = pread(g_model_fd, dst + pos, want, (off_t)(offset + pos));
} while (nread < 0 && errno == EINTR);
if (nread <= 0) {
ok = 0;
break;
}
pos += (uint64_t)nread;
}
const double dt = ds4_gpu_now_ms() - t0;
if (read_bytes) *read_bytes = pos;
if (ms_out) *ms_out = dt;
if (!ok || pos != len) {
fprintf(stderr,
"ds4: Metal streaming expert explicit pread failed offset=%.2f GiB len=%.2f MiB read=%.2f MiB\n",
ds4_gpu_gib(offset),
ds4_gpu_mib(len),
ds4_gpu_mib(pos));
return 0;
}
return 1;
}
static void *ds4_gpu_stream_expert_pread_worker(void *arg) {
ds4_gpu_stream_expert_pread_worker_args *wa =
(ds4_gpu_stream_expert_pread_worker_args *)arg;
for (uint32_t i = wa->worker_index; i < wa->n_tasks; i += wa->n_workers) {
ds4_gpu_stream_expert_pread_task *task = &wa->tasks[i];
task->ok = ds4_gpu_stream_expert_pread_into(task->offset,
task->len,
task->dst,
&task->read_bytes,
&task->ms);
}
return NULL;
}
static pthread_mutex_t g_stream_expert_pread_pool_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t g_stream_expert_pread_pool_start_cond = PTHREAD_COND_INITIALIZER;
static pthread_cond_t g_stream_expert_pread_pool_done_cond = PTHREAD_COND_INITIALIZER;
static pthread_t g_stream_expert_pread_pool_threads[18];
static uint32_t g_stream_expert_pread_pool_thread_count;
static uint32_t g_stream_expert_pread_pool_active_workers;
static uint32_t g_stream_expert_pread_pool_remaining_workers;
static uint32_t g_stream_expert_pread_pool_n_tasks;
static uint32_t g_stream_expert_pread_pool_next_task;
static uint64_t g_stream_expert_pread_pool_generation;
static ds4_gpu_stream_expert_pread_task *g_stream_expert_pread_pool_tasks;
static int g_stream_expert_pread_pool_initialized;
static int g_stream_expert_pread_pool_stopping;
static int ds4_gpu_stream_expert_pread_pool_enabled(void) {
const char *env = getenv("DS4_METAL_STREAMING_EXPERT_PREAD_POOL");
return !(env && strcmp(env, "0") == 0);
}
static void *ds4_gpu_stream_expert_pread_pool_worker(void *arg) {
const uint32_t worker_index = (uint32_t)(uintptr_t)arg;
uint64_t seen_generation = 0;
for (;;) {
pthread_mutex_lock(&g_stream_expert_pread_pool_mutex);
while (!g_stream_expert_pread_pool_stopping &&
g_stream_expert_pread_pool_generation == seen_generation) {
pthread_cond_wait(&g_stream_expert_pread_pool_start_cond,
&g_stream_expert_pread_pool_mutex);
}
if (g_stream_expert_pread_pool_stopping) {
pthread_mutex_unlock(&g_stream_expert_pread_pool_mutex);
break;
}
seen_generation = g_stream_expert_pread_pool_generation;
if (worker_index >= g_stream_expert_pread_pool_active_workers) {
pthread_mutex_unlock(&g_stream_expert_pread_pool_mutex);
continue;
}
for (;;) {
const uint32_t task_index =
g_stream_expert_pread_pool_next_task++;
if (task_index >= g_stream_expert_pread_pool_n_tasks) break;
ds4_gpu_stream_expert_pread_task *task =
&g_stream_expert_pread_pool_tasks[task_index];
pthread_mutex_unlock(&g_stream_expert_pread_pool_mutex);
task->ok = ds4_gpu_stream_expert_pread_into(task->offset,
task->len,
task->dst,
&task->read_bytes,
&task->ms);
pthread_mutex_lock(&g_stream_expert_pread_pool_mutex);
}
if (g_stream_expert_pread_pool_remaining_workers > 0 &&
--g_stream_expert_pread_pool_remaining_workers == 0) {
g_stream_expert_pread_pool_tasks = NULL;
g_stream_expert_pread_pool_n_tasks = 0;
g_stream_expert_pread_pool_active_workers = 0;
pthread_cond_signal(&g_stream_expert_pread_pool_done_cond);
}
pthread_mutex_unlock(&g_stream_expert_pread_pool_mutex);
}
return NULL;
}
static int ds4_gpu_stream_expert_pread_pool_init(uint32_t n_threads) {
if (g_stream_expert_pread_pool_initialized) return 1;
if (!ds4_gpu_stream_expert_pread_pool_enabled() || n_threads <= 1) return 0;
if (n_threads > 18) n_threads = 18;
pthread_mutex_lock(&g_stream_expert_pread_pool_mutex);
g_stream_expert_pread_pool_thread_count = n_threads;
g_stream_expert_pread_pool_stopping = 0;
g_stream_expert_pread_pool_generation = 0;
g_stream_expert_pread_pool_tasks = NULL;
g_stream_expert_pread_pool_n_tasks = 0;
g_stream_expert_pread_pool_next_task = 0;
g_stream_expert_pread_pool_active_workers = 0;
g_stream_expert_pread_pool_remaining_workers = 0;
pthread_mutex_unlock(&g_stream_expert_pread_pool_mutex);
uint32_t started = 0;
for (uint32_t i = 0; i < n_threads; i++) {
const int rc = pthread_create(&g_stream_expert_pread_pool_threads[i],
NULL,
ds4_gpu_stream_expert_pread_pool_worker,
(void *)(uintptr_t)i);
if (rc != 0) {
fprintf(stderr,
"ds4: Metal streaming expert pread pool thread creation failed: %s\n",
strerror(rc));
pthread_mutex_lock(&g_stream_expert_pread_pool_mutex);
g_stream_expert_pread_pool_stopping = 1;
g_stream_expert_pread_pool_generation++;
pthread_cond_broadcast(&g_stream_expert_pread_pool_start_cond);
pthread_mutex_unlock(&g_stream_expert_pread_pool_mutex);
for (uint32_t j = 0; j < started; j++) {
(void)pthread_join(g_stream_expert_pread_pool_threads[j], NULL);
}
pthread_mutex_lock(&g_stream_expert_pread_pool_mutex);
g_stream_expert_pread_pool_thread_count = 0;
g_stream_expert_pread_pool_stopping = 0;
pthread_mutex_unlock(&g_stream_expert_pread_pool_mutex);
return 0;
}
started++;
}
g_stream_expert_pread_pool_initialized = 1;
return 1;
}
static int ds4_gpu_stream_expert_pread_pool_begin(
ds4_gpu_stream_expert_pread_task *tasks,
uint32_t n_tasks,
uint32_t n_workers) {
if (n_workers <= 1) return 0;
const uint32_t limit = ds4_gpu_stream_expert_pread_thread_limit();
if (!ds4_gpu_stream_expert_pread_pool_init(limit)) return 0;
pthread_mutex_lock(&g_stream_expert_pread_pool_mutex);
if (!g_stream_expert_pread_pool_initialized ||
g_stream_expert_pread_pool_stopping ||
g_stream_expert_pread_pool_thread_count == 0) {
pthread_mutex_unlock(&g_stream_expert_pread_pool_mutex);
return 0;
}
if (n_workers > g_stream_expert_pread_pool_thread_count) {
n_workers = g_stream_expert_pread_pool_thread_count;
}
if (n_workers == 0) {
pthread_mutex_unlock(&g_stream_expert_pread_pool_mutex);
return 0;
}
if (g_stream_expert_pread_pool_remaining_workers != 0 ||
g_stream_expert_pread_pool_tasks != NULL) {
pthread_mutex_unlock(&g_stream_expert_pread_pool_mutex);
return 0;
}
g_stream_expert_pread_pool_tasks = tasks;
g_stream_expert_pread_pool_n_tasks = n_tasks;
g_stream_expert_pread_pool_next_task = 0;
g_stream_expert_pread_pool_active_workers = n_workers;
g_stream_expert_pread_pool_remaining_workers = n_workers;
g_stream_expert_pread_pool_generation++;
pthread_cond_broadcast(&g_stream_expert_pread_pool_start_cond);
pthread_mutex_unlock(&g_stream_expert_pread_pool_mutex);
return 1;
}
static int ds4_gpu_stream_expert_pread_pool_wait(void) {
if (!g_stream_expert_pread_pool_initialized) return 0;
pthread_mutex_lock(&g_stream_expert_pread_pool_mutex);
while (g_stream_expert_pread_pool_remaining_workers != 0) {
pthread_cond_wait(&g_stream_expert_pread_pool_done_cond,
&g_stream_expert_pread_pool_mutex);
}
pthread_mutex_unlock(&g_stream_expert_pread_pool_mutex);
return 1;
}
static int ds4_gpu_stream_expert_pread_pool_dispatch(
ds4_gpu_stream_expert_pread_task *tasks,
uint32_t n_tasks,
uint32_t n_workers) {
if (!ds4_gpu_stream_expert_pread_pool_begin(tasks, n_tasks, n_workers)) {
return 0;
}
return ds4_gpu_stream_expert_pread_pool_wait();
}
static void ds4_gpu_stream_expert_pread_pool_shutdown(void) {
if (!g_stream_expert_pread_pool_initialized) return;
pthread_mutex_lock(&g_stream_expert_pread_pool_mutex);
g_stream_expert_pread_pool_stopping = 1;
g_stream_expert_pread_pool_generation++;
pthread_cond_broadcast(&g_stream_expert_pread_pool_start_cond);
pthread_mutex_unlock(&g_stream_expert_pread_pool_mutex);
const uint32_t n_threads = g_stream_expert_pread_pool_thread_count;
for (uint32_t i = 0; i < n_threads; i++) {
(void)pthread_join(g_stream_expert_pread_pool_threads[i], NULL);
}
pthread_mutex_lock(&g_stream_expert_pread_pool_mutex);
g_stream_expert_pread_pool_thread_count = 0;
g_stream_expert_pread_pool_active_workers = 0;
g_stream_expert_pread_pool_remaining_workers = 0;
g_stream_expert_pread_pool_n_tasks = 0;
g_stream_expert_pread_pool_next_task = 0;
g_stream_expert_pread_pool_tasks = NULL;
g_stream_expert_pread_pool_initialized = 0;
g_stream_expert_pread_pool_stopping = 0;
pthread_mutex_unlock(&g_stream_expert_pread_pool_mutex);
}
static int ds4_gpu_stream_expert_pread_tasks(
ds4_gpu_stream_expert_pread_task *tasks,
uint32_t n_tasks,
uint64_t *total_bytes,
double *wall_ms) {
if (total_bytes) *total_bytes = 0;
if (wall_ms) *wall_ms = 0.0;
if (!tasks || n_tasks == 0) return 1;
const uint32_t n_workers =
ds4_gpu_stream_expert_pread_thread_count(n_tasks);
const double t0 = ds4_gpu_now_ms();
int ok = 1;
if (n_workers <= 1) {
ds4_gpu_stream_expert_pread_worker_args wa = {
.tasks = tasks,
.n_tasks = n_tasks,
.worker_index = 0,
.n_workers = 1,
};
(void)ds4_gpu_stream_expert_pread_worker(&wa);
} else if (!ds4_gpu_stream_expert_pread_pool_dispatch(tasks,
n_tasks,
n_workers)) {
pthread_t threads[18];
ds4_gpu_stream_expert_pread_worker_args args[18];
uint32_t started = 0;
for (uint32_t i = 0; i < n_workers; i++) {
args[i].tasks = tasks;
args[i].n_tasks = n_tasks;
args[i].worker_index = i;
args[i].n_workers = n_workers;
const int rc = pthread_create(&threads[i],
NULL,
ds4_gpu_stream_expert_pread_worker,
&args[i]);
if (rc != 0) {
fprintf(stderr,
"ds4: Metal streaming expert pread thread creation failed: %s\n",
strerror(rc));
ok = 0;
break;
}
started++;
}
for (uint32_t i = 0; i < started; i++) {
if (pthread_join(threads[i], NULL) != 0) ok = 0;
}
}
const double dt = ds4_gpu_now_ms() - t0;
uint64_t bytes = 0;
for (uint32_t i = 0; i < n_tasks; i++) {
if (!tasks[i].ok) ok = 0;
if (bytes > UINT64_MAX - tasks[i].read_bytes) {
bytes = UINT64_MAX;
} else {
bytes += tasks[i].read_bytes;
}
}
if (total_bytes) *total_bytes = bytes;
if (wall_ms) *wall_ms = dt;
return ok;
}
static void ds4_gpu_stream_expert_cache_warn_mlock_failure(
uint64_t failed_len,
int err) {
if (g_stream_expert_cache_mlock_warned) return;
g_stream_expert_cache_mlock_warned = 1;
const uint64_t gib = 1024ull * 1024ull * 1024ull;
const uint32_t budget = ds4_gpu_stream_expert_cache_requested_budget();
uint64_t requested = 0;
if (budget != 0 && g_stream_expert_cache_expert_bytes != 0) {
requested =
budget > UINT64_MAX / g_stream_expert_cache_expert_bytes ?
UINT64_MAX :
(uint64_t)budget * g_stream_expert_cache_expert_bytes;
}
uint64_t suggested_gib = g_stream_expert_cache_mlock_bytes / gib;
if (suggested_gib > 1) suggested_gib--;
fprintf(stderr,
"ds4: warning: streaming expert cache could not mlock all buffers\n");
if (requested != 0) {
fprintf(stderr,
"ds4: requested cache: %u experts / %.2f GiB\n",
budget,
ds4_gpu_gib(requested));
} else {
fprintf(stderr,
"ds4: requested cache: %u experts\n",
budget);
}
fprintf(stderr,
"ds4: locked so far: %.2f GiB\n",
ds4_gpu_gib(g_stream_expert_cache_mlock_bytes));
fprintf(stderr,
"ds4: failed buffer: %.2f MiB (%s)\n",
ds4_gpu_mib(failed_len),
err != 0 ? strerror(err) : "mlock unavailable");
fprintf(stderr,
"ds4: macOS may page unlocked expert buffers, causing poor or unstable speed\n");
if (g_stream_expert_cache_mlock_budget_cap != 0 &&
g_stream_expert_cache_expert_bytes != 0) {
const uint64_t capped_bytes =
g_stream_expert_cache_mlock_budget_cap >
UINT64_MAX / g_stream_expert_cache_expert_bytes ?
UINT64_MAX :
(uint64_t)g_stream_expert_cache_mlock_budget_cap *
g_stream_expert_cache_expert_bytes;
fprintf(stderr,
"ds4: using locked cache cap: %u experts / %.2f GiB\n",
g_stream_expert_cache_mlock_budget_cap,
ds4_gpu_gib(capped_bytes));
}
if (suggested_gib != 0) {
fprintf(stderr,
"ds4: try: --ssd-streaming-cache-experts %" PRIu64 "GB\n",
suggested_gib);
} else {
fprintf(stderr,
"ds4: try a smaller --ssd-streaming-cache-experts NGB budget\n");
}
}
static uint32_t ds4_gpu_stream_expert_slab_locked_slot_count(void) {
uint32_t count = 0;
uint32_t total = g_stream_expert_cache_slab_total_slots;
if (total > DS4_METAL_STREAM_EXPERT_CACHE_MAX_ENTRIES) {
total = DS4_METAL_STREAM_EXPERT_CACHE_MAX_ENTRIES;
}
for (uint32_t slot = 0; slot < total; slot++) {
if (g_stream_expert_cache_slab_slot_locked[slot]) count++;
}
return count;
}
static void ds4_gpu_stream_expert_cache_cap_budget_to_locked(void) {
uint32_t cap = g_stream_expert_cache_entry_count;
const uint32_t locked_slots = ds4_gpu_stream_expert_slab_locked_slot_count();
if (locked_slots != 0 && locked_slots < cap) cap = locked_slots;
const uint64_t gib = 1024ull * 1024ull * 1024ull;
uint64_t safe_gib = g_stream_expert_cache_mlock_bytes / gib;
if (safe_gib > 1) safe_gib--;
if (safe_gib != 0 && g_stream_expert_cache_expert_bytes != 0) {
uint64_t safe_bytes =
safe_gib > UINT64_MAX / gib ? UINT64_MAX : safe_gib * gib;
uint64_t safe_cap64 = safe_bytes / g_stream_expert_cache_expert_bytes;
if (safe_cap64 > UINT32_MAX) safe_cap64 = UINT32_MAX;
if (safe_cap64 != 0 && safe_cap64 < cap) {
cap = (uint32_t)safe_cap64;
}
}
if (cap == 0) return;
if (g_stream_expert_cache_mlock_budget_cap == 0 ||
cap < g_stream_expert_cache_mlock_budget_cap) {
g_stream_expert_cache_mlock_budget_cap = cap;
}
}
static id<MTLBuffer> ds4_gpu_stream_expert_alloc_buffer(
uint64_t len,
NSString *label) {
if (!g_device ||
len == 0 ||
len > (uint64_t)NSUIntegerMax) {
return nil;
}
id<MTLBuffer> buffer = [g_device newBufferWithLength:(NSUInteger)len
options:MTLResourceStorageModeShared];
if (!buffer) {
fprintf(stderr,
"ds4: Metal streaming expert explicit buffer allocation failed (%.2f MiB)\n",
ds4_gpu_mib(len));
return nil;
}
buffer.label = label;
g_stream_expert_cache_buffer_allocs++;
if (g_ssd_streaming_mode) {
void *ptr = [buffer contents];
const NSUInteger n = [buffer length];
const double t0 = ds4_gpu_now_ms();
if (ptr && n != 0 && mlock(ptr, (size_t)n) == 0) {
const double dt = ds4_gpu_now_ms() - t0;
g_stream_expert_cache_mlock_ms += dt;
if (g_stream_expert_cache_mlock_bytes > UINT64_MAX - (uint64_t)n) {
g_stream_expert_cache_mlock_bytes = UINT64_MAX;
} else {
g_stream_expert_cache_mlock_bytes += (uint64_t)n;
}
} else {
const double dt = ds4_gpu_now_ms() - t0;
g_stream_expert_cache_mlock_ms += dt;
g_stream_expert_cache_mlock_failures++;
if (g_stream_expert_cache_mlock_fail_bytes > UINT64_MAX - (uint64_t)n) {
g_stream_expert_cache_mlock_fail_bytes = UINT64_MAX;
} else {
g_stream_expert_cache_mlock_fail_bytes += (uint64_t)n;
}
const int err = ptr && n != 0 ? errno : 0;
ds4_gpu_stream_expert_cache_cap_budget_to_locked();
ds4_gpu_stream_expert_cache_warn_mlock_failure((uint64_t)n, err);
if (getenv("DS4_METAL_STREAMING_EXPERT_BUFFER_MLOCK_PROFILE") != NULL) {
fprintf(stderr,
"ds4: Metal streaming expert buffer mlock failed len=%.2f MiB: %s\n",
ds4_gpu_mib((uint64_t)n),
err != 0 ? strerror(err) : "mlock unavailable");
}
return nil;
}
}
return buffer;
}
static void ds4_gpu_stream_expert_unlock_explicit_buffer(id<MTLBuffer> buffer) {
if (!g_ssd_streaming_mode || !buffer) return;
void *ptr = [buffer contents];
const NSUInteger n = [buffer length];
if (!ptr || n == 0 || munlock(ptr, (size_t)n) != 0) return;
if (g_stream_expert_cache_mlock_bytes >= (uint64_t)n) {
g_stream_expert_cache_mlock_bytes -= (uint64_t)n;
} else {
g_stream_expert_cache_mlock_bytes = 0;
}
}
static int ds4_gpu_stream_expert_combined_buffer_enabled(void) {
return g_ssd_streaming_mode &&
getenv("DS4_METAL_DISABLE_STREAMING_EXPERT_COMBINED_BUFFER") == NULL;
}
static int ds4_gpu_stream_expert_slab_enabled(void) {
return ds4_gpu_stream_expert_combined_buffer_enabled() &&
getenv("DS4_METAL_DISABLE_STREAMING_EXPERT_SLABS") == NULL;
}
/*
* Large PRO caches otherwise create thousands of small shared Metal buffers.
* Slabs keep the buffer object set small while locking pages only for slots
* that actually hold a streamed expert.
*/
static uint64_t ds4_gpu_stream_expert_slab_target_bytes(void) {
const uint64_t mib = 1024ull * 1024ull;
uint64_t target = 4096ull * mib;
const char *env = getenv("DS4_METAL_STREAMING_EXPERT_SLAB_MB");
if (env && env[0]) {
char *end = NULL;
unsigned long long v = strtoull(env, &end, 10);
if (end != env && *end == '\0' && v != 0) {
target = v > UINT64_MAX / mib ? UINT64_MAX : (uint64_t)v * mib;
}
}
return target;
}
static id<MTLBuffer> ds4_gpu_stream_expert_alloc_slab_buffer(
uint64_t len,
NSString *label) {
if (!g_device ||
len == 0 ||
len > (uint64_t)NSUIntegerMax) {
return nil;
}
id<MTLBuffer> buffer = [g_device newBufferWithLength:(NSUInteger)len
options:MTLResourceStorageModeShared];
if (!buffer) {
fprintf(stderr,
"ds4: Metal streaming expert slab allocation failed (%.2f MiB)\n",
ds4_gpu_mib(len));
return nil;
}
buffer.label = label;
g_stream_expert_cache_buffer_allocs++;
return buffer;
}
static int ds4_gpu_stream_expert_slab_slot_range(
uint32_t slot,
uint32_t *slab_index,
uint64_t *slot_base) {
for (uint32_t i = 0; i < g_stream_expert_cache_slab_count; i++) {
const uint32_t start = g_stream_expert_cache_slab_start_slot[i];
const uint32_t count = g_stream_expert_cache_slab_slot_count[i];
if (slot < start || slot >= start + count) continue;
if (slab_index) *slab_index = i;
if (slot_base) {
*slot_base =
(uint64_t)(slot - start) * g_stream_expert_cache_slab_slot_bytes;
}
return 1;
}
return 0;
}
static int ds4_gpu_stream_expert_slab_slot_for_buffer(
id<MTLBuffer> buffer,
NSUInteger gate_inner,
uint32_t *slot_out) {
if (!buffer || g_stream_expert_cache_slab_slot_bytes == 0 || !slot_out) {
return 0;
}
for (uint32_t i = 0; i < g_stream_expert_cache_slab_count; i++) {
if (g_stream_expert_cache_slabs[i] != buffer) continue;
const uint64_t inner = (uint64_t)gate_inner;
const uint64_t slot_bytes = g_stream_expert_cache_slab_slot_bytes;
if (slot_bytes == 0 || inner % slot_bytes != 0) return 0;
const uint64_t local_slot = inner / slot_bytes;
if (local_slot >= g_stream_expert_cache_slab_slot_count[i]) return 0;
const uint64_t slot =
(uint64_t)g_stream_expert_cache_slab_start_slot[i] + local_slot;
if (slot > UINT32_MAX) return 0;
*slot_out = (uint32_t)slot;
return 1;
}
return 0;
}
static void ds4_gpu_stream_expert_slab_push_free_slot(uint32_t slot) {
if (slot >= DS4_METAL_STREAM_EXPERT_CACHE_MAX_ENTRIES ||
g_stream_expert_cache_free_slot_count >=
DS4_METAL_STREAM_EXPERT_CACHE_MAX_ENTRIES) {
return;
}
g_stream_expert_cache_free_slots[g_stream_expert_cache_free_slot_count++] =
slot;
}
static int ds4_gpu_stream_expert_slab_lock_slot(uint32_t slot) {
if (slot >= DS4_METAL_STREAM_EXPERT_CACHE_MAX_ENTRIES ||
g_stream_expert_cache_slab_slot_locked[slot]) {
return 1;
}
uint32_t slab = UINT32_MAX;
uint64_t base = 0;
if (!ds4_gpu_stream_expert_slab_slot_range(slot, &slab, &base) ||
slab >= g_stream_expert_cache_slab_count ||
!g_stream_expert_cache_slabs[slab] ||
g_stream_expert_cache_slab_slot_bytes == 0 ||
base > (uint64_t)NSUIntegerMax) {
return 0;
}
void *contents = [g_stream_expert_cache_slabs[slab] contents];
if (!contents) return 0;
const double t0 = ds4_gpu_now_ms();
void *ptr = (uint8_t *)contents + (NSUInteger)base;
const size_t n = (size_t)g_stream_expert_cache_slab_slot_bytes;
if (mlock(ptr, n) == 0) {
const double dt = ds4_gpu_now_ms() - t0;
g_stream_expert_cache_mlock_ms += dt;
if (g_stream_expert_cache_mlock_bytes >
UINT64_MAX - g_stream_expert_cache_slab_slot_bytes) {
g_stream_expert_cache_mlock_bytes = UINT64_MAX;
} else {
g_stream_expert_cache_mlock_bytes +=
g_stream_expert_cache_slab_slot_bytes;
}
g_stream_expert_cache_slab_slot_locked[slot] = 1;
return 1;
}
const double dt = ds4_gpu_now_ms() - t0;
g_stream_expert_cache_mlock_ms += dt;
g_stream_expert_cache_mlock_failures++;
if (g_stream_expert_cache_mlock_fail_bytes >
UINT64_MAX - g_stream_expert_cache_slab_slot_bytes) {
g_stream_expert_cache_mlock_fail_bytes = UINT64_MAX;
} else {
g_stream_expert_cache_mlock_fail_bytes +=
g_stream_expert_cache_slab_slot_bytes;
}
ds4_gpu_stream_expert_cache_cap_budget_to_locked();
ds4_gpu_stream_expert_cache_warn_mlock_failure(
g_stream_expert_cache_slab_slot_bytes,
errno);
return 0;
}
static int ds4_gpu_stream_expert_slab_unlock_slot(uint32_t slot) {
if (slot >= DS4_METAL_STREAM_EXPERT_CACHE_MAX_ENTRIES ||
!g_stream_expert_cache_slab_slot_locked[slot]) {
return 1;
}
uint32_t slab = UINT32_MAX;
uint64_t base = 0;
if (!ds4_gpu_stream_expert_slab_slot_range(slot, &slab, &base) ||
slab >= g_stream_expert_cache_slab_count ||
!g_stream_expert_cache_slabs[slab] ||
g_stream_expert_cache_slab_slot_bytes == 0 ||
base > (uint64_t)NSUIntegerMax) {
return 0;
}
void *contents = [g_stream_expert_cache_slabs[slab] contents];
if (!contents) return 0;
void *ptr = (uint8_t *)contents + (NSUInteger)base;
const size_t n = (size_t)g_stream_expert_cache_slab_slot_bytes;
if (munlock(ptr, n) != 0) return 0;
g_stream_expert_cache_slab_slot_locked[slot] = 0;
if (g_stream_expert_cache_mlock_bytes >= g_stream_expert_cache_slab_slot_bytes) {
g_stream_expert_cache_mlock_bytes -= g_stream_expert_cache_slab_slot_bytes;
} else {
g_stream_expert_cache_mlock_bytes = 0;
}
return 1;
}
static int ds4_gpu_stream_expert_slab_slot_buffers(
uint32_t slot,
uint64_t gate_expert_bytes,
uint64_t down_expert_bytes,
__strong id<MTLBuffer> *gate_buf,
__strong id<MTLBuffer> *up_buf,
__strong id<MTLBuffer> *down_buf,
NSUInteger *gate_inner,
NSUInteger *up_inner,
NSUInteger *down_inner) {
uint32_t slab = UINT32_MAX;
uint64_t base = 0;
if (!ds4_gpu_stream_expert_slab_slot_range(slot, &slab, &base) ||
slab >= g_stream_expert_cache_slab_count ||
!g_stream_expert_cache_slabs[slab]) {
return 0;
}
if (gate_expert_bytes > (UINT64_MAX - down_expert_bytes) / 2ull ||
base > UINT64_MAX - (gate_expert_bytes * 2ull + down_expert_bytes) ||
base + gate_expert_bytes * 2ull + down_expert_bytes >
(uint64_t)NSUIntegerMax) {
return 0;
}
id<MTLBuffer> b = g_stream_expert_cache_slabs[slab];
if (!ds4_gpu_stream_expert_slab_lock_slot(slot)) return 0;
*gate_buf = b;
*up_buf = b;
*down_buf = b;
*gate_inner = (NSUInteger)base;
*up_inner = (NSUInteger)(base + gate_expert_bytes);
*down_inner = (NSUInteger)(base + gate_expert_bytes * 2ull);
return 1;
}
static int ds4_gpu_stream_expert_alloc_slab_slot(
uint64_t gate_expert_bytes,
uint64_t down_expert_bytes,
__strong id<MTLBuffer> *gate_buf,
__strong id<MTLBuffer> *up_buf,
__strong id<MTLBuffer> *down_buf,
NSUInteger *gate_inner,
NSUInteger *up_inner,
NSUInteger *down_inner) {
if (!ds4_gpu_stream_expert_slab_enabled() ||
!gate_buf || !up_buf || !down_buf ||
!gate_inner || !up_inner || !down_inner ||
gate_expert_bytes == 0 || down_expert_bytes == 0 ||
gate_expert_bytes > (UINT64_MAX - down_expert_bytes) / 2ull) {
return 0;
}
uint64_t slot_bytes = gate_expert_bytes * 2ull + down_expert_bytes;
if (slot_bytes == 0 || slot_bytes > (uint64_t)NSUIntegerMax) return 0;
const uint64_t page = (uint64_t)getpagesize();
if (page != 0) {
slot_bytes = round_up_u64(slot_bytes, page);
if (slot_bytes == 0 || slot_bytes > (uint64_t)NSUIntegerMax) return 0;
}
if (g_stream_expert_cache_slab_slot_bytes != 0 &&
g_stream_expert_cache_slab_slot_bytes != slot_bytes) {
return 0;
}
g_stream_expert_cache_slab_slot_bytes = slot_bytes;
if (g_stream_expert_cache_free_slot_count != 0) {
const uint32_t slot =
g_stream_expert_cache_free_slots[--g_stream_expert_cache_free_slot_count];
return ds4_gpu_stream_expert_slab_slot_buffers(slot,
gate_expert_bytes,
down_expert_bytes,
gate_buf,
up_buf,
down_buf,
gate_inner,
up_inner,
down_inner);
}
uint32_t slab = g_stream_expert_cache_slab_count;
if (slab != 0 &&
g_stream_expert_cache_slab_slots_used[slab - 1] <
g_stream_expert_cache_slab_slot_count[slab - 1]) {
slab--;
} else {
if (g_stream_expert_cache_slab_count >=
DS4_METAL_STREAM_EXPERT_CACHE_MAX_SLABS) {
return 0;
}
const uint32_t budget = ds4_gpu_stream_expert_cache_configured_budget();
if (budget != 0 && g_stream_expert_cache_slab_total_slots >= budget) {
return 0;
}
uint64_t target = ds4_gpu_stream_expert_slab_target_bytes();
uint64_t slots64 = target / slot_bytes;
if (slots64 == 0) slots64 = 1;
if (slots64 > UINT32_MAX) slots64 = UINT32_MAX;
uint32_t slots = (uint32_t)slots64;
if (budget != 0) {
const uint32_t remaining =
budget - g_stream_expert_cache_slab_total_slots;
if (slots > remaining) slots = remaining;
}
if (slots == 0) return 0;
id<MTLBuffer> slab_buffer = nil;
while (slots != 0) {
if ((uint64_t)slots <= UINT64_MAX / slot_bytes &&
(uint64_t)slots * slot_bytes <= (uint64_t)NSUIntegerMax) {
slab_buffer =
ds4_gpu_stream_expert_alloc_slab_buffer(
(uint64_t)slots * slot_bytes,
@"ds4_stream_expert_slab");
if (slab_buffer) break;
}
slots /= 2u;
}
if (!slab_buffer || slots == 0) return 0;
slab = g_stream_expert_cache_slab_count++;
g_stream_expert_cache_slabs[slab] = slab_buffer;
g_stream_expert_cache_slab_start_slot[slab] =
g_stream_expert_cache_slab_total_slots;
g_stream_expert_cache_slab_slot_count[slab] = slots;
g_stream_expert_cache_slab_slots_used[slab] = 0;
g_stream_expert_cache_slab_total_slots += slots;
}
const uint32_t local_slot = g_stream_expert_cache_slab_slots_used[slab]++;
const uint32_t slot =
g_stream_expert_cache_slab_start_slot[slab] + local_slot;
return ds4_gpu_stream_expert_slab_slot_buffers(slot,
gate_expert_bytes,
down_expert_bytes,
gate_buf,
up_buf,
down_buf,
gate_inner,
up_inner,
down_inner);
}
static uint64_t ds4_gpu_stream_expert_buffer_object_count(
id<MTLBuffer> gate,
id<MTLBuffer> up,
id<MTLBuffer> down) {
if (!gate || !up || !down) return 0;
if (gate == up && gate == down) return 1;
if (gate == up || gate == down || up == down) return 2;
return 3;
}
static int ds4_gpu_stream_expert_evict_dontneed_enabled(void) {
return g_ssd_streaming_mode &&
getenv("DS4_METAL_ENABLE_STREAMING_EXPERT_EVICT_DONTNEED") != NULL &&
getenv("DS4_METAL_DISABLE_STREAMING_EXPERT_EVICT_DONTNEED") == NULL;
}
static void ds4_gpu_stream_expert_evict_dontneed_range(
const void *model_map,
uint64_t model_size,
uint64_t offset,
uint64_t len) {
if (!ds4_gpu_stream_expert_evict_dontneed_enabled() ||
!model_map ||
model_size == 0 ||
offset > model_size ||
len == 0 ||
len > model_size - offset) {
return;
}
#if defined(POSIX_MADV_DONTNEED)
const uint64_t page = (uint64_t)getpagesize();
const uint64_t page_offset = offset & ~(page - 1);
const uint64_t leading = offset - page_offset;
if (len > UINT64_MAX - leading ||
leading + len > UINT64_MAX - (page - 1)) {
return;
}
uint64_t advise_bytes = round_up_u64(leading + len, page);
if (advise_bytes > model_size - page_offset) {
advise_bytes = model_size - page_offset;
}
if (advise_bytes == 0 || advise_bytes > (uint64_t)SIZE_MAX) return;
const uintptr_t base = (uintptr_t)model_map;
if (page_offset > (uint64_t)(UINTPTR_MAX - base)) return;
void *addr = (void *)(base + (uintptr_t)page_offset);
const int rc = posix_madvise(addr, (size_t)advise_bytes, POSIX_MADV_DONTNEED);
if (rc == 0) {
if (g_stream_expert_cache_evict_advise_bytes > UINT64_MAX - advise_bytes) {
g_stream_expert_cache_evict_advise_bytes = UINT64_MAX;
} else {
g_stream_expert_cache_evict_advise_bytes += advise_bytes;
}
} else if (getenv("DS4_METAL_STREAMING_EXPERT_EVICT_DONTNEED_PROFILE") != NULL) {
fprintf(stderr,
"ds4: Metal streaming expert evict DONTNEED failed offset=%.2f GiB len=%.2f MiB: %s\n",
ds4_gpu_gib(offset),
ds4_gpu_mib(len),
strerror(rc));
}
#else
(void)model_map;
(void)model_size;
(void)offset;
(void)len;
#endif
}
static int ds4_gpu_stream_expert_split_requested(void) {
return g_ssd_streaming_mode;
}
static uint32_t ds4_gpu_stream_expert_split_min_decode_tokens(void) {
return 4;
}
static uint32_t ds4_gpu_stream_expert_split_min_cached(void) {
uint32_t min_cached = 1024;
const uint32_t budget = ds4_gpu_stream_expert_cache_configured_budget();
if (budget != 0 && budget < min_cached * 2u) {
min_cached = budget / 2u;
}
return min_cached;
}
static int ds4_gpu_stream_expert_split_ready(void) {
if (!ds4_gpu_stream_expert_split_requested()) return 0;
if (g_stream_expert_cache_decode_tokens <
ds4_gpu_stream_expert_split_min_decode_tokens()) {
return 0;
}
return g_stream_expert_cache_entry_count >=
ds4_gpu_stream_expert_split_min_cached();
}
static void ds4_gpu_stream_expert_cache_decay_route_hotness(void) {
for (uint32_t layer = 0;
layer < DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER;
layer++) {
for (uint32_t expert = 0;
expert < DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT;
expert++) {
g_stream_expert_cache_route_hotness[layer][expert] >>= 1;
}
}
}
void ds4_gpu_stream_expert_cache_reset_route_hotness(void) {
memset(g_stream_expert_cache_route_hotness,
0,
sizeof(g_stream_expert_cache_route_hotness));
g_stream_expert_cache_hotness_decay_token =
g_stream_expert_cache_decode_tokens;
}
static void ds4_gpu_stream_expert_cache_maybe_decay_route_hotness(void) {
if (g_stream_expert_cache_decode_tokens == 0) return;
if (g_stream_expert_cache_hotness_decay_token == 0) {
g_stream_expert_cache_hotness_decay_token =
g_stream_expert_cache_decode_tokens;
return;
}
while (g_stream_expert_cache_decode_tokens -
g_stream_expert_cache_hotness_decay_token >=
DS4_METAL_STREAM_EXPERT_HOTNESS_DECAY_TOKENS) {
ds4_gpu_stream_expert_cache_decay_route_hotness();
g_stream_expert_cache_hotness_decay_token +=
DS4_METAL_STREAM_EXPERT_HOTNESS_DECAY_TOKENS;
}
}
static void ds4_gpu_stream_expert_cache_note_route_hotness(
uint32_t layer,
uint32_t expert,
uint32_t amount) {
if (layer >= DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER ||
expert >= DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT ||
amount == 0) {
return;
}
uint32_t *hotness = &g_stream_expert_cache_route_hotness[layer][expert];
if (*hotness > UINT32_MAX - amount) {
*hotness = UINT32_MAX;
} else {
*hotness += amount;
}
}
static void ds4_gpu_stream_expert_cache_note_selected_hotness(
uint32_t layer,
const int32_t *selected_ids,
uint32_t n_selected) {
if (!selected_ids ||
layer >= DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER ||
n_selected == 0) {
return;
}
ds4_gpu_stream_expert_cache_maybe_decay_route_hotness();
for (uint32_t i = 0; i < n_selected; i++) {
if (selected_ids[i] < 0 ||
selected_ids[i] >=
(int32_t)DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT) {
continue;
}
ds4_gpu_stream_expert_cache_note_route_hotness(
layer,
(uint32_t)selected_ids[i],
1);
}
}
static void ds4_gpu_stream_expert_cache_note_frequency_hotness(
uint32_t layer,
const uint32_t *frequency,
uint32_t n_total_expert) {
if (!frequency || layer >= DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER) {
return;
}
if (n_total_expert > DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT) {
n_total_expert = DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT;
}
ds4_gpu_stream_expert_cache_maybe_decay_route_hotness();
for (uint32_t expert = 0; expert < n_total_expert; expert++) {
ds4_gpu_stream_expert_cache_note_route_hotness(layer,
expert,
frequency[expert]);
}
}
static void ds4_gpu_stream_expert_cache_note_token(uint32_t layer_index) {
if (!g_ssd_streaming_mode ||
g_stream_expert_cache_decode_tokens == UINT64_MAX) {
return;
}
/* GLM starts with dense layers; a pipeline slice may start even later.
* Typed and generic routed kernels must advance the same aging clock. */
if (layer_index < g_stream_expert_cache_first_decode_layer)
g_stream_expert_cache_first_decode_layer = layer_index;
if (layer_index != g_stream_expert_cache_first_decode_layer) return;
g_stream_expert_cache_decode_tokens++;
ds4_gpu_stream_expert_cache_maybe_decay_route_hotness();
}
static int ds4_gpu_stream_compact_addr_requested(void) {
return g_ssd_streaming_mode &&
getenv("DS4_METAL_ENABLE_STREAMING_COMPACT_ADDR") != NULL &&
getenv("DS4_METAL_DISABLE_STREAMING_COMPACT_ADDR") == NULL &&
getenv("DS4_METAL_DISABLE_STREAMING_EXPERT_ADDR_TABLE") == NULL;
}
static int ds4_gpu_stream_expert_addr_table_requested(void) {
return g_ssd_streaming_mode &&
(getenv("DS4_METAL_ENABLE_STREAMING_EXPERT_ADDR_TABLE") != NULL ||
getenv("DS4_METAL_ENABLE_STREAMING_EXPERT_HIT_VALIDATOR") != NULL ||
getenv("DS4_METAL_ENABLE_STREAMING_EXPERT_MASKED_ADDR") != NULL ||
g_stream_prefill_batch_selected_addr_building ||
g_glm_stream_expert_addr_table_building ||
(getenv("DS4_METAL_ENABLE_STREAMING_PREFILL_BATCH_SELECTED_ADDR") != NULL &&
getenv("DS4_METAL_DISABLE_STREAMING_PREFILL_BATCH_SELECTED_ADDR") == NULL) ||
ds4_gpu_stream_expert_split_requested()) &&
getenv("DS4_METAL_DISABLE_STREAMING_EXPERT_ADDR_TABLE") == NULL;
}
static int ds4_gpu_stream_expert_addr_table_kernel_requested(void) {
return g_ssd_streaming_mode &&
(getenv("DS4_METAL_ENABLE_STREAMING_EXPERT_ADDR_TABLE") != NULL ||
getenv("DS4_METAL_ENABLE_STREAMING_EXPERT_HIT_VALIDATOR") != NULL ||
getenv("DS4_METAL_ENABLE_STREAMING_EXPERT_MASKED_ADDR") != NULL ||
ds4_gpu_stream_expert_split_ready()) &&
getenv("DS4_METAL_DISABLE_STREAMING_EXPERT_ADDR_TABLE") == NULL;
}
static int ds4_gpu_stream_expert_masked_addr_requested(void) {
return g_ssd_streaming_mode &&
(getenv("DS4_METAL_ENABLE_STREAMING_EXPERT_MASKED_ADDR") != NULL ||
ds4_gpu_stream_expert_split_ready()) &&
getenv("DS4_METAL_DISABLE_STREAMING_EXPERT_MASKED_ADDR") == NULL;
}
static int ds4_gpu_stream_expert_hit_validator_requested(void) {
return g_ssd_streaming_mode &&
getenv("DS4_METAL_ENABLE_STREAMING_EXPERT_HIT_VALIDATOR") != NULL &&
getenv("DS4_METAL_DISABLE_STREAMING_EXPERT_HIT_VALIDATOR") == NULL;
}
static uint32_t ds4_gpu_stream_prefill_batch_selected_addr_auto_max(
uint32_t n_total_expert) {
const char *env = getenv("DS4_METAL_STREAMING_PREFILL_BATCH_SELECTED_ADDR_MAX");
if (env && env[0]) {
char *end = NULL;
const long v = strtol(env, &end, 10);
if (end != env) {
if (v <= 0) return 0;
if ((unsigned long)v > (unsigned long)UINT32_MAX) return UINT32_MAX;
return (uint32_t)v;
}
}
if (n_total_expert == 384) return 800u;
if (n_total_expert == 256) return 760u;
return 0;
}
static uint32_t ds4_gpu_stream_prefill_batch_selected_addr_auto_min(
uint32_t n_total_expert) {
const char *env = getenv("DS4_METAL_STREAMING_PREFILL_BATCH_SELECTED_ADDR_MIN");
if (env && env[0]) {
char *end = NULL;
const long v = strtol(env, &end, 10);
if (end != env) {
if (v <= 0) return 0;
if ((unsigned long)v > (unsigned long)UINT32_MAX) return UINT32_MAX;
return (uint32_t)v;
}
}
if (n_total_expert == 384 || n_total_expert == 256) return 2u;
return 0;
}
static int ds4_gpu_stream_prefill_batch_selected_addr_enabled(
uint32_t n_tokens,
uint32_t n_total_expert,
uint32_t n_expert,
uint32_t gate_type,
uint32_t down_type) {
if (!g_ssd_streaming_mode ||
n_tokens <= 1 ||
n_total_expert == 0 ||
n_expert != 6 ||
gate_type != DS4_METAL_TENSOR_IQ2_XXS ||
down_type != DS4_METAL_TENSOR_Q2_K ||
g_quality_mode ||
getenv("DS4_METAL_DISABLE_STREAMING_PREFILL_BATCH_SELECTED_ADDR") != NULL ||
getenv("DS4_METAL_DISABLE_STREAMING_EXPERT_ADDR_TABLE") != NULL ||
getenv("DS4_METAL_MOE_WRITE_CLAMPED_ACT") != NULL ||
getenv("DS4_METAL_DISABLE_ROUTED_PAIR_SWIGLU_FUSION") != NULL) {
return 0;
}
/* All unique experts for one layer must fit simultaneously because the
* address-table kernels consume them in one dispatch. Once the global
* cache fills, preparation reuses entries owned by other layers. */
if (ds4_gpu_stream_expert_cache_configured_count() < n_total_expert) {
return 0;
}
if (getenv("DS4_METAL_ENABLE_STREAMING_PREFILL_BATCH_SELECTED_ADDR") != NULL) {
return 1;
}
const uint32_t max_tokens =
ds4_gpu_stream_prefill_batch_selected_addr_auto_max(n_total_expert);
const uint32_t min_tokens =
ds4_gpu_stream_prefill_batch_selected_addr_auto_min(n_total_expert);
return max_tokens != 0 && n_tokens >= min_tokens && n_tokens <= max_tokens;
}
static int ds4_gpu_glm_streaming_prefill_full_layer_active(void) {
return g_glm_streaming_prefill_full_layer_runtime ||
getenv("DS4_METAL_GLM_STREAMING_PREFILL_FULL_LAYER") != NULL;
}
static int ds4_gpu_stream_full_expert_addr_table_requested(void) {
return g_ssd_streaming_mode &&
getenv("DS4_METAL_ENABLE_STREAMING_FULL_EXPERT_ADDR_TABLE") != NULL &&
getenv("DS4_METAL_DISABLE_STREAMING_FULL_EXPERT_ADDR_TABLE") == NULL;
}
static uint64_t ds4_gpu_buffer_address(id<MTLBuffer> buffer, NSUInteger inner) {
if (!buffer) return 0;
#if TARGET_OS_OSX
if (@available(macOS 13.0, *)) {
return (uint64_t)[buffer gpuAddress] + (uint64_t)inner;
}
#endif
return 0;
}
static int ds4_gpu_stream_compact_addr_ensure_buffers(uint32_t layer) {
if (!g_device || layer >= DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER) return 0;
const NSUInteger addr_bytes = 6u * sizeof(uint64_t);
const NSUInteger ids_bytes = 6u * sizeof(int32_t);
for (uint32_t i = 0; i < 4; i++) {
id<MTLBuffer> current = nil;
NSUInteger bytes = addr_bytes;
NSString *label = @"ds4_stream_compact_gate_addresses";
switch (i) {
case 0:
current = g_stream_compact_gate_addr_buffers[layer];
label = @"ds4_stream_compact_gate_addresses";
break;
case 1:
current = g_stream_compact_up_addr_buffers[layer];
label = @"ds4_stream_compact_up_addresses";
break;
case 2:
current = g_stream_compact_down_addr_buffers[layer];
label = @"ds4_stream_compact_down_addresses";
break;
default:
current = g_stream_compact_selected_buffers[layer];
bytes = ids_bytes;
label = @"ds4_stream_compact_selected_ids";
break;
}
if (current) continue;
id<MTLBuffer> b = [g_device newBufferWithLength:bytes
options:MTLResourceStorageModeShared];
if (!b) {
fprintf(stderr, "ds4: Metal streaming compact address buffer allocation failed\n");
return 0;
}
b.label = label;
memset([b contents], 0, bytes);
switch (i) {
case 0:
g_stream_compact_gate_addr_buffers[layer] = b;
break;
case 1:
g_stream_compact_up_addr_buffers[layer] = b;
break;
case 2:
g_stream_compact_down_addr_buffers[layer] = b;
break;
default:
g_stream_compact_selected_buffers[layer] = b;
break;
}
}
return 1;
}
static int ds4_gpu_stream_compact_addr_prepare(
uint32_t layer,
ds4_gpu_stream_expert_cache_entry * const entries[6],
uint32_t n_entries,
id<MTLBuffer> *gate_addrs,
id<MTLBuffer> *up_addrs,
id<MTLBuffer> *down_addrs,
id<MTLBuffer> *selected_ids) {
if (layer >= DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER ||
!entries || !gate_addrs || !up_addrs || !down_addrs || !selected_ids ||
n_entries == 0 || n_entries > 6) {
return 0;
}
if (!ds4_gpu_stream_compact_addr_ensure_buffers(layer)) return 0;
uint64_t gate_values[6] = {0, 0, 0, 0, 0, 0};
uint64_t up_values[6] = {0, 0, 0, 0, 0, 0};
uint64_t down_values[6] = {0, 0, 0, 0, 0, 0};
int32_t slot_ids[6] = {0, 1, 2, 3, 4, 5};
for (uint32_t i = 0; i < n_entries; i++) {
ds4_gpu_stream_expert_cache_entry *e = entries[i];
if (!e || !e->gate_buffer || !e->up_buffer || !e->down_buffer) {
return 0;
}
gate_values[i] = ds4_gpu_buffer_address(e->gate_buffer, e->gate_inner);
up_values[i] = ds4_gpu_buffer_address(e->up_buffer, e->up_inner);
down_values[i] = ds4_gpu_buffer_address(e->down_buffer, e->down_inner);
if (gate_values[i] == 0 || up_values[i] == 0 || down_values[i] == 0) {
fprintf(stderr, "ds4: Metal streaming compact address path requires GPU addresses\n");
return 0;
}
}
const NSUInteger addr_bytes = 6u * sizeof(uint64_t);
const NSUInteger ids_bytes = 6u * sizeof(int32_t);
id<MTLBuffer> gb = g_stream_compact_gate_addr_buffers[layer];
id<MTLBuffer> ub = g_stream_compact_up_addr_buffers[layer];
id<MTLBuffer> db = g_stream_compact_down_addr_buffers[layer];
id<MTLBuffer> ib = g_stream_compact_selected_buffers[layer];
memcpy([gb contents], gate_values, addr_bytes);
memcpy([ub contents], up_values, addr_bytes);
memcpy([db contents], down_values, addr_bytes);
memcpy([ib contents], slot_ids, ids_bytes);
*gate_addrs = gb;
*up_addrs = ub;
*down_addrs = db;
*selected_ids = ib;
return 1;
}
static int ds4_gpu_stream_selected_ids_prepare(
uint32_t layer,
const int32_t *selected_ids,
uint32_t n_selected,
id<MTLBuffer> *selected_buf,
NSUInteger *selected_off) {
if (!g_device ||
!selected_ids ||
!selected_buf ||
!selected_off ||
layer >= DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER ||
n_selected == 0 ||
n_selected > DS4_METAL_MAX_ROUTED_EXPERT_USED) {
return 0;
}
const NSUInteger bytes =
(NSUInteger)DS4_METAL_MAX_ROUTED_EXPERT_USED * sizeof(int32_t);
id<MTLBuffer> b = g_stream_selected_id_buffers[layer];
if (!b) {
b = [g_device newBufferWithLength:bytes
options:MTLResourceStorageModeShared];
if (!b) {
fprintf(stderr, "ds4: Metal streaming selected-id buffer allocation failed\n");
return 0;
}
b.label = @"ds4_stream_selected_ids";
g_stream_selected_id_buffers[layer] = b;
}
int32_t ids[DS4_METAL_MAX_ROUTED_EXPERT_USED] = {0};
memcpy(ids, selected_ids, (size_t)n_selected * sizeof(ids[0]));
memcpy([b contents], ids, bytes);
*selected_buf = b;
*selected_off = 0;
return 1;
}
static int ds4_gpu_stream_expert_cache_ensure_addr_buffers(uint32_t layer) {
if (!g_device ||
layer >= DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER) {
return 0;
}
const NSUInteger bytes =
(NSUInteger)DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT * sizeof(uint64_t);
id<MTLBuffer> buffers[3] = {
g_stream_expert_cache_gate_addr_buffers[layer],
g_stream_expert_cache_up_addr_buffers[layer],
g_stream_expert_cache_down_addr_buffers[layer],
};
for (uint32_t i = 0; i < 3; i++) {
if (buffers[i]) continue;
id<MTLBuffer> b = [g_device newBufferWithLength:bytes
options:MTLResourceStorageModeShared];
if (!b) {
fprintf(stderr, "ds4: Metal streaming expert address table allocation failed\n");
return 0;
}
b.label =
i == 0 ? @"ds4_stream_expert_gate_addresses" :
(i == 1 ? @"ds4_stream_expert_up_addresses" :
@"ds4_stream_expert_down_addresses");
memset([b contents], 0, bytes);
if (i == 0) {
g_stream_expert_cache_gate_addr_buffers[layer] = b;
} else if (i == 1) {
g_stream_expert_cache_up_addr_buffers[layer] = b;
} else {
g_stream_expert_cache_down_addr_buffers[layer] = b;
}
}
return 1;
}
static void ds4_gpu_stream_expert_cache_zero_addr_slot(uint32_t layer, uint32_t expert) {
if (layer >= DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER ||
expert >= DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT) {
return;
}
id<MTLBuffer> buffers[3] = {
g_stream_expert_cache_gate_addr_buffers[layer],
g_stream_expert_cache_up_addr_buffers[layer],
g_stream_expert_cache_down_addr_buffers[layer],
};
const NSUInteger off = (NSUInteger)expert * sizeof(uint64_t);
for (uint32_t i = 0; i < 3; i++) {
if (!buffers[i]) continue;
uint64_t *addr = (uint64_t *)((uint8_t *)[buffers[i] contents] + off);
*addr = 0;
}
}
static int ds4_gpu_stream_expert_cache_set_addr_slot_raw(
uint32_t layer,
uint32_t expert,
id<MTLBuffer> gate_buffer,
NSUInteger gate_inner,
id<MTLBuffer> up_buffer,
NSUInteger up_inner,
id<MTLBuffer> down_buffer,
NSUInteger down_inner) {
if (!ds4_gpu_stream_expert_cache_ensure_addr_buffers(layer)) return 0;
const uint64_t values[3] = {
ds4_gpu_buffer_address(gate_buffer, gate_inner),
ds4_gpu_buffer_address(up_buffer, up_inner),
ds4_gpu_buffer_address(down_buffer, down_inner),
};
if (values[0] == 0 || values[1] == 0 || values[2] == 0) {
fprintf(stderr, "ds4: Metal streaming expert address table requires GPU addresses\n");
return 0;
}
id<MTLBuffer> buffers[3] = {
g_stream_expert_cache_gate_addr_buffers[layer],
g_stream_expert_cache_up_addr_buffers[layer],
g_stream_expert_cache_down_addr_buffers[layer],
};
const NSUInteger off = (NSUInteger)expert * sizeof(uint64_t);
for (uint32_t i = 0; i < 3; i++) {
uint64_t *addr = (uint64_t *)((uint8_t *)[buffers[i] contents] + off);
*addr = values[i];
}
return 1;
}
static int ds4_gpu_stream_expert_cache_set_addr_slot(
uint32_t layer,
uint32_t expert,
id<MTLBuffer> gate_buffer,
NSUInteger gate_inner,
id<MTLBuffer> up_buffer,
NSUInteger up_inner,
id<MTLBuffer> down_buffer,
NSUInteger down_inner) {
if (!ds4_gpu_stream_expert_addr_table_requested()) return 1;
return ds4_gpu_stream_expert_cache_set_addr_slot_raw(layer,
expert,
gate_buffer,
gate_inner,
up_buffer,
up_inner,
down_buffer,
down_inner);
}
static int ds4_gpu_stream_expert_cache_addr_buffers(
uint32_t layer,
id<MTLBuffer> *gate,
id<MTLBuffer> *up,
id<MTLBuffer> *down) {
if (!ds4_gpu_stream_expert_cache_ensure_addr_buffers(layer)) return 0;
if (gate) *gate = g_stream_expert_cache_gate_addr_buffers[layer];
if (up) *up = g_stream_expert_cache_up_addr_buffers[layer];
if (down) *down = g_stream_expert_cache_down_addr_buffers[layer];
return g_stream_expert_cache_gate_addr_buffers[layer] &&
g_stream_expert_cache_up_addr_buffers[layer] &&
g_stream_expert_cache_down_addr_buffers[layer];
}
static void ds4_gpu_stream_full_expert_addr_clear_layer(uint32_t layer) {
if (layer >= DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER) return;
ds4_gpu_stream_expert_cache_entry *e = &g_stream_full_expert_addr_entry[layer];
e->gate_buffer = nil;
e->up_buffer = nil;
e->down_buffer = nil;
e->model_map = NULL;
e->model_size = 0;
e->gate_abs_offset = 0;
e->up_abs_offset = 0;
e->down_abs_offset = 0;
e->gate_expert_bytes = 0;
e->down_expert_bytes = 0;
e->logical_bytes = 0;
e->last_used = 0;
e->use_count = 0;
e->gate_inner = 0;
e->up_inner = 0;
e->down_inner = 0;
e->slab_slot = 0;
e->valid = 0;
e->slab_backed = 0;
}
static int ds4_gpu_stream_full_expert_addr_table_prepare(
const void *model_map,
uint64_t model_size,
uint32_t layer,
uint32_t n_total_expert,
uint64_t gate_abs_offset,
uint64_t up_abs_offset,
uint64_t down_abs_offset,
uint64_t gate_expert_bytes,
uint64_t down_expert_bytes,
id<MTLBuffer> *gate_addrs,
id<MTLBuffer> *up_addrs,
id<MTLBuffer> *down_addrs,
ds4_gpu_stream_expert_cache_entry **entry_out) {
if (!ds4_gpu_stream_full_expert_addr_table_requested()) return 0;
if (!model_map || model_size == 0 ||
layer >= DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER ||
n_total_expert == 0 ||
n_total_expert > DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT ||
gate_expert_bytes == 0 ||
down_expert_bytes == 0 ||
n_total_expert > UINT64_MAX / gate_expert_bytes ||
n_total_expert > UINT64_MAX / down_expert_bytes) {
return 0;
}
const uint64_t gate_tensor_bytes = (uint64_t)n_total_expert * gate_expert_bytes;
const uint64_t down_tensor_bytes = (uint64_t)n_total_expert * down_expert_bytes;
if (gate_abs_offset > model_size ||
up_abs_offset > model_size ||
down_abs_offset > model_size ||
gate_tensor_bytes > model_size - gate_abs_offset ||
gate_tensor_bytes > model_size - up_abs_offset ||
down_tensor_bytes > model_size - down_abs_offset) {
return 0;
}
ds4_gpu_stream_expert_cache_entry *entry =
&g_stream_full_expert_addr_entry[layer];
if (!entry->valid ||
entry->model_map != model_map ||
entry->model_size != model_size ||
entry->gate_abs_offset != gate_abs_offset ||
entry->up_abs_offset != up_abs_offset ||
entry->down_abs_offset != down_abs_offset ||
entry->gate_expert_bytes != gate_expert_bytes ||
entry->down_expert_bytes != down_expert_bytes ||
!entry->gate_buffer ||
!entry->up_buffer ||
!entry->down_buffer) {
ds4_gpu_stream_full_expert_addr_clear_layer(layer);
uint64_t gate_inner = 0;
uint64_t up_inner = 0;
uint64_t down_inner = 0;
id<MTLBuffer> gate_buf =
ds4_gpu_wrap_model_exact_range_owned(model_map,
model_size,
gate_abs_offset,
gate_tensor_bytes,
&gate_inner);
id<MTLBuffer> up_buf =
ds4_gpu_wrap_model_exact_range_owned(model_map,
model_size,
up_abs_offset,
gate_tensor_bytes,
&up_inner);
id<MTLBuffer> down_buf =
ds4_gpu_wrap_model_exact_range_owned(model_map,
model_size,
down_abs_offset,
down_tensor_bytes,
&down_inner);
if (!gate_buf || !up_buf || !down_buf) return 0;
entry->gate_buffer = gate_buf;
entry->up_buffer = up_buf;
entry->down_buffer = down_buf;
entry->model_map = model_map;
entry->model_size = model_size;
entry->gate_abs_offset = gate_abs_offset;
entry->up_abs_offset = up_abs_offset;
entry->down_abs_offset = down_abs_offset;
entry->gate_expert_bytes = gate_expert_bytes;
entry->down_expert_bytes = down_expert_bytes;
entry->logical_bytes = gate_tensor_bytes * 2ull + down_tensor_bytes;
entry->gate_inner = (NSUInteger)gate_inner;
entry->up_inner = (NSUInteger)up_inner;
entry->down_inner = (NSUInteger)down_inner;
entry->valid = 1;
if (!ds4_gpu_stream_expert_cache_ensure_addr_buffers(layer)) {
ds4_gpu_stream_full_expert_addr_clear_layer(layer);
return 0;
}
const uint64_t gate_base = ds4_gpu_buffer_address(entry->gate_buffer,
entry->gate_inner);
const uint64_t up_base = ds4_gpu_buffer_address(entry->up_buffer,
entry->up_inner);
const uint64_t down_base = ds4_gpu_buffer_address(entry->down_buffer,
entry->down_inner);
if (gate_base == 0 || up_base == 0 || down_base == 0) {
fprintf(stderr, "ds4: Metal full streaming expert address table requires GPU addresses\n");
ds4_gpu_stream_full_expert_addr_clear_layer(layer);
return 0;
}
id<MTLBuffer> buffers[3] = {
g_stream_expert_cache_gate_addr_buffers[layer],
g_stream_expert_cache_up_addr_buffers[layer],
g_stream_expert_cache_down_addr_buffers[layer],
};
uint64_t *gate_addr = (uint64_t *)[buffers[0] contents];
uint64_t *up_addr = (uint64_t *)[buffers[1] contents];
uint64_t *down_addr = (uint64_t *)[buffers[2] contents];
for (uint32_t expert = 0; expert < DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT; expert++) {
if (expert < n_total_expert) {
gate_addr[expert] = gate_base + (uint64_t)expert * gate_expert_bytes;
up_addr[expert] = up_base + (uint64_t)expert * gate_expert_bytes;
down_addr[expert] = down_base + (uint64_t)expert * down_expert_bytes;
} else {
gate_addr[expert] = 0;
up_addr[expert] = 0;
down_addr[expert] = 0;
}
}
}
if (gate_addrs) *gate_addrs = g_stream_expert_cache_gate_addr_buffers[layer];
if (up_addrs) *up_addrs = g_stream_expert_cache_up_addr_buffers[layer];
if (down_addrs) *down_addrs = g_stream_expert_cache_down_addr_buffers[layer];
if (entry_out) *entry_out = entry;
return entry->valid &&
g_stream_expert_cache_gate_addr_buffers[layer] &&
g_stream_expert_cache_up_addr_buffers[layer] &&
g_stream_expert_cache_down_addr_buffers[layer];
}
static id<MTLBuffer> ds4_gpu_stream_expert_validate_status_buffer(void) {
if (!g_device) return nil;
if (g_stream_expert_validate_status_buffer) {
return g_stream_expert_validate_status_buffer;
}
const NSUInteger bytes =
(NSUInteger)DS4_METAL_STREAM_EXPERT_VALIDATE_WORDS * sizeof(uint32_t);
id<MTLBuffer> b = [g_device newBufferWithLength:bytes
options:MTLResourceStorageModeShared];
if (!b) {
fprintf(stderr, "ds4: Metal streaming expert validator allocation failed\n");
return nil;
}
b.label = @"ds4_stream_expert_validate_status";
memset([b contents], 0, bytes);
g_stream_expert_validate_status_buffer = b;
return b;
}
static int ds4_gpu_encode_stream_expert_cache_validate(
id<MTLCommandBuffer> cb,
const ds4_gpu_stream_expert_validate_args *args,
id<MTLBuffer> selected,
NSUInteger selected_off,
id<MTLBuffer> gate_addrs,
id<MTLBuffer> up_addrs,
id<MTLBuffer> down_addrs,
id<MTLBuffer> status) {
if (!cb || !args || !selected || !gate_addrs || !up_addrs || !down_addrs ||
!status || !g_moe_stream_expert_cache_validate_pipeline ||
args->n_total_expert == 0 || args->n_total_expert > 384 ||
args->n_expert == 0 || args->n_expert > 6) {
return 0;
}
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:g_moe_stream_expert_cache_validate_pipeline];
[enc setBytes:args length:sizeof(*args) atIndex:0];
[enc setBuffer:selected offset:selected_off atIndex:1];
[enc setBuffer:gate_addrs offset:0 atIndex:2];
[enc setBuffer:up_addrs offset:0 atIndex:3];
[enc setBuffer:down_addrs offset:0 atIndex:4];
[enc setBuffer:status offset:0 atIndex:5];
[enc dispatchThreadgroups:MTLSizeMake(1, 1, 1)
threadsPerThreadgroup:MTLSizeMake(1, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_stream_expert_cache_validate_selected(
const ds4_gpu_tensor *selected,
id<MTLBuffer> gate_addrs,
id<MTLBuffer> up_addrs,
id<MTLBuffer> down_addrs,
uint32_t n_total_expert,
uint32_t n_expert,
int32_t selected_ids[6],
uint32_t *all_cached,
uint32_t *miss_mask,
uint32_t *invalid_mask) {
if (!selected || !selected_ids || !all_cached || !miss_mask || !invalid_mask) {
return 0;
}
id<MTLBuffer> selectedbuf = ds4_gpu_tensor_buffer(selected);
id<MTLBuffer> status = ds4_gpu_stream_expert_validate_status_buffer();
if (!selectedbuf || !status) return 0;
const NSUInteger status_bytes =
(NSUInteger)DS4_METAL_STREAM_EXPERT_VALIDATE_WORDS * sizeof(uint32_t);
memset([status contents], 0, status_bytes);
ds4_gpu_stream_expert_validate_args args = {
.n_total_expert = n_total_expert,
.n_expert = n_expert,
};
const int had_batch = g_batch_cb != nil;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
if (!ds4_gpu_encode_stream_expert_cache_validate(cb,
&args,
selectedbuf,
ds4_gpu_tensor_offset(selected),
gate_addrs,
up_addrs,
down_addrs,
status)) {
return 0;
}
if (had_batch) {
if (ds4_gpu_end_commands() == 0) return 0;
} else if (!ds4_gpu_finish_command_buffer(cb, owned,
"streaming expert cache validator")) {
return 0;
}
const uint32_t *words = (const uint32_t *)[status contents];
*all_cached = words[0];
*miss_mask = words[1];
*invalid_mask = words[2];
for (uint32_t i = 0; i < 6; i++) {
selected_ids[i] = (int32_t)words[4 + i];
}
if (had_batch && ds4_gpu_begin_commands() == 0) return 0;
return 1;
}
static void ds4_gpu_stream_expert_cache_clear_entry_internal(
uint32_t layer,
uint32_t expert,
int count_eviction,
int recycle_slab_slot,
ds4_gpu_stream_expert_reusable_buffers *reuse) {
if (layer >= DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER ||
expert >= DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT) {
return;
}
if (reuse) {
reuse->gate_buffer = nil;
reuse->up_buffer = nil;
reuse->down_buffer = nil;
reuse->gate_inner = 0;
reuse->up_inner = 0;
reuse->down_inner = 0;
}
ds4_gpu_stream_expert_cache_entry *e =
&g_stream_expert_cache[layer][expert];
if (!e->valid) return;
if (ds4_gpu_stream_expert_cache_entry_inflight(e)) {
return;
}
const uint64_t bytes = e->logical_bytes;
ds4_gpu_stream_expert_evict_dontneed_range(e->model_map,
e->model_size,
e->gate_abs_offset,
e->gate_expert_bytes);
ds4_gpu_stream_expert_evict_dontneed_range(e->model_map,
e->model_size,
e->up_abs_offset,
e->gate_expert_bytes);
ds4_gpu_stream_expert_evict_dontneed_range(e->model_map,
e->model_size,
e->down_abs_offset,
e->down_expert_bytes);
ds4_gpu_stream_expert_cache_zero_addr_slot(layer, expert);
if (reuse) {
reuse->gate_buffer = e->gate_buffer;
reuse->up_buffer = e->up_buffer;
reuse->down_buffer = e->down_buffer;
reuse->gate_inner = e->gate_inner;
reuse->up_inner = e->up_inner;
reuse->down_inner = e->down_inner;
} else if (e->slab_backed && recycle_slab_slot) {
ds4_gpu_stream_expert_slab_push_free_slot(e->slab_slot);
} else if (!e->slab_backed) {
ds4_gpu_stream_expert_unlock_explicit_buffer(e->gate_buffer);
if (e->up_buffer != e->gate_buffer) {
ds4_gpu_stream_expert_unlock_explicit_buffer(e->up_buffer);
}
if (e->down_buffer != e->gate_buffer &&
e->down_buffer != e->up_buffer) {
ds4_gpu_stream_expert_unlock_explicit_buffer(e->down_buffer);
}
}
e->gate_buffer = nil;
e->up_buffer = nil;
e->down_buffer = nil;
e->model_map = NULL;
e->model_size = 0;
e->gate_abs_offset = 0;
e->up_abs_offset = 0;
e->down_abs_offset = 0;
e->gate_expert_bytes = 0;
e->down_expert_bytes = 0;
e->logical_bytes = 0;
e->last_used = 0;
e->use_count = 0;
e->gate_inner = 0;
e->up_inner = 0;
e->down_inner = 0;
e->inflight_seq = 0;
e->slab_slot = 0;
e->valid = 0;
e->slab_backed = 0;
if (g_stream_expert_cache_layer_count[layer] > 0) {
g_stream_expert_cache_layer_count[layer]--;
}
if (g_stream_expert_cache_entry_count > 0) {
g_stream_expert_cache_entry_count--;
}
if (g_stream_expert_cache_bytes >= bytes) {
g_stream_expert_cache_bytes -= bytes;
} else {
g_stream_expert_cache_bytes = 0;
}
if (count_eviction) {
g_stream_expert_cache_evictions++;
g_stream_expert_cache_layer_evictions[layer]++;
}
}
static void ds4_gpu_stream_expert_cache_clear_entry(
uint32_t layer,
uint32_t expert,
int count_eviction) {
ds4_gpu_stream_expert_cache_clear_entry_internal(layer,
expert,
count_eviction,
1,
NULL);
}
static void ds4_gpu_stream_expert_cache_clear_all(int reset_stats) {
ds4_gpu_stream_expert_pending_load_clear();
g_stream_expert_cache_done_seq = g_stream_expert_cache_cb_seq;
g_stream_expert_cache_batch_seq = 0;
g_stream_expert_cache_owned_seq = 0;
g_stream_expert_cache_pending_max_seq = 0;
for (uint32_t layer = 0; layer < DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER; layer++) {
for (uint32_t expert = 0; expert < DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT; expert++) {
g_stream_expert_cache[layer][expert].inflight_seq = 0;
ds4_gpu_stream_expert_cache_clear_entry(layer, expert, 0);
}
ds4_gpu_stream_full_expert_addr_clear_layer(layer);
g_stream_expert_cache_layer_count[layer] = 0;
id<MTLBuffer> buffers[3] = {
g_stream_expert_cache_gate_addr_buffers[layer],
g_stream_expert_cache_up_addr_buffers[layer],
g_stream_expert_cache_down_addr_buffers[layer],
};
for (uint32_t i = 0; i < 3; i++) {
if (!buffers[i]) continue;
const NSUInteger bytes =
(NSUInteger)DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT * sizeof(uint64_t);
memset([buffers[i] contents], 0, bytes);
}
}
g_stream_expert_cache_bytes = 0;
g_stream_expert_cache_entry_count = 0;
for (uint32_t i = 0; i < g_stream_expert_cache_slab_count; i++) {
g_stream_expert_cache_slabs[i] = nil;
g_stream_expert_cache_slab_start_slot[i] = 0;
g_stream_expert_cache_slab_slot_count[i] = 0;
g_stream_expert_cache_slab_slots_used[i] = 0;
}
g_stream_expert_cache_slab_count = 0;
g_stream_expert_cache_slab_total_slots = 0;
g_stream_expert_cache_free_slot_count = 0;
g_stream_expert_cache_slab_slot_bytes = 0;
memset(g_stream_expert_cache_slab_slot_locked,
0,
sizeof(g_stream_expert_cache_slab_slot_locked));
if (reset_stats) {
g_stream_expert_cache_hits = 0;
g_stream_expert_cache_misses = 0;
g_stream_expert_cache_evictions = 0;
g_stream_expert_cache_wraps = 0;
g_stream_expert_cache_clock = 0;
g_stream_expert_cache_evict_advise_bytes = 0;
g_stream_expert_cache_willneed_advise_bytes = 0;
g_stream_expert_cache_pread_bytes = 0;
g_stream_expert_cache_pread_ms = 0.0;
g_stream_expert_cache_mlock_bytes = 0;
g_stream_expert_cache_mlock_fail_bytes = 0;
g_stream_expert_cache_mlock_failures = 0;
g_stream_expert_cache_mlock_ms = 0.0;
g_stream_expert_cache_mlock_warned = 0;
g_stream_expert_cache_mlock_budget_cap = 0;
g_stream_expert_cache_mlock_relief_applied = 0;
g_stream_expert_cache_buffer_allocs = 0;
g_stream_expert_cache_buffer_reuses = 0;
g_stream_expert_cache_decode_tokens = 0;
g_stream_expert_cache_first_decode_layer = UINT32_MAX;
g_stream_expert_cache_hotness_decay_token = 0;
memset(g_stream_expert_cache_route_hotness,
0,
sizeof(g_stream_expert_cache_route_hotness));
g_stream_expert_timing_selected_calls = 0;
g_stream_expert_timing_selected_read_ms = 0.0;
g_stream_expert_timing_selected_sync_ms = 0.0;
g_stream_expert_timing_selected_copy_ms = 0.0;
g_stream_expert_timing_selected_bind_ms = 0.0;
g_stream_expert_timing_split_layers = 0;
g_stream_expert_timing_split_resident_experts = 0;
g_stream_expert_timing_split_missing_experts = 0;
g_stream_expert_timing_split_resident_ms = 0.0;
g_stream_expert_timing_split_missing_ms = 0.0;
g_stream_expert_timing_split_missing_load_ms = 0.0;
g_stream_expert_timing_split_missing_slot_ms = 0.0;
g_stream_expert_timing_split_missing_prune_ms = 0.0;
g_stream_expert_timing_split_missing_addr_ms = 0.0;
g_stream_expert_timing_split_missing_wait_ms = 0.0;
g_stream_expert_timing_load_calls = 0;
g_stream_expert_timing_load_prepare_ms = 0.0;
g_stream_expert_timing_load_pread_ms = 0.0;
g_stream_expert_timing_load_install_ms = 0.0;
g_stream_expert_timing_prepare_batch_reuse_calls = 0;
g_stream_expert_timing_prepare_batch_reuse_ms = 0.0;
g_stream_expert_timing_prepare_buffer_calls = 0;
g_stream_expert_timing_prepare_buffer_ms = 0.0;
g_stream_expert_timing_prepare_task_experts = 0;
g_stream_expert_timing_prepare_task_ms = 0.0;
g_stream_expert_timing_reuse_scan_calls = 0;
g_stream_expert_timing_reuse_scan_entries = 0;
g_stream_expert_timing_reuse_scan_ms = 0.0;
g_stream_expert_timing_reuse_clear_ms = 0.0;
g_stream_expert_timing_readahead_calls = 0;
g_stream_expert_timing_readahead_bytes = 0;
g_stream_expert_timing_readahead_ms = 0.0;
g_stream_expert_timing_cache_all_resident_layers = 0;
g_stream_expert_timing_cache_all_missing_layers = 0;
g_stream_expert_timing_cache_mixed_layers = 0;
g_stream_expert_timing_cache_resident_experts = 0;
g_stream_expert_timing_cache_missing_experts = 0;
g_stream_expert_timing_last_report =
(ds4_gpu_stream_expert_timing_snapshot){0};
memset(g_stream_expert_cache_layer_hits,
0,
sizeof(g_stream_expert_cache_layer_hits));
memset(g_stream_expert_cache_layer_misses,
0,
sizeof(g_stream_expert_cache_layer_misses));
memset(g_stream_expert_cache_layer_evictions,
0,
sizeof(g_stream_expert_cache_layer_evictions));
memset(g_stream_expert_cache_layer_pread_bytes,
0,
sizeof(g_stream_expert_cache_layer_pread_bytes));
memset(g_stream_expert_cache_layer_pread_ms,
0,
sizeof(g_stream_expert_cache_layer_pread_ms));
}
}
static int ds4_gpu_stream_expert_cache_is_protected(
uint32_t expert,
const int32_t *protect_ids,
uint32_t n_protect) {
if (!protect_ids) return 0;
for (uint32_t i = 0; i < n_protect; i++) {
if (protect_ids[i] >= 0 && (uint32_t)protect_ids[i] == expert) {
return 1;
}
}
return 0;
}
static void ds4_gpu_stream_expert_cache_prune_layer(
uint32_t layer,
uint32_t n_total_expert,
uint32_t n_selected,
const int32_t *protect_ids,
uint32_t n_protect) {
if (layer >= DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER) return;
uint32_t cap = ds4_gpu_stream_expert_cache_effective_cap(layer,
n_total_expert,
n_selected);
if (cap == 0) return;
/*
* Route hotness counts selected experts even when they miss. Hit-count
* LFU penalizes experts that are repeatedly selected but evicted before a
* second hit, which keeps too many decode layers in the mixed-cache path.
*/
while (g_stream_expert_cache_layer_count[layer] > cap) {
uint32_t victim = UINT32_MAX;
uint32_t lowest_hotness = UINT32_MAX;
uint64_t oldest = UINT64_MAX;
for (uint32_t expert = 0; expert < n_total_expert; expert++) {
ds4_gpu_stream_expert_cache_entry *e =
&g_stream_expert_cache[layer][expert];
if (!e->valid ||
ds4_gpu_stream_expert_cache_entry_inflight(e) ||
ds4_gpu_stream_expert_cache_is_protected(expert, protect_ids, n_protect)) {
continue;
}
const uint32_t hotness =
g_stream_expert_cache_route_hotness[layer][expert];
if (hotness < lowest_hotness ||
(hotness == lowest_hotness && e->last_used < oldest)) {
lowest_hotness = hotness;
oldest = e->last_used;
victim = expert;
}
}
if (victim == UINT32_MAX) break;
ds4_gpu_stream_expert_cache_clear_entry(layer, victim, 1);
}
}
static int ds4_gpu_stream_expert_cache_entry_protected(
uint32_t layer,
uint32_t expert,
uint32_t protect_layer,
const int32_t *protect_ids,
uint32_t n_protect) {
if (layer < DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER &&
expert < DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT &&
ds4_gpu_stream_expert_cache_entry_inflight(
&g_stream_expert_cache[layer][expert])) {
return 1;
}
return layer == protect_layer &&
ds4_gpu_stream_expert_cache_is_protected(expert,
protect_ids,
n_protect);
}
static int ds4_gpu_stream_expert_cache_entry_reusable(
const ds4_gpu_stream_expert_cache_entry *e,
uint64_t gate_expert_bytes,
uint64_t down_expert_bytes) {
return e &&
e->valid &&
e->gate_buffer &&
e->up_buffer &&
e->down_buffer &&
e->gate_expert_bytes == gate_expert_bytes &&
e->down_expert_bytes == down_expert_bytes;
}
static int ds4_gpu_stream_expert_cache_take_reusable(
int force_reuse,
uint32_t protect_layer,
const int32_t *protect_ids,
uint32_t n_protect,
uint64_t gate_expert_bytes,
uint64_t down_expert_bytes,
ds4_gpu_stream_expert_reusable_buffers *reuse) {
if (!reuse) return 0;
reuse->gate_buffer = nil;
reuse->up_buffer = nil;
reuse->down_buffer = nil;
reuse->gate_inner = 0;
reuse->up_inner = 0;
reuse->down_inner = 0;
const uint32_t budget = ds4_gpu_stream_expert_cache_configured_budget();
if (budget == 0 ||
(!force_reuse && g_stream_expert_cache_entry_count < budget)) {
return 0;
}
int waited_inflight = 0;
retry:
;
uint32_t victim_layer = UINT32_MAX;
uint32_t victim_expert = UINT32_MAX;
uint32_t lowest_hotness = UINT32_MAX;
uint64_t oldest = UINT64_MAX;
int skipped_inflight = 0;
const int timing = ds4_gpu_stream_expert_timing_summary_enabled();
const double scan_t0 = timing ? ds4_gpu_now_ms() : 0.0;
uint64_t scan_entries = 0;
for (uint32_t layer = 0;
layer < DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER;
layer++) {
for (uint32_t expert = 0;
expert < DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT;
expert++) {
scan_entries++;
ds4_gpu_stream_expert_cache_entry *e =
&g_stream_expert_cache[layer][expert];
if (!ds4_gpu_stream_expert_cache_entry_reusable(e,
gate_expert_bytes,
down_expert_bytes)) {
continue;
}
if (ds4_gpu_stream_expert_cache_entry_inflight(e)) {
skipped_inflight = 1;
continue;
}
if (ds4_gpu_stream_expert_cache_entry_protected(layer,
expert,
protect_layer,
protect_ids,
n_protect)) {
continue;
}
const uint32_t hotness =
g_stream_expert_cache_route_hotness[layer][expert];
if (hotness < lowest_hotness ||
(hotness == lowest_hotness && e->last_used < oldest)) {
lowest_hotness = hotness;
oldest = e->last_used;
victim_layer = layer;
victim_expert = expert;
}
}
}
if (timing) {
ds4_gpu_stream_expert_timing_note_reuse_scan(scan_entries,
ds4_gpu_now_ms() - scan_t0);
}
if (victim_layer == UINT32_MAX || victim_expert == UINT32_MAX) {
if (skipped_inflight && !waited_inflight &&
!ds4_gpu_stream_expert_cache_on_service_thread()) {
waited_inflight = 1;
if (!ds4_gpu_stream_expert_cache_wait_inflight(
"streaming expert cache reuse")) {
return 0;
}
goto retry;
}
return 0;
}
const double clear_t0 = timing ? ds4_gpu_now_ms() : 0.0;
ds4_gpu_stream_expert_cache_clear_entry_internal(victim_layer,
victim_expert,
1,
1,
reuse);
if (timing) {
ds4_gpu_stream_expert_timing_note_reuse_clear(ds4_gpu_now_ms() -
clear_t0);
}
if (!reuse->gate_buffer || !reuse->up_buffer || !reuse->down_buffer) {
reuse->gate_buffer = nil;
reuse->up_buffer = nil;
reuse->down_buffer = nil;
reuse->gate_inner = 0;
reuse->up_inner = 0;
reuse->down_inner = 0;
return 0;
}
g_stream_expert_cache_buffer_reuses +=
ds4_gpu_stream_expert_buffer_object_count(reuse->gate_buffer,
reuse->up_buffer,
reuse->down_buffer);
return 1;
}
static uint32_t ds4_gpu_stream_expert_cache_take_reusable_batch(
uint32_t n_needed,
uint32_t protect_layer,
const int32_t *protect_ids,
uint32_t n_protect,
uint64_t gate_expert_bytes,
uint64_t down_expert_bytes,
ds4_gpu_stream_expert_reusable_buffers *reuses) {
if (!reuses || n_needed == 0) return 0;
if (n_needed > DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED) {
n_needed = DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED;
}
for (uint32_t i = 0; i < n_needed; i++) {
reuses[i].gate_buffer = nil;
reuses[i].up_buffer = nil;
reuses[i].down_buffer = nil;
reuses[i].gate_inner = 0;
reuses[i].up_inner = 0;
reuses[i].down_inner = 0;
}
const uint32_t budget = ds4_gpu_stream_expert_cache_configured_budget();
if (budget == 0 || g_stream_expert_cache_entry_count < budget) {
return 0;
}
int waited_inflight = 0;
retry:
;
uint32_t victim_layers[DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED];
uint32_t victim_experts[DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED];
uint32_t victim_hotness[DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED];
uint64_t victim_last_used[DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED];
uint32_t victim_count = 0;
int skipped_inflight = 0;
const int timing = ds4_gpu_stream_expert_timing_summary_enabled();
const double scan_t0 = timing ? ds4_gpu_now_ms() : 0.0;
uint64_t scan_entries = 0;
for (uint32_t i = 0; i < n_needed; i++) {
victim_layers[i] = UINT32_MAX;
victim_experts[i] = UINT32_MAX;
victim_hotness[i] = UINT32_MAX;
victim_last_used[i] = UINT64_MAX;
}
for (uint32_t layer = 0;
layer < DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER;
layer++) {
for (uint32_t expert = 0;
expert < DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT;
expert++) {
scan_entries++;
ds4_gpu_stream_expert_cache_entry *e =
&g_stream_expert_cache[layer][expert];
if (!ds4_gpu_stream_expert_cache_entry_reusable(e,
gate_expert_bytes,
down_expert_bytes)) {
continue;
}
if (ds4_gpu_stream_expert_cache_entry_inflight(e)) {
skipped_inflight = 1;
continue;
}
if (ds4_gpu_stream_expert_cache_entry_protected(layer,
expert,
protect_layer,
protect_ids,
n_protect)) {
continue;
}
const uint32_t hotness =
g_stream_expert_cache_route_hotness[layer][expert];
const uint64_t last_used = e->last_used;
if (victim_count < n_needed) {
victim_layers[victim_count] = layer;
victim_experts[victim_count] = expert;
victim_hotness[victim_count] = hotness;
victim_last_used[victim_count] = last_used;
victim_count++;
continue;
}
uint32_t worst = 0;
for (uint32_t i = 1; i < victim_count; i++) {
if (victim_hotness[i] > victim_hotness[worst] ||
(victim_hotness[i] == victim_hotness[worst] &&
victim_last_used[i] > victim_last_used[worst])) {
worst = i;
}
}
if (hotness < victim_hotness[worst] ||
(hotness == victim_hotness[worst] &&
last_used < victim_last_used[worst])) {
victim_layers[worst] = layer;
victim_experts[worst] = expert;
victim_hotness[worst] = hotness;
victim_last_used[worst] = last_used;
}
}
}
if (timing) {
ds4_gpu_stream_expert_timing_note_reuse_scan(scan_entries,
ds4_gpu_now_ms() - scan_t0);
}
if (victim_count == 0) {
if (skipped_inflight && !waited_inflight) {
waited_inflight = 1;
if (!ds4_gpu_stream_expert_cache_wait_inflight(
"streaming expert cache batch reuse")) {
return 0;
}
goto retry;
}
return 0;
}
uint32_t reuse_count = 0;
const double clear_t0 = timing ? ds4_gpu_now_ms() : 0.0;
for (uint32_t i = 0; i < victim_count; i++) {
if (victim_layers[i] == UINT32_MAX ||
victim_experts[i] == UINT32_MAX) {
continue;
}
ds4_gpu_stream_expert_cache_clear_entry_internal(victim_layers[i],
victim_experts[i],
1,
1,
&reuses[reuse_count]);
if (!reuses[reuse_count].gate_buffer ||
!reuses[reuse_count].up_buffer ||
!reuses[reuse_count].down_buffer) {
reuses[reuse_count].gate_buffer = nil;
reuses[reuse_count].up_buffer = nil;
reuses[reuse_count].down_buffer = nil;
reuses[reuse_count].gate_inner = 0;
reuses[reuse_count].up_inner = 0;
reuses[reuse_count].down_inner = 0;
continue;
}
g_stream_expert_cache_buffer_reuses +=
ds4_gpu_stream_expert_buffer_object_count(
reuses[reuse_count].gate_buffer,
reuses[reuse_count].up_buffer,
reuses[reuse_count].down_buffer);
reuse_count++;
}
if (timing) {
ds4_gpu_stream_expert_timing_note_reuse_clear(ds4_gpu_now_ms() -
clear_t0);
}
return reuse_count;
}
static int ds4_gpu_stream_expert_batch_reuse_enabled(
uint64_t gate_expert_bytes,
uint64_t down_expert_bytes) {
if (gate_expert_bytes > (UINT64_MAX - down_expert_bytes) / 2ull) {
return 0;
}
const uint64_t slot_bytes = gate_expert_bytes * 2ull + down_expert_bytes;
/*
* One global victim scan per selected miss is measurable for GLM Q2-size
* slots, but batching larger Q4-size slots regressed short decode on M5.
* Keep larger slots on the older single-victim path until profiling says
* otherwise.
*/
return slot_bytes <= 16ull * 1024ull * 1024ull;
}
static uint32_t ds4_gpu_stream_expert_cache_release_mlock_margin(
uint32_t protect_layer,
const int32_t *protect_ids,
uint32_t n_protect) {
if (g_stream_expert_cache_mlock_relief_applied ||
g_stream_expert_cache_slab_slot_bytes == 0) {
return 0;
}
const uint32_t locked_before =
ds4_gpu_stream_expert_slab_locked_slot_count();
if (locked_before == 0) return 0;
const uint32_t target_release = (locked_before + 9u) / 10u;
uint32_t released = 0;
while (released < target_release) {
uint32_t victim_layer = UINT32_MAX;
uint32_t victim_expert = UINT32_MAX;
uint32_t victim_slot = UINT32_MAX;
uint32_t lowest_hotness = UINT32_MAX;
uint64_t oldest = UINT64_MAX;
for (uint32_t layer = 0;
layer < DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER;
layer++) {
for (uint32_t expert = 0;
expert < DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT;
expert++) {
ds4_gpu_stream_expert_cache_entry *e =
&g_stream_expert_cache[layer][expert];
if (!e->valid ||
!e->slab_backed ||
e->slab_slot >= DS4_METAL_STREAM_EXPERT_CACHE_MAX_ENTRIES ||
!g_stream_expert_cache_slab_slot_locked[e->slab_slot] ||
ds4_gpu_stream_expert_cache_entry_inflight(e) ||
ds4_gpu_stream_expert_cache_entry_protected(layer,
expert,
protect_layer,
protect_ids,
n_protect)) {
continue;
}
const uint32_t hotness =
g_stream_expert_cache_route_hotness[layer][expert];
if (hotness < lowest_hotness ||
(hotness == lowest_hotness && e->last_used < oldest)) {
lowest_hotness = hotness;
oldest = e->last_used;
victim_layer = layer;
victim_expert = expert;
victim_slot = e->slab_slot;
}
}
}
if (victim_layer == UINT32_MAX ||
victim_expert == UINT32_MAX ||
victim_slot == UINT32_MAX) {
break;
}
ds4_gpu_stream_expert_cache_clear_entry_internal(victim_layer,
victim_expert,
1,
0,
NULL);
if (g_stream_expert_cache[victim_layer][victim_expert].valid) {
break;
}
if (ds4_gpu_stream_expert_slab_unlock_slot(victim_slot)) {
released++;
}
}
if (released == 0) return 0;
g_stream_expert_cache_mlock_relief_applied = 1;
uint32_t cap = g_stream_expert_cache_entry_count;
const uint32_t locked_after =
ds4_gpu_stream_expert_slab_locked_slot_count();
if (locked_after != 0 && locked_after < cap) cap = locked_after;
if (cap != 0 &&
(g_stream_expert_cache_mlock_budget_cap == 0 ||
cap < g_stream_expert_cache_mlock_budget_cap)) {
g_stream_expert_cache_mlock_budget_cap = cap;
}
const uint64_t released_bytes =
(uint64_t)released * g_stream_expert_cache_slab_slot_bytes;
fprintf(stderr,
"ds4: released locked cache margin: %u experts / %.2f GiB; "
"runtime cache cap now %u experts\n",
released,
ds4_gpu_gib(released_bytes),
g_stream_expert_cache_mlock_budget_cap);
return released;
}
static int ds4_gpu_stream_expert_cache_take_capped_reusable(
uint32_t protect_layer,
const int32_t *protect_ids,
uint32_t n_protect,
uint64_t gate_expert_bytes,
uint64_t down_expert_bytes,
__strong id<MTLBuffer> *gate_buf,
__strong id<MTLBuffer> *up_buf,
__strong id<MTLBuffer> *down_buf,
NSUInteger *gate_inner,
NSUInteger *up_inner,
NSUInteger *down_inner) {
if (g_stream_expert_cache_mlock_budget_cap == 0) return 0;
ds4_gpu_stream_expert_cache_release_mlock_margin(protect_layer,
protect_ids,
n_protect);
ds4_gpu_stream_expert_reusable_buffers reuse = { nil, nil, nil, 0, 0, 0 };
if (!ds4_gpu_stream_expert_cache_take_reusable(1,
protect_layer,
protect_ids,
n_protect,
gate_expert_bytes,
down_expert_bytes,
&reuse)) {
return 0;
}
*gate_buf = reuse.gate_buffer;
*up_buf = reuse.up_buffer;
*down_buf = reuse.down_buffer;
*gate_inner = reuse.gate_inner;
*up_inner = reuse.up_inner;
*down_inner = reuse.down_inner;
return *gate_buf && *up_buf && *down_buf;
}
static int ds4_gpu_stream_expert_cache_prepare_load_buffers(
uint32_t layer,
uint32_t expert,
uint32_t protect_layer,
const int32_t *protect_ids,
uint32_t n_protect,
uint64_t gate_expert_bytes,
uint64_t down_expert_bytes,
int force_reuse,
__strong id<MTLBuffer> *gate_buf,
__strong id<MTLBuffer> *up_buf,
__strong id<MTLBuffer> *down_buf,
NSUInteger *gate_inner,
NSUInteger *up_inner,
NSUInteger *down_inner) {
if (!gate_buf || !up_buf || !down_buf ||
!gate_inner || !up_inner || !down_inner ||
layer >= DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER ||
expert >= DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT) {
return 0;
}
*gate_buf = nil;
*up_buf = nil;
*down_buf = nil;
*gate_inner = 0;
*up_inner = 0;
*down_inner = 0;
ds4_gpu_stream_expert_reusable_buffers reuse = { nil, nil, nil, 0, 0, 0 };
ds4_gpu_stream_expert_cache_entry *e =
&g_stream_expert_cache[layer][expert];
if (e->valid && ds4_gpu_stream_expert_cache_entry_inflight(e)) {
if (ds4_gpu_stream_expert_cache_on_service_thread()) return 0;
if (!ds4_gpu_stream_expert_cache_wait_inflight(
"streaming expert cache replacement")) {
return 0;
}
e = &g_stream_expert_cache[layer][expert];
}
if (ds4_gpu_stream_expert_cache_entry_reusable(e,
gate_expert_bytes,
down_expert_bytes)) {
ds4_gpu_stream_expert_cache_clear_entry_internal(layer,
expert,
0,
1,
&reuse);
if (reuse.gate_buffer && reuse.up_buffer && reuse.down_buffer) {
g_stream_expert_cache_buffer_reuses +=
ds4_gpu_stream_expert_buffer_object_count(reuse.gate_buffer,
reuse.up_buffer,
reuse.down_buffer);
}
} else if (e->valid) {
ds4_gpu_stream_expert_cache_clear_entry(layer, expert, 0);
}
if (!reuse.gate_buffer || !reuse.up_buffer || !reuse.down_buffer) {
if (!ds4_gpu_stream_expert_cache_take_reusable(force_reuse,
protect_layer,
protect_ids,
n_protect,
gate_expert_bytes,
down_expert_bytes,
&reuse)) {
reuse.gate_buffer = nil;
reuse.up_buffer = nil;
reuse.down_buffer = nil;
}
}
if (reuse.gate_buffer && reuse.up_buffer && reuse.down_buffer) {
*gate_buf = reuse.gate_buffer;
*up_buf = reuse.up_buffer;
*down_buf = reuse.down_buffer;
*gate_inner = reuse.gate_inner;
*up_inner = reuse.up_inner;
*down_inner = reuse.down_inner;
return 1;
}
if (ds4_gpu_stream_expert_combined_buffer_enabled()) {
if (gate_expert_bytes > UINT64_MAX - gate_expert_bytes ||
gate_expert_bytes * 2ull > UINT64_MAX - down_expert_bytes ||
gate_expert_bytes > (uint64_t)NSUIntegerMax ||
gate_expert_bytes * 2ull > (uint64_t)NSUIntegerMax ||
gate_expert_bytes * 2ull + down_expert_bytes >
(uint64_t)NSUIntegerMax) {
return 0;
}
if (g_stream_expert_cache_mlock_budget_cap != 0) {
return ds4_gpu_stream_expert_cache_take_capped_reusable(
protect_layer, protect_ids, n_protect,
gate_expert_bytes, down_expert_bytes,
gate_buf, up_buf, down_buf,
gate_inner, up_inner, down_inner);
}
if (ds4_gpu_stream_expert_alloc_slab_slot(gate_expert_bytes,
down_expert_bytes,
gate_buf,
up_buf,
down_buf,
gate_inner,
up_inner,
down_inner)) {
return 1;
}
if (g_stream_expert_cache_mlock_budget_cap != 0) {
return ds4_gpu_stream_expert_cache_take_capped_reusable(
protect_layer, protect_ids, n_protect,
gate_expert_bytes, down_expert_bytes,
gate_buf, up_buf, down_buf,
gate_inner, up_inner, down_inner);
}
const uint64_t up_off = gate_expert_bytes;
const uint64_t down_off = gate_expert_bytes * 2ull;
const uint64_t combined_bytes = down_off + down_expert_bytes;
id<MTLBuffer> combined =
ds4_gpu_stream_expert_alloc_buffer(combined_bytes,
@"ds4_stream_expert_combined");
if (!combined) {
return ds4_gpu_stream_expert_cache_take_capped_reusable(
protect_layer, protect_ids, n_protect,
gate_expert_bytes, down_expert_bytes,
gate_buf, up_buf, down_buf,
gate_inner, up_inner, down_inner);
}
*gate_buf = combined;
*up_buf = combined;
*down_buf = combined;
*gate_inner = 0;
*up_inner = (NSUInteger)up_off;
*down_inner = (NSUInteger)down_off;
return 1;
}
id<MTLBuffer> explicit_gate =
ds4_gpu_stream_expert_alloc_buffer(gate_expert_bytes,
@"ds4_stream_expert_gate");
id<MTLBuffer> explicit_up =
ds4_gpu_stream_expert_alloc_buffer(gate_expert_bytes,
@"ds4_stream_expert_up");
id<MTLBuffer> explicit_down =
ds4_gpu_stream_expert_alloc_buffer(down_expert_bytes,
@"ds4_stream_expert_down");
if (!explicit_gate || !explicit_up || !explicit_down) {
ds4_gpu_stream_expert_unlock_explicit_buffer(explicit_gate);
ds4_gpu_stream_expert_unlock_explicit_buffer(explicit_up);
ds4_gpu_stream_expert_unlock_explicit_buffer(explicit_down);
return ds4_gpu_stream_expert_cache_take_capped_reusable(
protect_layer, protect_ids, n_protect,
gate_expert_bytes, down_expert_bytes,
gate_buf, up_buf, down_buf,
gate_inner, up_inner, down_inner);
}
*gate_buf = explicit_gate;
*up_buf = explicit_up;
*down_buf = explicit_down;
return 1;
}
static void ds4_gpu_stream_expert_cache_prune_global(
uint32_t protect_layer,
const int32_t *protect_ids,
uint32_t n_protect) {
const uint32_t budget = ds4_gpu_stream_expert_cache_configured_budget();
if (budget == 0 || g_stream_expert_cache_entry_count <= budget) return;
while (g_stream_expert_cache_entry_count > budget) {
uint32_t victim_layer = UINT32_MAX;
uint32_t victim_expert = UINT32_MAX;
uint32_t lowest_hotness = UINT32_MAX;
uint64_t oldest = UINT64_MAX;
for (uint32_t layer = 0;
layer < DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER;
layer++) {
for (uint32_t expert = 0;
expert < DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT;
expert++) {
ds4_gpu_stream_expert_cache_entry *e =
&g_stream_expert_cache[layer][expert];
if (!e->valid ||
ds4_gpu_stream_expert_cache_entry_protected(layer,
expert,
protect_layer,
protect_ids,
n_protect)) {
continue;
}
const uint32_t hotness =
g_stream_expert_cache_route_hotness[layer][expert];
if (hotness < lowest_hotness ||
(hotness == lowest_hotness && e->last_used < oldest)) {
lowest_hotness = hotness;
oldest = e->last_used;
victim_layer = layer;
victim_expert = expert;
}
}
}
if (victim_layer == UINT32_MAX || victim_expert == UINT32_MAX) break;
ds4_gpu_stream_expert_cache_clear_entry(victim_layer, victim_expert, 1);
}
}
static int ds4_gpu_stream_expert_cache_entry_matches(
const ds4_gpu_stream_expert_cache_entry *e,
const void *model_map,
uint64_t model_size,
uint64_t gate_abs_offset,
uint64_t up_abs_offset,
uint64_t down_abs_offset,
uint64_t gate_expert_bytes,
uint64_t down_expert_bytes) {
return e &&
e->valid &&
e->model_map == model_map &&
e->model_size == model_size &&
e->gate_abs_offset == gate_abs_offset &&
e->up_abs_offset == up_abs_offset &&
e->down_abs_offset == down_abs_offset &&
e->gate_expert_bytes == gate_expert_bytes &&
e->down_expert_bytes == down_expert_bytes &&
e->gate_buffer && e->up_buffer && e->down_buffer;
}
static ds4_gpu_stream_expert_cache_entry *ds4_gpu_stream_expert_cache_peek(
const void *model_map,
uint64_t model_size,
uint32_t layer,
uint32_t expert,
uint32_t n_total_expert,
uint32_t n_selected,
uint64_t gate_abs_offset,
uint64_t up_abs_offset,
uint64_t down_abs_offset,
uint64_t gate_expert_bytes,
uint64_t down_expert_bytes) {
if (!g_ssd_streaming_mode ||
layer >= DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER ||
expert >= DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT ||
expert >= n_total_expert ||
!ds4_gpu_stream_expert_cache_note_expert_size(gate_expert_bytes,
down_expert_bytes) ||
ds4_gpu_stream_expert_cache_effective_cap(layer,
n_total_expert,
n_selected) == 0) {
return NULL;
}
ds4_gpu_stream_expert_cache_entry *e =
&g_stream_expert_cache[layer][expert];
if (!ds4_gpu_stream_expert_cache_entry_matches(e,
model_map,
model_size,
gate_abs_offset,
up_abs_offset,
down_abs_offset,
gate_expert_bytes,
down_expert_bytes)) {
return NULL;
}
e->last_used = ++g_stream_expert_cache_clock;
e->use_count++;
g_stream_expert_cache_hits++;
g_stream_expert_cache_layer_hits[layer]++;
return e;
}
static ds4_gpu_stream_expert_cache_entry *
ds4_gpu_stream_expert_cache_install_loaded(
const void *model_map,
uint64_t model_size,
uint32_t layer,
uint32_t expert,
uint64_t gate_abs_offset,
uint64_t up_abs_offset,
uint64_t down_abs_offset,
uint64_t gate_expert_bytes,
uint64_t down_expert_bytes,
id<MTLBuffer> gate_buf,
id<MTLBuffer> up_buf,
id<MTLBuffer> down_buf,
NSUInteger gate_inner,
NSUInteger up_inner,
NSUInteger down_inner) {
if (!gate_buf || !up_buf || !down_buf ||
layer >= DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER ||
expert >= DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT) {
return NULL;
}
if (gate_expert_bytes > (UINT64_MAX - down_expert_bytes) / 2ull) {
fprintf(stderr, "ds4: Metal streaming expert cache byte size overflow\n");
return NULL;
}
const uint64_t logical_bytes = gate_expert_bytes * 2ull + down_expert_bytes;
ds4_gpu_stream_expert_cache_entry *e =
&g_stream_expert_cache[layer][expert];
if (e->valid) {
if (ds4_gpu_stream_expert_cache_entry_inflight(e) &&
ds4_gpu_stream_expert_cache_on_service_thread()) {
return NULL;
}
if (ds4_gpu_stream_expert_cache_entry_inflight(e) &&
!ds4_gpu_stream_expert_cache_wait_inflight(
"streaming expert cache install")) {
return NULL;
}
if (ds4_gpu_stream_expert_cache_entry_inflight(e)) return NULL;
ds4_gpu_stream_expert_cache_clear_entry(layer, expert, 0);
if (e->valid) return NULL;
}
if (!ds4_gpu_stream_expert_cache_set_addr_slot(layer,
expert,
gate_buf,
gate_inner,
up_buf,
up_inner,
down_buf,
down_inner)) {
return NULL;
}
e->gate_buffer = gate_buf;
e->up_buffer = up_buf;
e->down_buffer = down_buf;
e->model_map = model_map;
e->model_size = model_size;
e->gate_abs_offset = gate_abs_offset;
e->up_abs_offset = up_abs_offset;
e->down_abs_offset = down_abs_offset;
e->gate_expert_bytes = gate_expert_bytes;
e->down_expert_bytes = down_expert_bytes;
e->logical_bytes = logical_bytes;
e->last_used = ++g_stream_expert_cache_clock;
e->use_count = 1;
e->gate_inner = gate_inner;
e->up_inner = up_inner;
e->down_inner = down_inner;
e->inflight_seq = 0;
uint32_t slab_slot = 0;
if (gate_buf == up_buf &&
gate_buf == down_buf &&
ds4_gpu_stream_expert_slab_slot_for_buffer(gate_buf,
gate_inner,
&slab_slot)) {
e->slab_backed = 1;
e->slab_slot = slab_slot;
} else {
e->slab_backed = 0;
e->slab_slot = 0;
}
e->valid = 1;
g_stream_expert_cache_layer_count[layer]++;
if (g_stream_expert_cache_entry_count < UINT32_MAX) {
g_stream_expert_cache_entry_count++;
}
if (g_stream_expert_cache_bytes > UINT64_MAX - logical_bytes) {
g_stream_expert_cache_bytes = UINT64_MAX;
} else {
g_stream_expert_cache_bytes += logical_bytes;
}
g_stream_expert_cache_misses++;
g_stream_expert_cache_layer_misses[layer]++;
g_stream_expert_cache_wraps += 3;
return e;
}
static ds4_gpu_stream_expert_cache_entry *ds4_gpu_stream_expert_cache_get_protected(
const void *model_map,
uint64_t model_size,
uint32_t layer,
uint32_t expert,
uint32_t n_total_expert,
uint32_t n_selected,
uint64_t gate_abs_offset,
uint64_t up_abs_offset,
uint64_t down_abs_offset,
uint64_t gate_expert_bytes,
uint64_t down_expert_bytes,
const int32_t *protect_ids,
uint32_t n_protect) {
if (!g_ssd_streaming_mode ||
layer >= DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER ||
expert >= DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT ||
expert >= n_total_expert ||
!ds4_gpu_stream_expert_cache_note_expert_size(gate_expert_bytes,
down_expert_bytes) ||
ds4_gpu_stream_expert_cache_effective_cap(layer,
n_total_expert,
n_selected) == 0) {
return NULL;
}
ds4_gpu_stream_expert_cache_entry *e =
&g_stream_expert_cache[layer][expert];
if (ds4_gpu_stream_expert_cache_entry_matches(e,
model_map,
model_size,
gate_abs_offset,
up_abs_offset,
down_abs_offset,
gate_expert_bytes,
down_expert_bytes)) {
e->last_used = ++g_stream_expert_cache_clock;
e->use_count++;
g_stream_expert_cache_hits++;
g_stream_expert_cache_layer_hits[layer]++;
return e;
}
ds4_gpu_stream_expert_readahead_range(gate_abs_offset, gate_expert_bytes);
ds4_gpu_stream_expert_readahead_range(up_abs_offset, gate_expert_bytes);
ds4_gpu_stream_expert_readahead_range(down_abs_offset, down_expert_bytes);
id<MTLBuffer> gate_buf = nil;
id<MTLBuffer> up_buf = nil;
id<MTLBuffer> down_buf = nil;
NSUInteger gate_inner = 0;
NSUInteger up_inner = 0;
NSUInteger down_inner = 0;
const int32_t protect_one = (int32_t)expert;
if (!protect_ids || n_protect == 0) {
protect_ids = &protect_one;
n_protect = 1;
}
const uint32_t cache_budget =
ds4_gpu_stream_expert_cache_configured_budget();
const int force_reuse =
cache_budget != 0 && g_stream_expert_cache_entry_count >= cache_budget;
if (!ds4_gpu_stream_expert_cache_prepare_load_buffers(layer,
expert,
layer,
protect_ids,
n_protect,
gate_expert_bytes,
down_expert_bytes,
force_reuse,
&gate_buf,
&up_buf,
&down_buf,
&gate_inner,
&up_inner,
&down_inner)) {
return NULL;
}
if (!gate_buf || !up_buf || !down_buf) return NULL;
uint8_t *gate_dst = (uint8_t *)[gate_buf contents] + gate_inner;
uint8_t *up_dst = (uint8_t *)[up_buf contents] + up_inner;
uint8_t *down_dst = (uint8_t *)[down_buf contents] + down_inner;
if (!gate_dst || !up_dst || !down_dst) return NULL;
ds4_gpu_stream_expert_pread_task tasks[3] = {
{
.offset = gate_abs_offset,
.len = gate_expert_bytes,
.dst = gate_dst,
},
{
.offset = up_abs_offset,
.len = gate_expert_bytes,
.dst = up_dst,
},
{
.offset = down_abs_offset,
.len = down_expert_bytes,
.dst = down_dst,
},
};
uint64_t read_bytes = 0;
double read_ms = 0.0;
if (!ds4_gpu_stream_expert_pread_tasks(tasks, 3, &read_bytes, &read_ms)) {
return NULL;
}
ds4_gpu_stream_expert_cache_note_pread(layer, read_bytes, read_ms);
if (getenv("DS4_METAL_STREAMING_EXPERT_PREAD_PROFILE") != NULL) {
fprintf(stderr,
"ds4: Metal streaming expert parallel pread layer=%u experts=1 tensors=3 "
"threads=%u bytes=%.2f GiB wall=%.3f ms\n",
layer,
ds4_gpu_stream_expert_pread_thread_count(3),
ds4_gpu_gib(read_bytes),
read_ms);
}
return ds4_gpu_stream_expert_cache_install_loaded(model_map,
model_size,
layer,
expert,
gate_abs_offset,
up_abs_offset,
down_abs_offset,
gate_expert_bytes,
down_expert_bytes,
gate_buf,
up_buf,
down_buf,
gate_inner,
up_inner,
down_inner);
}
static ds4_gpu_stream_expert_cache_entry *ds4_gpu_stream_expert_cache_get(
const void *model_map,
uint64_t model_size,
uint32_t layer,
uint32_t expert,
uint32_t n_total_expert,
uint32_t n_selected,
uint64_t gate_abs_offset,
uint64_t up_abs_offset,
uint64_t down_abs_offset,
uint64_t gate_expert_bytes,
uint64_t down_expert_bytes) {
return ds4_gpu_stream_expert_cache_get_protected(model_map,
model_size,
layer,
expert,
n_total_expert,
n_selected,
gate_abs_offset,
up_abs_offset,
down_abs_offset,
gate_expert_bytes,
down_expert_bytes,
NULL,
0);
}
static int ds4_gpu_stream_expert_pending_load_profile_enabled(void) {
return getenv("DS4_METAL_STREAMING_EXPERT_EARLY_LOAD_PROFILE") != NULL;
}
static void ds4_gpu_stream_expert_pending_load_release_buffers(
ds4_gpu_stream_expert_pending_load *p) {
if (!p) return;
for (uint32_t i = 0; i < DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED; i++) {
p->gate_bufs[i] = nil;
p->up_bufs[i] = nil;
p->down_bufs[i] = nil;
p->gate_inners[i] = 0;
p->up_inners[i] = 0;
p->down_inners[i] = 0;
}
}
static int ds4_gpu_stream_expert_pending_load_install(
ds4_gpu_stream_expert_pending_load *p,
ds4_gpu_stream_expert_cache_entry *entries[DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED],
double elapsed_ms) {
if (!p || p->n_loads == 0) return 1;
uint64_t read_bytes = 0;
int ok = 1;
for (uint32_t i = 0; i < p->n_tasks; i++) {
if (!p->tasks[i].ok) ok = 0;
if (read_bytes > UINT64_MAX - p->tasks[i].read_bytes) {
read_bytes = UINT64_MAX;
} else {
read_bytes += p->tasks[i].read_bytes;
}
}
if (!ok) return 0;
ds4_gpu_stream_expert_cache_note_pread(p->layer, read_bytes, elapsed_ms);
const int load_timing = ds4_gpu_stream_expert_timing_summary_enabled();
double load_t0 = load_timing ? ds4_gpu_now_ms() : 0.0;
double load_install_ms = 0.0;
ds4_gpu_stream_expert_cache_entry
*loaded_entries[DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED];
for (uint32_t i = 0; i < DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED; i++) {
loaded_entries[i] = NULL;
}
for (uint32_t load_i = 0; load_i < p->n_loads; load_i++) {
const uint32_t slot = p->load_slots[load_i];
const uint32_t expert = (uint32_t)p->selected_ids[slot];
ds4_gpu_stream_expert_cache_entry *entry =
ds4_gpu_stream_expert_cache_install_loaded(p->model_map,
p->model_size,
p->layer,
expert,
p->gate_abs_offsets[slot],
p->up_abs_offsets[slot],
p->down_abs_offsets[slot],
p->gate_expert_bytes,
p->down_expert_bytes,
p->gate_bufs[load_i],
p->up_bufs[load_i],
p->down_bufs[load_i],
p->gate_inners[load_i],
p->up_inners[load_i],
p->down_inners[load_i]);
if (!entry) return 0;
loaded_entries[slot] = entry;
if (entries) entries[slot] = entry;
}
for (uint32_t i = 0; i < p->n_selected; i++) {
if ((p->missing_mask & (1u << i)) == 0) continue;
if (entries && entries[i]) continue;
const uint32_t source = p->source_slots[i];
if (source >= p->n_selected) return 0;
ds4_gpu_stream_expert_cache_entry *entry = entries && entries[source] ?
entries[source] : loaded_entries[source];
if (!entry) return 0;
entry->use_count++;
if (entries) entries[i] = entry;
}
if (load_timing) {
load_install_ms = ds4_gpu_now_ms() - load_t0;
ds4_gpu_stream_expert_timing_note_load_detail(p->prepare_ms,
elapsed_ms,
load_install_ms);
}
if (ds4_gpu_stream_expert_pending_load_profile_enabled()) {
fprintf(stderr,
"ds4: Metal streaming expert early-load finish layer=%u experts=%u tensors=%u bytes=%.2f GiB wall=%.3f ms\n",
p->layer,
p->n_loads,
p->n_tasks,
ds4_gpu_gib(read_bytes),
elapsed_ms);
}
return 1;
}
static int ds4_gpu_stream_expert_pending_load_finish(
ds4_gpu_stream_expert_cache_entry *entries[DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED]) {
ds4_gpu_stream_expert_pending_load *p = &g_stream_expert_pending_load;
if (!p->active) return 1;
const double start_ms = p->start_ms;
if (!ds4_gpu_stream_expert_pread_pool_wait()) {
ds4_gpu_stream_expert_pending_load_release_buffers(p);
p->active = 0;
return 0;
}
const double elapsed_ms = ds4_gpu_now_ms() - start_ms;
p->active = 0;
const int ok = ds4_gpu_stream_expert_pending_load_install(p,
entries,
elapsed_ms);
ds4_gpu_stream_expert_pending_load_release_buffers(p);
p->n_tasks = 0;
p->n_loads = 0;
p->prepare_ms = 0.0;
return ok;
}
static void ds4_gpu_stream_expert_pending_load_clear(void) {
if (!g_stream_expert_pending_load.active) {
ds4_gpu_stream_expert_pending_load_release_buffers(
&g_stream_expert_pending_load);
return;
}
(void)ds4_gpu_stream_expert_pending_load_finish(NULL);
}
static int ds4_gpu_stream_expert_pending_load_matches(
const void *model_map,
uint64_t model_size,
uint32_t layer,
const int32_t *selected_ids,
uint32_t n_total_expert,
uint32_t n_selected,
uint64_t gate_expert_bytes,
uint64_t down_expert_bytes) {
ds4_gpu_stream_expert_pending_load *p = &g_stream_expert_pending_load;
if (!p->active ||
p->model_map != model_map ||
p->model_size != model_size ||
p->layer != layer ||
p->n_total_expert != n_total_expert ||
p->n_selected != n_selected ||
p->gate_expert_bytes != gate_expert_bytes ||
p->down_expert_bytes != down_expert_bytes) {
return 0;
}
for (uint32_t i = 0; i < n_selected; i++) {
if (p->selected_ids[i] != selected_ids[i]) return 0;
}
return 1;
}
int ds4_gpu_stream_expert_cache_begin_selected_load(
const ds4_gpu_stream_expert_table *table,
const int32_t *selected_ids,
uint32_t n_selected) {
if (!g_ssd_streaming_mode ||
getenv("DS4_METAL_DISABLE_STREAMING_EXPERT_EARLY_LOAD") != NULL) {
return 1;
}
if (!table) return 0;
const void *model_map = table->model_map;
const uint64_t model_size = table->model_size;
const uint32_t layer = table->layer;
const uint32_t n_total_expert = table->n_total_expert;
const uint64_t gate_offset = table->gate_offset;
const uint64_t up_offset = table->up_offset;
const uint64_t down_offset = table->down_offset;
const uint64_t gate_expert_bytes = table->gate_expert_bytes;
const uint64_t down_expert_bytes = table->down_expert_bytes;
if (!model_map || !selected_ids ||
layer >= DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER ||
n_selected == 0 ||
n_selected > DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED ||
n_total_expert == 0 ||
n_total_expert > DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT ||
!ds4_gpu_stream_expert_cache_note_expert_size(gate_expert_bytes,
down_expert_bytes) ||
ds4_gpu_stream_expert_cache_effective_cap(layer,
n_total_expert,
n_selected) == 0) {
return 1;
}
if (!g_initialized && !ds4_gpu_init()) return 0;
if (ds4_gpu_stream_expert_pending_load_matches(model_map,
model_size,
layer,
selected_ids,
n_total_expert,
n_selected,
gate_expert_bytes,
down_expert_bytes)) {
return 1;
}
ds4_gpu_stream_expert_pending_load_clear();
ds4_gpu_stream_expert_pending_load *p = &g_stream_expert_pending_load;
p->active = 0;
p->model_map = model_map;
p->model_size = model_size;
p->layer = layer;
p->n_total_expert = n_total_expert;
p->n_selected = n_selected;
p->missing_mask = 0;
p->n_loads = 0;
p->n_tasks = 0;
p->gate_expert_bytes = gate_expert_bytes;
p->down_expert_bytes = down_expert_bytes;
p->prepare_ms = 0.0;
const int load_timing = ds4_gpu_stream_expert_timing_summary_enabled();
const double load_prepare_t0 = load_timing ? ds4_gpu_now_ms() : 0.0;
for (uint32_t i = 0; i < DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED; i++) {
p->selected_ids[i] = -1;
p->load_slots[i] = 0;
p->source_slots[i] = UINT32_MAX;
p->gate_abs_offsets[i] = 0;
p->up_abs_offsets[i] = 0;
p->down_abs_offsets[i] = 0;
p->gate_bufs[i] = nil;
p->up_bufs[i] = nil;
p->down_bufs[i] = nil;
p->gate_inners[i] = 0;
p->up_inners[i] = 0;
p->down_inners[i] = 0;
}
memset(p->tasks, 0, sizeof(p->tasks));
for (uint32_t i = 0; i < n_selected; i++) {
if (selected_ids[i] < 0 || (uint32_t)selected_ids[i] >= n_total_expert) {
fprintf(stderr,
"ds4: Metal streaming early-load expert id %d is outside 0..%u\n",
selected_ids[i],
n_total_expert);
return 0;
}
p->selected_ids[i] = selected_ids[i];
const uint64_t expert_id = (uint64_t)(uint32_t)selected_ids[i];
if (expert_id > UINT64_MAX / gate_expert_bytes ||
expert_id > UINT64_MAX / down_expert_bytes) {
fprintf(stderr, "ds4: Metal streaming early-load offset overflow\n");
return 0;
}
const uint64_t gate_rel = expert_id * gate_expert_bytes;
const uint64_t down_rel = expert_id * down_expert_bytes;
if (gate_rel > UINT64_MAX - gate_offset ||
gate_rel > UINT64_MAX - up_offset ||
down_rel > UINT64_MAX - down_offset) {
fprintf(stderr, "ds4: Metal streaming early-load offset overflow\n");
return 0;
}
p->gate_abs_offsets[i] = gate_offset + gate_rel;
p->up_abs_offsets[i] = up_offset + gate_rel;
p->down_abs_offsets[i] = down_offset + down_rel;
ds4_gpu_stream_expert_cache_entry *e =
&g_stream_expert_cache[layer][(uint32_t)selected_ids[i]];
if (ds4_gpu_stream_expert_cache_entry_matches(e,
model_map,
model_size,
p->gate_abs_offsets[i],
p->up_abs_offsets[i],
p->down_abs_offsets[i],
gate_expert_bytes,
down_expert_bytes)) {
continue;
}
uint32_t source = UINT32_MAX;
for (uint32_t prev = 0; prev < i; prev++) {
if (selected_ids[prev] == selected_ids[i] &&
p->gate_abs_offsets[prev] == p->gate_abs_offsets[i] &&
p->up_abs_offsets[prev] == p->up_abs_offsets[i] &&
p->down_abs_offsets[prev] == p->down_abs_offsets[i] &&
(p->missing_mask & (1u << prev)) != 0) {
source = p->source_slots[prev] != UINT32_MAX ?
p->source_slots[prev] : prev;
break;
}
}
if (source != UINT32_MAX) {
p->source_slots[i] = source;
p->missing_mask |= 1u << i;
continue;
}
p->source_slots[i] = i;
p->missing_mask |= 1u << i;
p->load_slots[p->n_loads++] = i;
}
if (p->n_loads == 0) return 1;
const uint32_t cache_budget =
ds4_gpu_stream_expert_cache_configured_budget();
uint32_t reserved_entries = g_stream_expert_cache_entry_count;
ds4_gpu_stream_expert_reusable_buffers
batch_reuse[DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED];
for (uint32_t i = 0; i < DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED; i++) {
batch_reuse[i] =
(ds4_gpu_stream_expert_reusable_buffers){ nil, nil, nil, 0, 0, 0 };
}
uint32_t batch_reuse_count = 0;
if (cache_budget != 0 &&
reserved_entries >= cache_budget &&
p->n_loads > 1 &&
ds4_gpu_stream_expert_batch_reuse_enabled(gate_expert_bytes,
down_expert_bytes)) {
const double reuse_t0 = load_timing ? ds4_gpu_now_ms() : 0.0;
batch_reuse_count =
ds4_gpu_stream_expert_cache_take_reusable_batch(
p->n_loads,
layer,
selected_ids,
n_selected,
gate_expert_bytes,
down_expert_bytes,
batch_reuse);
if (load_timing) {
ds4_gpu_stream_expert_timing_note_prepare_batch_reuse(
ds4_gpu_now_ms() - reuse_t0);
}
}
for (uint32_t load_i = 0; load_i < p->n_loads; load_i++) {
const uint32_t slot = p->load_slots[load_i];
const uint32_t expert = (uint32_t)p->selected_ids[slot];
const int force_reuse =
cache_budget != 0 && reserved_entries >= cache_budget;
ds4_gpu_stream_expert_readahead_range(p->gate_abs_offsets[slot],
gate_expert_bytes);
ds4_gpu_stream_expert_readahead_range(p->up_abs_offsets[slot],
gate_expert_bytes);
ds4_gpu_stream_expert_readahead_range(p->down_abs_offsets[slot],
down_expert_bytes);
if (load_i < batch_reuse_count &&
batch_reuse[load_i].gate_buffer &&
batch_reuse[load_i].up_buffer &&
batch_reuse[load_i].down_buffer) {
p->gate_bufs[load_i] = batch_reuse[load_i].gate_buffer;
p->up_bufs[load_i] = batch_reuse[load_i].up_buffer;
p->down_bufs[load_i] = batch_reuse[load_i].down_buffer;
p->gate_inners[load_i] = batch_reuse[load_i].gate_inner;
p->up_inners[load_i] = batch_reuse[load_i].up_inner;
p->down_inners[load_i] = batch_reuse[load_i].down_inner;
} else {
const double buffer_t0 = load_timing ? ds4_gpu_now_ms() : 0.0;
const int prepared =
ds4_gpu_stream_expert_cache_prepare_load_buffers(layer,
expert,
layer,
selected_ids,
n_selected,
gate_expert_bytes,
down_expert_bytes,
force_reuse,
&p->gate_bufs[load_i],
&p->up_bufs[load_i],
&p->down_bufs[load_i],
&p->gate_inners[load_i],
&p->up_inners[load_i],
&p->down_inners[load_i]);
if (load_timing) {
ds4_gpu_stream_expert_timing_note_prepare_buffer(
ds4_gpu_now_ms() - buffer_t0);
}
if (!prepared) {
ds4_gpu_stream_expert_pending_load_release_buffers(p);
return 0;
}
}
if (!force_reuse && reserved_entries < UINT32_MAX) {
reserved_entries++;
}
uint8_t *gate_dst = (uint8_t *)[p->gate_bufs[load_i] contents] +
p->gate_inners[load_i];
uint8_t *up_dst = (uint8_t *)[p->up_bufs[load_i] contents] +
p->up_inners[load_i];
uint8_t *down_dst = (uint8_t *)[p->down_bufs[load_i] contents] +
p->down_inners[load_i];
if (!gate_dst || !up_dst || !down_dst) {
ds4_gpu_stream_expert_pending_load_release_buffers(p);
return 0;
}
const double task_t0 = load_timing ? ds4_gpu_now_ms() : 0.0;
p->tasks[p->n_tasks++] = (ds4_gpu_stream_expert_pread_task) {
.offset = p->gate_abs_offsets[slot],
.len = gate_expert_bytes,
.dst = gate_dst,
};
p->tasks[p->n_tasks++] = (ds4_gpu_stream_expert_pread_task) {
.offset = p->up_abs_offsets[slot],
.len = gate_expert_bytes,
.dst = up_dst,
};
p->tasks[p->n_tasks++] = (ds4_gpu_stream_expert_pread_task) {
.offset = p->down_abs_offsets[slot],
.len = down_expert_bytes,
.dst = down_dst,
};
if (load_timing) {
ds4_gpu_stream_expert_timing_note_prepare_task(
1,
ds4_gpu_now_ms() - task_t0);
}
}
const uint32_t n_workers =
ds4_gpu_stream_expert_pread_thread_count(p->n_tasks);
p->start_ms = ds4_gpu_now_ms();
if (load_timing) {
p->prepare_ms = p->start_ms - load_prepare_t0;
}
if (ds4_gpu_stream_expert_pread_pool_begin(p->tasks,
p->n_tasks,
n_workers)) {
p->active = 1;
if (ds4_gpu_stream_expert_pending_load_profile_enabled()) {
fprintf(stderr,
"ds4: Metal streaming expert early-load begin layer=%u experts=%u tensors=%u threads=%u\n",
layer,
p->n_loads,
p->n_tasks,
n_workers);
}
return 1;
}
uint64_t read_bytes = 0;
double read_ms = 0.0;
if (!ds4_gpu_stream_expert_pread_tasks(p->tasks,
p->n_tasks,
&read_bytes,
&read_ms)) {
ds4_gpu_stream_expert_pending_load_release_buffers(p);
return 0;
}
(void)read_bytes;
if (!ds4_gpu_stream_expert_pending_load_install(p, NULL, read_ms)) {
ds4_gpu_stream_expert_pending_load_release_buffers(p);
return 0;
}
ds4_gpu_stream_expert_pending_load_release_buffers(p);
p->n_tasks = 0;
p->n_loads = 0;
p->prepare_ms = 0.0;
return 1;
}
static int ds4_gpu_stream_expert_cache_load_selected_missing_with_source(
const void *model_map,
uint64_t model_size,
uint32_t layer,
const int32_t *selected_ids,
uint32_t n_total_expert,
uint32_t n_selected,
const uint64_t *gate_abs_offsets,
const uint64_t *up_abs_offsets,
const uint64_t *down_abs_offsets,
uint64_t gate_expert_bytes,
uint64_t down_expert_bytes,
uint32_t missing_mask,
int gpu_copy_source,
ds4_gpu_stream_expert_cache_entry **entries) {
if (!g_ssd_streaming_mode ||
!model_map ||
!selected_ids ||
!gate_abs_offsets ||
!up_abs_offsets ||
!down_abs_offsets ||
!entries ||
layer >= DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER ||
n_selected == 0 ||
n_selected > DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED ||
n_total_expert == 0 ||
n_total_expert > DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT ||
!ds4_gpu_stream_expert_cache_note_expert_size(gate_expert_bytes,
down_expert_bytes) ||
ds4_gpu_stream_expert_cache_effective_cap(layer,
n_total_expert,
n_selected) == 0) {
return 0;
}
missing_mask &= (1u << n_selected) - 1u;
if (missing_mask == 0) return 1;
if (g_stream_expert_pending_load.active &&
n_selected <= DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED) {
if (ds4_gpu_stream_expert_pending_load_matches(model_map,
model_size,
layer,
selected_ids,
n_total_expert,
n_selected,
gate_expert_bytes,
down_expert_bytes)) {
if (!ds4_gpu_stream_expert_pending_load_finish(entries)) return 0;
for (uint32_t i = 0; i < n_selected; i++) {
if ((missing_mask & (1u << i)) != 0 && entries[i]) {
missing_mask &= ~(1u << i);
}
}
if (missing_mask == 0) return 1;
} else {
ds4_gpu_stream_expert_pending_load_clear();
}
} else if (g_stream_expert_pending_load.active) {
ds4_gpu_stream_expert_pending_load_clear();
}
const int load_timing = ds4_gpu_stream_expert_timing_summary_enabled();
double load_t0 = load_timing ? ds4_gpu_now_ms() : 0.0;
double load_prepare_ms = 0.0;
double load_install_ms = 0.0;
uint32_t load_slots[DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED];
uint32_t source_slots[DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED];
for (uint32_t i = 0; i < DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED; i++) {
load_slots[i] = 0;
source_slots[i] = UINT32_MAX;
}
uint32_t n_loads = 0;
for (uint32_t i = 0; i < n_selected; i++) {
if ((missing_mask & (1u << i)) == 0) continue;
if (entries[i]) {
source_slots[i] = i;
continue;
}
if (selected_ids[i] < 0 || (uint32_t)selected_ids[i] >= n_total_expert) {
fprintf(stderr,
"ds4: Metal streaming selected missing expert id %d is outside 0..%u\n",
selected_ids[i],
n_total_expert);
return 0;
}
uint32_t source = UINT32_MAX;
for (uint32_t prev = 0; prev < i; prev++) {
if (selected_ids[prev] == selected_ids[i] &&
gate_abs_offsets[prev] == gate_abs_offsets[i] &&
up_abs_offsets[prev] == up_abs_offsets[i] &&
down_abs_offsets[prev] == down_abs_offsets[i] &&
(entries[prev] || (missing_mask & (1u << prev)) != 0)) {
source = source_slots[prev] != UINT32_MAX ?
source_slots[prev] : prev;
break;
}
}
if (source != UINT32_MAX) {
source_slots[i] = source;
continue;
}
source_slots[i] = i;
load_slots[n_loads++] = i;
}
if (n_loads == 0) {
for (uint32_t i = 0; i < n_selected; i++) {
if ((missing_mask & (1u << i)) == 0 || entries[i]) continue;
const uint32_t source = source_slots[i];
if (source >= n_selected || !entries[source]) return 0;
entries[i] = entries[source];
entries[i]->use_count++;
}
return 1;
}
__strong id<MTLBuffer> gate_bufs[DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED];
__strong id<MTLBuffer> up_bufs[DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED];
__strong id<MTLBuffer> down_bufs[DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED];
NSUInteger gate_inners[DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED];
NSUInteger up_inners[DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED];
NSUInteger down_inners[DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED];
for (uint32_t i = 0; i < DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED; i++) {
gate_bufs[i] = nil;
up_bufs[i] = nil;
down_bufs[i] = nil;
gate_inners[i] = 0;
up_inners[i] = 0;
down_inners[i] = 0;
}
ds4_gpu_stream_expert_pread_task tasks[DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED * 3u];
memset(tasks, 0, sizeof(tasks));
uint32_t n_tasks = 0;
const uint32_t cache_budget =
ds4_gpu_stream_expert_cache_configured_budget();
uint32_t reserved_entries = g_stream_expert_cache_entry_count;
ds4_gpu_stream_expert_reusable_buffers
batch_reuse[DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED];
for (uint32_t i = 0; i < DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED; i++) {
batch_reuse[i] =
(ds4_gpu_stream_expert_reusable_buffers){ nil, nil, nil, 0, 0, 0 };
}
uint32_t batch_reuse_count = 0;
if (cache_budget != 0 &&
reserved_entries >= cache_budget &&
n_loads > 1 &&
ds4_gpu_stream_expert_batch_reuse_enabled(gate_expert_bytes,
down_expert_bytes)) {
const double reuse_t0 = load_timing ? ds4_gpu_now_ms() : 0.0;
batch_reuse_count =
ds4_gpu_stream_expert_cache_take_reusable_batch(
n_loads,
layer,
selected_ids,
n_selected,
gate_expert_bytes,
down_expert_bytes,
batch_reuse);
if (load_timing) {
ds4_gpu_stream_expert_timing_note_prepare_batch_reuse(
ds4_gpu_now_ms() - reuse_t0);
}
}
for (uint32_t load_i = 0; load_i < n_loads; load_i++) {
const uint32_t slot = load_slots[load_i];
const uint32_t expert = (uint32_t)selected_ids[slot];
const int force_reuse =
cache_budget != 0 && reserved_entries >= cache_budget;
if (!gpu_copy_source) {
ds4_gpu_stream_expert_readahead_range(gate_abs_offsets[slot],
gate_expert_bytes);
ds4_gpu_stream_expert_readahead_range(up_abs_offsets[slot],
gate_expert_bytes);
ds4_gpu_stream_expert_readahead_range(down_abs_offsets[slot],
down_expert_bytes);
}
if (load_i < batch_reuse_count &&
batch_reuse[load_i].gate_buffer &&
batch_reuse[load_i].up_buffer &&
batch_reuse[load_i].down_buffer) {
gate_bufs[load_i] = batch_reuse[load_i].gate_buffer;
up_bufs[load_i] = batch_reuse[load_i].up_buffer;
down_bufs[load_i] = batch_reuse[load_i].down_buffer;
gate_inners[load_i] = batch_reuse[load_i].gate_inner;
up_inners[load_i] = batch_reuse[load_i].up_inner;
down_inners[load_i] = batch_reuse[load_i].down_inner;
} else {
const double buffer_t0 = load_timing ? ds4_gpu_now_ms() : 0.0;
const int prepared =
ds4_gpu_stream_expert_cache_prepare_load_buffers(layer,
expert,
layer,
selected_ids,
n_selected,
gate_expert_bytes,
down_expert_bytes,
force_reuse,
&gate_bufs[load_i],
&up_bufs[load_i],
&down_bufs[load_i],
&gate_inners[load_i],
&up_inners[load_i],
&down_inners[load_i]);
if (load_timing) {
ds4_gpu_stream_expert_timing_note_prepare_buffer(
ds4_gpu_now_ms() - buffer_t0);
}
if (!prepared) {
return 0;
}
}
if (!force_reuse && reserved_entries < UINT32_MAX) {
reserved_entries++;
}
if (!gate_bufs[load_i] || !up_bufs[load_i] || !down_bufs[load_i]) {
return 0;
}
uint8_t *gate_dst = (uint8_t *)[gate_bufs[load_i] contents] +
gate_inners[load_i];
uint8_t *up_dst = (uint8_t *)[up_bufs[load_i] contents] +
up_inners[load_i];
uint8_t *down_dst = (uint8_t *)[down_bufs[load_i] contents] +
down_inners[load_i];
if (!gate_dst || !up_dst || !down_dst) return 0;
if (gpu_copy_source &&
(gate_abs_offsets[slot] > model_size ||
gate_expert_bytes > model_size - gate_abs_offsets[slot] ||
up_abs_offsets[slot] > model_size ||
gate_expert_bytes > model_size - up_abs_offsets[slot] ||
down_abs_offsets[slot] > model_size ||
down_expert_bytes > model_size - down_abs_offsets[slot])) {
fprintf(stderr,
"ds4: Metal streaming mapped expert source is outside the model mapping\n");
return 0;
}
const double task_t0 = load_timing ? ds4_gpu_now_ms() : 0.0;
tasks[n_tasks++] = (ds4_gpu_stream_expert_pread_task) {
.offset = gate_abs_offsets[slot],
.len = gate_expert_bytes,
.dst = gate_dst,
};
tasks[n_tasks++] = (ds4_gpu_stream_expert_pread_task) {
.offset = up_abs_offsets[slot],
.len = gate_expert_bytes,
.dst = up_dst,
};
tasks[n_tasks++] = (ds4_gpu_stream_expert_pread_task) {
.offset = down_abs_offsets[slot],
.len = down_expert_bytes,
.dst = down_dst,
};
if (load_timing) {
ds4_gpu_stream_expert_timing_note_prepare_task(
1,
ds4_gpu_now_ms() - task_t0);
}
}
if (load_timing) {
const double now_ms = ds4_gpu_now_ms();
load_prepare_ms = now_ms - load_t0;
load_t0 = now_ms;
}
uint64_t read_bytes = 0;
double read_ms = 0.0;
int ok = 1;
if (gpu_copy_source) {
if (!g_batch_cb ||
gate_expert_bytes > (uint64_t)NSUIntegerMax ||
down_expert_bytes > (uint64_t)NSUIntegerMax) {
return 0;
}
__strong id<MTLBuffer> gate_srcs[DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED];
__strong id<MTLBuffer> up_srcs[DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED];
__strong id<MTLBuffer> down_srcs[DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED];
NSUInteger gate_src_inners[DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED];
NSUInteger up_src_inners[DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED];
NSUInteger down_src_inners[DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED];
for (uint32_t load_i = 0; load_i < n_loads; load_i++) {
const uint32_t slot = load_slots[load_i];
uint64_t gate_inner = 0;
uint64_t up_inner = 0;
uint64_t down_inner = 0;
gate_srcs[load_i] = ds4_gpu_wrap_model_range(model_map,
model_size,
gate_abs_offsets[slot],
gate_expert_bytes,
&gate_inner);
up_srcs[load_i] = ds4_gpu_wrap_model_range(model_map,
model_size,
up_abs_offsets[slot],
gate_expert_bytes,
&up_inner);
down_srcs[load_i] = ds4_gpu_wrap_model_range(model_map,
model_size,
down_abs_offsets[slot],
down_expert_bytes,
&down_inner);
if (!gate_srcs[load_i] || !up_srcs[load_i] || !down_srcs[load_i] ||
gate_inner > (uint64_t)NSUIntegerMax ||
up_inner > (uint64_t)NSUIntegerMax ||
down_inner > (uint64_t)NSUIntegerMax) {
return 0;
}
gate_src_inners[load_i] = (NSUInteger)gate_inner;
up_src_inners[load_i] = (NSUInteger)up_inner;
down_src_inners[load_i] = (NSUInteger)down_inner;
}
ds4_gpu_close_batch_encoder();
id<MTLBlitCommandEncoder> blit = ds4_gpu_blit_encoder(g_batch_cb, "expert_cache_fill", n_loads * 3u);
if (!blit) return 0;
for (uint32_t load_i = 0; load_i < n_loads; load_i++) {
[blit copyFromBuffer:gate_srcs[load_i]
sourceOffset:gate_src_inners[load_i]
toBuffer:gate_bufs[load_i]
destinationOffset:gate_inners[load_i]
size:(NSUInteger)gate_expert_bytes];
[blit copyFromBuffer:up_srcs[load_i]
sourceOffset:up_src_inners[load_i]
toBuffer:up_bufs[load_i]
destinationOffset:up_inners[load_i]
size:(NSUInteger)gate_expert_bytes];
[blit copyFromBuffer:down_srcs[load_i]
sourceOffset:down_src_inners[load_i]
toBuffer:down_bufs[load_i]
destinationOffset:down_inners[load_i]
size:(NSUInteger)down_expert_bytes];
const uint64_t bytes =
gate_expert_bytes > (UINT64_MAX - down_expert_bytes) / 2u ?
UINT64_MAX : gate_expert_bytes * 2u + down_expert_bytes;
read_bytes = read_bytes > UINT64_MAX - bytes ?
UINT64_MAX : read_bytes + bytes;
}
[blit endEncoding];
g_batch_has_work = YES;
} else {
ok = ds4_gpu_stream_expert_pread_tasks(tasks,
n_tasks,
&read_bytes,
&read_ms);
}
if (!ok) return 0;
if (load_timing) {
load_t0 = ds4_gpu_now_ms();
}
if (!gpu_copy_source) {
ds4_gpu_stream_expert_cache_note_pread(layer, read_bytes, read_ms);
}
if (gpu_copy_source &&
getenv("DS4_METAL_STREAMING_PREFILL_CACHE_SEED_PROFILE") != NULL) {
fprintf(stderr,
"ds4: Metal streaming expert GPU cache copy layer=%u "
"experts=%u bytes=%.2f GiB\n",
layer,
n_loads,
ds4_gpu_gib(read_bytes));
} else if (!gpu_copy_source &&
getenv("DS4_METAL_STREAMING_EXPERT_PREAD_PROFILE") != NULL) {
fprintf(stderr,
"ds4: Metal streaming expert parallel pread layer=%u experts=%u tensors=%u "
"threads=%u bytes=%.2f GiB wall=%.3f ms\n",
layer,
n_loads,
n_tasks,
ds4_gpu_stream_expert_pread_thread_count(n_tasks),
ds4_gpu_gib(read_bytes),
read_ms);
}
for (uint32_t load_i = 0; load_i < n_loads; load_i++) {
const uint32_t slot = load_slots[load_i];
const uint32_t expert = (uint32_t)selected_ids[slot];
ds4_gpu_stream_expert_cache_entry *entry =
ds4_gpu_stream_expert_cache_install_loaded(model_map,
model_size,
layer,
expert,
gate_abs_offsets[slot],
up_abs_offsets[slot],
down_abs_offsets[slot],
gate_expert_bytes,
down_expert_bytes,
gate_bufs[load_i],
up_bufs[load_i],
down_bufs[load_i],
gate_inners[load_i],
up_inners[load_i],
down_inners[load_i]);
if (!entry) return 0;
entries[slot] = entry;
}
for (uint32_t i = 0; i < n_selected; i++) {
if ((missing_mask & (1u << i)) == 0 || entries[i]) continue;
const uint32_t source = source_slots[i];
if (source >= n_selected || !entries[source]) return 0;
entries[i] = entries[source];
entries[i]->use_count++;
}
for (uint32_t i = 0; i < n_selected; i++) {
if ((missing_mask & (1u << i)) != 0 && !entries[i]) return 0;
}
if (load_timing && !gpu_copy_source) {
load_install_ms = ds4_gpu_now_ms() - load_t0;
ds4_gpu_stream_expert_timing_note_load_detail(load_prepare_ms,
read_ms,
load_install_ms);
}
return 1;
}
static int ds4_gpu_stream_expert_cache_load_selected_missing(
const void *model_map,
uint64_t model_size,
uint32_t layer,
const int32_t *selected_ids,
uint32_t n_total_expert,
uint32_t n_selected,
const uint64_t *gate_abs_offsets,
const uint64_t *up_abs_offsets,
const uint64_t *down_abs_offsets,
uint64_t gate_expert_bytes,
uint64_t down_expert_bytes,
uint32_t missing_mask,
ds4_gpu_stream_expert_cache_entry **entries) {
return ds4_gpu_stream_expert_cache_load_selected_missing_with_source(
model_map,
model_size,
layer,
selected_ids,
n_total_expert,
n_selected,
gate_abs_offsets,
up_abs_offsets,
down_abs_offsets,
gate_expert_bytes,
down_expert_bytes,
missing_mask,
0,
entries);
}
static void ds4_gpu_glm_stream_selected_prefetch_set(
const ds4_gpu_stream_expert_table *table,
const int32_t *selected_ids,
uint32_t n_selected) {
g_glm_stream_selected_prefetch.active = 0;
if (!table || !selected_ids ||
n_selected == 0 ||
n_selected > DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED) {
return;
}
g_glm_stream_selected_prefetch.model_map = table->model_map;
g_glm_stream_selected_prefetch.model_size = table->model_size;
g_glm_stream_selected_prefetch.layer = table->layer;
g_glm_stream_selected_prefetch.n_total_expert = table->n_total_expert;
g_glm_stream_selected_prefetch.n_selected = n_selected;
g_glm_stream_selected_prefetch.gate_offset = table->gate_offset;
g_glm_stream_selected_prefetch.up_offset = table->up_offset;
g_glm_stream_selected_prefetch.down_offset = table->down_offset;
g_glm_stream_selected_prefetch.gate_expert_bytes = table->gate_expert_bytes;
g_glm_stream_selected_prefetch.down_expert_bytes = table->down_expert_bytes;
for (uint32_t i = 0; i < n_selected; i++) {
g_glm_stream_selected_prefetch.selected_ids[i] = selected_ids[i];
}
g_glm_stream_selected_prefetch.active = 1;
}
static int ds4_gpu_glm_stream_selected_prefetch_take(
const void *model_map,
uint64_t model_size,
uint32_t layer,
uint32_t n_total_expert,
uint32_t n_selected,
uint64_t gate_offset,
uint64_t up_offset,
uint64_t down_offset,
uint64_t gate_expert_bytes,
uint64_t down_expert_bytes,
int32_t *selected_ids_out) {
if (!selected_ids_out || !g_glm_stream_selected_prefetch.active) return 0;
ds4_gpu_glm_stream_selected_prefetch *p = &g_glm_stream_selected_prefetch;
if (p->model_map != model_map ||
p->model_size != model_size ||
p->layer != layer ||
p->n_total_expert != n_total_expert ||
p->n_selected != n_selected ||
p->gate_offset != gate_offset ||
p->up_offset != up_offset ||
p->down_offset != down_offset ||
p->gate_expert_bytes != gate_expert_bytes ||
p->down_expert_bytes != down_expert_bytes) {
return 0;
}
for (uint32_t i = 0; i < n_selected; i++) {
selected_ids_out[i] = p->selected_ids[i];
}
p->active = 0;
return 1;
}
int ds4_gpu_glm_stream_expert_cache_begin_selected_load_tensor(
const ds4_gpu_stream_expert_table *table,
const ds4_gpu_tensor *selected,
uint32_t n_selected) {
g_glm_stream_selected_prefetch.active = 0;
if (!g_ssd_streaming_mode ||
getenv("DS4_METAL_DISABLE_GLM_STREAMING_EXPERT_EARLY_LOAD") != NULL) {
return 1;
}
if (!table || !selected ||
n_selected == 0 ||
n_selected > DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED) {
return 1;
}
const int timing = ds4_gpu_stream_expert_timing_summary_enabled();
double t0 = timing ? ds4_gpu_now_ms() : 0.0;
double selected_sync_ms = 0.0;
double selected_copy_ms = 0.0;
double selected_bind_ms = 0.0;
const int had_batch = g_batch_cb != nil;
if (had_batch) {
if (ds4_gpu_end_commands() == 0) return 0;
if (timing) {
selected_sync_ms = ds4_gpu_now_ms() - t0;
t0 = ds4_gpu_now_ms();
}
}
int32_t selected_ids[DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED];
for (uint32_t i = 0; i < DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED; i++) {
selected_ids[i] = -1;
}
int ok = ds4_gpu_tensor_read(selected,
0,
selected_ids,
(uint64_t)n_selected * sizeof(selected_ids[0])) != 0;
if (timing) {
selected_copy_ms = ds4_gpu_now_ms() - t0;
t0 = ds4_gpu_now_ms();
}
if (ok) {
ok = ds4_gpu_stream_expert_cache_begin_selected_load(table,
selected_ids,
n_selected) != 0;
}
if (timing) {
selected_bind_ms = ds4_gpu_now_ms() - t0;
ds4_gpu_stream_expert_timing_note_selected(selected_sync_ms,
selected_copy_ms,
selected_bind_ms);
}
if (ok) {
ds4_gpu_glm_stream_selected_prefetch_set(table,
selected_ids,
n_selected);
}
if (had_batch && ds4_gpu_begin_commands() == 0) ok = 0;
return ok;
}
static void ds4_gpu_stream_expert_cache_clear_layer(uint32_t layer) {
if (layer >= DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER) return;
for (uint32_t expert = 0;
expert < DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT;
expert++) {
ds4_gpu_stream_expert_cache_clear_entry(layer, expert, 0);
}
g_stream_expert_cache_layer_count[layer] = 0;
}
static int ds4_gpu_stream_expert_cache_prepare_selected_batch(
const void *model_map,
uint64_t model_size,
uint32_t layer,
const ds4_gpu_tensor *selected,
uint32_t n_tokens,
uint32_t n_total_expert,
uint32_t n_selected,
uint64_t gate_offset,
uint64_t up_offset,
uint64_t down_offset,
uint64_t gate_expert_bytes,
uint64_t down_expert_bytes,
id<MTLBuffer> *gate_addrs,
id<MTLBuffer> *up_addrs,
id<MTLBuffer> *down_addrs,
ds4_gpu_stream_expert_cache_entry **resources,
uint32_t *n_resources,
uint32_t *unique_out,
id<MTLBuffer> *overflow_gate,
id<MTLBuffer> *overflow_up,
id<MTLBuffer> *overflow_down) {
if (overflow_gate) *overflow_gate = nil;
if (overflow_up) *overflow_up = nil;
if (overflow_down) *overflow_down = nil;
if (!g_ssd_streaming_mode ||
!model_map ||
!selected ||
!gate_addrs ||
!up_addrs ||
!down_addrs ||
!resources ||
!n_resources ||
!unique_out ||
!overflow_gate ||
!overflow_up ||
!overflow_down ||
layer >= DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER ||
n_tokens == 0 ||
n_selected == 0 ||
n_selected > DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED ||
n_total_expert == 0 ||
n_total_expert > DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT ||
gate_expert_bytes == 0 ||
down_expert_bytes == 0) {
return 0;
}
if (n_tokens > UINT32_MAX / n_selected) return 0;
const uint64_t n_ids = (uint64_t)n_tokens * n_selected;
if (n_ids > SIZE_MAX / sizeof(int32_t)) return 0;
int32_t *ids = malloc((size_t)n_ids * sizeof(ids[0]));
if (!ids) return 0;
const bool profile =
getenv("DS4_METAL_STREAMING_PREFILL_BATCH_SELECTED_ADDR_PROFILE") != NULL;
const double t0 = profile ? ds4_gpu_now_ms() : 0.0;
int ok = ds4_gpu_tensor_read(selected,
0,
ids,
n_ids * sizeof(ids[0]));
const double t_read = profile ? ds4_gpu_now_ms() : 0.0;
bool seen[DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT] = { false };
uint32_t frequency[DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT] = { 0 };
int32_t unique_ids[DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT];
uint32_t unique_count = 0;
*n_resources = 0;
*unique_out = 0;
if (ok) {
if (!ds4_gpu_stream_expert_cache_ensure_addr_buffers(layer)) {
ok = 0;
}
}
if (ok) {
for (uint64_t i = 0; i < n_ids; i++) {
const int32_t selected_id = ids[i];
if (selected_id < 0 || (uint32_t)selected_id >= n_total_expert) {
fprintf(stderr,
"ds4: Metal streaming batch selected expert id %d is outside 0..%u at layer %u\n",
selected_id,
n_total_expert,
layer);
ok = 0;
break;
}
frequency[(uint32_t)selected_id]++;
if (!seen[(uint32_t)selected_id]) {
seen[(uint32_t)selected_id] = true;
unique_ids[unique_count++] = selected_id;
}
}
}
if (ok) {
ds4_gpu_stream_expert_cache_note_frequency_hotness(layer,
frequency,
n_total_expert);
}
/*
* When the layer's unique selected set does not fit the cache budget, the
* extra experts are addressed straight into whole-tensor mapped model views
* instead of falling back to a different MoE kernel path. The address-table
* kernels read identical expert bytes either way, so the cache/view split
* does not change the computed logits.
*/
uint32_t view_served = 0;
uint64_t overflow_gate_inner = 0;
uint64_t overflow_up_inner = 0;
uint64_t overflow_down_inner = 0;
uint64_t unique_gate_offsets[DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT];
uint64_t unique_up_offsets[DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT];
uint64_t unique_down_offsets[DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT];
ds4_gpu_stream_expert_cache_entry
*unique_entries[DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT];
uint32_t load_unique[DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT];
__strong id<MTLBuffer>
gate_bufs[DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT];
__strong id<MTLBuffer>
up_bufs[DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT];
__strong id<MTLBuffer>
down_bufs[DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT];
NSUInteger gate_inners[DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT];
NSUInteger up_inners[DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT];
NSUInteger down_inners[DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT];
for (uint32_t i = 0; i < DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT; i++) {
unique_gate_offsets[i] = 0;
unique_up_offsets[i] = 0;
unique_down_offsets[i] = 0;
unique_entries[i] = NULL;
load_unique[i] = 0;
gate_bufs[i] = nil;
up_bufs[i] = nil;
down_bufs[i] = nil;
gate_inners[i] = 0;
up_inners[i] = 0;
down_inners[i] = 0;
}
ds4_gpu_stream_expert_pread_task *tasks = NULL;
uint32_t n_loads = 0;
uint32_t n_tasks = 0;
double load_prepare_ms = 0.0;
double load_install_ms = 0.0;
double load_timing_t0 = ds4_gpu_stream_expert_timing_summary_enabled() ?
ds4_gpu_now_ms() : 0.0;
const int load_timing = load_timing_t0 != 0.0;
if (ok && unique_count != 0) {
tasks = calloc((size_t)unique_count * 3u, sizeof(tasks[0]));
if (!tasks) ok = 0;
}
if (ok) {
const uint32_t cache_budget =
ds4_gpu_stream_expert_cache_configured_budget();
uint32_t reserved_entries = g_stream_expert_cache_entry_count;
for (uint32_t u = 0; u < unique_count; u++) {
const uint32_t expert = (uint32_t)unique_ids[u];
if ((uint64_t)expert > UINT64_MAX / gate_expert_bytes ||
(uint64_t)expert > UINT64_MAX / down_expert_bytes) {
fprintf(stderr, "ds4: Metal streaming batch selected expert offset overflow\n");
ok = 0;
break;
}
const uint64_t gate_rel = (uint64_t)expert * gate_expert_bytes;
const uint64_t down_rel = (uint64_t)expert * down_expert_bytes;
if (gate_rel > UINT64_MAX - gate_offset ||
gate_rel > UINT64_MAX - up_offset ||
down_rel > UINT64_MAX - down_offset) {
fprintf(stderr, "ds4: Metal streaming batch selected expert offset overflow\n");
ok = 0;
break;
}
unique_gate_offsets[u] = gate_offset + gate_rel;
unique_up_offsets[u] = up_offset + gate_rel;
unique_down_offsets[u] = down_offset + down_rel;
ds4_gpu_stream_expert_cache_entry *entry =
ds4_gpu_stream_expert_cache_peek(model_map,
model_size,
layer,
expert,
n_total_expert,
n_selected,
unique_gate_offsets[u],
unique_up_offsets[u],
unique_down_offsets[u],
gate_expert_bytes,
down_expert_bytes);
if (entry) {
unique_entries[u] = entry;
continue;
}
const int force_reuse =
cache_budget != 0 && reserved_entries >= cache_budget;
/* The worker pool reads this entire batch below. Serial read-ahead
* here waits for the same pages before parallel I/O can begin. */
const double buffer_t0 = load_timing ? ds4_gpu_now_ms() : 0.0;
const int prepared =
ds4_gpu_stream_expert_cache_prepare_load_buffers(layer,
expert,
layer,
unique_ids,
unique_count,
gate_expert_bytes,
down_expert_bytes,
force_reuse,
&gate_bufs[n_loads],
&up_bufs[n_loads],
&down_bufs[n_loads],
&gate_inners[n_loads],
&up_inners[n_loads],
&down_inners[n_loads]);
if (load_timing) {
ds4_gpu_stream_expert_timing_note_prepare_buffer(
ds4_gpu_now_ms() - buffer_t0);
}
if (!prepared) {
ok = 0;
break;
}
if (!force_reuse && reserved_entries < UINT32_MAX) {
reserved_entries++;
}
if (!gate_bufs[n_loads] ||
!up_bufs[n_loads] ||
!down_bufs[n_loads]) {
ok = 0;
break;
}
uint8_t *gate_dst = (uint8_t *)[gate_bufs[n_loads] contents] +
gate_inners[n_loads];
uint8_t *up_dst = (uint8_t *)[up_bufs[n_loads] contents] +
up_inners[n_loads];
uint8_t *down_dst = (uint8_t *)[down_bufs[n_loads] contents] +
down_inners[n_loads];
if (!gate_dst || !up_dst || !down_dst) {
ok = 0;
break;
}
load_unique[n_loads] = u;
const double task_t0 = load_timing ? ds4_gpu_now_ms() : 0.0;
tasks[n_tasks++] = (ds4_gpu_stream_expert_pread_task) {
.offset = unique_gate_offsets[u],
.len = gate_expert_bytes,
.dst = gate_dst,
};
tasks[n_tasks++] = (ds4_gpu_stream_expert_pread_task) {
.offset = unique_up_offsets[u],
.len = gate_expert_bytes,
.dst = up_dst,
};
tasks[n_tasks++] = (ds4_gpu_stream_expert_pread_task) {
.offset = unique_down_offsets[u],
.len = down_expert_bytes,
.dst = down_dst,
};
if (load_timing) {
ds4_gpu_stream_expert_timing_note_prepare_task(
1,
ds4_gpu_now_ms() - task_t0);
}
n_loads++;
}
}
if (ok && n_loads != 0) {
if (load_timing_t0 != 0.0) {
const double now_ms = ds4_gpu_now_ms();
load_prepare_ms = now_ms - load_timing_t0;
load_timing_t0 = now_ms;
}
uint64_t read_bytes = 0;
double read_ms = 0.0;
ok = ds4_gpu_stream_expert_pread_tasks(tasks,
n_tasks,
&read_bytes,
&read_ms);
if (ok) {
ds4_gpu_stream_expert_cache_note_pread(layer, read_bytes, read_ms);
}
if (load_timing_t0 != 0.0) {
load_timing_t0 = ds4_gpu_now_ms();
}
if (ok && getenv("DS4_METAL_STREAMING_EXPERT_PREAD_PROFILE") != NULL) {
fprintf(stderr,
"ds4: Metal streaming batch expert parallel pread layer=%u experts=%u tensors=%u "
"threads=%u bytes=%.2f GiB wall=%.3f ms\n",
layer,
n_loads,
n_tasks,
ds4_gpu_stream_expert_pread_thread_count(n_tasks),
ds4_gpu_gib(read_bytes),
read_ms);
}
if (ok) {
for (uint32_t load_i = 0; load_i < n_loads; load_i++) {
const uint32_t u = load_unique[load_i];
const uint32_t expert = (uint32_t)unique_ids[u];
ds4_gpu_stream_expert_cache_entry *entry =
ds4_gpu_stream_expert_cache_install_loaded(model_map,
model_size,
layer,
expert,
unique_gate_offsets[u],
unique_up_offsets[u],
unique_down_offsets[u],
gate_expert_bytes,
down_expert_bytes,
gate_bufs[load_i],
up_bufs[load_i],
down_bufs[load_i],
gate_inners[load_i],
up_inners[load_i],
down_inners[load_i]);
if (!entry) {
ok = 0;
break;
}
unique_entries[u] = entry;
}
}
if (load_timing_t0 != 0.0) {
load_install_ms = ds4_gpu_now_ms() - load_timing_t0;
ds4_gpu_stream_expert_timing_note_load_detail(load_prepare_ms,
read_ms,
load_install_ms);
}
}
if (tasks) free(tasks);
if (ok) {
for (uint32_t u = 0; u < unique_count; u++) {
ds4_gpu_stream_expert_cache_entry *entry = unique_entries[u];
const uint32_t expert = (uint32_t)unique_ids[u];
if (!entry) {
const uint64_t gate_rel = unique_gate_offsets[u] - gate_offset;
const uint64_t down_rel = unique_down_offsets[u] - down_offset;
if (!*overflow_gate) {
const uint64_t gate_tensor_bytes =
(uint64_t)n_total_expert * gate_expert_bytes;
const uint64_t down_tensor_bytes =
(uint64_t)n_total_expert * down_expert_bytes;
uint64_t gate_view_inner = 0;
uint64_t up_view_inner = 0;
uint64_t down_view_inner = 0;
id<MTLBuffer> gv = ds4_gpu_wrap_model_range(model_map,
model_size,
gate_offset,
gate_tensor_bytes,
&gate_view_inner);
id<MTLBuffer> uv = ds4_gpu_wrap_model_range(model_map,
model_size,
up_offset,
gate_tensor_bytes,
&up_view_inner);
id<MTLBuffer> dv = ds4_gpu_wrap_model_range(model_map,
model_size,
down_offset,
down_tensor_bytes,
&down_view_inner);
if (!gv || !uv || !dv) {
fprintf(stderr,
"ds4: Metal streaming prefill batch selected addr "
"failed to map overflow expert views at layer %u\n",
layer);
ok = 0;
break;
}
*overflow_gate = gv;
*overflow_up = uv;
*overflow_down = dv;
overflow_gate_inner = gate_view_inner;
overflow_up_inner = up_view_inner;
overflow_down_inner = down_view_inner;
}
if (!ds4_gpu_stream_expert_cache_set_addr_slot(
layer,
expert,
*overflow_gate,
(NSUInteger)(overflow_gate_inner + gate_rel),
*overflow_up,
(NSUInteger)(overflow_up_inner + gate_rel),
*overflow_down,
(NSUInteger)(overflow_down_inner + down_rel))) {
ok = 0;
break;
}
view_served++;
continue;
}
const uint32_t extra_uses =
frequency[expert] > 0 ? frequency[expert] - 1u : 0;
if (extra_uses != 0) {
if (entry->use_count > UINT64_MAX - extra_uses) {
entry->use_count = UINT64_MAX;
} else {
entry->use_count += extra_uses;
}
if (g_stream_expert_cache_hits > UINT64_MAX - extra_uses) {
g_stream_expert_cache_hits = UINT64_MAX;
} else {
g_stream_expert_cache_hits += extra_uses;
}
if (g_stream_expert_cache_layer_hits[layer] >
UINT64_MAX - extra_uses) {
g_stream_expert_cache_layer_hits[layer] = UINT64_MAX;
} else {
g_stream_expert_cache_layer_hits[layer] += extra_uses;
}
}
resources[*n_resources] = entry;
(*n_resources)++;
}
}
free(ids);
if (!ok || (*n_resources == 0 && view_served == 0)) {
ds4_gpu_stream_expert_cache_clear_layer(layer);
return 0;
}
if (!ds4_gpu_stream_expert_cache_addr_buffers(layer,
gate_addrs,
up_addrs,
down_addrs)) {
ds4_gpu_stream_expert_cache_clear_layer(layer);
return 0;
}
*unique_out = *n_resources + view_served;
if (view_served != 0 &&
ds4_gpu_stream_expert_timing_summary_enabled()) {
fprintf(stderr,
"ds4: Metal streaming prefill batch selected addr layer=%u "
"served %u/%u unique experts via mapped views (cache budget %u)\n",
layer,
view_served,
unique_count,
ds4_gpu_stream_expert_cache_configured_budget());
}
if (profile) {
const double t_done = ds4_gpu_now_ms();
uint64_t per_expert_bytes = UINT64_MAX;
uint64_t logical_bytes = UINT64_MAX;
if (gate_expert_bytes <= (UINT64_MAX - down_expert_bytes) / 2ull) {
per_expert_bytes = gate_expert_bytes * 2ull + down_expert_bytes;
if (per_expert_bytes == 0 ||
*n_resources <= UINT64_MAX / per_expert_bytes) {
logical_bytes = (uint64_t)(*n_resources) * per_expert_bytes;
}
}
fprintf(stderr,
"ds4: Metal streaming prefill batch selected addr layer=%u "
"tokens=%u unique=%u read=%.3f ms wrap=%.3f ms bytes=%.2f GiB\n",
layer,
n_tokens,
*n_resources,
t_read - t0,
t_done - t_read,
ds4_gpu_gib(logical_bytes));
}
return 1;
}
int ds4_gpu_stream_expert_cache_seed_selected(
const ds4_gpu_stream_expert_table *table,
const int32_t *selected_ids,
uint32_t n_selected) {
if (!g_ssd_streaming_mode) return 1;
if (!table) return 0;
const void *model_map = table->model_map;
const uint64_t model_size = table->model_size;
const uint32_t layer = table->layer;
const uint32_t n_total_expert = table->n_total_expert;
const uint64_t gate_offset = table->gate_offset;
const uint64_t up_offset = table->up_offset;
const uint64_t down_offset = table->down_offset;
const uint64_t gate_expert_bytes = table->gate_expert_bytes;
const uint64_t down_expert_bytes = table->down_expert_bytes;
if (!model_map || !selected_ids ||
layer >= DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER ||
n_selected == 0 ||
n_selected > 6 ||
n_total_expert == 0 ||
n_total_expert > DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT ||
!ds4_gpu_stream_expert_cache_note_expert_size(gate_expert_bytes,
down_expert_bytes) ||
ds4_gpu_stream_expert_cache_effective_cap(layer,
n_total_expert,
n_selected) == 0) {
return 1;
}
if (!g_initialized && !ds4_gpu_init()) return 0;
ds4_gpu_stream_expert_cache_note_selected_hotness(layer,
selected_ids,
n_selected);
for (uint32_t i = 0; i < n_selected; i++) {
if (selected_ids[i] < 0 || (uint32_t)selected_ids[i] >= n_total_expert) {
fprintf(stderr,
"ds4: Metal prefill expert-cache seed selected expert id %d is outside 0..%u\n",
selected_ids[i],
n_total_expert);
return 0;
}
const uint64_t expert_id = (uint64_t)(uint32_t)selected_ids[i];
if (expert_id > UINT64_MAX / gate_expert_bytes ||
expert_id > UINT64_MAX / down_expert_bytes) {
fprintf(stderr, "ds4: Metal prefill expert-cache seed offset overflow\n");
return 0;
}
const uint64_t gate_rel = expert_id * gate_expert_bytes;
const uint64_t down_rel = expert_id * down_expert_bytes;
if (gate_rel > UINT64_MAX - gate_offset ||
gate_rel > UINT64_MAX - up_offset ||
down_rel > UINT64_MAX - down_offset) {
fprintf(stderr, "ds4: Metal prefill expert-cache seed offset overflow\n");
return 0;
}
if (!ds4_gpu_stream_expert_cache_get(model_map,
model_size,
layer,
(uint32_t)selected_ids[i],
n_total_expert,
n_selected,
gate_offset + gate_rel,
up_offset + gate_rel,
down_offset + down_rel,
gate_expert_bytes,
down_expert_bytes)) {
return 0;
}
}
ds4_gpu_stream_expert_cache_prune_layer(layer,
n_total_expert,
n_selected,
selected_ids,
n_selected);
ds4_gpu_stream_expert_cache_prune_global(layer,
selected_ids,
n_selected);
return 1;
}
static int ds4_gpu_stream_expert_cache_seed_experts_impl(
const ds4_gpu_stream_expert_table *table,
const int32_t *expert_ids,
const uint32_t *expert_priorities,
uint32_t n_experts,
int gpu_copy_source) {
if (!g_ssd_streaming_mode) return 1;
if (!table) return 0;
const void *model_map = table->model_map;
const uint64_t model_size = table->model_size;
const uint32_t layer = table->layer;
const uint32_t n_total_expert = table->n_total_expert;
const uint64_t gate_offset = table->gate_offset;
const uint64_t up_offset = table->up_offset;
const uint64_t down_offset = table->down_offset;
const uint64_t gate_expert_bytes = table->gate_expert_bytes;
const uint64_t down_expert_bytes = table->down_expert_bytes;
if (!model_map || !expert_ids ||
layer >= DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER ||
n_experts == 0 ||
n_total_expert == 0 ||
n_total_expert > DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT ||
!ds4_gpu_stream_expert_cache_note_expert_size(gate_expert_bytes,
down_expert_bytes) ||
ds4_gpu_stream_expert_cache_effective_cap(layer,
n_total_expert,
1) == 0) {
return 1;
}
if (!g_initialized && !ds4_gpu_init()) return 0;
ds4_gpu_stream_expert_cache_maybe_decay_route_hotness();
uint32_t remaining = n_experts;
while (remaining != 0) {
const uint32_t batch =
remaining > 6u ? 6u : remaining;
remaining -= batch;
int32_t selected_ids[6] = { -1, -1, -1, -1, -1, -1 };
uint64_t gate_abs_offsets[6] = { 0, 0, 0, 0, 0, 0 };
uint64_t up_abs_offsets[6] = { 0, 0, 0, 0, 0, 0 };
uint64_t down_abs_offsets[6] = { 0, 0, 0, 0, 0, 0 };
ds4_gpu_stream_expert_cache_entry *entries[6] = {
NULL, NULL, NULL, NULL, NULL, NULL
};
uint32_t missing_mask = 0;
for (uint32_t i = 0; i < batch; i++) {
const int32_t expert = expert_ids[remaining + i];
const uint32_t priority =
expert_priorities ? expert_priorities[remaining + i] : 0;
if (expert < 0 || (uint32_t)expert >= n_total_expert) {
fprintf(stderr,
"ds4: Metal streaming hotlist seed expert id %d is outside 0..%u\n",
expert,
n_total_expert);
return 0;
}
const uint64_t expert_id = (uint64_t)(uint32_t)expert;
if (expert_id > UINT64_MAX / gate_expert_bytes ||
expert_id > UINT64_MAX / down_expert_bytes) {
fprintf(stderr, "ds4: Metal streaming hotlist seed offset overflow\n");
return 0;
}
const uint64_t gate_rel = expert_id * gate_expert_bytes;
const uint64_t down_rel = expert_id * down_expert_bytes;
if (gate_rel > UINT64_MAX - gate_offset ||
gate_rel > UINT64_MAX - up_offset ||
down_rel > UINT64_MAX - down_offset) {
fprintf(stderr, "ds4: Metal streaming hotlist seed offset overflow\n");
return 0;
}
ds4_gpu_stream_expert_cache_note_route_hotness(
layer,
(uint32_t)expert,
priority != 0 ? priority : 1u);
selected_ids[i] = expert;
gate_abs_offsets[i] = gate_offset + gate_rel;
up_abs_offsets[i] = up_offset + gate_rel;
down_abs_offsets[i] = down_offset + down_rel;
entries[i] = ds4_gpu_stream_expert_cache_peek(model_map,
model_size,
layer,
(uint32_t)expert,
n_total_expert,
batch,
gate_abs_offsets[i],
up_abs_offsets[i],
down_abs_offsets[i],
gate_expert_bytes,
down_expert_bytes);
if (!entries[i]) missing_mask |= 1u << i;
if (entries[i] && priority != 0 &&
entries[i]->use_count < (uint64_t)priority) {
entries[i]->use_count = (uint64_t)priority;
}
}
if (missing_mask != 0 &&
!ds4_gpu_stream_expert_cache_load_selected_missing_with_source(
model_map,
model_size,
layer,
selected_ids,
n_total_expert,
batch,
gate_abs_offsets,
up_abs_offsets,
down_abs_offsets,
gate_expert_bytes,
down_expert_bytes,
missing_mask,
gpu_copy_source,
entries)) {
return 0;
}
if (expert_priorities) {
for (uint32_t i = 0; i < batch; i++) {
const uint32_t priority = expert_priorities[remaining + i];
if (entries[i] && priority != 0 &&
entries[i]->use_count < (uint64_t)priority) {
entries[i]->use_count = (uint64_t)priority;
}
}
}
}
const uint32_t protect_n = n_experts < 6u ? n_experts : 6u;
ds4_gpu_stream_expert_cache_prune_layer(layer,
n_total_expert,
1,
expert_ids,
protect_n);
ds4_gpu_stream_expert_cache_prune_global(layer,
expert_ids,
protect_n);
return 1;
}
int ds4_gpu_stream_expert_cache_seed_experts(
const ds4_gpu_stream_expert_table *table,
const int32_t *expert_ids,
const uint32_t *expert_priorities,
uint32_t n_experts) {
@autoreleasepool {
return ds4_gpu_stream_expert_cache_seed_experts_impl(table,
expert_ids,
expert_priorities,
n_experts,
0);
}
}
int ds4_gpu_stream_expert_cache_seed_experts_gpu_copy(
const ds4_gpu_stream_expert_table *table,
const int32_t *expert_ids,
const uint32_t *expert_priorities,
uint32_t n_experts) {
/* The C prefill caller has no pool. In particular, blit encoders retain
* destination slabs even after the cache drops its last references. */
@autoreleasepool {
return ds4_gpu_stream_expert_cache_seed_experts_impl(table,
expert_ids,
expert_priorities,
n_experts,
1);
}
}
static uint32_t ds4_gpu_q4_expert_table_group_size(uint32_t n_total_expert) {
const char *env = getenv("DS4_METAL_Q4_EXPERT_TABLE_GROUP_SIZE");
if (!env || !env[0]) return 1;
char *end = NULL;
unsigned long v = strtoul(env, &end, 10);
if (end == env || *end != '\0' || v < 2 || v > n_total_expert) {
return 1;
}
return (uint32_t)v;
}
static bool ds4_gpu_q4_table_queue_residency_requested(void) {
return getenv("DS4_METAL_Q4_TABLE_QUEUE_RESIDENCY_SET") != NULL;
}
static bool ds4_gpu_q4_table_queue_residency_available(void) {
#if TARGET_OS_OSX
if (@available(macOS 15.0, *)) {
return g_queue && [g_queue respondsToSelector:@selector(addResidencySet:)];
}
#endif
return false;
}
static bool ds4_gpu_q4_non_streaming_opt_in_enabled(void) {
return getenv("DS4_METAL_ENABLE_Q4_SELECTED_EXPERT_VIEWS") != NULL ||
getenv("DS4_METAL_ENABLE_PRO_Q4_SELECTED_EXPERT_VIEWS") != NULL ||
getenv("DS4_METAL_ENABLE_Q4_EXPERT_TABLE") != NULL ||
getenv("DS4_METAL_ENABLE_Q4_EXPERT_ADDRESS_TABLE") != NULL ||
getenv("DS4_METAL_ENABLE_PRO_Q4_EXPERT_TABLE_AUTO") != NULL ||
getenv("DS4_METAL_ENABLE_PRO_Q4_EXPERT_ADDRESS_AUTO") != NULL;
}
static bool ds4_gpu_q4_selected_paths_allowed(void) {
if (g_ssd_streaming_mode) return true;
if (g_glm_model_mode) return false;
return ds4_gpu_q4_non_streaming_opt_in_enabled();
}
int ds4_gpu_pro_q4_expert_table_auto_available(void) {
if (!g_initialized && !ds4_gpu_init()) return 0;
/* Indirect tables retain every expert and its residency set. They cannot
* coexist with a bounded SSD cache, even when only six experts are read. */
return !g_ssd_streaming_mode &&
getenv("DS4_METAL_ENABLE_PRO_Q4_EXPERT_TABLE_AUTO") != NULL &&
getenv("DS4_METAL_DISABLE_PRO_Q4_EXPERT_TABLE_AUTO") == NULL &&
getenv("DS4_METAL_DISABLE_Q4_EXPERT_TABLE") == NULL &&
ds4_gpu_q4_table_queue_residency_available();
}
static bool ds4_gpu_q4_table_queue_residency_enabled(bool auto_queue_residency) {
return (auto_queue_residency || ds4_gpu_q4_table_queue_residency_requested()) &&
ds4_gpu_q4_table_queue_residency_available();
}
static bool ds4_gpu_q4_table_model_residency_enabled(void) {
return getenv("DS4_METAL_Q4_TABLE_MODEL_RESIDENCY_SET") != NULL;
}
static bool ds4_gpu_pro_q4_expert_indirect_shape_supported(
uint32_t n_total_expert,
uint32_t n_expert,
uint64_t gate_tensor_bytes,
uint64_t down_tensor_bytes) {
const uint64_t q4_selected_min_tensor_bytes = 2ull * 1024ull * 1024ull * 1024ull;
return n_total_expert == 384 &&
n_expert == 6 &&
gate_tensor_bytes >= q4_selected_min_tensor_bytes &&
down_tensor_bytes >= q4_selected_min_tensor_bytes;
}
static bool ds4_gpu_pro_q4_expert_table_auto_enabled(
uint32_t n_total_expert,
uint32_t n_expert,
uint64_t gate_tensor_bytes,
uint64_t down_tensor_bytes) {
/*
* This path lets the shader choose among exact per-expert resources.
* It is only automatic when a Metal queue residency set can make every
* indirect expert resource visible up front; otherwise the selected
* exact-slice path remains the fallback.
*/
return ds4_gpu_pro_q4_expert_indirect_shape_supported(n_total_expert,
n_expert,
gate_tensor_bytes,
down_tensor_bytes) &&
ds4_gpu_pro_q4_expert_table_auto_available();
}
static bool ds4_gpu_pro_q4_expert_address_auto_enabled(
uint32_t n_total_expert,
uint32_t n_expert,
uint64_t gate_tensor_bytes,
uint64_t down_tensor_bytes) {
/*
* GPU-address expert tables are useful for experiments, but they are not
* safe as an automatic path unless every indirect expert resource is made
* visible to Metal. Keep this behind an explicit opt-in while the selected
* active-slice path remains the correctness/performance baseline.
*/
return !g_ssd_streaming_mode &&
getenv("DS4_METAL_ENABLE_PRO_Q4_EXPERT_ADDRESS_AUTO") != NULL &&
ds4_gpu_q4_selected_paths_allowed() &&
ds4_gpu_pro_q4_expert_indirect_shape_supported(n_total_expert,
n_expert,
gate_tensor_bytes,
down_tensor_bytes) &&
g_moe_mul_mv_addr_q4_k_pair_swiglu_pipeline != nil &&
g_moe_mul_mv_addr_q4_k_sum6_pipeline != nil &&
getenv("DS4_METAL_DISABLE_PRO_Q4_EXPERT_TABLE_AUTO") == NULL &&
getenv("DS4_METAL_DISABLE_PRO_Q4_EXPERT_ADDRESS_AUTO") == NULL &&
getenv("DS4_METAL_DISABLE_Q4_EXPERT_ADDRESS_TABLE") == NULL &&
ds4_gpu_q4_table_queue_residency_available();
}
static bool ds4_gpu_q4_table_bind_anchors_enabled(void) {
return getenv("DS4_METAL_Q4_TABLE_BIND_ANCHORS") != NULL;
}
static int ds4_gpu_use_model_residency_set(id<MTLCommandBuffer> cb) {
if (!ds4_gpu_q4_table_model_residency_enabled()) return 1;
#if TARGET_OS_OSX
if (@available(macOS 15.0, *)) {
if (cb && g_model_residency_set && [cb respondsToSelector:@selector(useResidencySet:)]) {
[cb useResidencySet:g_model_residency_set];
return 1;
}
}
#endif
fprintf(stderr, "ds4: Metal Q4 table model residency set is not available\n");
return 0;
}
static int ds4_gpu_bind_q4_expert_table_anchors(
id<MTLComputeCommandEncoder> enc,
DS4MetalQ4ExpertTable *table,
NSUInteger first_index,
NSUInteger max_count) {
if (!ds4_gpu_q4_table_bind_anchors_enabled()) return 1;
if (!enc || !table || !table.expertBuffers) return 0;
const NSUInteger count = [table.expertBuffers count];
if (count > max_count) {
fprintf(stderr,
"ds4: Metal Q4 table anchor count %lu exceeds available slots %lu\n",
(unsigned long)count,
(unsigned long)max_count);
return 0;
}
for (NSUInteger i = 0; i < count; i++) {
[enc setBuffer:[table.expertBuffers objectAtIndex:i]
offset:0
atIndex:first_index + i];
}
return 1;
}
static id ds4_gpu_q4_expert_table_residency_set(NSMutableArray<id<MTLBuffer>> *buffers) {
if (!buffers || [buffers count] == 0 ||
getenv("DS4_METAL_Q4_TABLE_RESIDENCY_SET") == NULL ||
getenv("DS4_METAL_Q4_TABLE_PER_TENSOR_RESIDENCY_SET") == NULL) {
return nil;
}
#if TARGET_OS_OSX
if (@available(macOS 15.0, *)) {
MTLResidencySetDescriptor *desc = [[MTLResidencySetDescriptor alloc] init];
desc.label = @"ds4_q4_expert_table";
desc.initialCapacity = [buffers count];
NSError *error = nil;
id residency_set = [g_device newResidencySetWithDescriptor:desc error:&error];
if (!residency_set) {
fprintf(stderr, "ds4: Metal Q4 expert table residency set creation failed: %s\n",
[[error localizedDescription] UTF8String]);
return nil;
}
for (id<MTLBuffer> buffer in buffers) {
[residency_set addAllocation:buffer];
}
[residency_set commit];
[residency_set requestResidency];
return residency_set;
}
#endif
return nil;
}
static void ds4_gpu_q4_residency_add_table(id residency_set,
DS4MetalQ4ExpertTable *table) {
#if TARGET_OS_OSX
if (!residency_set || !table) return;
if (@available(macOS 15.0, *)) {
if (table.argumentBuffer) {
[residency_set addAllocation:table.argumentBuffer];
}
if (table.addressBuffer) {
[residency_set addAllocation:table.addressBuffer];
}
for (id<MTLBuffer> buffer in table.expertBuffers) {
[residency_set addAllocation:buffer];
}
}
#else
(void)residency_set;
(void)table;
#endif
}
static id ds4_gpu_q4_expert_layer_residency_set(DS4MetalQ4ExpertTable *gate_table,
DS4MetalQ4ExpertTable *up_table,
DS4MetalQ4ExpertTable *down_table,
bool auto_queue_residency) {
const bool queue_residency =
ds4_gpu_q4_table_queue_residency_enabled(auto_queue_residency);
if (!gate_table || !up_table || !down_table ||
!g_device || !g_q4_expert_layer_residency_cache ||
(getenv("DS4_METAL_Q4_TABLE_RESIDENCY_SET") == NULL &&
!queue_residency)) {
return nil;
}
#if TARGET_OS_OSX
if (@available(macOS 15.0, *)) {
NSString *key = [NSString stringWithFormat:@"%p:%p:%p",
gate_table, up_table, down_table];
DS4MetalQ4LayerResidency *cached =
[g_q4_expert_layer_residency_cache objectForKey:key];
if (cached) return cached.residencySet;
const NSUInteger capacity =
[gate_table.expertBuffers count] +
[up_table.expertBuffers count] +
[down_table.expertBuffers count] + 3u;
MTLResidencySetDescriptor *desc = [[MTLResidencySetDescriptor alloc] init];
desc.label = @"ds4_q4_expert_layer";
desc.initialCapacity = capacity;
NSError *error = nil;
id residency_set = [g_device newResidencySetWithDescriptor:desc error:&error];
if (!residency_set) {
fprintf(stderr,
"ds4: Metal Q4 expert layer residency set creation failed: %s\n",
[[error localizedDescription] UTF8String]);
return nil;
}
ds4_gpu_q4_residency_add_table(residency_set, gate_table);
ds4_gpu_q4_residency_add_table(residency_set, up_table);
ds4_gpu_q4_residency_add_table(residency_set, down_table);
[residency_set commit];
[residency_set requestResidency];
DS4MetalQ4LayerResidency *entry = [DS4MetalQ4LayerResidency new];
entry.residencySet = residency_set;
if (queue_residency) {
[g_queue addResidencySet:residency_set];
entry.addedToQueue = YES;
}
[g_q4_expert_layer_residency_cache setObject:entry forKey:key];
return residency_set;
}
#endif
return nil;
}
static DS4MetalQ4ExpertTable *ds4_gpu_q4_expert_table(
const void *model_map,
uint64_t model_size,
uint64_t tensor_offset,
uint64_t expert_bytes,
uint32_t n_total_expert,
id<MTLArgumentEncoder> encoder);
static DS4MetalQ4ExpertTable *ds4_gpu_q4_expert_address_table(
const void *model_map,
uint64_t model_size,
uint64_t tensor_offset,
uint64_t expert_bytes,
uint32_t n_total_expert);
int ds4_gpu_preload_q4_expert_tables(const void *model_map, uint64_t model_size,
uint64_t gate_offset, uint64_t up_offset, uint64_t down_offset,
uint64_t gate_expert_bytes, uint64_t down_expert_bytes,
uint32_t n_total_expert) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!model_map || model_size == 0 || gate_expert_bytes == 0 ||
down_expert_bytes == 0 || n_total_expert == 0) {
return 0;
}
if ((uint64_t)n_total_expert > UINT64_MAX / gate_expert_bytes ||
(uint64_t)n_total_expert > UINT64_MAX / down_expert_bytes) {
fprintf(stderr, "ds4: Metal Q4 expert table preload byte size overflow\n");
return 0;
}
const uint64_t gate_tensor_bytes = (uint64_t)n_total_expert * gate_expert_bytes;
const uint64_t down_tensor_bytes = (uint64_t)n_total_expert * down_expert_bytes;
const bool address_auto =
ds4_gpu_pro_q4_expert_address_auto_enabled(n_total_expert,
6,
gate_tensor_bytes,
down_tensor_bytes);
const bool table_auto =
ds4_gpu_pro_q4_expert_table_auto_enabled(n_total_expert,
6,
gate_tensor_bytes,
down_tensor_bytes);
if (!address_auto && !table_auto) {
return 1;
}
if (address_auto) {
@autoreleasepool {
DS4MetalQ4ExpertTable *gate_table =
ds4_gpu_q4_expert_address_table(model_map,
model_size,
gate_offset,
gate_expert_bytes,
n_total_expert);
DS4MetalQ4ExpertTable *up_table =
ds4_gpu_q4_expert_address_table(model_map,
model_size,
up_offset,
gate_expert_bytes,
n_total_expert);
DS4MetalQ4ExpertTable *down_table =
ds4_gpu_q4_expert_address_table(model_map,
model_size,
down_offset,
down_expert_bytes,
n_total_expert);
if (!gate_table || !up_table || !down_table) {
return 0;
}
id residency = ds4_gpu_q4_expert_layer_residency_set(gate_table,
up_table,
down_table,
true);
if (!residency) {
fprintf(stderr, "ds4: Metal Q4 expert address table preload failed to create queue residency set\n");
return 0;
}
}
return 1;
}
if (!g_moe_table_q4_pair_gate_encoder || !g_moe_table_q4_pair_up_encoder ||
!g_moe_table_q4_sum_down_encoder) {
fprintf(stderr, "ds4: Metal Q4 expert table preload missing argument encoders\n");
return 0;
}
@autoreleasepool {
DS4MetalQ4ExpertTable *gate_table =
ds4_gpu_q4_expert_table(model_map,
model_size,
gate_offset,
gate_expert_bytes,
n_total_expert,
g_moe_table_q4_pair_gate_encoder);
DS4MetalQ4ExpertTable *up_table =
ds4_gpu_q4_expert_table(model_map,
model_size,
up_offset,
gate_expert_bytes,
n_total_expert,
g_moe_table_q4_pair_up_encoder);
DS4MetalQ4ExpertTable *down_table =
ds4_gpu_q4_expert_table(model_map,
model_size,
down_offset,
down_expert_bytes,
n_total_expert,
g_moe_table_q4_sum_down_encoder);
if (!gate_table || !up_table || !down_table) {
return 0;
}
id residency = ds4_gpu_q4_expert_layer_residency_set(gate_table,
up_table,
down_table,
true);
if (!residency) {
fprintf(stderr, "ds4: Metal Q4 expert table preload failed to create queue residency set\n");
return 0;
}
}
return 1;
}
static DS4MetalQ4ExpertTable *ds4_gpu_q4_expert_table(
const void *model_map,
uint64_t model_size,
uint64_t tensor_offset,
uint64_t expert_bytes,
uint32_t n_total_expert,
id<MTLArgumentEncoder> encoder) {
if (g_ssd_streaming_mode || !model_map || !g_device || !g_q4_expert_table_cache || !encoder ||
model_size == 0 || expert_bytes == 0 ||
n_total_expert == 0 || n_total_expert > 384) {
return nil;
}
if ((uint64_t)n_total_expert > UINT64_MAX / expert_bytes) {
fprintf(stderr, "ds4: Metal Q4 expert table byte size overflow\n");
return nil;
}
const uint64_t tensor_bytes = (uint64_t)n_total_expert * expert_bytes;
if (tensor_offset > model_size || tensor_bytes > model_size - tensor_offset) {
fprintf(stderr, "ds4: Metal Q4 expert table is outside the mapped model\n");
return nil;
}
const uint32_t table_group_size =
ds4_gpu_q4_expert_table_group_size(n_total_expert);
NSString *key = [NSString stringWithFormat:@"%p:%llu:%llu:%llu:%u:%llu:%u",
model_map,
(unsigned long long)model_size,
(unsigned long long)tensor_offset,
(unsigned long long)expert_bytes,
n_total_expert,
(unsigned long long)[encoder encodedLength],
table_group_size];
DS4MetalQ4ExpertTable *cached = [g_q4_expert_table_cache objectForKey:key];
if (cached) return cached;
id<MTLBuffer> arg_buffer =
[g_device newBufferWithLength:[encoder encodedLength]
options:MTLResourceStorageModeShared];
if (!arg_buffer) {
fprintf(stderr, "ds4: Metal Q4 expert table argument buffer allocation failed\n");
return nil;
}
arg_buffer.label = @"ds4_q4_expert_table";
[encoder setArgumentBuffer:arg_buffer offset:0];
NSMutableArray<id<MTLBuffer>> *expert_buffers =
[NSMutableArray arrayWithCapacity:table_group_size > 1 ?
(n_total_expert + table_group_size - 1u) / table_group_size :
n_total_expert];
if (!expert_buffers) return nil;
if (table_group_size > 1) {
for (uint32_t first = 0; first < n_total_expert; first += table_group_size) {
const uint32_t remaining = n_total_expert - first;
const uint32_t group_n =
remaining < table_group_size ? remaining : table_group_size;
if ((uint64_t)first > UINT64_MAX / expert_bytes ||
(uint64_t)group_n > UINT64_MAX / expert_bytes) {
fprintf(stderr, "ds4: Metal Q4 expert table group byte overflow\n");
return nil;
}
const uint64_t rel = (uint64_t)first * expert_bytes;
const uint64_t group_bytes = (uint64_t)group_n * expert_bytes;
if (rel > UINT64_MAX - tensor_offset) {
fprintf(stderr, "ds4: Metal Q4 expert table group offset overflow\n");
return nil;
}
uint64_t inner = 0;
id<MTLBuffer> group_buf =
ds4_gpu_wrap_model_range(model_map,
model_size,
tensor_offset + rel,
group_bytes,
&inner);
if (!group_buf) return nil;
for (uint32_t j = 0; j < group_n; j++) {
const uint64_t expert_inner = inner + (uint64_t)j * expert_bytes;
[encoder setBuffer:group_buf offset:(NSUInteger)expert_inner atIndex:first + j];
}
[expert_buffers addObject:group_buf];
}
} else {
for (uint32_t i = 0; i < n_total_expert; i++) {
const uint64_t rel = (uint64_t)i * expert_bytes;
if (rel > UINT64_MAX - tensor_offset) {
fprintf(stderr, "ds4: Metal Q4 expert table offset overflow\n");
return nil;
}
uint64_t inner = 0;
id<MTLBuffer> expert_buf =
ds4_gpu_wrap_model_exact_range_owned(model_map,
model_size,
tensor_offset + rel,
expert_bytes,
&inner);
if (!expert_buf) return nil;
[encoder setBuffer:expert_buf offset:(NSUInteger)inner atIndex:i];
[expert_buffers addObject:expert_buf];
}
}
if (getenv("DS4_METAL_Q4_EXPERT_TABLE_PROFILE") != NULL) {
fprintf(stderr,
"ds4: Metal Q4 expert table: experts=%u group=%u buffers=%lu expert_bytes=%.2f MiB\n",
n_total_expert,
table_group_size,
(unsigned long)[expert_buffers count],
ds4_gpu_mib(expert_bytes));
}
DS4MetalQ4ExpertTable *table = [DS4MetalQ4ExpertTable new];
table.argumentBuffer = arg_buffer;
table.expertBuffers = expert_buffers;
table.residencySet = ds4_gpu_q4_expert_table_residency_set(expert_buffers);
table.nExpert = n_total_expert;
table.expertBytes = expert_bytes;
[g_q4_expert_table_cache setObject:table forKey:key];
return table;
}
static DS4MetalQ4ExpertTable *ds4_gpu_q4_expert_address_table(
const void *model_map,
uint64_t model_size,
uint64_t tensor_offset,
uint64_t expert_bytes,
uint32_t n_total_expert) {
if (g_ssd_streaming_mode || !model_map || !g_device || !g_q4_expert_table_cache ||
model_size == 0 || expert_bytes == 0 ||
n_total_expert == 0 || n_total_expert > 384) {
return nil;
}
if ((uint64_t)n_total_expert > UINT64_MAX / expert_bytes) {
fprintf(stderr, "ds4: Metal Q4 expert address table byte size overflow\n");
return nil;
}
const uint64_t tensor_bytes = (uint64_t)n_total_expert * expert_bytes;
if (tensor_offset > model_size || tensor_bytes > model_size - tensor_offset) {
fprintf(stderr, "ds4: Metal Q4 expert address table is outside the mapped model\n");
return nil;
}
const uint32_t table_group_size =
ds4_gpu_q4_expert_table_group_size(n_total_expert);
NSString *key = [NSString stringWithFormat:@"addr:%p:%llu:%llu:%llu:%u:%u",
model_map,
(unsigned long long)model_size,
(unsigned long long)tensor_offset,
(unsigned long long)expert_bytes,
n_total_expert,
table_group_size];
DS4MetalQ4ExpertTable *cached = [g_q4_expert_table_cache objectForKey:key];
if (cached) return cached;
id<MTLBuffer> address_buffer =
[g_device newBufferWithLength:(NSUInteger)n_total_expert * sizeof(uint64_t)
options:MTLResourceStorageModeShared];
if (!address_buffer) {
fprintf(stderr, "ds4: Metal Q4 expert address table allocation failed\n");
return nil;
}
address_buffer.label = @"ds4_q4_expert_address_table";
uint64_t *addresses = (uint64_t *)[address_buffer contents];
if (!addresses) return nil;
NSMutableArray<id<MTLBuffer>> *expert_buffers =
[NSMutableArray arrayWithCapacity:table_group_size > 1 ?
(n_total_expert + table_group_size - 1u) / table_group_size :
n_total_expert];
if (!expert_buffers) return nil;
#if TARGET_OS_OSX
if (@available(macOS 13.0, *)) {
for (uint32_t first = 0; first < n_total_expert; first += table_group_size) {
const uint32_t remaining = n_total_expert - first;
const uint32_t group_n =
remaining < table_group_size ? remaining : table_group_size;
if ((uint64_t)first > UINT64_MAX / expert_bytes ||
(uint64_t)group_n > UINT64_MAX / expert_bytes) {
fprintf(stderr, "ds4: Metal Q4 expert address table group byte overflow\n");
return nil;
}
const uint64_t rel = (uint64_t)first * expert_bytes;
const uint64_t group_bytes = (uint64_t)group_n * expert_bytes;
if (rel > UINT64_MAX - tensor_offset) {
fprintf(stderr, "ds4: Metal Q4 expert address table group offset overflow\n");
return nil;
}
uint64_t inner = 0;
id<MTLBuffer> group_buf = nil;
if (table_group_size > 1) {
group_buf = ds4_gpu_wrap_model_range(model_map,
model_size,
tensor_offset + rel,
group_bytes,
&inner);
} else {
group_buf = ds4_gpu_wrap_model_exact_range_owned(model_map,
model_size,
tensor_offset + rel,
expert_bytes,
&inner);
}
if (!group_buf) return nil;
const uint64_t base_address = (uint64_t)[group_buf gpuAddress] + inner;
for (uint32_t j = 0; j < group_n; j++) {
addresses[first + j] = base_address + (uint64_t)j * expert_bytes;
}
[expert_buffers addObject:group_buf];
}
} else
#endif
{
fprintf(stderr, "ds4: Metal GPU addresses require macOS 13 or newer\n");
return nil;
}
if (getenv("DS4_METAL_Q4_EXPERT_TABLE_PROFILE") != NULL) {
fprintf(stderr,
"ds4: Metal Q4 expert address table: experts=%u group=%u buffers=%lu expert_bytes=%.2f MiB\n",
n_total_expert,
table_group_size,
(unsigned long)[expert_buffers count],
ds4_gpu_mib(expert_bytes));
}
DS4MetalQ4ExpertTable *table = [DS4MetalQ4ExpertTable new];
table.addressBuffer = address_buffer;
table.expertBuffers = expert_buffers;
table.nExpert = n_total_expert;
table.expertBytes = expert_bytes;
[g_q4_expert_table_cache setObject:table forKey:key];
return table;
}
static void ds4_gpu_use_q4_expert_table_resources(
id<MTLCommandBuffer> cb,
id<MTLComputeCommandEncoder> enc,
DS4MetalQ4ExpertTable *table,
bool queue_residency) {
if (!enc || !table) return;
if (table.argumentBuffer) {
[enc useResource:table.argumentBuffer usage:MTLResourceUsageRead];
}
if (table.addressBuffer) {
[enc useResource:table.addressBuffer usage:MTLResourceUsageRead];
}
if (!queue_residency &&
getenv("DS4_METAL_Q4_TABLE_RESIDENCY_SET") != NULL &&
table.residencySet &&
cb &&
[cb respondsToSelector:@selector(useResidencySet:)]) {
[cb useResidencySet:table.residencySet];
}
if (getenv("DS4_METAL_Q4_TABLE_USE_RESOURCES") == NULL &&
getenv("DS4_METAL_Q4_ADDR_USE_RESOURCES") == NULL) {
return;
}
const NSUInteger count = [table.expertBuffers count];
for (NSUInteger base = 0; base < count; base += 64u) {
const NSUInteger n = count - base < 64u ? count - base : 64u;
__unsafe_unretained id<MTLResource> resources[64];
for (NSUInteger i = 0; i < n; i++) {
resources[i] = [table.expertBuffers objectAtIndex:base + i];
}
[enc useResources:resources count:n usage:MTLResourceUsageRead];
}
}
int ds4_gpu_indexer_score_one_tensor(
ds4_gpu_tensor *scores,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *weights,
const ds4_gpu_tensor *index_comp,
uint32_t n_comp,
uint32_t n_head,
uint32_t head_dim,
float scale) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!scores || !q || !weights || !index_comp ||
n_comp == 0 || n_head == 0 || head_dim == 0) {
return 0;
}
@autoreleasepool {
const uint64_t q_bytes = (uint64_t)n_head * head_dim * sizeof(float);
const uint64_t weight_bytes = (uint64_t)n_head * sizeof(float);
const uint64_t comp_bytes = (uint64_t)n_comp * head_dim * sizeof(float);
const uint64_t score_bytes = (uint64_t)n_comp * sizeof(float);
id<MTLBuffer> qbuf = ds4_gpu_tensor_buffer(q);
id<MTLBuffer> wbuf = ds4_gpu_tensor_buffer(weights);
id<MTLBuffer> compbuf = ds4_gpu_tensor_buffer(index_comp);
id<MTLBuffer> scorebuf = ds4_gpu_tensor_buffer(scores);
if (!qbuf || !wbuf || !compbuf || !scorebuf ||
ds4_gpu_tensor_bytes(q) < q_bytes ||
ds4_gpu_tensor_bytes(weights) < weight_bytes ||
ds4_gpu_tensor_bytes(index_comp) < comp_bytes ||
ds4_gpu_tensor_bytes(scores) < score_bytes) {
fprintf(stderr, "ds4: Metal graph indexer score received undersized buffers\n");
return 0;
}
if (n_head == 64 && head_dim == 128) {
id<MTLComputePipelineState> direct_pipeline =
ds4_gpu_hot_pipeline(g_dsv4_indexer_score_one_direct_pipeline,
"kernel_dsv4_indexer_score_one_direct");
if (!direct_pipeline) return 0;
ds4_gpu_dsv4_indexer_scores_fused_args args = {
.n_comp = n_comp,
.n_tokens = 1,
.n_head = n_head,
.head_dim = head_dim,
.pos0 = 0,
.ratio = 4,
.q_token_stride = (uint64_t)n_head * head_dim * sizeof(float),
.q_head_stride = (uint64_t)head_dim * sizeof(float),
.weights_token_stride = (uint64_t)n_head * sizeof(float),
.index_row_stride = (uint64_t)head_dim * sizeof(float),
.score_token_stride = (uint64_t)n_comp * sizeof(float),
.scale = scale,
};
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:direct_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:qbuf offset:ds4_gpu_tensor_offset(q) atIndex:1];
[enc setBuffer:wbuf offset:ds4_gpu_tensor_offset(weights) atIndex:2];
[enc setBuffer:compbuf offset:ds4_gpu_tensor_offset(index_comp) atIndex:3];
[enc setBuffer:scorebuf offset:ds4_gpu_tensor_offset(scores) atIndex:4];
[enc setThreadgroupMemoryLength:(128u + 4u) * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(n_comp, 1, 1)
threadsPerThreadgroup:MTLSizeMake(32, 4, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "indexer direct score")) return 0;
return 1;
}
const uint64_t head_score_bytes = (uint64_t)n_comp * n_head * sizeof(float);
if (!ds4_gpu_ensure_scratch_buffer(&g_indexer_head_scores_buffer,
&g_indexer_head_scores_bytes,
(NSUInteger)head_score_bytes,
"ds4_indexer_head_scores")) {
return 0;
}
ds4_gpu_q8_0_matvec_args dot_args =
ds4_gpu_make_f32_mv_args(head_dim, n_comp, n_head);
ds4_gpu_mv_dispatch dot_dispatch =
ds4_gpu_make_plain_mv_dispatch(head_dim, 1);
dot_args.nr0 = dot_dispatch.nr0;
id<MTLComputePipelineState> dot_pipeline =
ds4_gpu_get_mul_mv_pipeline(dot_dispatch.function_name, dot_dispatch.nsg);
if (!dot_pipeline) return 0;
ds4_gpu_dsv4_indexer_weighted_sum_args sum_args = {
.ne00 = (int64_t)n_comp,
.ne01 = 1,
.ne02 = (int64_t)n_head,
.nb00 = sizeof(float),
.nb01 = (uint64_t)n_comp * sizeof(float),
.nb02 = (uint64_t)n_comp * sizeof(float),
.ne10 = (int64_t)n_head,
.ne11 = 1,
.nb10 = sizeof(float),
.nb11 = (uint64_t)n_head * sizeof(float),
.ne0 = (int64_t)n_comp,
.ne1 = 1,
.nb0 = sizeof(float),
.nb1 = (uint64_t)n_comp * sizeof(float),
.scale = scale,
};
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:dot_pipeline];
[enc setBytes:&dot_args length:sizeof(dot_args) atIndex:0];
[enc setBuffer:compbuf offset:ds4_gpu_tensor_offset(index_comp) atIndex:1];
[enc setBuffer:qbuf offset:ds4_gpu_tensor_offset(q) atIndex:2];
[enc setBuffer:g_indexer_head_scores_buffer offset:0 atIndex:3];
if (dot_dispatch.smem) {
[enc setThreadgroupMemoryLength:dot_dispatch.smem atIndex:0];
}
[enc dispatchThreadgroups:MTLSizeMake(((NSUInteger)n_comp + (NSUInteger)dot_dispatch.nr0 - 1u) / (NSUInteger)dot_dispatch.nr0,
n_head,
1)
threadsPerThreadgroup:MTLSizeMake(32, (NSUInteger)dot_dispatch.nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:g_dsv4_indexer_weighted_sum_pipeline];
[enc setBytes:&sum_args length:sizeof(sum_args) atIndex:0];
[enc setBuffer:g_indexer_head_scores_buffer offset:0 atIndex:1];
[enc setBuffer:wbuf offset:ds4_gpu_tensor_offset(weights) atIndex:2];
[enc setBuffer:scorebuf offset:ds4_gpu_tensor_offset(scores) atIndex:3];
[enc dispatchThreadgroups:MTLSizeMake(((NSUInteger)n_comp + 255u) / 256u, 1, 1)
threadsPerThreadgroup:MTLSizeMake(256, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "indexer score")) return 0;
}
return 1;
}
static int ds4_gpu_indexer_scores_batch_tensor(
ds4_gpu_tensor *scores,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *weights,
const ds4_gpu_tensor *index_comp,
uint32_t n_comp,
uint32_t n_tokens,
uint32_t pos0,
uint32_t n_head,
uint32_t head_dim,
uint32_t ratio,
float scale) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!scores || !q || !weights || !index_comp ||
n_comp == 0 || n_tokens == 0 || n_head == 0 || head_dim == 0 || ratio == 0) {
return 0;
}
@autoreleasepool {
const uint64_t q_bytes = (uint64_t)n_tokens * n_head * head_dim * sizeof(float);
const uint64_t weight_bytes = (uint64_t)n_tokens * n_head * sizeof(float);
const uint64_t comp_bytes = (uint64_t)n_comp * head_dim * sizeof(float);
const uint64_t score_bytes = (uint64_t)n_comp * n_tokens * sizeof(float);
id<MTLBuffer> qbuf = ds4_gpu_tensor_buffer(q);
id<MTLBuffer> wbuf = ds4_gpu_tensor_buffer(weights);
id<MTLBuffer> compbuf = ds4_gpu_tensor_buffer(index_comp);
id<MTLBuffer> scorebuf = ds4_gpu_tensor_buffer(scores);
if (!qbuf || !wbuf || !compbuf || !scorebuf ||
ds4_gpu_tensor_bytes(q) < q_bytes ||
ds4_gpu_tensor_bytes(weights) < weight_bytes ||
ds4_gpu_tensor_bytes(index_comp) < comp_bytes ||
ds4_gpu_tensor_bytes(scores) < score_bytes) {
fprintf(stderr, "ds4: Metal graph indexer prefill scores received undersized buffers\n");
return 0;
}
if (head_dim != 128) {
fprintf(stderr, "ds4: Metal fused DS4 indexer scores expect 128-wide rows\n");
return 0;
}
/*
* The NAX/TensorOps score builder is a prefill-only win. At small
* batches and in one-token decode the setup cost is not amortized, so
* those paths keep the older direct/tiled score kernels.
*/
const bool use_nax = ds4_gpu_mpp_available() && n_tokens >= 16u;
id<MTLComputePipelineState> pipeline = ds4_gpu_get_pipeline(
use_nax ? "kernel_dsv4_indexer_scores_nax" :
(g_quality_mode ? "kernel_dsv4_indexer_scores_tiled_f32"
: "kernel_dsv4_indexer_scores_tiled"));
if (!pipeline) return 0;
ds4_gpu_dsv4_indexer_scores_fused_args args = {
.n_comp = n_comp,
.n_tokens = n_tokens,
.n_head = n_head,
.head_dim = head_dim,
.pos0 = pos0,
.ratio = ratio,
.q_token_stride = (uint64_t)n_head * head_dim * sizeof(float),
.q_head_stride = (uint64_t)head_dim * sizeof(float),
.weights_token_stride = (uint64_t)n_head * sizeof(float),
.index_row_stride = (uint64_t)head_dim * sizeof(float),
.score_token_stride = (uint64_t)n_comp * sizeof(float),
.scale = scale,
};
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:qbuf offset:ds4_gpu_tensor_offset(q) atIndex:1];
[enc setBuffer:wbuf offset:ds4_gpu_tensor_offset(weights) atIndex:2];
[enc setBuffer:compbuf offset:ds4_gpu_tensor_offset(index_comp) atIndex:3];
[enc setBuffer:scorebuf offset:ds4_gpu_tensor_offset(scores) atIndex:4];
if (use_nax) {
const NSUInteger q_shared = 2u * 32u * 32u;
const NSUInteger k_shared = 32u * 128u;
const NSUInteger dot_shared = 32u * 32u;
[enc setThreadgroupMemoryLength:(q_shared + k_shared) * sizeof(uint16_t) +
dot_shared * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(((NSUInteger)n_comp + 31u) / 32u,
((NSUInteger)n_tokens + 15u) / 16u,
1)
threadsPerThreadgroup:MTLSizeMake(128, 1, 1)];
} else if (g_quality_mode) {
const NSUInteger q_shared = 8u * 128u;
const NSUInteger k_shared = 32u * 128u;
const NSUInteger dot_shared = 8u * 32u;
[enc setThreadgroupMemoryLength:(q_shared + k_shared + dot_shared) * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(((NSUInteger)n_comp + 31u) / 32u,
((NSUInteger)n_tokens + 7u) / 8u,
1)
threadsPerThreadgroup:MTLSizeMake(32, 4, 1)];
} else {
const NSUInteger q_shared = 8u * 128u;
const NSUInteger k_shared = 32u * 128u;
const NSUInteger dot_shared = 8u * 32u;
[enc setThreadgroupMemoryLength:(q_shared + k_shared) * sizeof(uint16_t) +
dot_shared * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(((NSUInteger)n_comp + 31u) / 32u,
((NSUInteger)n_tokens + 7u) / 8u,
1)
threadsPerThreadgroup:MTLSizeMake(32, 4, 1)];
}
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "indexer prefill scores")) return 0;
}
return 1;
}
int ds4_gpu_indexer_scores_prefill_tensor(
ds4_gpu_tensor *scores,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *weights,
const ds4_gpu_tensor *index_comp,
uint32_t n_comp,
uint32_t n_tokens,
uint32_t n_head,
uint32_t head_dim,
uint32_t ratio,
float scale) {
return ds4_gpu_indexer_scores_batch_tensor(scores,
q,
weights,
index_comp,
n_comp,
n_tokens,
0,
n_head,
head_dim,
ratio,
scale);
}
int ds4_gpu_indexer_scores_decode_batch_tensor(
ds4_gpu_tensor *scores,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *weights,
const ds4_gpu_tensor *index_comp,
uint32_t n_comp,
uint32_t n_tokens,
uint32_t pos0,
uint32_t n_head,
uint32_t head_dim,
uint32_t ratio,
float scale) {
return ds4_gpu_indexer_scores_batch_tensor(scores,
q,
weights,
index_comp,
n_comp,
n_tokens,
pos0,
n_head,
head_dim,
ratio,
scale);
}
/* Radix select, deterministic compaction and one in-place sort of the winners
* (kernel_topk_select_*): every dispatch spans the GPU, where the merge ladder
* ends on two or three threadgroups. Returns 0 without encoding anything when
* the pipelines are unavailable, so the caller keeps the ladder. */
static int ds4_gpu_indexer_topk_select(id<MTLBuffer> selbuf, NSUInteger sel_off,
id<MTLBuffer> scorebuf, NSUInteger score_off,
uint32_t n, uint32_t rows, uint32_t top_k) {
id<MTLComputePipelineState> hist = ds4_gpu_get_pipeline("kernel_topk_select_hist");
id<MTLComputePipelineState> count = ds4_gpu_get_pipeline("kernel_topk_select_count");
id<MTLComputePipelineState> write = ds4_gpu_get_pipeline("kernel_topk_select_write");
id<MTLComputePipelineState> sort = ds4_gpu_get_pipeline("kernel_topk_select_sort");
id<MTLComputePipelineState> zero = ds4_gpu_get_pipeline("kernel_topk_select_zero");
if (!hist || !count || !write || !sort || !zero ||
hist.maxTotalThreadsPerThreadgroup < 1024u || count.maxTotalThreadsPerThreadgroup < 1024u ||
write.maxTotalThreadsPerThreadgroup < 1024u || sort.maxTotalThreadsPerThreadgroup < 1024u) return 0;
const uint32_t n_tg = (n + 2047u) / 2048u;
/* sized for the widest row (2^23 keys), so the scratch is allocated once per row count */
const NSUInteger state_off = (NSUInteger)rows * 4u * 2048u * sizeof(uint32_t);
const NSUInteger counts_off = state_off + (NSUInteger)rows * 4u * sizeof(uint32_t);
const NSUInteger bytes = counts_off + (NSUInteger)rows * 4096u * 2u * sizeof(uint32_t);
id<MTLBuffer> before = g_topk_select_buffer;
if (!ds4_gpu_ensure_scratch_buffer(&g_topk_select_buffer, &g_topk_select_bytes, bytes,
"ds4_topk_select")) return 0;
ds4_gpu_kargs_topk_select args = {n, rows, top_k, 0, g_topk_select_parity, n_tg};
g_topk_select_parity ^= 1u;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc;
if (g_topk_select_buffer != before) { /* a fresh scratch starts with empty histograms */
const uint32_t words = (uint32_t)(g_topk_select_bytes / sizeof(uint32_t));
enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:zero];
[enc setBuffer:g_topk_select_buffer offset:0 atIndex:0];
[enc setBytes:&words length:sizeof(words) atIndex:1];
[enc dispatchThreadgroups:MTLSizeMake((words + 255u) / 256u, 1, 1)
threadsPerThreadgroup:MTLSizeMake(256, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
}
for (args.pass = 0; args.pass < 3u; args.pass++) {
enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:hist];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:scorebuf offset:score_off atIndex:1];
[enc setBuffer:g_topk_select_buffer offset:0 atIndex:2];
[enc setThreadgroupMemoryLength:2048u * sizeof(uint32_t) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(n_tg, rows, 1) threadsPerThreadgroup:MTLSizeMake(1024, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
}
enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:count];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:scorebuf offset:score_off atIndex:1];
[enc setBuffer:g_topk_select_buffer offset:0 atIndex:2];
[enc setBuffer:g_topk_select_buffer offset:state_off atIndex:3];
[enc setBuffer:g_topk_select_buffer offset:counts_off atIndex:4];
[enc dispatchThreadgroups:MTLSizeMake(n_tg, rows, 1) threadsPerThreadgroup:MTLSizeMake(1024, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:write];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:scorebuf offset:score_off atIndex:1];
[enc setBuffer:g_topk_select_buffer offset:state_off atIndex:2];
[enc setBuffer:g_topk_select_buffer offset:counts_off atIndex:3];
[enc setBuffer:selbuf offset:sel_off atIndex:4];
[enc dispatchThreadgroups:MTLSizeMake(n_tg, rows, 1) threadsPerThreadgroup:MTLSizeMake(1024, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:sort];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:scorebuf offset:score_off atIndex:1];
[enc setBuffer:selbuf offset:sel_off atIndex:2];
[enc setThreadgroupMemoryLength:2u * 2048u * sizeof(uint32_t) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(rows, 1, 1) threadsPerThreadgroup:MTLSizeMake(1024, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_finish_command_buffer(cb, owned, "indexer top-k select") ? 1 : 0;
}
static int ds4_gpu_indexer_topk_tensor_impl(
ds4_gpu_tensor *selected,
const ds4_gpu_tensor *scores,
uint32_t n_comp,
uint32_t n_tokens,
uint32_t top_k,
uint32_t causal_start,
uint32_t causal_ratio) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!selected || !scores || n_comp == 0 || n_tokens == 0 || top_k == 0 || top_k > n_comp) return 0;
@autoreleasepool {
id<MTLComputePipelineState> sort_pipeline = causal_ratio ?
ds4_gpu_get_pipeline(getenv("DS4_METAL_DISABLE_V41_TOPK_SHUFFLE") ?
"kernel_argsort_f32_i32_desc_causal" : "kernel_argsort_f32_i32_desc_causal_shuffle") :
g_argsort_f32_i32_desc_pipeline;
id<MTLComputePipelineState> merge_pipeline = causal_ratio ?
ds4_gpu_get_pipeline(top_k == 512u && !getenv("DS4_METAL_DISABLE_V41_TOPK_PREFIX") ?
"kernel_argsort_merge_f32_i32_desc_causal_prefix" :
"kernel_argsort_merge_f32_i32_desc_causal") : g_argsort_merge_f32_i32_desc_pipeline;
if (!sort_pipeline || !merge_pipeline) return 0;
const uint64_t score_bytes = (uint64_t)n_comp * n_tokens * sizeof(float);
const uint64_t selected_bytes = (uint64_t)top_k * n_tokens * sizeof(uint32_t);
id<MTLBuffer> scorebuf = ds4_gpu_tensor_buffer(scores);
id<MTLBuffer> selbuf = ds4_gpu_tensor_buffer(selected);
if (!scorebuf || !selbuf ||
ds4_gpu_tensor_bytes(scores) < score_bytes ||
ds4_gpu_tensor_bytes(selected) < selected_bytes) {
fprintf(stderr, "ds4: Metal graph indexer top-k received undersized buffers\n");
return 0;
}
/* wide rows: the merge ladder's last rounds run on two or three threadgroups */
if (!causal_ratio && top_k <= 2048u && n_comp >= 16384u && n_comp <= (1u << 23) &&
!getenv("DS4_METAL_DISABLE_V41_TOPK_SELECT") &&
ds4_gpu_indexer_topk_select(selbuf, ds4_gpu_tensor_offset(selected), scorebuf,
ds4_gpu_tensor_offset(scores), n_comp, n_tokens, top_k)) return 1;
NSUInteger max_threads = g_argsort_f32_i32_desc_pipeline.maxTotalThreadsPerThreadgroup;
if (max_threads == 0) max_threads = 256;
int32_t nth = 1;
while ((uint32_t)nth < n_comp && (uint64_t)2u * (uint64_t)nth <= (uint64_t)max_threads) {
nth *= 2;
}
if (causal_ratio && (sort_pipeline.maxTotalThreadsPerThreadgroup < (NSUInteger)nth ||
(causal_start + 1u) / causal_ratio <= (uint32_t)nth / 2u)) return 0;
const int32_t npr = (int32_t)((n_comp + (uint32_t)nth - 1u) / (uint32_t)nth);
const int32_t block_top_k = (int32_t)(top_k < (uint32_t)nth ? top_k : (uint32_t)nth);
int32_t work_width = (int32_t)top_k;
if (npr > 1) {
const int32_t last_block = (int32_t)n_comp - (npr - 1) * nth;
work_width = (npr - 1) * block_top_k + (last_block < block_top_k ? last_block : block_top_k);
}
const uint64_t scratch_row_bytes = (uint64_t)work_width * sizeof(uint32_t);
const bool one_pass = npr <= 1;
const uint64_t scratch_bytes = one_pass ? scratch_row_bytes * n_tokens :
2u * scratch_row_bytes * n_tokens;
if (!ds4_gpu_ensure_scratch_buffer(&g_indexer_topk_buffer,
&g_indexer_topk_bytes,
(NSUInteger)scratch_bytes,
"ds4_indexer_topk")) {
return 0;
}
ds4_gpu_kargs_argsort args = {
.ne00 = (int32_t)n_comp,
.ne01 = (int32_t)n_tokens,
.ne02 = 1,
.ne03 = 1,
.nb00 = sizeof(float),
.nb01 = (uint64_t)n_comp * sizeof(float),
.nb02 = (uint64_t)n_comp * n_tokens * sizeof(float),
.nb03 = (uint64_t)n_comp * n_tokens * sizeof(float),
.ne0 = work_width,
.ne1 = (int32_t)n_tokens,
.ne2 = 1,
.ne3 = 1,
.top_k = block_top_k,
.causal_start = causal_start,
.causal_ratio = causal_ratio,
};
// kernel_argsort_f32_i32_desc stages the block's scores behind the
// index array: nth int32 indices + nth float scores.
const NSUInteger smem = (((NSUInteger)nth * (sizeof(int32_t) + sizeof(float))) + 15u) & ~(NSUInteger)15u;
NSUInteger cur_off = 0;
NSUInteger next_off = (NSUInteger)scratch_row_bytes * n_tokens;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:sort_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:scorebuf offset:ds4_gpu_tensor_offset(scores) atIndex:1];
[enc setBuffer:one_pass ? selbuf : g_indexer_topk_buffer
offset:one_pass ? ds4_gpu_tensor_offset(selected) : cur_off
atIndex:2];
[enc setThreadgroupMemoryLength:smem atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)npr * n_tokens, 1, 1)
threadsPerThreadgroup:MTLSizeMake((NSUInteger)nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
int32_t len = block_top_k;
while (len < work_width) {
const int32_t nm = (work_width + 2 * len - 1) / (2 * len);
const bool final_merge = nm == 1;
NSUInteger merge_threads = merge_pipeline.maxTotalThreadsPerThreadgroup;
if (merge_threads == 0 || merge_threads > 512u) merge_threads = 512u;
if (merge_threads > (NSUInteger)len) merge_threads = (NSUInteger)len;
if (merge_threads == 0) merge_threads = 1;
ds4_gpu_kargs_argsort_merge merge_args = {
.ne00 = (int64_t)n_comp,
.ne01 = (int64_t)n_tokens,
.ne02 = 1,
.ne03 = 1,
.nb00 = sizeof(float),
.nb01 = (uint64_t)n_comp * sizeof(float),
.nb02 = (uint64_t)n_comp * n_tokens * sizeof(float),
.nb03 = (uint64_t)n_comp * n_tokens * sizeof(float),
.ne0 = work_width,
.ne1 = (int32_t)n_tokens,
.ne2 = 1,
.ne3 = 1,
.top_k = nm == 1 ? (int32_t)top_k : work_width,
.len = len,
.causal_start = causal_start,
.causal_ratio = causal_ratio,
.block_width = (uint32_t)nth,
.block_top_k = (uint32_t)block_top_k,
};
enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:merge_pipeline];
[enc setBytes:&merge_args length:sizeof(merge_args) atIndex:0];
[enc setBuffer:scorebuf offset:ds4_gpu_tensor_offset(scores) atIndex:1];
[enc setBuffer:g_indexer_topk_buffer offset:cur_off atIndex:2];
[enc setBuffer:final_merge ? selbuf : g_indexer_topk_buffer
offset:final_merge ? ds4_gpu_tensor_offset(selected) : next_off
atIndex:3];
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)nm * n_tokens, 1, 1)
threadsPerThreadgroup:MTLSizeMake(merge_threads, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
const NSUInteger tmp = cur_off;
cur_off = next_off;
next_off = tmp;
len <<= 1;
}
if (!ds4_gpu_finish_command_buffer(cb, owned, "indexer top-k")) return 0;
}
return 1;
}
int ds4_gpu_indexer_topk_tensor(ds4_gpu_tensor *selected, const ds4_gpu_tensor *scores,
uint32_t n_comp, uint32_t n_tokens, uint32_t top_k) {
return ds4_gpu_indexer_topk_tensor_impl(selected, scores, n_comp, n_tokens, top_k, 0, 0);
}
int ds4_gpu_dsv41_indexer_topk_batch(ds4_gpu_tensor *selected, const ds4_gpu_tensor *scores,
uint32_t width, uint32_t rows, uint32_t start, uint32_t ratio) {
if ((ratio != 1u && ratio != 2u) || !rows || rows > UINT32_MAX - start ||
width > INT32_MAX || rows > INT32_MAX || (start + rows) / ratio > width ||
(start + 1u) / ratio <= 512u) return 0;
return ds4_gpu_indexer_topk_tensor_impl(selected, scores, width, rows, 512u, start, ratio);
}
int ds4_gpu_dsv41_indexer_all_batch(ds4_gpu_tensor *selected, uint32_t rows,
uint32_t start, uint32_t ratio) {
if (!ratio || !rows || rows > UINT32_MAX - start || (start + rows) / ratio > 512u ||
!selected || ds4_gpu_tensor_bytes(selected) < (uint64_t)rows * 512u * sizeof(int32_t)) return 0;
if (!g_initialized && !ds4_gpu_init()) return 0;
@autoreleasepool {
id<MTLComputePipelineState> pipeline = ds4_gpu_get_pipeline("kernel_dsv41_indexer_all");
if (!pipeline) return 0;
const uint32_t args[] = {rows, start, ratio, 512u};
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
id<MTLComputeCommandEncoder> enc = cb ? ds4_gpu_compute_encoder(cb) : nil;
if (!enc) return 0;
[enc setComputePipelineState:pipeline];
[enc setBytes:args length:sizeof(args) atIndex:0];
[enc setBuffer:ds4_gpu_tensor_buffer(selected) offset:ds4_gpu_tensor_offset(selected) atIndex:1];
[enc dispatchThreads:MTLSizeMake(512u, rows, 1)
threadsPerThreadgroup:MTLSizeMake(256u, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_finish_command_buffer(cb, owned, "V4.1 indexer select all");
}
}
int ds4_gpu_argmax_tensor(
ds4_gpu_tensor *out_idx,
const ds4_gpu_tensor *logits,
uint32_t n_vocab) {
if (!out_idx || !logits || n_vocab == 0) return 0;
if (ds4_gpu_tensor_bytes(out_idx) < sizeof(int32_t) ||
ds4_gpu_tensor_bytes(logits) < (uint64_t)n_vocab * sizeof(float)) {
fprintf(stderr, "ds4: Metal graph argmax received undersized buffers\n");
return 0;
}
return ds4_gpu_indexer_topk_tensor(out_idx, logits, n_vocab, 1, 1);
}
static int ds4_gpu_dsv4_topk_mask_tensor_impl(
ds4_gpu_tensor *mask,
const ds4_gpu_tensor *topk,
uint32_t n_comp,
uint32_t n_tokens,
uint32_t top_k,
uint32_t mask_stride) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!mask || !topk || n_comp == 0 || n_tokens == 0 || top_k == 0 || mask_stride < n_comp) return 0;
@autoreleasepool {
const uint64_t topk_bytes = (uint64_t)top_k * n_tokens * sizeof(int32_t);
const uint64_t mask_bytes = ((uint64_t)mask_stride * (n_tokens - 1u) + n_comp) * sizeof(float);
id<MTLBuffer> topkbuf = ds4_gpu_tensor_buffer(topk);
id<MTLBuffer> maskbuf = ds4_gpu_tensor_buffer(mask);
if (!topkbuf || !maskbuf ||
ds4_gpu_tensor_bytes(topk) < topk_bytes ||
ds4_gpu_tensor_bytes(mask) < mask_bytes) {
fprintf(stderr, "ds4: Metal dsv4 top-k mask received undersized buffers\n");
return 0;
}
ds4_gpu_dsv4_topk_mask_args args = {
.ne00 = (int64_t)top_k,
.ne01 = (int64_t)n_tokens,
.nb00 = sizeof(int32_t),
.nb01 = (uint64_t)top_k * sizeof(int32_t),
.ne0 = (int64_t)n_comp,
.ne1 = (int64_t)n_tokens,
.nb0 = sizeof(float),
.nb1 = (uint64_t)mask_stride * sizeof(float),
};
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:g_dsv4_topk_mask_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:topkbuf offset:ds4_gpu_tensor_offset(topk) atIndex:1];
[enc setBuffer:maskbuf offset:ds4_gpu_tensor_offset(mask) atIndex:2];
[enc dispatchThreadgroups:MTLSizeMake((((NSUInteger)n_comp * n_tokens) + 255u) / 256u, 1, 1)
threadsPerThreadgroup:MTLSizeMake(256, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:g_dsv4_topk_mask_scatter_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:topkbuf offset:ds4_gpu_tensor_offset(topk) atIndex:1];
[enc setBuffer:maskbuf offset:ds4_gpu_tensor_offset(mask) atIndex:2];
[enc dispatchThreadgroups:MTLSizeMake((((NSUInteger)top_k * n_tokens) + 255u) / 256u, 1, 1)
threadsPerThreadgroup:MTLSizeMake(256, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "dsv4 top-k mask")) return 0;
}
return 1;
}
int ds4_gpu_dsv4_topk_mask_tensor(
ds4_gpu_tensor *mask,
const ds4_gpu_tensor *topk,
uint32_t n_comp,
uint32_t n_tokens,
uint32_t top_k) {
return ds4_gpu_dsv4_topk_mask_tensor_impl(mask, topk, n_comp, n_tokens, top_k, n_comp);
}
static int ds4_gpu_matmul_q8_0_legacy_tensor(
ds4_gpu_tensor *out,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint64_t in_dim,
uint64_t out_dim,
const ds4_gpu_tensor *x,
uint64_t n_tok,
bool prefer_decode_mpp,
bool prefill_default,
int round_bf16,
int *fused_round) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if ((in_dim & 31u) != 0 ||
in_dim > UINT32_MAX || out_dim > UINT32_MAX || n_tok > UINT32_MAX) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> xbuf = ds4_gpu_tensor_buffer(x);
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out);
const uint64_t x_bytes = n_tok * in_dim * sizeof(float);
const uint64_t out_bytes = n_tok * out_dim * sizeof(float);
if (!xbuf || !outbuf ||
ds4_gpu_tensor_bytes(x) < x_bytes ||
ds4_gpu_tensor_bytes(out) < out_bytes) {
fprintf(stderr, "ds4: Metal Q8_0 tensor matmul received undersized activation buffers\n");
return 0;
}
const uint64_t blocks = in_dim / 32;
const uint64_t row_bytes = blocks * 34;
const uint64_t weight_bytes = out_dim * row_bytes;
if (weight_offset > model_size || weight_bytes > model_size - weight_offset) {
fprintf(stderr, "ds4: Metal Q8_0 tensor matmul range is outside the mapped model\n");
return 0;
}
uint64_t inner_offset = 0;
id<MTLBuffer> wbuf = ds4_gpu_wrap_model_range(model_map,
model_size,
weight_offset,
weight_bytes,
&inner_offset);
if (!wbuf) {
return 0;
}
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
if (n_tok == 1) {
if (ds4_gpu_mpp_available() &&
prefer_decode_mpp &&
(in_dim % 64u) == 0) {
const char *nax_fn = "kernel_mul_mm_q8_0_f32_nax_direct_rhs";
id<MTLComputePipelineState> mpp_pipeline =
ds4_gpu_get_mul_mm_pipeline(nax_fn, false, false);
if (mpp_pipeline) {
ds4_gpu_mul_mm_args args =
ds4_gpu_make_mm_args(in_dim, out_dim, n_tok, row_bytes);
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:mpp_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:wbuf offset:(NSUInteger)inner_offset atIndex:1];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:2];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(out) atIndex:3];
[enc setThreadgroupMemoryLength:2u * 64u * 32u * sizeof(uint16_t) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(1u,
((NSUInteger)out_dim + 63u) / 64u,
1u)
threadsPerThreadgroup:MTLSizeMake(128, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "Q8_0 decode MPP matmul")) {
return 0;
}
return 1;
}
ds4_gpu_warn_mpp_fallback();
}
ds4_gpu_q8_0_matvec_args mv_args = ds4_gpu_make_q8_0_mv_args(in_dim, out_dim);
ds4_gpu_mv_dispatch mv_dispatch = ds4_gpu_make_q8_0_mv_dispatch();
if (out_dim > 65536u) mv_dispatch.nsg = 8;
mv_args.nr0 = mv_dispatch.nr0;
const char *mv_fn = mv_dispatch.function_name;
if (round_bf16 && !strcmp(mv_fn, "kernel_mul_mv_q8_0_f32")) {
mv_fn = round_bf16 == 2 ? "kernel_mul_mv_q8_0_f32_bf16io" : "kernel_mul_mv_q8_0_f32_bf16";
if (fused_round) *fused_round = 1;
}
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_mul_mv_pipeline(mv_fn, mv_dispatch.nsg);
if (!pipeline) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&mv_args length:sizeof(mv_args) atIndex:0];
[enc setBuffer:wbuf offset:(NSUInteger)inner_offset atIndex:1];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:2];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(out) atIndex:3];
[enc setThreadgroupMemoryLength:mv_dispatch.smem atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(((NSUInteger)out_dim + (NSUInteger)mv_dispatch.nr0 - 1u) / (NSUInteger)mv_dispatch.nr0,
1,
1)
threadsPerThreadgroup:MTLSizeMake(32, (NSUInteger)mv_dispatch.nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "Q8_0 tensor matvec")) {
return 0;
}
return 1;
}
const uint64_t mv_ext_max_tokens =
ds4_gpu_env_u64("DS4_METAL_Q8_MV_EXT_MAX_TOKENS", 16u, 2u, 128u);
if (n_tok <= mv_ext_max_tokens && (in_dim % 128u) == 0) {
const int16_t nsg = ds4_gpu_mv_ext_nsg();
const int16_t nxpsg = ds4_gpu_mv_ext_nxpsg(in_dim, n_tok);
const int16_t r1ptg = ds4_gpu_mv_ext_r1ptg(n_tok);
const char *fn_name = ds4_gpu_mv_ext_name(1, r1ptg);
id<MTLComputePipelineState> pipeline =
fn_name ? ds4_gpu_get_mul_mv_ext_pipeline(fn_name, nsg, nxpsg) : nil;
if (!pipeline) return 0;
const int16_t nypsg = 32 / nxpsg;
const uint64_t r0ptg = (uint64_t)nypsg * (uint64_t)nsg;
ds4_gpu_mul_mv_ext_args args =
ds4_gpu_make_mv_ext_args(in_dim, out_dim, n_tok, 34, row_bytes);
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:wbuf offset:(NSUInteger)inner_offset atIndex:1];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:2];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(out) atIndex:3];
[enc dispatchThreadgroups:MTLSizeMake(((NSUInteger)out_dim + (NSUInteger)r0ptg - 1u) / (NSUInteger)r0ptg,
((NSUInteger)n_tok + (NSUInteger)r1ptg - 1u) / (NSUInteger)r1ptg,
1)
threadsPerThreadgroup:MTLSizeMake(32, (NSUInteger)nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "Q8_0 tensor mul_mv_ext")) {
return 0;
}
return 1;
}
/*
* Dense Q8_0 prefill is the cleanest DS4 TensorOps shape: M/N/K are
* aligned and the RHS activation matrix is already dense. The retained
* kernel dequantizes each 64x32 weight tile to half in threadgroup
* memory, then uses direct-RHS MPP for the activation tile. This avoids
* staging RHS into threadgroup memory and was the direct replacement for
* the slower generic MPP prototype.
*/
/*
* An unaligned token count used to send the entire projection through
* the generic kernel. Run its aligned prefix through TensorOps and
* leave only the final partial tile to the boundary-safe kernel.
* Tiny prompts do not amortize the second dispatch, while --quality
* deliberately retains the single-kernel arithmetic schedule.
*/
const bool split_nax_prefix =
!g_quality_mode && n_tok >= 192u && (n_tok % 32u) != 0u;
const uint64_t nax_rows =
(n_tok % 32u) == 0u ? n_tok :
(split_nax_prefix ? n_tok - (n_tok % 32u) : 0u);
uint64_t generic_row0 = 0u;
uint64_t generic_rows = n_tok;
if (ds4_gpu_mpp_available() &&
nax_rows >= 32u &&
(in_dim % 64u) == 0 &&
(out_dim % 64u) == 0) {
uint64_t nax_tile_n = 32u;
if ((nax_rows % 128u) == 0) {
nax_tile_n = 128u;
} else if ((nax_rows % 64u) == 0) {
nax_tile_n = 64u;
}
const char *nax_fn = nax_tile_n == 128u
? "kernel_mul_mm_q8_0_f32_nax_direct_rhs_n128"
: (nax_tile_n == 64u
? "kernel_mul_mm_q8_0_f32_nax_direct_rhs_n64"
: "kernel_mul_mm_q8_0_f32_nax_direct_rhs");
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_mul_mm_pipeline(nax_fn, false, false);
if (pipeline) {
ds4_gpu_mul_mm_args args =
ds4_gpu_make_mm_args(in_dim, out_dim, nax_rows, row_bytes);
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:wbuf offset:(NSUInteger)inner_offset atIndex:1];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:2];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(out) atIndex:3];
[enc setThreadgroupMemoryLength:2u * 64u * 32u * sizeof(uint16_t) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)(nax_rows / nax_tile_n),
(NSUInteger)out_dim / 64u,
1)
threadsPerThreadgroup:MTLSizeMake(128, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (nax_rows == n_tok) {
if (!ds4_gpu_finish_command_buffer(cb, owned, "Q8_0 NAX tensor matmul")) {
return 0;
}
return 1;
}
generic_row0 = nax_rows;
generic_rows = n_tok - nax_rows;
}
if (!pipeline) ds4_gpu_warn_mpp_fallback();
}
const bool bc_inp = (in_dim % 32u) != 0;
const bool bc_out =
(out_dim % 64u) != 0 || (generic_rows % 32u) != 0;
/* Amortize dequantization across token tiles with identical half
* operands. Default is scoped by the Qwen entry point to M3 Ultra. */
const int unpack_override = ds4_gpu_env_bool("DS4_QWEN4_Q8_PREFILL_UNPACK");
const bool prefill_unpack = generic_rows >= 32u &&
(unpack_override >= 0 ? unpack_override == 1 : prefill_default);
const char *fn = "kernel_mul_mm_q8_0_f32";
id<MTLBuffer> mat_weights = wbuf;
NSUInteger mat_offset = (NSUInteger)inner_offset;
uint64_t mat_row_bytes = row_bytes;
const bool unpack_fits = in_dim > 0u && out_dim > 0u && out_dim <= (64u << 20) / in_dim;
const uint64_t unpack_bytes = unpack_fits ? in_dim * out_dim * sizeof(uint16_t) : 0u;
if (prefill_unpack && unpack_fits) {
if (!ds4_gpu_ensure_scratch_buffer(&g_q8_prefill_scratch_buffer,
&g_q8_prefill_scratch_bytes, (NSUInteger)unpack_bytes, "q8 prefill weights")) return 0;
id<MTLComputePipelineState> unpack = ds4_gpu_get_pipeline("kernel_q8_prefill_unpack");
if (!unpack) return 0;
const uint32_t n_blocks = (uint32_t)(in_dim * out_dim / 32u);
id<MTLComputeCommandEncoder> unpack_enc = ds4_gpu_compute_encoder(cb);
[unpack_enc setComputePipelineState:unpack];
[unpack_enc setBytes:&n_blocks length:sizeof(n_blocks) atIndex:0];
[unpack_enc setBuffer:wbuf offset:(NSUInteger)inner_offset atIndex:1];
[unpack_enc setBuffer:g_q8_prefill_scratch_buffer offset:0 atIndex:2];
[unpack_enc dispatchThreadgroups:MTLSizeMake(((NSUInteger)n_blocks * 8u + 255u)/256u, 1, 1)
threadsPerThreadgroup:MTLSizeMake(256, 1, 1)];
ds4_gpu_end_compute_encoder(cb, unpack_enc);
fn = "kernel_mul_mm_f16_f32";
mat_weights = g_q8_prefill_scratch_buffer;
mat_offset = 0;
mat_row_bytes = in_dim * sizeof(uint16_t);
}
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_mul_mm_pipeline(fn, bc_inp, bc_out);
if (!pipeline) return 0;
ds4_gpu_mul_mm_args args =
ds4_gpu_make_mm_args(in_dim, out_dim, generic_rows, mat_row_bytes);
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:mat_weights offset:mat_offset atIndex:1];
[enc setBuffer:xbuf
offset:ds4_gpu_tensor_offset(x) +
(NSUInteger)(generic_row0 * in_dim * sizeof(float))
atIndex:2];
[enc setBuffer:outbuf
offset:ds4_gpu_tensor_offset(out) +
(NSUInteger)(generic_row0 * out_dim * sizeof(float))
atIndex:3];
[enc setThreadgroupMemoryLength:(bc_out ? 8192u : 6144u) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(((NSUInteger)generic_rows + 31u) / 32u,
((NSUInteger)out_dim + 63u) / 64u,
1)
threadsPerThreadgroup:MTLSizeMake(128, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "Q8_0 tensor matmul")) {
return 0;
}
}
return 1;
}
static int ds4_gpu_matmul_q8_0_tensor_impl(
ds4_gpu_tensor *out,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint64_t in_dim,
uint64_t out_dim,
const ds4_gpu_tensor *x,
uint64_t n_tok, bool prefill_default,
int round_bf16, int *fused_round) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if ((in_dim & 31u) != 0 ||
in_dim > UINT32_MAX || out_dim > UINT32_MAX || n_tok > UINT32_MAX) {
return 0;
}
const int profile_requested =
n_tok > 8u && ds4_gpu_env_bool("DS4_METAL_Q8_PREFILL_PROFILE") > 0;
int profile_prefill = 0;
int split_batch_for_profile = 0;
const char *profile_label = NULL;
char profile_label_buf[128];
char profile_fallback[128];
if (profile_requested) {
snprintf(profile_fallback, sizeof(profile_fallback),
"q8 weight_off=%llu in=%llu out=%llu tok=%llu",
(unsigned long long)weight_offset,
(unsigned long long)in_dim,
(unsigned long long)out_dim,
(unsigned long long)n_tok);
snprintf(profile_label_buf, sizeof(profile_label_buf), "%s", profile_fallback);
profile_label = profile_label_buf;
const char *profile_filter = getenv("DS4_METAL_Q8_PREFILL_PROFILE_FILTER");
profile_prefill =
profile_requested &&
(!profile_filter || !profile_filter[0] ||
strstr(profile_label, profile_filter) != NULL);
}
if (profile_prefill) {
if (g_batch_cb) {
if (ds4_gpu_end_commands() == 0 || ds4_gpu_begin_commands() == 0) {
return 0;
}
split_batch_for_profile = 1;
}
}
const double profile_t0 = profile_prefill ? ds4_gpu_now_ms() : 0.0;
int ok = ds4_gpu_matmul_q8_0_legacy_tensor(out, model_map, model_size,
weight_offset, in_dim, out_dim,
x, n_tok, false, prefill_default,
round_bf16, fused_round);
if (profile_prefill) {
if (split_batch_for_profile && ds4_gpu_end_commands() == 0) {
ok = 0;
}
const double elapsed_ms = ds4_gpu_now_ms() - profile_t0;
fprintf(stderr,
"ds4: Metal Q8_0 prefill profile %s in=%llu out=%llu tok=%llu %.3f ms\n",
profile_label ? profile_label : profile_fallback,
(unsigned long long)in_dim,
(unsigned long long)out_dim,
(unsigned long long)n_tok,
elapsed_ms);
if (split_batch_for_profile && ds4_gpu_begin_commands() == 0) {
ok = 0;
}
}
return ok;
}
int ds4_gpu_matmul_q8_0_tensor(
ds4_gpu_tensor *out,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint64_t in_dim,
uint64_t out_dim,
const ds4_gpu_tensor *x,
uint64_t n_tok) {
return ds4_gpu_matmul_q8_0_tensor_impl(out, model_map, model_size, weight_offset, in_dim, out_dim, x, n_tok, false, 0, NULL);
}
static int ds4_gpu_matmul_q8_0_rows_tensor_impl(
ds4_gpu_tensor *out, const void *model_map, uint64_t model_size,
uint64_t weight_offset, uint64_t in_dim, uint64_t out_dim,
const ds4_gpu_tensor *x, uint32_t n_rows, int round_bf16);
/* The matvec rounds its output to bf16 in the kernel where the single-row Q8
* path applies; any other path gets the separate rounding pass. */
int ds4_gpu_matmul_q8_0_bf16_tensor(
ds4_gpu_tensor *out,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint64_t in_dim,
uint64_t out_dim,
const ds4_gpu_tensor *x,
uint64_t n_tok) {
int fused = 0;
if (n_tok >= 2 && n_tok <= 8 &&
ds4_gpu_matmul_q8_0_rows_tensor_impl(out, model_map, model_size, weight_offset,
in_dim, out_dim, x, (uint32_t)n_tok, 1)) return 1;
if (!ds4_gpu_matmul_q8_0_tensor_impl(out, model_map, model_size, weight_offset, in_dim, out_dim, x, n_tok, false, 1, &fused)) return 0;
return fused ? 1 : ds4_gpu_dsv41_quantize(out, (uint32_t)out_dim, (uint32_t)n_tok, DS4_V41_BF16);
}
/* As above, with the activation rounded to bf16 on load as well; rows other
* than one round the activation in place first. */
int ds4_gpu_matmul_q8_0_bf16io_tensor(
ds4_gpu_tensor *out,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint64_t in_dim,
uint64_t out_dim,
ds4_gpu_tensor *x,
uint64_t n_tok) {
if (n_tok >= 2 && n_tok <= 8 &&
ds4_gpu_matmul_q8_0_rows_tensor_impl(out, model_map, model_size, weight_offset,
in_dim, out_dim, x, (uint32_t)n_tok, 2)) return 1;
if (n_tok != 1) {
if (!ds4_gpu_dsv41_quantize(x, (uint32_t)in_dim, (uint32_t)n_tok, DS4_V41_BF16)) return 0;
return ds4_gpu_matmul_q8_0_bf16_tensor(out, model_map, model_size, weight_offset, in_dim, out_dim, x, n_tok);
}
return ds4_gpu_matmul_q8_0_tensor_impl(out, model_map, model_size, weight_offset, in_dim, out_dim, x, 1, false, 2, NULL);
}
int ds4_gpu_qwen4_matmul_q8_0_tensor(
ds4_gpu_tensor *out,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint64_t in_dim,
uint64_t out_dim,
const ds4_gpu_tensor *x,
uint64_t n_tok) {
if (!g_initialized && !ds4_gpu_init()) return 0;
return ds4_gpu_matmul_q8_0_tensor_impl(out, model_map, model_size, weight_offset, in_dim, out_dim, x, n_tok,
ds4_gpu_device_name_contains("M3 Ultra"), 0, NULL);
}
/* The matrix rows kernels round a few outputs differently from the row kernels. */
static int ds4_gpu_v41_rows_mma_off(void) {
static int off = -1;
if (off < 0) off = getenv("DS4_METAL_DISABLE_V41_ROWS_MMA") != NULL;
return off;
}
/* Two to eight rows share one pass over the weights (the rows kernels);
* each row's arithmetic is the single-row matvec's. round_bf16 as in
* ds4_gpu_matmul_q8_0_tensor_impl; rounding needs the rows kernel. */
static int ds4_gpu_matmul_q8_0_rows_tensor_impl(
ds4_gpu_tensor *out,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint64_t in_dim,
uint64_t out_dim,
const ds4_gpu_tensor *x,
uint32_t n_rows,
int round_bf16) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!out || !x || !model_map || n_rows == 0 ||
n_rows > INT32_MAX || in_dim == 0 || out_dim == 0 ||
(in_dim & 31u) != 0 || in_dim > UINT32_MAX ||
out_dim > UINT32_MAX ||
in_dim > UINT64_MAX / n_rows / sizeof(float) ||
out_dim > UINT64_MAX / n_rows / sizeof(float) ||
ds4_gpu_tensor_bytes(x) <
(uint64_t)n_rows * in_dim * sizeof(float) ||
ds4_gpu_tensor_bytes(out) <
(uint64_t)n_rows * out_dim * sizeof(float)) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> xbuf = ds4_gpu_tensor_buffer(x);
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out);
const uint64_t blocks = in_dim / 32u;
const uint64_t row_bytes = blocks * 34u;
if (!xbuf || !outbuf ||
out_dim > UINT64_MAX / row_bytes) {
return 0;
}
const uint64_t weight_bytes = out_dim * row_bytes;
if (weight_offset > model_size ||
weight_bytes > model_size - weight_offset) {
return 0;
}
uint64_t inner_offset = 0;
id<MTLBuffer> wbuf = ds4_gpu_wrap_model_range(
model_map, model_size, weight_offset, weight_bytes,
&inner_offset);
if (!wbuf) return 0;
ds4_gpu_mv_dispatch dispatch = ds4_gpu_make_q8_0_mv_dispatch();
if (out_dim > 65536u) dispatch.nsg = 8;
ds4_gpu_q8_0_matvec_args args =
ds4_gpu_make_q8_0_mv_args(in_dim, out_dim);
args.ne11 = (int32_t)n_rows;
args.nb12 = (uint64_t)n_rows * in_dim * sizeof(float);
args.nb13 = args.nb12;
args.ne1 = (int32_t)n_rows;
args.nr0 = dispatch.nr0;
/* the rows one after another on the single-row layout, four outputs
* per simdgroup */
const bool rows_kernel = n_rows >= 2u && n_rows <= 8u;
if (!rows_kernel && round_bf16) return 0;
if (rows_kernel) { dispatch.nr0 = 4; args.nr0 = 4; }
const char *rows_name = round_bf16 == 2 ? "kernel_mul_mv_q8_0_f32_bf16io_rows_seq4" :
round_bf16 == 1 ? "kernel_mul_mv_q8_0_f32_bf16_rows_seq4" : "kernel_mul_mv_q8_0_f32_rows_seq4";
/* the matrix rows kernel pays off from three rows (four on the
* widest K); eight simdgroups split a K of 4096 or more */
const int mma_off = ds4_gpu_v41_rows_mma_off();
const int mma = in_dim >= 4096u ? 8 : 4;
const bool mma_kernel = !mma_off && rows_kernel && n_rows >= (in_dim >= 8192u ? 4u : 3u) &&
(in_dim % (mma == 8 ? 256u : 128u)) == 0 && (out_dim % 8u) == 0;
id<MTLComputePipelineState> pipeline = mma_kernel ?
ds4_gpu_get_pipeline(mma == 8 ?
(round_bf16 == 2 ? "kernel_mul_mv_q8_0_f32_bf16io_mma_rows8" :
round_bf16 == 1 ? "kernel_mul_mv_q8_0_f32_bf16_mma_rows8" : "kernel_mul_mv_q8_0_f32_mma_rows8") :
(round_bf16 == 2 ? "kernel_mul_mv_q8_0_f32_bf16io_mma_rows" :
round_bf16 == 1 ? "kernel_mul_mv_q8_0_f32_bf16_mma_rows" : "kernel_mul_mv_q8_0_f32_mma_rows")) :
ds4_gpu_get_mul_mv_pipeline(rows_kernel ? rows_name : dispatch.function_name, dispatch.nsg);
if (!pipeline) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:wbuf offset:(NSUInteger)inner_offset atIndex:1];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:2];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(out) atIndex:3];
if (mma_kernel) {
/* per simdgroup an activation and a weight block, then the partial tiles */
[enc setThreadgroupMemoryLength:(NSUInteger)mma * (2u * 8u * 32u + 64u) * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)out_dim / 8u, 1, 1)
threadsPerThreadgroup:MTLSizeMake(32, (NSUInteger)mma, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_finish_command_buffer(cb, owned, "Q8_0 decode-row matmul");
}
[enc setThreadgroupMemoryLength:(rows_kernel ? 32u * sizeof(float) * 4u : dispatch.smem) atIndex:0];
[enc dispatchThreadgroups:
MTLSizeMake(((NSUInteger)out_dim +
(NSUInteger)dispatch.nr0 - 1u) /
(NSUInteger)dispatch.nr0,
rows_kernel ? 1u : (NSUInteger)n_rows,
1)
threadsPerThreadgroup:
MTLSizeMake(32, (NSUInteger)dispatch.nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_finish_command_buffer(
cb, owned, "Q8_0 exact decode-row matvec");
}
}
int ds4_gpu_matmul_q8_0_decode_rows_exact_tensor(
ds4_gpu_tensor *out,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint64_t in_dim,
uint64_t out_dim,
const ds4_gpu_tensor *x,
uint32_t n_rows) {
return ds4_gpu_matmul_q8_0_rows_tensor_impl(out, model_map, model_size, weight_offset,
in_dim, out_dim, x, n_rows, 0);
}
int ds4_gpu_dsv41_project_pair_q8(ds4_gpu_tensor *out_a, ds4_gpu_tensor *out_b,
const void *model_map, uint64_t model_size,
uint64_t offset_a, uint64_t offset_b, uint32_t in_dim,
uint32_t out_a_dim, uint32_t out_b_dim, const ds4_gpu_tensor *x) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!out_a || !out_b || !x || !model_map || !in_dim || (in_dim & 31u) ||
!out_a_dim || !out_b_dim || out_a_dim > 65536u || out_b_dim > 65536u ||
ds4_gpu_tensor_bytes(x) < (uint64_t)in_dim * sizeof(float) ||
ds4_gpu_tensor_bytes(out_a) < (uint64_t)out_a_dim * sizeof(float) ||
ds4_gpu_tensor_bytes(out_b) < (uint64_t)out_b_dim * sizeof(float)) return 0;
@autoreleasepool {
const uint64_t row_bytes = (uint64_t)(in_dim / 32u) * 34u;
const uint64_t bytes_a = row_bytes * out_a_dim, bytes_b = row_bytes * out_b_dim;
if (offset_a > model_size || bytes_a > model_size - offset_a ||
offset_b > model_size || bytes_b > model_size - offset_b) return 0;
uint64_t inner_a = 0, inner_b = 0;
id<MTLBuffer> wa = ds4_gpu_wrap_model_range(model_map, model_size, offset_a, bytes_a, &inner_a);
id<MTLBuffer> wb = ds4_gpu_wrap_model_range(model_map, model_size, offset_b, bytes_b, &inner_b);
if (!wa || !wb) return 0;
const ds4_gpu_mv_dispatch dispatch = ds4_gpu_make_q8_0_mv_dispatch();
ds4_gpu_q8_0_matvec_args args_a = ds4_gpu_make_q8_0_mv_args(in_dim, out_a_dim);
ds4_gpu_q8_0_matvec_args args_b = ds4_gpu_make_q8_0_mv_args(in_dim, out_b_dim);
args_a.nr0 = args_b.nr0 = dispatch.nr0;
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_mul_mv_pipeline("kernel_mul_mv_q8_0_f32_bf16_pair", dispatch.nsg);
if (!pipeline) return 0;
const NSUInteger nr0 = (NSUInteger)dispatch.nr0;
const NSUInteger groups = ((NSUInteger)out_a_dim + nr0 - 1u) / nr0 +
((NSUInteger)out_b_dim + nr0 - 1u) / nr0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
id<MTLComputeCommandEncoder> enc = cb ? ds4_gpu_compute_encoder(cb) : nil;
if (!enc) return 0;
[enc setComputePipelineState:pipeline];
[enc setBytes:&args_a length:sizeof(args_a) atIndex:0];
[enc setBytes:&args_b length:sizeof(args_b) atIndex:1];
[enc setBuffer:wa offset:(NSUInteger)inner_a atIndex:2];
[enc setBuffer:wb offset:(NSUInteger)inner_b atIndex:3];
[enc setBuffer:ds4_gpu_tensor_buffer(x) offset:ds4_gpu_tensor_offset(x) atIndex:4];
[enc setBuffer:ds4_gpu_tensor_buffer(out_a) offset:ds4_gpu_tensor_offset(out_a) atIndex:5];
[enc setBuffer:ds4_gpu_tensor_buffer(out_b) offset:ds4_gpu_tensor_offset(out_b) atIndex:6];
[enc setThreadgroupMemoryLength:dispatch.smem atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(groups, 1, 1)
threadsPerThreadgroup:MTLSizeMake(32, (NSUInteger)dispatch.nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_finish_command_buffer(cb, owned, "V4.1 paired Q8_0 matvec");
}
}
int ds4_gpu_matmul_q8_0_decode_mpp_tensor(
ds4_gpu_tensor *out,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint64_t in_dim,
uint64_t out_dim,
const ds4_gpu_tensor *x,
uint64_t n_tok) {
return ds4_gpu_matmul_q8_0_legacy_tensor(out, model_map, model_size,
weight_offset, in_dim, out_dim,
x, n_tok, true, false, 0, NULL);
}
int ds4_gpu_matmul_q8_0_decode_mpp_model_view_tensor(
ds4_gpu_tensor *out,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint64_t in_dim,
uint64_t out_dim,
const ds4_gpu_tensor *x,
uint64_t n_tok) {
return ds4_gpu_matmul_q8_0_legacy_tensor(out, model_map, model_size,
weight_offset, in_dim, out_dim,
x, n_tok, true, false, 0, NULL);
}
static const char *ds4_gpu_q4_mv_ext_name(uint32_t weight_type, int16_t r1ptg) {
const char *prefix = NULL;
if (weight_type == DS4_METAL_TENSOR_Q4_K) {
prefix = "kernel_mul_mv_ext_q4_K_f32_r1_";
} else if (weight_type == DS4_METAL_TENSOR_Q4_0) {
prefix = "kernel_mul_mv_ext_q4_0_f32_r1_";
} else {
return NULL;
}
switch (r1ptg) {
case 1: return weight_type == DS4_METAL_TENSOR_Q4_K ?
"kernel_mul_mv_ext_q4_K_f32_r1_1" : "kernel_mul_mv_ext_q4_0_f32_r1_1";
case 2: return weight_type == DS4_METAL_TENSOR_Q4_K ?
"kernel_mul_mv_ext_q4_K_f32_r1_2" : "kernel_mul_mv_ext_q4_0_f32_r1_2";
case 3: return weight_type == DS4_METAL_TENSOR_Q4_K ?
"kernel_mul_mv_ext_q4_K_f32_r1_3" : "kernel_mul_mv_ext_q4_0_f32_r1_3";
case 4: return weight_type == DS4_METAL_TENSOR_Q4_K ?
"kernel_mul_mv_ext_q4_K_f32_r1_4" : "kernel_mul_mv_ext_q4_0_f32_r1_4";
case 5: return weight_type == DS4_METAL_TENSOR_Q4_K ?
"kernel_mul_mv_ext_q4_K_f32_r1_5" : "kernel_mul_mv_ext_q4_0_f32_r1_5";
default:
(void)prefix;
return NULL;
}
}
static const char *ds4_gpu_q4_mm_name(uint32_t weight_type) {
switch (weight_type) {
case DS4_METAL_TENSOR_Q4_0: return "kernel_mul_mm_q4_0_f32";
case DS4_METAL_TENSOR_Q4_K: return "kernel_mul_mm_q4_K_f32";
default: return NULL;
}
}
static const char *ds4_gpu_q4_nax_name(uint32_t weight_type, uint64_t tile_n) {
if (weight_type == DS4_METAL_TENSOR_Q4_0) {
return tile_n == 128u ? "kernel_mul_mm_q4_0_f32_nax_direct_rhs_n128" :
tile_n == 64u ? "kernel_mul_mm_q4_0_f32_nax_direct_rhs_n64" :
"kernel_mul_mm_q4_0_f32_nax_direct_rhs";
}
if (weight_type == DS4_METAL_TENSOR_Q4_K) {
return tile_n == 128u ? "kernel_mul_mm_q4_K_f32_nax_direct_rhs_n128" :
tile_n == 64u ? "kernel_mul_mm_q4_K_f32_nax_direct_rhs_n64" :
"kernel_mul_mm_q4_K_f32_nax_direct_rhs";
}
return NULL;
}
static int ds4_gpu_matmul_quant_impl_tensor(
ds4_gpu_tensor *out,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint32_t weight_type,
uint64_t in_dim,
uint64_t out_dim,
const ds4_gpu_tensor *x,
uint64_t n_tok,
bool prefer_decode_mpp) {
if (weight_type == DS4_METAL_TENSOR_Q8_0) {
return ds4_gpu_matmul_q8_0_legacy_tensor(out,
model_map,
model_size,
weight_offset,
in_dim,
out_dim,
x,
n_tok,
prefer_decode_mpp, false, 0, NULL);
}
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!out || !x || !model_map ||
in_dim == 0 || out_dim == 0 || n_tok == 0 ||
in_dim > UINT32_MAX || out_dim > UINT32_MAX || n_tok > UINT32_MAX) {
return 0;
}
uint64_t row_bytes = 0;
if (!ds4_gpu_quant_row_bytes(weight_type, (uint32_t)in_dim, &row_bytes)) {
fprintf(stderr, "ds4: Metal quant matmul received unsupported type/dim (%u, in=%llu)\n",
weight_type,
(unsigned long long)in_dim);
return 0;
}
@autoreleasepool {
id<MTLBuffer> xbuf = ds4_gpu_tensor_buffer(x);
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out);
const uint64_t x_bytes = n_tok * in_dim * sizeof(float);
const uint64_t out_bytes = n_tok * out_dim * sizeof(float);
if (!xbuf || !outbuf ||
ds4_gpu_tensor_bytes(x) < x_bytes ||
ds4_gpu_tensor_bytes(out) < out_bytes) {
fprintf(stderr, "ds4: Metal quant tensor matmul received undersized activation buffers\n");
return 0;
}
if (out_dim > UINT64_MAX / row_bytes) return 0;
const uint64_t weight_bytes = out_dim * row_bytes;
if (weight_offset > model_size || weight_bytes > model_size - weight_offset) {
fprintf(stderr, "ds4: Metal quant tensor matmul range is outside the mapped model\n");
return 0;
}
uint64_t inner_offset = 0;
id<MTLBuffer> wbuf = ds4_gpu_wrap_model_range(model_map,
model_size,
weight_offset,
weight_bytes,
&inner_offset);
if (!wbuf) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
/*
* Small-batch Q4_K goes to the classic (llama.cpp-style) matvec:
* the mul_mv_ext family tops out around 220 GB/s on M5 for the GLM
* DenseQ4 decode shapes while this impl streams 530-650 GB/s
* (misc/q4mv_bench.m). Falls through to ext when unavailable.
*/
if (weight_type == DS4_METAL_TENSOR_Q4_K &&
n_tok <= 8u &&
(in_dim % 256u) == 0 &&
getenv("DS4_METAL_DISABLE_Q4_MV_CLASSIC") == NULL) {
const int16_t nsg = 2;
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_mul_mv_ext_pipeline("kernel_mul_mv_q4_K_dense_f32", nsg, 8);
if (pipeline) {
ds4_gpu_q8_0_matvec_args args = {
.ne00 = (int32_t)in_dim,
.ne01 = (int32_t)out_dim,
.ne02 = 1,
.nb00 = 1,
.nb01 = row_bytes,
.nb02 = row_bytes * out_dim,
.nb03 = row_bytes * out_dim,
.ne10 = (int32_t)in_dim,
.ne11 = (int32_t)n_tok,
.ne12 = 1,
.nb10 = sizeof(float),
.nb11 = in_dim * sizeof(float),
.nb12 = in_dim * n_tok * sizeof(float),
.nb13 = in_dim * n_tok * sizeof(float),
.ne0 = (int32_t)out_dim,
.ne1 = (int32_t)n_tok,
.nr0 = 2,
.r2 = 1,
.r3 = 1,
};
const uint64_t rows_ptg = (uint64_t)nsg * 2u;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:wbuf offset:(NSUInteger)inner_offset atIndex:1];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:2];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(out) atIndex:3];
[enc setThreadgroupMemoryLength:32 atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(((NSUInteger)out_dim + rows_ptg - 1u) / rows_ptg,
(NSUInteger)n_tok,
1)
threadsPerThreadgroup:MTLSizeMake(32, (NSUInteger)nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "Q4_K classic mul_mv")) return 0;
return 1;
}
}
if (n_tok <= 8u && (in_dim % 128u) == 0) {
const int16_t nsg = 2;
const int16_t nxpsg = ds4_gpu_mv_ext_nxpsg(in_dim, n_tok);
const int16_t r1ptg = (n_tok == 1u) ? 1 : ds4_gpu_mv_ext_r1ptg(n_tok);
const char *fn_name = ds4_gpu_q4_mv_ext_name(weight_type, r1ptg);
id<MTLComputePipelineState> pipeline =
fn_name ? ds4_gpu_get_mul_mv_ext_pipeline(fn_name, nsg, nxpsg) : nil;
if (pipeline) {
const int16_t nypsg = 32 / nxpsg;
const uint64_t r0ptg = (uint64_t)nypsg * (uint64_t)nsg;
ds4_gpu_mul_mv_ext_args args =
ds4_gpu_make_mv_ext_args(in_dim, out_dim, n_tok, row_bytes, row_bytes);
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:wbuf offset:(NSUInteger)inner_offset atIndex:1];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:2];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(out) atIndex:3];
[enc dispatchThreadgroups:MTLSizeMake(((NSUInteger)out_dim + (NSUInteger)r0ptg - 1u) / (NSUInteger)r0ptg,
((NSUInteger)n_tok + (NSUInteger)r1ptg - 1u) / (NSUInteger)r1ptg,
1)
threadsPerThreadgroup:MTLSizeMake(32, (NSUInteger)nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "Q4 tensor mul_mv_ext")) return 0;
return 1;
}
}
if (ds4_gpu_mpp_available() &&
n_tok >= 32u &&
(in_dim % 64u) == 0 &&
(out_dim % 64u) == 0 &&
(n_tok % 32u) == 0) {
uint64_t nax_tile_n = 32u;
if ((n_tok % 128u) == 0) {
nax_tile_n = 128u;
} else if ((n_tok % 64u) == 0) {
nax_tile_n = 64u;
}
const char *nax_fn = ds4_gpu_q4_nax_name(weight_type, nax_tile_n);
id<MTLComputePipelineState> pipeline =
nax_fn ? ds4_gpu_get_mul_mm_pipeline(nax_fn, false, false) : nil;
if (pipeline) {
ds4_gpu_mul_mm_args args = ds4_gpu_make_mm_args(in_dim, out_dim, n_tok, row_bytes);
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:wbuf offset:(NSUInteger)inner_offset atIndex:1];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:2];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(out) atIndex:3];
[enc setThreadgroupMemoryLength:2u * 64u * 32u * sizeof(uint16_t) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)(n_tok / nax_tile_n),
(NSUInteger)out_dim / 64u,
1)
threadsPerThreadgroup:MTLSizeMake(128, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "Q4 NAX tensor matmul")) return 0;
return 1;
}
ds4_gpu_warn_mpp_fallback();
}
const char *mm_fn = ds4_gpu_q4_mm_name(weight_type);
const bool bc_inp = (in_dim % 32u) != 0;
const bool bc_out = (out_dim % 64u) != 0 || (n_tok % 32u) != 0;
id<MTLComputePipelineState> pipeline =
mm_fn ? ds4_gpu_get_mul_mm_pipeline(mm_fn, bc_inp, bc_out) : nil;
if (!pipeline) return 0;
ds4_gpu_mul_mm_args args = ds4_gpu_make_mm_args(in_dim, out_dim, n_tok, row_bytes);
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:wbuf offset:(NSUInteger)inner_offset atIndex:1];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:2];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(out) atIndex:3];
[enc setThreadgroupMemoryLength:(bc_out ? 8192u : 6144u) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(((NSUInteger)n_tok + 31u) / 32u,
((NSUInteger)out_dim + 63u) / 64u,
1)
threadsPerThreadgroup:MTLSizeMake(128, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "Q4 tensor matmul")) return 0;
}
return 1;
}
int ds4_gpu_matmul_quant_tensor(
ds4_gpu_tensor *out,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint32_t weight_type,
uint64_t in_dim,
uint64_t out_dim,
const ds4_gpu_tensor *x,
uint64_t n_tok) {
return ds4_gpu_matmul_quant_impl_tensor(out,
model_map,
model_size,
weight_offset,
weight_type,
in_dim,
out_dim,
x,
n_tok,
false);
}
int ds4_gpu_matmul_quant_decode_mpp_model_view_tensor(
ds4_gpu_tensor *out,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint32_t weight_type,
uint64_t in_dim,
uint64_t out_dim,
const ds4_gpu_tensor *x,
uint64_t n_tok) {
/* Classic Q8_0 matvec is substantially faster than MPP at n_tok=1. */
return ds4_gpu_matmul_quant_impl_tensor(out,
model_map,
model_size,
weight_offset,
weight_type,
in_dim,
out_dim,
x,
n_tok,
false);
}
int ds4_gpu_matmul_quant_rows_scalar_tensor(
ds4_gpu_tensor *out,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint32_t weight_type,
uint64_t in_dim,
uint64_t out_dim,
const ds4_gpu_tensor *x,
uint64_t n_tok) {
return ds4_gpu_matmul_quant_tensor(out,
model_map,
model_size,
weight_offset,
weight_type,
in_dim,
out_dim,
x,
n_tok);
}
int ds4_gpu_matmul_q8_0_rows_scalar_tensor(
ds4_gpu_tensor *out,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint64_t in_dim,
uint64_t out_dim,
const ds4_gpu_tensor *x,
uint64_t n_tok) {
if (n_tok == 1) {
return ds4_gpu_matmul_q8_0_tensor(out,
model_map,
model_size,
weight_offset,
in_dim,
out_dim,
x,
1);
}
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!out || !x || !model_map ||
n_tok == 0 || n_tok > INT32_MAX ||
(in_dim & 31u) != 0 ||
in_dim > UINT32_MAX || out_dim > UINT32_MAX ||
in_dim > UINT64_MAX / sizeof(float) ||
out_dim > UINT64_MAX / sizeof(float)) {
return 0;
}
const uint64_t x_row_bytes = in_dim * sizeof(float);
const uint64_t out_row_bytes = out_dim * sizeof(float);
if ((x_row_bytes != 0 && n_tok > UINT64_MAX / x_row_bytes) ||
(out_row_bytes != 0 && n_tok > UINT64_MAX / out_row_bytes)) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> xbuf = ds4_gpu_tensor_buffer(x);
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out);
const uint64_t x_bytes = n_tok * x_row_bytes;
const uint64_t out_bytes = n_tok * out_row_bytes;
if (!xbuf || !outbuf ||
ds4_gpu_tensor_bytes(x) < x_bytes ||
ds4_gpu_tensor_bytes(out) < out_bytes) {
fprintf(stderr, "ds4: Metal Q8_0 scalar-row matmul received undersized activation buffers\n");
return 0;
}
const uint64_t blocks = in_dim / 32;
if (blocks > UINT64_MAX / 34u) return 0;
const uint64_t row_bytes = blocks * 34u;
if (out_dim > UINT64_MAX / row_bytes) return 0;
const uint64_t weight_bytes = out_dim * row_bytes;
if (weight_offset > model_size || weight_bytes > model_size - weight_offset) {
fprintf(stderr, "ds4: Metal Q8_0 scalar-row matmul range is outside the mapped model\n");
return 0;
}
uint64_t inner_offset = 0;
id<MTLBuffer> wbuf =
ds4_gpu_wrap_model_range(model_map, model_size, weight_offset, weight_bytes, &inner_offset);
if (!wbuf) return 0;
ds4_gpu_q8_0_matvec_args mv_args = ds4_gpu_make_q8_0_mv_args(in_dim, out_dim);
ds4_gpu_mv_dispatch mv_dispatch = ds4_gpu_make_q8_0_mv_dispatch();
if (out_dim > 65536u) mv_dispatch.nsg = 8;
mv_args.nr0 = mv_dispatch.nr0;
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_mul_mv_pipeline(mv_dispatch.function_name, mv_dispatch.nsg);
if (!pipeline) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&mv_args length:sizeof(mv_args) atIndex:0];
[enc setBuffer:wbuf offset:(NSUInteger)inner_offset atIndex:1];
[enc setThreadgroupMemoryLength:mv_dispatch.smem atIndex:0];
const NSUInteger x_base = ds4_gpu_tensor_offset(x);
const NSUInteger out_base = ds4_gpu_tensor_offset(out);
for (uint64_t t = 0; t < n_tok; t++) {
[enc setBuffer:xbuf offset:x_base + (NSUInteger)(t * x_row_bytes) atIndex:2];
[enc setBuffer:outbuf offset:out_base + (NSUInteger)(t * out_row_bytes) atIndex:3];
[enc dispatchThreadgroups:MTLSizeMake(((NSUInteger)out_dim + (NSUInteger)mv_dispatch.nr0 - 1u) /
(NSUInteger)mv_dispatch.nr0,
1,
1)
threadsPerThreadgroup:MTLSizeMake(32, (NSUInteger)mv_dispatch.nsg, 1)];
}
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "Q8_0 scalar-row matvecs")) {
return 0;
}
}
return 1;
}
static int ds4_gpu_matmul_q8_0_pair_impl(
ds4_gpu_tensor *out0,
ds4_gpu_tensor *out1,
const void *model_map,
uint64_t model_size,
uint64_t weight0_offset,
uint64_t weight1_offset,
uint64_t in_dim,
uint64_t out0_dim,
uint64_t out1_dim,
const ds4_gpu_tensor *x,
uint64_t n_tok, bool concat) {
if (concat && (!in_dim || ((out0_dim | out1_dim) & 1u))) return 0;
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!out0 || !out1 || !model_map || !x || n_tok != 1 ||
out0_dim == 0 || out1_dim == 0 || (in_dim & 31u) != 0 ||
in_dim > UINT32_MAX || out0_dim > UINT32_MAX || out1_dim > UINT32_MAX) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> xbuf = ds4_gpu_tensor_buffer(x);
id<MTLBuffer> out0buf = ds4_gpu_tensor_buffer(out0);
id<MTLBuffer> out1buf = ds4_gpu_tensor_buffer(out1);
const uint64_t x_bytes = in_dim * sizeof(float);
const uint64_t out0_bytes = out0_dim * sizeof(float);
const uint64_t out1_bytes = out1_dim * sizeof(float);
if (!xbuf || !out0buf || !out1buf ||
ds4_gpu_tensor_bytes(x) < x_bytes ||
ds4_gpu_tensor_bytes(out0) < out0_bytes ||
ds4_gpu_tensor_bytes(out1) < out1_bytes) {
fprintf(stderr, "ds4: Metal paired Q8_0 matvec received undersized activation buffers\n");
return 0;
}
const uint64_t row_bytes = (in_dim / 32u) * 34u;
const uint64_t weight0_bytes = out0_dim * row_bytes;
const uint64_t weight1_bytes = out1_dim * row_bytes;
if (weight0_offset > model_size || weight0_bytes > model_size - weight0_offset ||
weight1_offset > model_size || weight1_bytes > model_size - weight1_offset) {
fprintf(stderr, "ds4: Metal paired Q8_0 matvec range is outside the mapped model\n");
return 0;
}
uint64_t inner0 = 0;
uint64_t inner1 = 0;
id<MTLBuffer> weight0buf =
ds4_gpu_wrap_model_range(model_map, model_size,
weight0_offset, weight0_bytes, &inner0);
id<MTLBuffer> weight1buf =
ds4_gpu_wrap_model_range(model_map, model_size,
weight1_offset, weight1_bytes, &inner1);
if (!weight0buf || !weight1buf) return 0;
ds4_gpu_mv_dispatch dispatch0 = ds4_gpu_make_q8_0_mv_dispatch();
ds4_gpu_mv_dispatch dispatch1 = ds4_gpu_make_q8_0_mv_dispatch();
if (out0_dim > 65536u) dispatch0.nsg = 8;
if (out1_dim > 65536u) dispatch1.nsg = 8;
/* A common threadgroup shape is required to retain each standalone
* reduction tree. Mixed 4/8-simdgroup extents use the existing fallback. */
if (dispatch0.nsg != dispatch1.nsg) return 0;
ds4_gpu_q8_0_matvec_args args0 = ds4_gpu_make_q8_0_mv_args(in_dim, out0_dim);
ds4_gpu_q8_0_matvec_args args1 = ds4_gpu_make_q8_0_mv_args(in_dim, out1_dim);
args0.nr0 = dispatch0.nr0;
args1.nr0 = dispatch1.nr0;
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_mul_mv_pipeline(concat ? "kernel_qwen4_q8_concat" : "kernel_mul_mv_q8_0_f32_pair", dispatch0.nsg);
if (!pipeline) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args0 length:sizeof(args0) atIndex:0];
[enc setBytes:&args1 length:sizeof(args1) atIndex:1];
[enc setBuffer:weight0buf offset:(NSUInteger)inner0 atIndex:2];
[enc setBuffer:weight1buf offset:(NSUInteger)inner1 atIndex:3];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:4];
[enc setBuffer:out0buf offset:ds4_gpu_tensor_offset(out0) atIndex:5];
[enc setBuffer:out1buf offset:ds4_gpu_tensor_offset(out1) atIndex:6];
[enc setThreadgroupMemoryLength:2u * dispatch0.smem atIndex:0];
const uint64_t max_out_dim = concat ? out0_dim + out1_dim : (out0_dim > out1_dim ? out0_dim : out1_dim);
[enc dispatchThreadgroups:MTLSizeMake(((NSUInteger)max_out_dim +
(NSUInteger)dispatch0.nr0 - 1u) /
(NSUInteger)dispatch0.nr0,
1,
1)
threadsPerThreadgroup:MTLSizeMake(32, (NSUInteger)dispatch0.nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "paired Q8_0 matvec")) return 0;
}
return 1;
}
int ds4_gpu_matmul_q8_0_pair_tensor(
ds4_gpu_tensor *out0,
ds4_gpu_tensor *out1,
const void *model_map,
uint64_t model_size,
uint64_t weight0_offset,
uint64_t weight1_offset,
uint64_t in_dim,
uint64_t out0_dim,
uint64_t out1_dim,
const ds4_gpu_tensor *x,
uint64_t n_tok) {
return ds4_gpu_matmul_q8_0_pair_impl(out0, out1, model_map, model_size, weight0_offset, weight1_offset, in_dim, out0_dim, out1_dim, x, n_tok, false);
}
int ds4_gpu_qwen4_q8_pair_tensor(
ds4_gpu_tensor *out0,
ds4_gpu_tensor *out1,
const void *model_map,
uint64_t model_size,
uint64_t weight0_offset,
uint64_t weight1_offset,
uint64_t in_dim,
uint64_t out0_dim,
uint64_t out1_dim,
const ds4_gpu_tensor *x,
uint64_t n_tok) {
return ds4_gpu_matmul_q8_0_pair_impl(out0, out1, model_map, model_size, weight0_offset, weight1_offset, in_dim, out0_dim, out1_dim, x, n_tok, true);
}
int ds4_gpu_matmul_q8_0_f16_out_tensor(
ds4_gpu_tensor *out_h,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint64_t in_dim,
uint64_t out_dim,
const ds4_gpu_tensor *x,
uint64_t n_tok) {
(void)out_h; (void)model_map; (void)model_size; (void)weight_offset;
(void)in_dim; (void)out_dim; (void)x; (void)n_tok;
return 0;
}
static int ds4_gpu_shared_gate_up_swiglu_q8_0_impl(
ds4_gpu_tensor *gate,
ds4_gpu_tensor *up,
ds4_gpu_tensor *mid,
const void *model_map,
uint64_t model_size,
uint64_t gate_offset,
uint64_t up_offset,
uint64_t in_dim,
uint64_t out_dim,
const ds4_gpu_tensor *x,
float clamp,
int store_gate_up, /* 1: gate and up too; 2: mid rounded to bf16 */
uint32_t rows) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!mid || !x || !model_map || !rows || rows > 65535u ||
(store_gate_up == 1 && (!gate || !up)) ||
(in_dim & 31u) != 0 ||
in_dim > UINT32_MAX || out_dim > UINT32_MAX ||
!isfinite(clamp) || clamp < 0.0f) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> xbuf = ds4_gpu_tensor_buffer(x);
id<MTLBuffer> midbuf = ds4_gpu_tensor_buffer(mid);
id<MTLBuffer> gatebuf = store_gate_up == 1 ?
ds4_gpu_tensor_buffer(gate) : midbuf;
id<MTLBuffer> upbuf = store_gate_up == 1 ?
ds4_gpu_tensor_buffer(up) : midbuf;
const uint64_t x_bytes = rows * in_dim * sizeof(float);
const uint64_t out_bytes = rows * out_dim * sizeof(float);
if (!xbuf || !gatebuf || !upbuf || !midbuf ||
ds4_gpu_tensor_bytes(x) < x_bytes ||
(store_gate_up == 1 && ds4_gpu_tensor_bytes(gate) < out_bytes) ||
(store_gate_up == 1 && ds4_gpu_tensor_bytes(up) < out_bytes) ||
ds4_gpu_tensor_bytes(mid) < out_bytes) {
fprintf(stderr, "ds4: Metal shared expert fused gate/up received undersized activation buffers\n");
return 0;
}
const uint64_t blocks = in_dim / 32;
const uint64_t row_bytes = blocks * 34;
const uint64_t weight_bytes = out_dim * row_bytes;
if (gate_offset > model_size || weight_bytes > model_size - gate_offset ||
up_offset > model_size || weight_bytes > model_size - up_offset) {
fprintf(stderr, "ds4: Metal shared expert fused gate/up range is outside the mapped model\n");
return 0;
}
uint64_t gate_inner = 0;
uint64_t up_inner = 0;
id<MTLBuffer> gate_wbuf = ds4_gpu_wrap_model_range(model_map,
model_size,
gate_offset,
weight_bytes,
&gate_inner);
id<MTLBuffer> up_wbuf = ds4_gpu_wrap_model_range(model_map,
model_size,
up_offset,
weight_bytes,
&up_inner);
if (!gate_wbuf || !up_wbuf) return 0;
ds4_gpu_q8_0_matvec_args args = ds4_gpu_make_q8_0_mv_args(in_dim, out_dim);
ds4_gpu_mv_dispatch mv_dispatch = ds4_gpu_make_q8_0_mv_dispatch();
args.nr0 = mv_dispatch.nr0;
args.ne11 = (int32_t)rows;
args.nb12 = rows * in_dim * sizeof(float);
args.ne1 = (int32_t)rows;
const char *fn_name = store_gate_up == 1 ?
"kernel_dsv4_shared_gate_up_swiglu_q8_0" : store_gate_up == 2 ?
(rows > 1u ? "kernel_dsv4_shared_mid_swiglu_q8_0_bf16_rows" :
"kernel_dsv4_shared_mid_swiglu_q8_0_bf16") :
"kernel_dsv4_shared_mid_swiglu_q8_0";
const int mma_off = ds4_gpu_v41_rows_mma_off();
const bool mma_kernel = !mma_off && store_gate_up == 2 && rows >= 3u && rows <= 8u &&
(in_dim % 256u) == 0 && (out_dim % 8u) == 0;
id<MTLComputePipelineState> pipeline = mma_kernel ?
ds4_gpu_get_pipeline("kernel_dsv4_shared_mid_swiglu_q8_0_bf16_mma_rows8") :
ds4_gpu_get_mul_mv_pipeline(fn_name, mv_dispatch.nsg);
if (!pipeline) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:gate_wbuf offset:(NSUInteger)gate_inner atIndex:1];
[enc setBuffer:up_wbuf offset:(NSUInteger)up_inner atIndex:2];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:3];
[enc setBuffer:gatebuf offset:(store_gate_up == 1 ?
ds4_gpu_tensor_offset(gate) :
ds4_gpu_tensor_offset(mid)) atIndex:4];
[enc setBuffer:upbuf offset:(store_gate_up == 1 ?
ds4_gpu_tensor_offset(up) :
ds4_gpu_tensor_offset(mid)) atIndex:5];
[enc setBuffer:midbuf offset:ds4_gpu_tensor_offset(mid) atIndex:6];
[enc setBytes:&clamp length:sizeof(clamp) atIndex:7];
if (mma_kernel) {
[enc setThreadgroupMemoryLength:(8u * 3u * 8u * 32u + 2u * 8u * 64u) * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)out_dim / 8u, 1, 1)
threadsPerThreadgroup:MTLSizeMake(32, 8, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_finish_command_buffer(cb, owned, "shared expert matrix rows");
}
[enc setThreadgroupMemoryLength:2u * mv_dispatch.smem atIndex:0];
[enc dispatchThreadgroups:(rows > 1u ?
MTLSizeMake((NSUInteger)rows,
((NSUInteger)out_dim + (NSUInteger)mv_dispatch.nr0 - 1u) / (NSUInteger)mv_dispatch.nr0, 1) :
MTLSizeMake(((NSUInteger)out_dim + (NSUInteger)mv_dispatch.nr0 - 1u) / (NSUInteger)mv_dispatch.nr0, 1, 1))
threadsPerThreadgroup:MTLSizeMake(32, (NSUInteger)mv_dispatch.nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb,
owned,
store_gate_up == 1 ?
"shared expert fused gate/up" :
"shared expert fused mid")) {
return 0;
}
}
return 1;
}
/* Decode-only fusion of the router logits matvec with the shared-expert
* gate/up SwiGLU: one dispatch instead of two on the same normalized FFN
* input. Bit-exact by construction (see metal/dense.metal). Returns 1 on
* success, 0 when the shape is unsupported (caller falls back), -1 on
* error. */
int ds4_gpu_router_shared_gate_up_q8_0_tensor(
ds4_gpu_tensor *router_logits,
ds4_gpu_tensor *gate,
ds4_gpu_tensor *up,
ds4_gpu_tensor *mid,
const void *model_map,
uint64_t model_size,
uint64_t router_weight_offset,
uint64_t gate_offset,
uint64_t up_offset,
uint64_t in_dim,
uint64_t router_out_dim,
uint64_t out_dim,
const ds4_gpu_tensor *x,
float clamp,
bool router_only) {
if (!g_initialized && !ds4_gpu_init()) return -1;
if (!router_logits || !gate || !up || !mid || !model_map || !x ||
(in_dim & 31u) != 0 || in_dim > UINT32_MAX ||
router_out_dim > UINT32_MAX || out_dim > UINT32_MAX ||
(router_out_dim & 1u) != 0 || (out_dim & 3u) != 0 ||
!isfinite(clamp) || clamp < 0.0f) {
return -1;
}
if (in_dim != 4096u) return 0;
@autoreleasepool {
const uint64_t router_row_bytes = in_dim * sizeof(uint16_t);
const uint64_t router_weight_bytes = router_row_bytes * router_out_dim;
const uint64_t q8_row_bytes = (in_dim / 32u) * 34u;
const uint64_t q8_weight_bytes = q8_row_bytes * out_dim;
if (router_weight_offset > model_size ||
router_weight_bytes > model_size - router_weight_offset ||
gate_offset > model_size || q8_weight_bytes > model_size - gate_offset ||
up_offset > model_size || q8_weight_bytes > model_size - up_offset) {
return -1;
}
id<MTLBuffer> xbuf = ds4_gpu_tensor_buffer(x);
id<MTLBuffer> rbuf = ds4_gpu_tensor_buffer(router_logits);
id<MTLBuffer> gatebuf = ds4_gpu_tensor_buffer(gate);
id<MTLBuffer> upbuf = ds4_gpu_tensor_buffer(up);
id<MTLBuffer> midbuf = ds4_gpu_tensor_buffer(mid);
if (!xbuf || !rbuf || !gatebuf || !upbuf || !midbuf ||
ds4_gpu_tensor_bytes(x) < in_dim * sizeof(float) ||
ds4_gpu_tensor_bytes(router_logits) < router_out_dim * sizeof(float) ||
ds4_gpu_tensor_bytes(gate) < out_dim * sizeof(float) ||
ds4_gpu_tensor_bytes(up) < out_dim * sizeof(float) ||
ds4_gpu_tensor_bytes(mid) < out_dim * sizeof(float)) {
return -1;
}
uint64_t router_inner = 0, gate_inner = 0, up_inner = 0;
id<MTLBuffer> router_wbuf = ds4_gpu_wrap_model_range(model_map, model_size, router_weight_offset, router_weight_bytes, &router_inner);
id<MTLBuffer> gate_wbuf = ds4_gpu_wrap_model_range(model_map, model_size, gate_offset, q8_weight_bytes, &gate_inner);
id<MTLBuffer> up_wbuf = ds4_gpu_wrap_model_range(model_map, model_size, up_offset, q8_weight_bytes, &up_inner);
if (!router_wbuf || !gate_wbuf || !up_wbuf) return -1;
ds4_gpu_f16_matvec_args rargs = ds4_gpu_make_f16_mv_args(in_dim, router_out_dim);
rargs.nr0 = 2;
ds4_gpu_q8_0_matvec_args sargs = ds4_gpu_make_q8_0_mv_args(in_dim, out_dim);
sargs.nr0 = 2;
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_pipeline("kernel_dsv4_router_shared_gate_up_q8_0");
if (!pipeline) return -1;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return -1;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&rargs length:sizeof(rargs) atIndex:0];
[enc setBytes:&sargs length:sizeof(sargs) atIndex:1];
[enc setBuffer:router_wbuf offset:(NSUInteger)router_inner atIndex:2];
[enc setBuffer:gate_wbuf offset:(NSUInteger)gate_inner atIndex:3];
[enc setBuffer:up_wbuf offset:(NSUInteger)up_inner atIndex:4];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:5];
[enc setBuffer:rbuf offset:ds4_gpu_tensor_offset(router_logits) atIndex:6];
[enc setBuffer:gatebuf offset:ds4_gpu_tensor_offset(gate) atIndex:7];
[enc setBuffer:upbuf offset:ds4_gpu_tensor_offset(up) atIndex:8];
[enc setBuffer:midbuf offset:ds4_gpu_tensor_offset(mid) atIndex:9];
[enc setBytes:&clamp length:sizeof(clamp) atIndex:10];
[enc setThreadgroupMemoryLength:256u * sizeof(float) atIndex:0];
const NSUInteger router_tgs = ((NSUInteger)router_out_dim + 1u) / 2u;
const NSUInteger shared_tgs = ((NSUInteger)out_dim / 2u + 1u) / 2u;
[enc dispatchThreadgroups:MTLSizeMake(router_tgs +
(router_only ? 0u : shared_tgs),
1, 1)
threadsPerThreadgroup:MTLSizeMake(32, 8, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "router + shared gate/up fused")) {
return -1;
}
}
return 1;
}
int ds4_gpu_router_project_select_fused_tensor(
ds4_gpu_tensor *router_logits,
ds4_gpu_tensor *probs,
ds4_gpu_tensor *selected,
ds4_gpu_tensor *weights,
const void *model_map,
uint64_t model_size,
uint64_t router_weight_offset,
uint64_t bias_offset,
bool has_bias,
const ds4_gpu_tensor *x) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!router_logits || !probs || !selected || !weights || !model_map || !x) {
return 0;
}
@autoreleasepool {
const uint32_t in_dim = 4096u;
const uint32_t n_expert = 256u;
const uint32_t n_used = 6u;
const uint64_t row_bytes = (uint64_t)in_dim * sizeof(uint16_t);
const uint64_t weight_bytes = row_bytes * n_expert;
const uint64_t bias_bytes = (uint64_t)n_expert * sizeof(float);
if (router_weight_offset > model_size ||
weight_bytes > model_size - router_weight_offset ||
(has_bias && (bias_offset > model_size ||
bias_bytes > model_size - bias_offset))) {
return 0;
}
id<MTLBuffer> xbuf = ds4_gpu_tensor_buffer(x);
id<MTLBuffer> logitsbuf = ds4_gpu_tensor_buffer(router_logits);
id<MTLBuffer> probsbuf = ds4_gpu_tensor_buffer(probs);
id<MTLBuffer> selectedbuf = ds4_gpu_tensor_buffer(selected);
id<MTLBuffer> weightsbuf = ds4_gpu_tensor_buffer(weights);
if (!xbuf || !logitsbuf || !probsbuf || !selectedbuf || !weightsbuf ||
ds4_gpu_tensor_bytes(x) < (uint64_t)in_dim * sizeof(float) ||
ds4_gpu_tensor_bytes(router_logits) < bias_bytes ||
ds4_gpu_tensor_bytes(probs) < bias_bytes ||
ds4_gpu_tensor_bytes(selected) < (uint64_t)n_used * sizeof(int32_t) ||
ds4_gpu_tensor_bytes(weights) < (uint64_t)n_used * sizeof(float)) {
return 0;
}
uint64_t router_inner = 0;
uint64_t bias_inner = 0;
id<MTLBuffer> routerbuf = ds4_gpu_wrap_model_range(
model_map, model_size, router_weight_offset,
weight_bytes, &router_inner);
id<MTLBuffer> biasbuf = has_bias ? ds4_gpu_wrap_model_range(
model_map, model_size, bias_offset, bias_bytes, &bias_inner) : nil;
if (!routerbuf || (has_bias && !biasbuf)) return 0;
id<MTLComputePipelineState> pipeline = ds4_gpu_get_pipeline(
"kernel_dsv4_router_project_select_fused");
if (!pipeline || pipeline.maxTotalThreadsPerThreadgroup < 256u) return 0;
if (!g_dsv4_completion_cache) return 0;
NSString *completion_key = [NSString stringWithFormat:@"router:%p:%llu",
(void *)logitsbuf,
(unsigned long long)ds4_gpu_tensor_offset(router_logits)];
id<MTLBuffer> completion =
[g_dsv4_completion_cache objectForKey:completion_key];
if (!completion) {
completion = [g_device newBufferWithLength:sizeof(uint32_t)
options:MTLResourceStorageModeShared];
if (!completion) return 0;
*((uint32_t *)[completion contents]) = 0u;
[g_dsv4_completion_cache setObject:completion
forKey:completion_key];
}
[g_transient_buffers addObject:completion];
ds4_gpu_f16_matvec_args rargs =
ds4_gpu_make_f16_mv_args(in_dim, n_expert);
rargs.nr0 = 2;
ds4_gpu_dsv4_router_select_one_args select_args = {
.has_bias = has_bias ? 1u : 0u,
.hash_mode = 0u,
.use_token_buffer = 0u,
.token = 0u,
.hash_rows = 0u,
};
const float zero_f32 = 0.0f;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&rargs length:sizeof(rargs) atIndex:0];
[enc setBytes:&select_args length:sizeof(select_args) atIndex:1];
[enc setBuffer:routerbuf offset:(NSUInteger)router_inner atIndex:2];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:3];
[enc setBuffer:logitsbuf offset:ds4_gpu_tensor_offset(router_logits) atIndex:4];
[enc setBuffer:probsbuf offset:ds4_gpu_tensor_offset(probs) atIndex:5];
if (has_bias) {
[enc setBuffer:biasbuf offset:(NSUInteger)bias_inner atIndex:6];
} else {
[enc setBytes:&zero_f32 length:sizeof(zero_f32) atIndex:6];
}
[enc setBuffer:selectedbuf offset:ds4_gpu_tensor_offset(selected) atIndex:7];
[enc setBuffer:weightsbuf offset:ds4_gpu_tensor_offset(weights) atIndex:8];
[enc setBuffer:completion offset:0 atIndex:9];
[enc setThreadgroupMemoryLength:1024u * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(128, 1, 1)
threadsPerThreadgroup:MTLSizeMake(256, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(
cb, owned, "router project/select fused")) return -1;
}
return 1;
}
int ds4_gpu_shared_gate_up_swiglu_q8_0_tensor(
ds4_gpu_tensor *gate,
ds4_gpu_tensor *up,
ds4_gpu_tensor *mid,
const void *model_map,
uint64_t model_size,
uint64_t gate_offset,
uint64_t up_offset,
uint64_t in_dim,
uint64_t out_dim,
const ds4_gpu_tensor *x,
float clamp) {
return ds4_gpu_shared_gate_up_swiglu_q8_0_impl(gate,
up,
mid,
model_map,
model_size,
gate_offset,
up_offset,
in_dim,
out_dim,
x,
clamp,
1, 1u);
}
int ds4_gpu_shared_mid_swiglu_q8_0_tensor(
ds4_gpu_tensor *mid,
const void *model_map,
uint64_t model_size,
uint64_t gate_offset,
uint64_t up_offset,
uint64_t in_dim,
uint64_t out_dim,
const ds4_gpu_tensor *x,
float clamp) {
return ds4_gpu_shared_gate_up_swiglu_q8_0_impl(NULL,
NULL,
mid,
model_map,
model_size,
gate_offset,
up_offset,
in_dim,
out_dim,
x,
clamp,
0, 1u);
}
int ds4_gpu_dsv41_shared_swiglu(ds4_gpu_tensor *mid, ds4_gpu_tensor *gate, ds4_gpu_tensor *up,
const void *model_map, uint64_t model_size, uint64_t gate_offset,
uint64_t up_offset, uint32_t in_dim, uint32_t out_dim,
const ds4_gpu_tensor *x, float clamp, uint32_t rows) {
(void)gate; (void)up;
return ds4_gpu_shared_gate_up_swiglu_q8_0_impl(NULL, NULL, mid, model_map, model_size,
gate_offset, up_offset, in_dim, out_dim,
x, clamp, 2, rows);
}
int ds4_gpu_shared_gate_up_swiglu_q8_0_model_view_tensor(
ds4_gpu_tensor *gate,
ds4_gpu_tensor *up,
ds4_gpu_tensor *mid,
const void *model_map,
uint64_t model_size,
uint64_t gate_offset,
uint64_t up_offset,
uint64_t in_dim,
uint64_t out_dim,
const ds4_gpu_tensor *x,
float clamp) {
return ds4_gpu_shared_gate_up_swiglu_q8_0_impl(gate,
up,
mid,
model_map,
model_size,
gate_offset,
up_offset,
in_dim,
out_dim,
x,
clamp,
1, 1u);
}
int ds4_gpu_shared_gate_up_swiglu_q8_0_rows_tensor(
ds4_gpu_tensor *gate,
ds4_gpu_tensor *up,
ds4_gpu_tensor *mid,
const void *model_map,
uint64_t model_size,
uint64_t gate_offset,
uint64_t up_offset,
uint64_t in_dim,
uint64_t out_dim,
const ds4_gpu_tensor *x,
uint64_t n_tok,
float clamp) {
if (n_tok == 1) {
return ds4_gpu_shared_gate_up_swiglu_q8_0_tensor(gate,
up,
mid,
model_map,
model_size,
gate_offset,
up_offset,
in_dim,
out_dim,
x,
clamp);
}
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!gate || !up || !mid || !x || !model_map ||
n_tok == 0 ||
(in_dim & 31u) != 0 || (in_dim % 128u) != 0 ||
in_dim > UINT32_MAX || out_dim > UINT32_MAX || n_tok > UINT32_MAX ||
!isfinite(clamp) || clamp < 0.0f) {
return 0;
}
const uint64_t mv_ext_max_tokens =
ds4_gpu_env_u64("DS4_METAL_Q8_MV_EXT_MAX_TOKENS", 16u, 2u, 128u);
if (n_tok > mv_ext_max_tokens) return 0;
@autoreleasepool {
id<MTLBuffer> xbuf = ds4_gpu_tensor_buffer(x);
id<MTLBuffer> gatebuf = ds4_gpu_tensor_buffer(gate);
id<MTLBuffer> upbuf = ds4_gpu_tensor_buffer(up);
id<MTLBuffer> midbuf = ds4_gpu_tensor_buffer(mid);
const uint64_t x_bytes = n_tok * in_dim * sizeof(float);
const uint64_t out_bytes = n_tok * out_dim * sizeof(float);
if (!xbuf || !gatebuf || !upbuf || !midbuf ||
ds4_gpu_tensor_bytes(x) < x_bytes ||
ds4_gpu_tensor_bytes(gate) < out_bytes ||
ds4_gpu_tensor_bytes(up) < out_bytes ||
ds4_gpu_tensor_bytes(mid) < out_bytes) {
fprintf(stderr, "ds4: Metal fused Q8_0 gate/up rows received undersized activation buffers\n");
return 0;
}
const uint64_t blocks = in_dim / 32;
const uint64_t row_bytes = blocks * 34;
const uint64_t weight_bytes = out_dim * row_bytes;
if (gate_offset > model_size || weight_bytes > model_size - gate_offset ||
up_offset > model_size || weight_bytes > model_size - up_offset) {
fprintf(stderr, "ds4: Metal fused Q8_0 gate/up rows range is outside the mapped model\n");
return 0;
}
uint64_t gate_inner = 0;
uint64_t up_inner = 0;
id<MTLBuffer> gate_wbuf =
ds4_gpu_wrap_model_range(model_map, model_size, gate_offset, weight_bytes, &gate_inner);
id<MTLBuffer> up_wbuf =
ds4_gpu_wrap_model_range(model_map, model_size, up_offset, weight_bytes, &up_inner);
if (!gate_wbuf || !up_wbuf) return 0;
const int16_t nsg = 2;
const int16_t nxpsg = ds4_gpu_mv_ext_nxpsg(in_dim, n_tok);
const int16_t r1ptg = ds4_gpu_mv_ext_r1ptg(n_tok);
const char *fn_name = ds4_gpu_mv_ext_q8_pair_swiglu_name(r1ptg);
id<MTLComputePipelineState> pipeline =
fn_name ? ds4_gpu_get_mul_mv_ext_pipeline(fn_name, nsg, nxpsg) : nil;
if (!pipeline) return 0;
const int16_t nypsg = 32 / nxpsg;
const uint64_t r0ptg = (uint64_t)nypsg * (uint64_t)nsg;
ds4_gpu_mul_mv_ext_args args =
ds4_gpu_make_mv_ext_args(in_dim, out_dim, n_tok, 34, row_bytes);
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:gate_wbuf offset:(NSUInteger)gate_inner atIndex:1];
[enc setBuffer:up_wbuf offset:(NSUInteger)up_inner atIndex:2];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:3];
[enc setBuffer:gatebuf offset:ds4_gpu_tensor_offset(gate) atIndex:4];
[enc setBuffer:upbuf offset:ds4_gpu_tensor_offset(up) atIndex:5];
[enc setBuffer:midbuf offset:ds4_gpu_tensor_offset(mid) atIndex:6];
[enc setBytes:&clamp length:sizeof(clamp) atIndex:7];
[enc dispatchThreadgroups:MTLSizeMake(((NSUInteger)out_dim + (NSUInteger)r0ptg - 1u) /
(NSUInteger)r0ptg,
((NSUInteger)n_tok + (NSUInteger)r1ptg - 1u) /
(NSUInteger)r1ptg,
1)
threadsPerThreadgroup:MTLSizeMake(32, (NSUInteger)nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "fused Q8_0 gate/up rows")) {
return 0;
}
}
return 1;
}
int ds4_gpu_shared_gate_up_swiglu_q8_0_rows_scalar_tensor(
ds4_gpu_tensor *gate,
ds4_gpu_tensor *up,
ds4_gpu_tensor *mid,
const void *model_map,
uint64_t model_size,
uint64_t gate_offset,
uint64_t up_offset,
uint64_t in_dim,
uint64_t out_dim,
const ds4_gpu_tensor *x,
uint64_t n_tok,
float clamp) {
if (n_tok == 1) {
return ds4_gpu_shared_gate_up_swiglu_q8_0_tensor(gate,
up,
mid,
model_map,
model_size,
gate_offset,
up_offset,
in_dim,
out_dim,
x,
clamp);
}
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!gate || !up || !mid || !x || !model_map ||
n_tok == 0 ||
(in_dim & 31u) != 0 ||
in_dim > UINT32_MAX || out_dim > UINT32_MAX || n_tok > UINT32_MAX ||
in_dim > UINT64_MAX / sizeof(float) ||
out_dim > UINT64_MAX / sizeof(float) ||
!isfinite(clamp) || clamp < 0.0f) {
return 0;
}
const uint64_t x_row_bytes = in_dim * sizeof(float);
const uint64_t out_row_bytes = out_dim * sizeof(float);
if ((x_row_bytes != 0 && n_tok > UINT64_MAX / x_row_bytes) ||
(out_row_bytes != 0 && n_tok > UINT64_MAX / out_row_bytes)) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> xbuf = ds4_gpu_tensor_buffer(x);
id<MTLBuffer> gatebuf = ds4_gpu_tensor_buffer(gate);
id<MTLBuffer> upbuf = ds4_gpu_tensor_buffer(up);
id<MTLBuffer> midbuf = ds4_gpu_tensor_buffer(mid);
const uint64_t x_bytes = n_tok * x_row_bytes;
const uint64_t out_bytes = n_tok * out_row_bytes;
if (!xbuf || !gatebuf || !upbuf || !midbuf ||
ds4_gpu_tensor_bytes(x) < x_bytes ||
ds4_gpu_tensor_bytes(gate) < out_bytes ||
ds4_gpu_tensor_bytes(up) < out_bytes ||
ds4_gpu_tensor_bytes(mid) < out_bytes) {
fprintf(stderr, "ds4: Metal shared expert scalar-row fused gate/up received undersized activation buffers\n");
return 0;
}
const uint64_t blocks = in_dim / 32;
if (blocks > UINT64_MAX / 34u) return 0;
const uint64_t row_bytes = blocks * 34u;
if (out_dim > UINT64_MAX / row_bytes) return 0;
const uint64_t weight_bytes = out_dim * row_bytes;
if (gate_offset > model_size || weight_bytes > model_size - gate_offset ||
up_offset > model_size || weight_bytes > model_size - up_offset) {
fprintf(stderr, "ds4: Metal shared expert scalar-row fused gate/up range is outside the mapped model\n");
return 0;
}
uint64_t gate_inner = 0;
uint64_t up_inner = 0;
id<MTLBuffer> gate_wbuf =
ds4_gpu_wrap_model_range(model_map, model_size, gate_offset, weight_bytes, &gate_inner);
id<MTLBuffer> up_wbuf =
ds4_gpu_wrap_model_range(model_map, model_size, up_offset, weight_bytes, &up_inner);
if (!gate_wbuf || !up_wbuf) return 0;
ds4_gpu_q8_0_matvec_args args = ds4_gpu_make_q8_0_mv_args(in_dim, out_dim);
ds4_gpu_mv_dispatch mv_dispatch = ds4_gpu_make_q8_0_mv_dispatch();
args.nr0 = mv_dispatch.nr0;
args.ne11 = (int32_t)n_tok;
args.nb12 = n_tok * x_row_bytes;
args.nb13 = args.nb12;
args.ne1 = (int32_t)n_tok;
const char *fn_name = "kernel_dsv4_shared_gate_up_swiglu_q8_0";
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_mul_mv_pipeline(fn_name, mv_dispatch.nsg);
if (!pipeline) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:gate_wbuf offset:(NSUInteger)gate_inner atIndex:1];
[enc setBuffer:up_wbuf offset:(NSUInteger)up_inner atIndex:2];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:3];
[enc setBuffer:gatebuf offset:ds4_gpu_tensor_offset(gate) atIndex:4];
[enc setBuffer:upbuf offset:ds4_gpu_tensor_offset(up) atIndex:5];
[enc setBuffer:midbuf offset:ds4_gpu_tensor_offset(mid) atIndex:6];
[enc setBytes:&clamp length:sizeof(clamp) atIndex:7];
[enc setThreadgroupMemoryLength:2u * mv_dispatch.smem atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(
((NSUInteger)out_dim + (NSUInteger)mv_dispatch.nr0 - 1u) /
(NSUInteger)mv_dispatch.nr0,
(NSUInteger)n_tok,
1)
threadsPerThreadgroup:
MTLSizeMake(32, (NSUInteger)mv_dispatch.nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "shared expert scalar-row fused gate/up")) {
return 0;
}
}
return 1;
}
static int ds4_gpu_matmul_f16_tensor_impl(
ds4_gpu_tensor *out,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint64_t in_dim,
uint64_t out_dim,
const ds4_gpu_tensor *x,
uint64_t n_tok,
bool exact_rows,
int round_bf16) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (in_dim > UINT32_MAX || out_dim > UINT32_MAX || n_tok > UINT32_MAX) return 0;
@autoreleasepool {
id<MTLBuffer> xbuf = ds4_gpu_tensor_buffer(x);
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out);
const uint64_t x_bytes = n_tok * in_dim * sizeof(float);
const uint64_t out_bytes = n_tok * out_dim * sizeof(float);
if (!xbuf || !outbuf ||
ds4_gpu_tensor_bytes(x) < x_bytes ||
ds4_gpu_tensor_bytes(out) < out_bytes) {
fprintf(stderr, "ds4: Metal F16 tensor matmul received undersized activation buffers\n");
return 0;
}
const uint64_t row_bytes = in_dim * sizeof(uint16_t);
const uint64_t weight_bytes = row_bytes * out_dim;
if (weight_offset > model_size || weight_bytes > model_size - weight_offset) {
fprintf(stderr, "ds4: Metal F16 tensor matmul range is outside the mapped model\n");
return 0;
}
uint64_t inner_offset = 0;
id<MTLBuffer> wbuf =
ds4_gpu_wrap_model_range(model_map,
model_size,
weight_offset,
weight_bytes,
&inner_offset);
if (!wbuf) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
if (n_tok == 1 || exact_rows) {
ds4_gpu_f16_matvec_args mv_args = ds4_gpu_make_f16_mv_args(in_dim, out_dim);
mv_args.ne11 = mv_args.ne1 = (int32_t)n_tok;
mv_args.nb12 = mv_args.nb13 = n_tok * in_dim * sizeof(float);
ds4_gpu_mv_dispatch mv_dispatch =
ds4_gpu_make_plain_mv_dispatch(in_dim, 0);
if (!g_quality_mode && (out_dim == 512u || out_dim == 1024u) && in_dim >= 4096u) {
mv_dispatch.nr0 = 4;
mv_dispatch.smem = 32u * 4u * sizeof(float);
}
/* One row per SIMD group doubles the threadgroup count of narrow
* projections; every row keeps its K walk and reduction tree. */
if (ds4_gpu_plain_mv_single_row(out_dim)) {
mv_dispatch.nr0 = 1;
mv_dispatch.smem = 32u * sizeof(float);
}
mv_args.nr0 = mv_dispatch.nr0;
/* a few rows share the weight pass: one simdgroup group per row,
* at most 32 simdgroups per threadgroup, so more rows go in chunks */
const uint32_t chunk = mv_dispatch.smem && (in_dim % 4u) == 0 && mv_dispatch.nsg >= 1 && mv_dispatch.nsg <= 16 ?
32u / (uint32_t)mv_dispatch.nsg : 1u;
const bool rows_kernel = n_tok >= 2 && n_tok <= 8 && chunk >= 2u;
const char *fn_name = rows_kernel ?
(round_bf16 ? "kernel_mul_mv_f16_f32_4_rows_bf16" : "kernel_mul_mv_f16_f32_4_rows") : mv_dispatch.function_name;
if (round_bf16 && !rows_kernel) { /* only the vectorized kernels round in place */
if (strcmp(fn_name, "kernel_mul_mv_f16_f32_4") != 0) return 0;
fn_name = "kernel_mul_mv_f16_f32_4_bf16";
}
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_mul_mv_pipeline(fn_name, mv_dispatch.nsg);
if (!pipeline) return 0;
for (uint64_t r = 0; r < n_tok; r += rows_kernel ? chunk : n_tok) {
const NSUInteger groups = rows_kernel ? (NSUInteger)(n_tok - r < chunk ? n_tok - r : chunk) : 1u;
if (rows_kernel) mv_args.ne11 = mv_args.ne1 = (int32_t)groups;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&mv_args length:sizeof(mv_args) atIndex:0];
[enc setBuffer:wbuf offset:(NSUInteger)inner_offset atIndex:1];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) + (NSUInteger)(r * in_dim * sizeof(float)) atIndex:2];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(out) + (NSUInteger)(r * out_dim * sizeof(float)) atIndex:3];
if (mv_dispatch.smem) {
[enc setThreadgroupMemoryLength:mv_dispatch.smem * groups atIndex:0];
}
[enc dispatchThreadgroups:MTLSizeMake(((NSUInteger)out_dim + (NSUInteger)mv_dispatch.nr0 - 1u) / (NSUInteger)mv_dispatch.nr0,
rows_kernel ? 1u : (NSUInteger)n_tok,
1)
threadsPerThreadgroup:MTLSizeMake(32, (NSUInteger)mv_dispatch.nsg * groups, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
}
if (!ds4_gpu_finish_command_buffer(cb, owned, "F16 tensor matvec")) return 0;
return 1;
}
if (n_tok <= 8 && (in_dim % 128u) == 0) {
const int16_t nsg = ds4_gpu_mv_ext_nsg();
const int16_t nxpsg = ds4_gpu_mv_ext_nxpsg(in_dim, n_tok);
const int16_t r1ptg = ds4_gpu_mv_ext_r1ptg(n_tok);
const char *fn_name = ds4_gpu_mv_ext_name(0, r1ptg);
id<MTLComputePipelineState> pipeline =
fn_name ? ds4_gpu_get_mul_mv_ext_pipeline(fn_name, nsg, nxpsg) : nil;
if (!pipeline) return 0;
const int16_t nypsg = 32 / nxpsg;
const uint64_t r0ptg = (uint64_t)nypsg * (uint64_t)nsg;
ds4_gpu_mul_mv_ext_args args =
ds4_gpu_make_mv_ext_args(in_dim, out_dim, n_tok, sizeof(uint16_t), row_bytes);
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:wbuf offset:(NSUInteger)inner_offset atIndex:1];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:2];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(out) atIndex:3];
[enc dispatchThreadgroups:MTLSizeMake(((NSUInteger)out_dim + (NSUInteger)r0ptg - 1u) / (NSUInteger)r0ptg,
((NSUInteger)n_tok + (NSUInteger)r1ptg - 1u) / (NSUInteger)r1ptg,
1)
threadsPerThreadgroup:MTLSizeMake(32, (NSUInteger)nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "F16 tensor mul_mv_ext")) return 0;
return 1;
}
/*
* Same direct-RHS TensorOps structure as Q8_0, but for F16 model
* matrices. The 128-token RHS tile is kept when the batch alignment
* allows it because the later tile_n=64 retest was neutral/slower.
*/
if (ds4_gpu_mpp_available() &&
n_tok >= 32u &&
(in_dim % 32u) == 0 &&
(out_dim % 64u) == 0 &&
(n_tok % 32u) == 0) {
uint64_t nax_tile_n = 32u;
if ((n_tok % 128u) == 0) {
nax_tile_n = 128u;
} else if ((n_tok % 64u) == 0) {
nax_tile_n = 64u;
}
const char *nax_fn = nax_tile_n == 128u
? "kernel_mul_mm_f16_f32_mpp_direct_rhs_n128"
: (nax_tile_n == 64u
? "kernel_mul_mm_f16_f32_mpp_direct_rhs_n64"
: "kernel_mul_mm_f16_f32_mpp_direct_rhs");
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_mul_mm_pipeline(nax_fn, false, false);
if (pipeline) {
ds4_gpu_mul_mm_args args = ds4_gpu_make_mm_args(in_dim, out_dim, n_tok, row_bytes);
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:wbuf offset:(NSUInteger)inner_offset atIndex:1];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:2];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(out) atIndex:3];
[enc setThreadgroupMemoryLength:2u * 64u * 32u * sizeof(uint16_t) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)(n_tok / nax_tile_n),
(NSUInteger)out_dim / 64u,
1)
threadsPerThreadgroup:MTLSizeMake(128, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "F16 NAX tensor matmul")) {
return 0;
}
return 1;
}
ds4_gpu_warn_mpp_fallback();
}
const bool bc_inp = (in_dim % 32u) != 0;
const bool bc_out = (out_dim % 64u) != 0 || (n_tok % 32u) != 0;
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_mul_mm_pipeline("kernel_mul_mm_f16_f32", bc_inp, bc_out);
if (!pipeline) return 0;
ds4_gpu_mul_mm_args args = ds4_gpu_make_mm_args(in_dim, out_dim, n_tok, row_bytes);
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:wbuf offset:(NSUInteger)inner_offset atIndex:1];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:2];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(out) atIndex:3];
[enc setThreadgroupMemoryLength:(bc_out ? 8192u : 6144u) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(((NSUInteger)n_tok + 31u) / 32u,
((NSUInteger)out_dim + 63u) / 64u,
1)
threadsPerThreadgroup:MTLSizeMake(128, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "F16 tensor matmul")) return 0;
}
return 1;
}
int ds4_gpu_matmul_f16_tensor(ds4_gpu_tensor *out,
const void *model_map, uint64_t model_size,
uint64_t weight_offset, uint64_t in_dim,
uint64_t out_dim, const ds4_gpu_tensor *x,
uint64_t n_tok) {
return ds4_gpu_matmul_f16_tensor_impl(out, model_map, model_size,
weight_offset, in_dim, out_dim, x, n_tok, false, 0);
}
int ds4_gpu_dsv41_project_f16_bf16(ds4_gpu_tensor *out, const void *model_map, uint64_t model_size,
uint64_t weight_offset, uint32_t in_dim, uint32_t out_dim,
const ds4_gpu_tensor *x) {
return ds4_gpu_matmul_f16_tensor_impl(out, model_map, model_size, weight_offset,
in_dim, out_dim, x, 1, false, 1);
}
int ds4_gpu_dsv41_projection_rows(ds4_gpu_tensor *out,
const void *model_map, uint64_t model_size,
uint64_t weight_offset, uint32_t width,
uint32_t outputs, uint32_t rows,
const ds4_gpu_tensor *in) {
if (!width || !outputs || !rows || rows > 8192u || !model_map) return 0;
return ds4_gpu_matmul_f16_tensor_impl(out, model_map, model_size,
weight_offset, width, outputs, in, rows, true, 0);
}
int ds4_gpu_matmul_f16_pair_tensor(
ds4_gpu_tensor *out_a,
ds4_gpu_tensor *out_b,
const void *model_map,
uint64_t model_size,
uint64_t weight_a_offset,
uint64_t weight_b_offset,
uint64_t in_dim,
uint64_t out_dim,
const ds4_gpu_tensor *x,
uint64_t n_tok) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (in_dim > UINT32_MAX || out_dim > UINT32_MAX || n_tok != 1 || (in_dim & 3u) != 0) return 0;
@autoreleasepool {
id<MTLBuffer> xbuf = ds4_gpu_tensor_buffer(x);
id<MTLBuffer> outabuf = ds4_gpu_tensor_buffer(out_a);
id<MTLBuffer> outbbuf = ds4_gpu_tensor_buffer(out_b);
const uint64_t x_bytes = in_dim * sizeof(float);
const uint64_t out_bytes = out_dim * sizeof(float);
if (!xbuf || !outabuf || !outbbuf ||
ds4_gpu_tensor_bytes(x) < x_bytes ||
ds4_gpu_tensor_bytes(out_a) < out_bytes ||
ds4_gpu_tensor_bytes(out_b) < out_bytes) {
fprintf(stderr, "ds4: Metal F16 paired matvec received undersized activation buffers\n");
return 0;
}
const uint64_t row_bytes = in_dim * sizeof(uint16_t);
const uint64_t weight_bytes = row_bytes * out_dim;
if (weight_a_offset > model_size || weight_bytes > model_size - weight_a_offset ||
weight_b_offset > model_size || weight_bytes > model_size - weight_b_offset) {
fprintf(stderr, "ds4: Metal F16 paired matvec range is outside the mapped model\n");
return 0;
}
uint64_t inner_a = 0;
uint64_t inner_b = 0;
id<MTLBuffer> wabuf = ds4_gpu_wrap_model_range(model_map, model_size,
weight_a_offset, weight_bytes,
&inner_a);
id<MTLBuffer> wbbuf = ds4_gpu_wrap_model_range(model_map, model_size,
weight_b_offset, weight_bytes,
&inner_b);
if (!wabuf || !wbbuf) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
ds4_gpu_f16_matvec_args mv_args = ds4_gpu_make_f16_mv_args(in_dim, out_dim);
ds4_gpu_mv_dispatch mv_dispatch = ds4_gpu_make_plain_mv_dispatch(in_dim, 0);
if (ds4_gpu_use_compressor_pair_nr4() &&
(out_dim == 512u || out_dim == 1024u) && in_dim >= 4096u) {
mv_dispatch.nr0 = 4;
mv_dispatch.smem = 32u * 4u * sizeof(float);
}
mv_args.nr0 = mv_dispatch.nr0;
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_mul_mv_pipeline("kernel_mul_mv_f16_f32_pair_4", mv_dispatch.nsg);
if (!pipeline) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&mv_args length:sizeof(mv_args) atIndex:0];
[enc setBuffer:wabuf offset:(NSUInteger)inner_a atIndex:1];
[enc setBuffer:wbbuf offset:(NSUInteger)inner_b atIndex:2];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:3];
[enc setBuffer:outabuf offset:ds4_gpu_tensor_offset(out_a) atIndex:4];
[enc setBuffer:outbbuf offset:ds4_gpu_tensor_offset(out_b) atIndex:5];
if (mv_dispatch.smem) {
[enc setThreadgroupMemoryLength:mv_dispatch.smem atIndex:0];
}
[enc dispatchThreadgroups:MTLSizeMake(((NSUInteger)out_dim + (NSUInteger)mv_dispatch.nr0 - 1u) / (NSUInteger)mv_dispatch.nr0,
1,
1)
threadsPerThreadgroup:MTLSizeMake(32, (NSUInteger)mv_dispatch.nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "F16 paired matvec")) return 0;
}
return 1;
}
int ds4_gpu_matmul_f16_pair_compressor_store_tensor(
ds4_gpu_tensor *out_kv,
ds4_gpu_tensor *out_score,
ds4_gpu_tensor *state_kv,
ds4_gpu_tensor *state_score,
const void *model_map,
uint64_t model_size,
uint64_t weight_kv_offset,
uint64_t weight_score_offset,
uint64_t ape_offset,
uint32_t ape_type,
uint64_t in_dim,
uint32_t width,
const ds4_gpu_tensor *x,
uint32_t ratio,
uint32_t pos) {
if (!g_initialized && !ds4_gpu_init()) return -1;
if ((g_quality_mode ||
(!ds4_gpu_device_name_contains("M3") &&
!ds4_gpu_device_name_contains("M5"))) ||
getenv("DS4_METAL_DISABLE_COMPRESSOR_PAIR_PROJ") != NULL ||
getenv("DS4_METAL_DISABLE_COMPRESSOR_STORE_ONE") != NULL) {
return 0;
}
if (!out_kv || !out_score || !state_kv || !state_score || !model_map || !x ||
in_dim == 0 || width == 0 || ratio == 0 ||
(ape_type != 0u && ape_type != 1u)) {
return -1;
}
if (in_dim != 4096u ||
(width != 256u && width != 512u && width != 1024u) ||
(ratio != 4u && ratio != 128u)) {
return 0;
}
@autoreleasepool {
const uint32_t state_rows = ratio == 4u ? 2u * ratio : ratio;
const uint64_t row_bytes = in_dim * sizeof(uint16_t);
const uint64_t weight_bytes = row_bytes * width;
const uint64_t out_bytes = (uint64_t)width * sizeof(float);
const uint64_t state_bytes =
(uint64_t)state_rows * width * sizeof(float);
const uint64_t ape_elem = ape_type == 1u ? sizeof(uint16_t) : sizeof(float);
const uint64_t ape_bytes = (uint64_t)ratio * width * ape_elem;
if (weight_kv_offset > model_size ||
weight_bytes > model_size - weight_kv_offset ||
weight_score_offset > model_size ||
weight_bytes > model_size - weight_score_offset ||
ape_offset > model_size || ape_bytes > model_size - ape_offset) {
return -1;
}
id<MTLBuffer> xbuf = ds4_gpu_tensor_buffer(x);
id<MTLBuffer> outkvbuf = ds4_gpu_tensor_buffer(out_kv);
id<MTLBuffer> outscorebuf = ds4_gpu_tensor_buffer(out_score);
id<MTLBuffer> statekvbuf = ds4_gpu_tensor_buffer(state_kv);
id<MTLBuffer> statescbuf = ds4_gpu_tensor_buffer(state_score);
if (!xbuf || !outkvbuf || !outscorebuf || !statekvbuf || !statescbuf ||
ds4_gpu_tensor_bytes(x) < in_dim * sizeof(float) ||
ds4_gpu_tensor_bytes(out_kv) < out_bytes ||
ds4_gpu_tensor_bytes(out_score) < out_bytes ||
ds4_gpu_tensor_bytes(state_kv) < state_bytes ||
ds4_gpu_tensor_bytes(state_score) < state_bytes) {
return -1;
}
uint64_t weight_kv_inner = 0;
uint64_t weight_score_inner = 0;
uint64_t ape_inner = 0;
id<MTLBuffer> weightkvbuf = ds4_gpu_wrap_model_range(
model_map, model_size, weight_kv_offset, weight_bytes,
&weight_kv_inner);
id<MTLBuffer> weightscorebuf = ds4_gpu_wrap_model_range(
model_map, model_size, weight_score_offset, weight_bytes,
&weight_score_inner);
id<MTLBuffer> apebuf = ds4_gpu_wrap_model_range(
model_map, model_size, ape_offset, ape_bytes, &ape_inner);
if (!weightkvbuf || !weightscorebuf || !apebuf) return -1;
ds4_gpu_f16_matvec_args mv_args =
ds4_gpu_make_f16_mv_args(in_dim, width);
ds4_gpu_mv_dispatch mv_dispatch =
ds4_gpu_make_plain_mv_dispatch(in_dim, 0);
if (ds4_gpu_use_compressor_pair_nr4() &&
(width == 512u || width == 1024u) && in_dim >= 4096u) {
mv_dispatch.nr0 = 4;
mv_dispatch.smem = 32u * 4u * sizeof(float);
}
mv_args.nr0 = mv_dispatch.nr0;
ds4_gpu_dsv4_compressor_store_one_args store_args = {
.width = width,
.ratio = ratio,
.pos = pos,
.ape_type = ape_type,
};
id<MTLComputePipelineState> pipeline = ds4_gpu_get_mul_mv_pipeline(
"kernel_mul_mv_f16_f32_pair_compressor_store_4",
mv_dispatch.nsg);
if (!pipeline) return -1;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return -1;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&mv_args length:sizeof(mv_args) atIndex:0];
[enc setBytes:&store_args length:sizeof(store_args) atIndex:1];
[enc setBuffer:weightkvbuf offset:(NSUInteger)weight_kv_inner atIndex:2];
[enc setBuffer:weightscorebuf offset:(NSUInteger)weight_score_inner atIndex:3];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:4];
[enc setBuffer:outkvbuf offset:ds4_gpu_tensor_offset(out_kv) atIndex:5];
[enc setBuffer:outscorebuf offset:ds4_gpu_tensor_offset(out_score) atIndex:6];
[enc setBuffer:apebuf offset:(NSUInteger)ape_inner atIndex:7];
[enc setBuffer:statekvbuf offset:ds4_gpu_tensor_offset(state_kv) atIndex:8];
[enc setBuffer:statescbuf offset:ds4_gpu_tensor_offset(state_score) atIndex:9];
if (mv_dispatch.smem) {
[enc setThreadgroupMemoryLength:mv_dispatch.smem atIndex:0];
}
[enc dispatchThreadgroups:MTLSizeMake(
((NSUInteger)width + (NSUInteger)mv_dispatch.nr0 - 1u) /
(NSUInteger)mv_dispatch.nr0,
1,
1)
threadsPerThreadgroup:MTLSizeMake(
32, (NSUInteger)mv_dispatch.nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(
cb, owned, "F16 paired matvec compressor state store")) {
return -1;
}
}
return 1;
}
/* Quad variant of the paired compressor projection: the attention compressor
* and indexer compressor pairs share the input activation and F16 matvec
* shape, so one dispatch covers all four matrices. Bit-exact by
* construction (see metal/dense.metal). Returns 1 when the fused dispatch
* ran, 0 when the caller should use the separate paths, -1 on error. */
int ds4_gpu_matmul_f16_quad_compressor_store_tensor(
ds4_gpu_tensor *out0_kv,
ds4_gpu_tensor *out0_score,
ds4_gpu_tensor *out1_kv,
ds4_gpu_tensor *out1_score,
ds4_gpu_tensor *state0_kv,
ds4_gpu_tensor *state0_score,
ds4_gpu_tensor *state1_kv,
ds4_gpu_tensor *state1_score,
const void *model_map,
uint64_t model_size,
uint64_t weight0_kv_offset,
uint64_t weight0_score_offset,
uint64_t weight1_kv_offset,
uint64_t weight1_score_offset,
uint64_t ape0_offset,
uint32_t ape0_type,
uint64_t ape1_offset,
uint32_t ape1_type,
uint64_t in_dim,
uint32_t width0,
uint32_t width1,
const ds4_gpu_tensor *x,
uint32_t ratio,
uint32_t pos) {
if (!g_initialized && !ds4_gpu_init()) return -1;
if (!out0_kv || !out0_score || !out1_kv || !out1_score ||
!state0_kv || !state0_score || !state1_kv || !state1_score ||
!model_map || !x ||
(ape0_type != 0u && ape0_type != 1u) ||
(ape1_type != 0u && ape1_type != 1u)) {
return -1;
}
if (in_dim != 4096u ||
(width0 != 512u && width0 != 1024u) ||
width1 != 256u || ratio != 4u) {
return 0;
}
@autoreleasepool {
const uint32_t state_rows = 2u * ratio;
const uint64_t row_bytes = in_dim * sizeof(uint16_t);
const uint64_t weight0_bytes = row_bytes * width0;
const uint64_t weight1_bytes = row_bytes * width1;
const uint64_t ape0_elem = ape0_type == 1u ? sizeof(uint16_t) : sizeof(float);
const uint64_t ape1_elem = ape1_type == 1u ? sizeof(uint16_t) : sizeof(float);
const uint64_t ape0_bytes = (uint64_t)ratio * width0 * ape0_elem;
const uint64_t ape1_bytes = (uint64_t)ratio * width1 * ape1_elem;
if (weight0_kv_offset > model_size || weight0_bytes > model_size - weight0_kv_offset ||
weight0_score_offset > model_size || weight0_bytes > model_size - weight0_score_offset ||
weight1_kv_offset > model_size || weight1_bytes > model_size - weight1_kv_offset ||
weight1_score_offset > model_size || weight1_bytes > model_size - weight1_score_offset ||
ape0_offset > model_size || ape0_bytes > model_size - ape0_offset ||
ape1_offset > model_size || ape1_bytes > model_size - ape1_offset) {
return -1;
}
id<MTLBuffer> xbuf = ds4_gpu_tensor_buffer(x);
id<MTLBuffer> out0kvbuf = ds4_gpu_tensor_buffer(out0_kv);
id<MTLBuffer> out0scbuf = ds4_gpu_tensor_buffer(out0_score);
id<MTLBuffer> out1kvbuf = ds4_gpu_tensor_buffer(out1_kv);
id<MTLBuffer> out1scbuf = ds4_gpu_tensor_buffer(out1_score);
id<MTLBuffer> state0kvbuf = ds4_gpu_tensor_buffer(state0_kv);
id<MTLBuffer> state0scbuf = ds4_gpu_tensor_buffer(state0_score);
id<MTLBuffer> state1kvbuf = ds4_gpu_tensor_buffer(state1_kv);
id<MTLBuffer> state1scbuf = ds4_gpu_tensor_buffer(state1_score);
if (!xbuf || !out0kvbuf || !out0scbuf || !out1kvbuf || !out1scbuf ||
!state0kvbuf || !state0scbuf || !state1kvbuf || !state1scbuf ||
ds4_gpu_tensor_bytes(x) < in_dim * sizeof(float) ||
ds4_gpu_tensor_bytes(out0_kv) < (uint64_t)width0 * sizeof(float) ||
ds4_gpu_tensor_bytes(out0_score) < (uint64_t)width0 * sizeof(float) ||
ds4_gpu_tensor_bytes(out1_kv) < (uint64_t)width1 * sizeof(float) ||
ds4_gpu_tensor_bytes(out1_score) < (uint64_t)width1 * sizeof(float) ||
ds4_gpu_tensor_bytes(state0_kv) < (uint64_t)state_rows * width0 * sizeof(float) ||
ds4_gpu_tensor_bytes(state0_score) < (uint64_t)state_rows * width0 * sizeof(float) ||
ds4_gpu_tensor_bytes(state1_kv) < (uint64_t)state_rows * width1 * sizeof(float) ||
ds4_gpu_tensor_bytes(state1_score) < (uint64_t)state_rows * width1 * sizeof(float)) {
return -1;
}
uint64_t w0kv_inner = 0, w0sc_inner = 0, w1kv_inner = 0, w1sc_inner = 0;
uint64_t ape0_inner = 0, ape1_inner = 0;
id<MTLBuffer> w0kvbuf = ds4_gpu_wrap_model_range(model_map, model_size, weight0_kv_offset, weight0_bytes, &w0kv_inner);
id<MTLBuffer> w0scbuf = ds4_gpu_wrap_model_range(model_map, model_size, weight0_score_offset, weight0_bytes, &w0sc_inner);
id<MTLBuffer> w1kvbuf = ds4_gpu_wrap_model_range(model_map, model_size, weight1_kv_offset, weight1_bytes, &w1kv_inner);
id<MTLBuffer> w1scbuf = ds4_gpu_wrap_model_range(model_map, model_size, weight1_score_offset, weight1_bytes, &w1sc_inner);
id<MTLBuffer> ape0buf = ds4_gpu_wrap_model_range(model_map, model_size, ape0_offset, ape0_bytes, &ape0_inner);
id<MTLBuffer> ape1buf = ds4_gpu_wrap_model_range(model_map, model_size, ape1_offset, ape1_bytes, &ape1_inner);
if (!w0kvbuf || !w0scbuf || !w1kvbuf || !w1scbuf || !ape0buf || !ape1buf) return -1;
ds4_gpu_f16_matvec_args mv_args = ds4_gpu_make_f16_mv_args(in_dim, width0);
mv_args.nr0 = 2;
ds4_gpu_dsv4_compressor_store_one_args store0_args = {
.width = width0,
.ratio = ratio,
.pos = pos,
.ape_type = ape0_type,
};
ds4_gpu_dsv4_compressor_store_one_args store1_args = {
.width = width1,
.ratio = ratio,
.pos = pos,
.ape_type = ape1_type,
};
id<MTLComputePipelineState> pipeline = ds4_gpu_get_mul_mv_pipeline(
"kernel_mul_mv_f16_f32_quad_compressor_store_4", 8);
if (!pipeline) return -1;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return -1;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&mv_args length:sizeof(mv_args) atIndex:0];
[enc setBytes:&store0_args length:sizeof(store0_args) atIndex:1];
[enc setBytes:&store1_args length:sizeof(store1_args) atIndex:2];
[enc setBuffer:w0kvbuf offset:(NSUInteger)w0kv_inner atIndex:3];
[enc setBuffer:w0scbuf offset:(NSUInteger)w0sc_inner atIndex:4];
[enc setBuffer:w1kvbuf offset:(NSUInteger)w1kv_inner atIndex:5];
[enc setBuffer:w1scbuf offset:(NSUInteger)w1sc_inner atIndex:6];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:7];
[enc setBuffer:out0kvbuf offset:ds4_gpu_tensor_offset(out0_kv) atIndex:8];
[enc setBuffer:out0scbuf offset:ds4_gpu_tensor_offset(out0_score) atIndex:9];
[enc setBuffer:out1kvbuf offset:(NSUInteger)ds4_gpu_tensor_offset(out1_kv) atIndex:10];
[enc setBuffer:out1scbuf offset:(NSUInteger)ds4_gpu_tensor_offset(out1_score) atIndex:11];
[enc setBuffer:ape0buf offset:(NSUInteger)ape0_inner atIndex:12];
[enc setBuffer:ape1buf offset:(NSUInteger)ape1_inner atIndex:13];
[enc setBuffer:state0kvbuf offset:ds4_gpu_tensor_offset(state0_kv) atIndex:14];
[enc setBuffer:state0scbuf offset:ds4_gpu_tensor_offset(state0_score) atIndex:15];
[enc setBuffer:state1kvbuf offset:ds4_gpu_tensor_offset(state1_kv) atIndex:16];
[enc setBuffer:state1scbuf offset:ds4_gpu_tensor_offset(state1_score) atIndex:17];
[enc setThreadgroupMemoryLength:32u * 2u * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(
((NSUInteger)width0 + (NSUInteger)width1 + 1u) / 2u, 1, 1)
threadsPerThreadgroup:MTLSizeMake(32, 8, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(
cb, owned, "F16 quad matvec compressor state store")) {
return -1;
}
}
return 1;
}
/* Decode-only emit-path fusion: finalize the freshly pooled attention and
* indexer compressor rows (norm + rope + fp8/commit + qat) in one dispatch
* instead of seven. Bit-exact vs the separate dispatches; see the kernel
* comment. Returns 1 when the fused dispatch ran, 0 to fall back. */
int ds4_gpu_dsv4_comp_row_finalize_tensor(
ds4_gpu_tensor *attn_stage,
ds4_gpu_tensor *attn_cache,
uint32_t attn_comp_row,
uint64_t attn_norm_offset,
ds4_gpu_tensor *index_cache,
uint32_t index_comp_row,
uint64_t index_norm_offset,
ds4_gpu_tensor *attn_state_kv,
ds4_gpu_tensor *attn_state_score,
ds4_gpu_tensor *index_state_kv,
ds4_gpu_tensor *index_state_score,
const void *model_map,
uint64_t model_size,
uint32_t pos,
uint32_t n_rot,
uint32_t n_ctx_orig,
float freq_base,
float freq_scale,
float ext_factor,
float attn_factor,
float beta_fast,
float beta_slow,
float rms_eps) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!attn_stage || !attn_cache || !index_cache || !model_map) return 0;
@autoreleasepool {
const uint64_t attn_norm_bytes = 512u * sizeof(float);
const uint64_t index_norm_bytes = 128u * sizeof(float);
if (attn_norm_offset > model_size ||
attn_norm_bytes > model_size - attn_norm_offset ||
index_norm_offset > model_size ||
index_norm_bytes > model_size - index_norm_offset) {
return 0;
}
if (!attn_state_kv || !attn_state_score || !index_state_kv ||
!index_state_score) return 0;
if (ds4_gpu_tensor_bytes(attn_state_kv) < 8u * 1024u * sizeof(float) ||
ds4_gpu_tensor_bytes(attn_state_score) < 8u * 1024u * sizeof(float) ||
ds4_gpu_tensor_bytes(index_state_kv) < 8u * 256u * sizeof(float) ||
ds4_gpu_tensor_bytes(index_state_score) < 8u * 256u * sizeof(float)) {
return 0;
}
if (ds4_gpu_tensor_bytes(attn_stage) < 512u * sizeof(float) ||
ds4_gpu_tensor_bytes(index_cache) <
((uint64_t)index_comp_row + 1u) * 128u * sizeof(float) ||
ds4_gpu_tensor_bytes(attn_cache) <
((uint64_t)attn_comp_row + 1u) * 512u * sizeof(uint16_t)) {
return 0;
}
uint64_t attn_norm_inner = 0, index_norm_inner = 0;
id<MTLBuffer> anbuf = ds4_gpu_wrap_model_range(
model_map, model_size, attn_norm_offset, attn_norm_bytes, &attn_norm_inner);
id<MTLBuffer> inbuf = ds4_gpu_wrap_model_range(
model_map, model_size, index_norm_offset, index_norm_bytes, &index_norm_inner);
if (!anbuf || !inbuf) return 0;
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_pipeline("kernel_dsv4_comp_row_finalize_f32");
if (!pipeline) return 0;
ds4_gpu_comp_finalize_args fin_args = {
.rope = {
.row_bytes = 0,
.token_bytes = 0,
.head_dim = 0,
.n_dims = (int32_t)n_rot,
.n_ctx_orig = (int32_t)n_ctx_orig,
.inverse = 0,
.pos0 = pos,
.pos_step = 0,
.freq_base = freq_base,
.freq_scale = freq_scale,
.ext_factor = ext_factor,
.attn_factor = attn_factor,
.beta_fast = beta_fast,
.beta_slow = beta_slow,
},
.rms_eps = rms_eps,
.pad0 = 0,
};
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&fin_args length:sizeof(fin_args) atIndex:0];
[enc setBuffer:ds4_gpu_tensor_buffer(attn_stage)
offset:ds4_gpu_tensor_offset(attn_stage) atIndex:1];
[enc setBuffer:anbuf offset:(NSUInteger)attn_norm_inner atIndex:2];
[enc setBuffer:ds4_gpu_tensor_buffer(attn_cache)
offset:ds4_gpu_tensor_offset(attn_cache) +
(NSUInteger)attn_comp_row * 512u * sizeof(uint16_t) atIndex:3];
[enc setBuffer:ds4_gpu_tensor_buffer(index_cache)
offset:ds4_gpu_tensor_offset(index_cache) +
(NSUInteger)index_comp_row * 128u * sizeof(float) atIndex:4];
[enc setBuffer:inbuf offset:(NSUInteger)index_norm_inner atIndex:5];
[enc setBuffer:ds4_gpu_tensor_buffer(attn_state_kv)
offset:ds4_gpu_tensor_offset(attn_state_kv) atIndex:6];
[enc setBuffer:ds4_gpu_tensor_buffer(attn_state_score)
offset:ds4_gpu_tensor_offset(attn_state_score) atIndex:7];
[enc setBuffer:ds4_gpu_tensor_buffer(index_state_kv)
offset:ds4_gpu_tensor_offset(index_state_kv) atIndex:8];
[enc setBuffer:ds4_gpu_tensor_buffer(index_state_score)
offset:ds4_gpu_tensor_offset(index_state_score) atIndex:9];
[enc setThreadgroupMemoryLength:256u * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(2u + (4u * 1024u + 4u * 256u + 255u) / 256u, 1, 1)
threadsPerThreadgroup:MTLSizeMake(256, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "compressor row finalize")) {
return 0;
}
}
return 1;
}
/* Decode-only fusion: q_a/kv Q8 pair projection + F16 quad compressor
* projection/store in one dispatch (both read the same normalized input).
* Bit-exact vs the separate dispatches; see the kernel comment. Returns
* 1 when the fused dispatch ran, 0 when the caller must use the separate
* paths, -1 on error. */
int ds4_gpu_qkv_pair_quad_compressor_store_tensor(
ds4_gpu_tensor *qr,
ds4_gpu_tensor *kv_raw,
ds4_gpu_tensor *out0_kv,
ds4_gpu_tensor *out0_score,
ds4_gpu_tensor *out1_kv,
ds4_gpu_tensor *out1_score,
ds4_gpu_tensor *state0_kv,
ds4_gpu_tensor *state0_score,
ds4_gpu_tensor *state1_kv,
ds4_gpu_tensor *state1_score,
const void *model_map,
uint64_t model_size,
uint64_t q_a_offset,
uint64_t kv_offset,
uint64_t weight0_kv_offset,
uint64_t weight0_score_offset,
uint64_t weight1_kv_offset,
uint64_t weight1_score_offset,
uint64_t ape0_offset,
uint32_t ape0_type,
uint64_t ape1_offset,
uint32_t ape1_type,
uint32_t in_dim,
uint32_t q_rank,
uint32_t kv_dim,
uint32_t width0,
uint32_t width1,
const ds4_gpu_tensor *x,
uint32_t ratio,
uint32_t pos) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!qr || !kv_raw || !out0_kv || !out0_score || !out1_kv || !out1_score ||
!state0_kv || !state0_score || !state1_kv || !state1_score ||
!model_map || !x || ratio == 0u || (in_dim & 31u) != 0 ||
(width0 & 1u) != 0 || (width1 & 1u) != 0) {
return 0;
}
@autoreleasepool {
const uint64_t q8_row_bytes = (in_dim / 32u) * 34u;
const uint64_t f16_row_bytes = (uint64_t)in_dim * sizeof(uint16_t);
const uint64_t q_a_bytes = (uint64_t)q_rank * q8_row_bytes;
const uint64_t kv_bytes = (uint64_t)kv_dim * q8_row_bytes;
const uint64_t weight0_bytes = (uint64_t)width0 * f16_row_bytes;
const uint64_t weight1_bytes = (uint64_t)width1 * f16_row_bytes;
const uint64_t state_rows = ratio == 4u ? 2u * ratio : ratio;
const uint64_t ape0_bytes = (uint64_t)width0 * ratio *
(ape0_type == 1u ? 2u : 4u);
const uint64_t ape1_bytes = (uint64_t)width1 * ratio *
(ape1_type == 1u ? 2u : 4u);
if (q_a_offset > model_size || q_a_bytes > model_size - q_a_offset ||
kv_offset > model_size || kv_bytes > model_size - kv_offset ||
weight0_kv_offset > model_size ||
weight0_bytes > model_size - weight0_kv_offset ||
weight0_score_offset > model_size ||
weight0_bytes > model_size - weight0_score_offset ||
weight1_kv_offset > model_size ||
weight1_bytes > model_size - weight1_kv_offset ||
weight1_score_offset > model_size ||
weight1_bytes > model_size - weight1_score_offset ||
ape0_offset > model_size || ape0_bytes > model_size - ape0_offset ||
ape1_offset > model_size || ape1_bytes > model_size - ape1_offset) {
return -1;
}
if (ds4_gpu_tensor_bytes(qr) < (uint64_t)q_rank * sizeof(float) ||
ds4_gpu_tensor_bytes(kv_raw) < (uint64_t)kv_dim * sizeof(float) ||
ds4_gpu_tensor_bytes(out0_kv) < (uint64_t)width0 * sizeof(float) ||
ds4_gpu_tensor_bytes(out0_score) < (uint64_t)width0 * sizeof(float) ||
ds4_gpu_tensor_bytes(out1_kv) < (uint64_t)width1 * sizeof(float) ||
ds4_gpu_tensor_bytes(out1_score) < (uint64_t)width1 * sizeof(float) ||
ds4_gpu_tensor_bytes(x) < (uint64_t)in_dim * sizeof(float) ||
ds4_gpu_tensor_bytes(state0_kv) < state_rows * width0 * sizeof(float) ||
ds4_gpu_tensor_bytes(state0_score) < state_rows * width0 * sizeof(float) ||
ds4_gpu_tensor_bytes(state1_kv) < state_rows * width1 * sizeof(float) ||
ds4_gpu_tensor_bytes(state1_score) < state_rows * width1 * sizeof(float)) {
return -1;
}
uint64_t q_a_inner = 0, kv_inner = 0;
uint64_t w0kv_inner = 0, w0sc_inner = 0, w1kv_inner = 0, w1sc_inner = 0;
uint64_t ape0_inner = 0, ape1_inner = 0;
id<MTLBuffer> qw0buf = ds4_gpu_wrap_model_range(model_map, model_size, q_a_offset, q_a_bytes, &q_a_inner);
id<MTLBuffer> qw1buf = ds4_gpu_wrap_model_range(model_map, model_size, kv_offset, kv_bytes, &kv_inner);
id<MTLBuffer> w0kvbuf = ds4_gpu_wrap_model_range(model_map, model_size, weight0_kv_offset, weight0_bytes, &w0kv_inner);
id<MTLBuffer> w0scbuf = ds4_gpu_wrap_model_range(model_map, model_size, weight0_score_offset, weight0_bytes, &w0sc_inner);
id<MTLBuffer> w1kvbuf = width1 != 0u
? ds4_gpu_wrap_model_range(model_map, model_size, weight1_kv_offset,
weight1_bytes, &w1kv_inner)
: w0kvbuf;
id<MTLBuffer> w1scbuf = width1 != 0u
? ds4_gpu_wrap_model_range(model_map, model_size, weight1_score_offset,
weight1_bytes, &w1sc_inner)
: w0scbuf;
id<MTLBuffer> ape0buf = ds4_gpu_wrap_model_range(model_map, model_size, ape0_offset, ape0_bytes, &ape0_inner);
id<MTLBuffer> ape1buf = width1 != 0u
? ds4_gpu_wrap_model_range(model_map, model_size, ape1_offset,
ape1_bytes, &ape1_inner)
: ape0buf;
if (width1 == 0u) {
w1kv_inner = w0kv_inner;
w1sc_inner = w0sc_inner;
ape1_inner = ape0_inner;
}
if (!qw0buf || !qw1buf || !w0kvbuf || !w0scbuf ||
!w1kvbuf || !w1scbuf || !ape0buf || !ape1buf) return -1;
ds4_gpu_q8_0_matvec_args args0 = ds4_gpu_make_q8_0_mv_args(in_dim, q_rank);
ds4_gpu_q8_0_matvec_args args1 = ds4_gpu_make_q8_0_mv_args(in_dim, kv_dim);
args0.nr0 = 2;
args1.nr0 = 2;
ds4_gpu_f16_matvec_args cargs = ds4_gpu_make_f16_mv_args(in_dim, width0);
cargs.nr0 = 2;
ds4_gpu_dsv4_compressor_store_one_args store0_args = {
.width = width0,
.ratio = ratio,
.pos = pos,
.ape_type = ape0_type,
};
ds4_gpu_dsv4_compressor_store_one_args store1_args = {
.width = width1,
.ratio = ratio,
.pos = pos,
.ape_type = ape1_type,
};
const uint32_t max_out = q_rank > kv_dim ? q_rank : kv_dim;
const uint32_t pair_vtgs = (max_out + 1u) / 2u;
id<MTLComputePipelineState> pipeline = ds4_gpu_get_mul_mv_pipeline(
"kernel_dsv4_qkv_pair_quad_compressor_store_q8_0", 8);
if (!pipeline) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args0 length:sizeof(args0) atIndex:0];
[enc setBytes:&args1 length:sizeof(args1) atIndex:1];
[enc setBytes:&cargs length:sizeof(cargs) atIndex:2];
[enc setBytes:&store0_args length:sizeof(store0_args) atIndex:3];
[enc setBytes:&store1_args length:sizeof(store1_args) atIndex:4];
[enc setBytes:&pair_vtgs length:sizeof(pair_vtgs) atIndex:5];
[enc setBuffer:qw0buf offset:(NSUInteger)q_a_inner atIndex:6];
[enc setBuffer:qw1buf offset:(NSUInteger)kv_inner atIndex:7];
[enc setBuffer:w0kvbuf offset:(NSUInteger)w0kv_inner atIndex:8];
[enc setBuffer:w0scbuf offset:(NSUInteger)w0sc_inner atIndex:9];
[enc setBuffer:w1kvbuf offset:(NSUInteger)w1kv_inner atIndex:10];
[enc setBuffer:w1scbuf offset:(NSUInteger)w1sc_inner atIndex:11];
[enc setBuffer:ds4_gpu_tensor_buffer(x) offset:ds4_gpu_tensor_offset(x) atIndex:12];
[enc setBuffer:ds4_gpu_tensor_buffer(qr) offset:ds4_gpu_tensor_offset(qr) atIndex:13];
[enc setBuffer:ds4_gpu_tensor_buffer(kv_raw) offset:ds4_gpu_tensor_offset(kv_raw) atIndex:14];
[enc setBuffer:ds4_gpu_tensor_buffer(out0_kv) offset:ds4_gpu_tensor_offset(out0_kv) atIndex:15];
[enc setBuffer:ds4_gpu_tensor_buffer(out0_score) offset:ds4_gpu_tensor_offset(out0_score) atIndex:16];
[enc setBuffer:ds4_gpu_tensor_buffer(out1_kv) offset:ds4_gpu_tensor_offset(out1_kv) atIndex:17];
[enc setBuffer:ds4_gpu_tensor_buffer(out1_score) offset:ds4_gpu_tensor_offset(out1_score) atIndex:18];
[enc setBuffer:ape0buf offset:(NSUInteger)ape0_inner atIndex:19];
[enc setBuffer:ape1buf offset:(NSUInteger)ape1_inner atIndex:20];
[enc setBuffer:ds4_gpu_tensor_buffer(state0_kv) offset:ds4_gpu_tensor_offset(state0_kv) atIndex:21];
[enc setBuffer:ds4_gpu_tensor_buffer(state0_score) offset:ds4_gpu_tensor_offset(state0_score) atIndex:22];
[enc setBuffer:ds4_gpu_tensor_buffer(state1_kv) offset:ds4_gpu_tensor_offset(state1_kv) atIndex:23];
[enc setBuffer:ds4_gpu_tensor_buffer(state1_score) offset:ds4_gpu_tensor_offset(state1_score) atIndex:24];
[enc setThreadgroupMemoryLength:2u * 2u * 2u * 32u * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(
(NSUInteger)(pair_vtgs + 1u) / 2u +
((NSUInteger)width0 + (NSUInteger)width1 + 1u) / 2u, 1, 1)
threadsPerThreadgroup:MTLSizeMake(32, 8, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(
cb, owned, "qkv pair + quad compressor store")) {
return -1;
}
}
return 1;
}
int ds4_gpu_matmul_f32_tensor(
ds4_gpu_tensor *out,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint64_t in_dim,
uint64_t out_dim,
const ds4_gpu_tensor *x,
uint64_t n_tok) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (in_dim > UINT32_MAX || out_dim > UINT32_MAX || n_tok == 0 || n_tok > UINT32_MAX) return 0;
if (in_dim > UINT64_MAX / n_tok / sizeof(float) ||
out_dim > UINT64_MAX / n_tok / sizeof(float)) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> xbuf = ds4_gpu_tensor_buffer(x);
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out);
const uint64_t x_bytes = n_tok * in_dim * sizeof(float);
const uint64_t out_bytes = n_tok * out_dim * sizeof(float);
if (!xbuf || !outbuf ||
ds4_gpu_tensor_bytes(x) < x_bytes ||
ds4_gpu_tensor_bytes(out) < out_bytes) {
fprintf(stderr, "ds4: Metal F32 tensor matmul received undersized activation buffers\n");
return 0;
}
const uint64_t row_bytes = in_dim * sizeof(float);
const uint64_t weight_bytes = row_bytes * out_dim;
if (weight_offset > model_size || weight_bytes > model_size - weight_offset) {
fprintf(stderr, "ds4: Metal F32 tensor matmul range is outside the mapped model\n");
return 0;
}
uint64_t inner_offset = 0;
id<MTLBuffer> wbuf =
ds4_gpu_wrap_model_range(model_map,
model_size,
weight_offset,
weight_bytes,
&inner_offset);
if (!wbuf) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
if (n_tok == 1) {
ds4_gpu_q8_0_matvec_args mv_args = ds4_gpu_make_f32_mv_args(in_dim, out_dim, 1);
ds4_gpu_mv_dispatch mv_dispatch = ds4_gpu_make_plain_mv_dispatch(in_dim, 1);
if (ds4_gpu_plain_mv_single_row(out_dim)) {
mv_dispatch.nr0 = 1;
mv_dispatch.smem = 32u * sizeof(float);
}
mv_args.nr0 = mv_dispatch.nr0;
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_mul_mv_pipeline(mv_dispatch.function_name, mv_dispatch.nsg);
if (!pipeline) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&mv_args length:sizeof(mv_args) atIndex:0];
[enc setBuffer:wbuf offset:(NSUInteger)inner_offset atIndex:1];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:2];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(out) atIndex:3];
if (mv_dispatch.smem) {
[enc setThreadgroupMemoryLength:mv_dispatch.smem atIndex:0];
}
[enc dispatchThreadgroups:MTLSizeMake(((NSUInteger)out_dim + (NSUInteger)mv_dispatch.nr0 - 1u) / (NSUInteger)mv_dispatch.nr0,
1,
1)
threadsPerThreadgroup:MTLSizeMake(32, (NSUInteger)mv_dispatch.nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "F32 tensor matvec")) return 0;
return 1;
}
const bool router_batch = in_dim == 5120u && out_dim == 384u &&
n_tok >= 32u && !g_quality_mode &&
!getenv("DS4_METAL_DISABLE_V41_ROUTER_BATCH");
if (router_batch && n_tok >= 256u && !getenv("DS4_METAL_DISABLE_V41_ROUTER_MM")) {
const bool bc_out = (n_tok % 32u) != 0;
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_mul_mm_pipeline("kernel_mul_mm_f32_f32_full", false, bc_out);
if (!pipeline) return 0;
ds4_gpu_mul_mm_args args = ds4_gpu_make_mm_args(in_dim, out_dim, n_tok, row_bytes);
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
if (!enc) return 0;
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:wbuf offset:(NSUInteger)inner_offset atIndex:1];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:2];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(out) atIndex:3];
[enc setThreadgroupMemoryLength:12288u atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(((NSUInteger)n_tok + 31u)/32u,
(NSUInteger)out_dim/64u, 1)
threadsPerThreadgroup:MTLSizeMake(128, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_finish_command_buffer(cb, owned, "F32 router prefill matmul");
}
if ((n_tok <= 8 || router_batch) && (in_dim % 128u) == 0) {
const int16_t nsg = router_batch ? 2 : ds4_gpu_mv_ext_nsg();
const int16_t nxpsg = ds4_gpu_mv_ext_nxpsg(in_dim, n_tok);
const int16_t r1ptg = ds4_gpu_mv_ext_r1ptg(n_tok);
const char *fn_name = ds4_gpu_mv_ext_f32_name(r1ptg);
id<MTLComputePipelineState> pipeline =
fn_name ? ds4_gpu_get_mul_mv_ext_pipeline(fn_name, nsg, nxpsg) : nil;
if (!pipeline) return 0;
const int16_t nypsg = 32 / nxpsg;
const uint64_t r0ptg = (uint64_t)nypsg * (uint64_t)nsg;
ds4_gpu_mul_mv_ext_args args =
ds4_gpu_make_mv_ext_args(in_dim, out_dim, n_tok, sizeof(float), row_bytes);
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:wbuf offset:(NSUInteger)inner_offset atIndex:1];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:2];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(out) atIndex:3];
[enc dispatchThreadgroups:MTLSizeMake(((NSUInteger)out_dim + (NSUInteger)r0ptg - 1u) / (NSUInteger)r0ptg,
((NSUInteger)n_tok + (NSUInteger)r1ptg - 1u) / (NSUInteger)r1ptg,
1)
threadsPerThreadgroup:MTLSizeMake(32, (NSUInteger)nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "F32 tensor mul_mv_ext")) return 0;
return 1;
}
/* Generic multi-row path (GLM prefill shapes: one grid row per
* token through the plain matvec pipeline). */
{
ds4_gpu_q8_0_matvec_args mv_args = ds4_gpu_make_f32_mv_args(in_dim, out_dim, n_tok);
ds4_gpu_mv_dispatch mv_dispatch = ds4_gpu_make_plain_mv_dispatch(in_dim, 1);
mv_args.nr0 = mv_dispatch.nr0;
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_mul_mv_pipeline(mv_dispatch.function_name, mv_dispatch.nsg);
if (!pipeline) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&mv_args length:sizeof(mv_args) atIndex:0];
[enc setBuffer:wbuf offset:(NSUInteger)inner_offset atIndex:1];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:2];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(out) atIndex:3];
if (mv_dispatch.smem) {
[enc setThreadgroupMemoryLength:mv_dispatch.smem atIndex:0];
}
[enc dispatchThreadgroups:MTLSizeMake(((NSUInteger)out_dim + (NSUInteger)mv_dispatch.nr0 - 1u) / (NSUInteger)mv_dispatch.nr0,
(NSUInteger)n_tok,
1)
threadsPerThreadgroup:MTLSizeMake(32, (NSUInteger)mv_dispatch.nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "F32 tensor matmul")) return 0;
}
}
return 1;
}
int ds4_gpu_repeat_hc_tensor(
ds4_gpu_tensor *out,
const ds4_gpu_tensor *row,
uint32_t n_embd,
uint32_t n_hc) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!out || !row || n_embd == 0 || n_hc == 0) return 0;
@autoreleasepool {
id<MTLBuffer> rowbuf = ds4_gpu_tensor_buffer(row);
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out);
const uint64_t row_bytes = (uint64_t)n_embd * sizeof(float);
const uint64_t out_bytes = row_bytes * n_hc;
if (!rowbuf || !outbuf ||
ds4_gpu_tensor_bytes(row) < row_bytes ||
ds4_gpu_tensor_bytes(out) < out_bytes) {
fprintf(stderr, "ds4: Metal HC repeat received undersized buffers\n");
return 0;
}
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
if (!ds4_gpu_encode_repeat_hc_embedding(cb,
rowbuf,
ds4_gpu_tensor_offset(row),
outbuf,
ds4_gpu_tensor_offset(out),
1,
n_embd,
n_hc)) {
return 0;
}
if (!ds4_gpu_finish_command_buffer(cb, owned, "HC repeat")) return 0;
}
return 1;
}
int ds4_gpu_repeat_hc_rows_tensor(
ds4_gpu_tensor *out,
const ds4_gpu_tensor *rows,
uint32_t n_tokens,
uint32_t n_embd,
uint32_t n_hc) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!out || !rows || n_tokens == 0 || n_embd == 0 || n_hc == 0) return 0;
@autoreleasepool {
id<MTLBuffer> rowsbuf = ds4_gpu_tensor_buffer(rows);
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out);
const uint64_t row_bytes = (uint64_t)n_embd * sizeof(float);
const uint64_t rows_bytes = (uint64_t)n_tokens * row_bytes;
const uint64_t out_bytes = rows_bytes * n_hc;
if (!rowsbuf || !outbuf ||
ds4_gpu_tensor_bytes(rows) < rows_bytes ||
ds4_gpu_tensor_bytes(out) < out_bytes) {
fprintf(stderr, "ds4: Metal HC row repeat received undersized buffers\n");
return 0;
}
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
if (!ds4_gpu_encode_repeat_hc_embedding(cb,
rowsbuf,
ds4_gpu_tensor_offset(rows),
outbuf,
ds4_gpu_tensor_offset(out),
n_tokens,
n_embd,
n_hc)) {
return 0;
}
if (!ds4_gpu_finish_command_buffer(cb, owned, "HC row repeat")) return 0;
}
return 1;
}
int ds4_gpu_rms_norm_plain_tensor(
ds4_gpu_tensor *out,
const ds4_gpu_tensor *x,
uint32_t n,
float eps) {
return ds4_gpu_rms_norm_plain_rows_tensor(out, x, n, 1, eps);
}
int ds4_gpu_rms_norm_plain_rows_tensor(
ds4_gpu_tensor *out,
const ds4_gpu_tensor *x,
uint32_t n,
uint32_t rows,
float eps) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (n == 0 || rows == 0 || (n & 3u) != 0) return 0;
@autoreleasepool {
id<MTLBuffer> xbuf = ds4_gpu_tensor_buffer(x);
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out);
const uint64_t bytes = (uint64_t)n * rows * sizeof(float);
if (!xbuf || !outbuf ||
ds4_gpu_tensor_bytes(x) < bytes ||
ds4_gpu_tensor_bytes(out) < bytes) {
fprintf(stderr, "ds4: Metal plain RMS norm received undersized activation buffers\n");
return 0;
}
ds4_gpu_rms_norm_args args = ds4_gpu_make_rms_norm_args(n, rows, eps);
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:g_rms_norm_plain_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:1];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:2];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:3];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(out) atIndex:4];
[enc setThreadgroupMemoryLength:32u * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(rows, 1, 1)
threadsPerThreadgroup:MTLSizeMake(ds4_gpu_rms_norm_threads(n), 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "plain RMS norm")) return 0;
}
return 1;
}
static int ds4_gpu_hc_rms_scale_project_mode(
uint32_t in_dim,
uint32_t out_dim,
uint32_t n_rows) {
const bool hard_shape = n_rows > 8u &&
(in_dim == 16384u || in_dim == 20480u || in_dim == 28672u) && out_dim == 24u;
if (!hard_shape) return 0;
if (in_dim == 20480u && n_rows < 512u) return 0;
if ((in_dim != 20480u && !ds4_gpu_device_name_contains("M3") &&
(g_test_flags & DS4_GPU_TEST_HC_RMS_SCALE_PROJ) == 0u) ||
getenv("DS4_METAL_DISABLE_HC_RMS_SCALE_PROJ") != NULL) {
return 0;
}
if (g_rms_norm_scale_pipeline == nil) {
return 0;
}
return 1;
}
int ds4_gpu_hc_rms_scale_project_f16_tensor(
ds4_gpu_tensor *out,
ds4_gpu_tensor *scale_scratch,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint32_t in_dim,
uint32_t out_dim,
const ds4_gpu_tensor *x,
uint32_t n_rows,
float eps) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!out || !scale_scratch || !model_map || !x ||
in_dim == 0u || out_dim == 0u || n_rows == 0u ||
n_rows > INT32_MAX) {
return 0;
}
const int mode =
ds4_gpu_hc_rms_scale_project_mode(in_dim, out_dim, n_rows);
if (mode < 0) return 0;
if (mode == 0) {
return ds4_gpu_rms_norm_plain_rows_tensor(
scale_scratch, x, in_dim, n_rows, eps) != 0 &&
ds4_gpu_matmul_f16_tensor(
out, model_map, model_size, weight_offset,
in_dim, out_dim, scale_scratch, n_rows) != 0;
}
@autoreleasepool {
const uint64_t x_elems = (uint64_t)in_dim * n_rows;
const uint64_t out_elems = (uint64_t)out_dim * n_rows;
if (x_elems > UINT64_MAX / sizeof(float) ||
out_elems > UINT64_MAX / sizeof(float)) {
return 0;
}
const uint64_t x_bytes = x_elems * sizeof(float);
const uint64_t out_bytes = out_elems * sizeof(float);
const uint64_t scale_bytes = (uint64_t)n_rows * sizeof(float);
id<MTLBuffer> xbuf = ds4_gpu_tensor_buffer(x);
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out);
id<MTLBuffer> scalebuf = ds4_gpu_tensor_buffer(scale_scratch);
if (!xbuf || !outbuf || !scalebuf ||
ds4_gpu_tensor_bytes(x) < x_bytes ||
ds4_gpu_tensor_bytes(out) < out_bytes ||
ds4_gpu_tensor_bytes(scale_scratch) < scale_bytes) {
fprintf(stderr, "ds4: Metal HC RMS scale projection received undersized activation buffers\n");
return 0;
}
const uint64_t weight_row_bytes = (uint64_t)in_dim * sizeof(uint16_t);
if ((uint64_t)out_dim > UINT64_MAX / weight_row_bytes) return 0;
const uint64_t weight_bytes = (uint64_t)out_dim * weight_row_bytes;
if (weight_offset > model_size ||
weight_bytes > model_size - weight_offset) {
fprintf(stderr, "ds4: Metal HC RMS scale projection weight range is outside the mapped model\n");
return 0;
}
const bool bc_inp = (in_dim % 32u) != 0u;
const bool bc_out = (out_dim % 64u) != 0u ||
(n_rows % 32u) != 0u;
id<MTLComputePipelineState> mm_pipeline =
ds4_gpu_get_mul_mm_pipeline(
"kernel_mul_mm_f16_f32_scaled", bc_inp, bc_out);
if (!mm_pipeline) {
return ds4_gpu_rms_norm_plain_rows_tensor(
scale_scratch, x, in_dim, n_rows, eps) != 0 &&
ds4_gpu_matmul_f16_tensor(
out, model_map, model_size, weight_offset,
in_dim, out_dim, scale_scratch, n_rows) != 0;
}
id<MTLComputePipelineState> scale_pipeline = ds4_gpu_hot_pipeline(
g_rms_norm_scale_pipeline, "kernel_rms_norm_scale_f32_4");
if (!scale_pipeline) return 0;
uint64_t weight_inner = 0;
id<MTLBuffer> weightbuf = ds4_gpu_wrap_model_range(
model_map, model_size, weight_offset, weight_bytes, &weight_inner);
if (!weightbuf) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
ds4_gpu_rms_norm_args norm_args =
ds4_gpu_make_rms_norm_args(in_dim, n_rows, eps);
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:scale_pipeline];
[enc setBytes:&norm_args length:sizeof(norm_args) atIndex:0];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:1];
[enc setBuffer:scalebuf
offset:ds4_gpu_tensor_offset(scale_scratch)
atIndex:2];
[enc setThreadgroupMemoryLength:32u * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(n_rows, 1, 1)
threadsPerThreadgroup:MTLSizeMake(
ds4_gpu_rms_norm_threads(in_dim), 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
ds4_gpu_mul_mm_args mm_args = ds4_gpu_make_mm_args(
in_dim, out_dim, n_rows, weight_row_bytes);
enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:mm_pipeline];
[enc setBytes:&mm_args length:sizeof(mm_args) atIndex:0];
[enc setBuffer:weightbuf offset:(NSUInteger)weight_inner atIndex:1];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:2];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(out) atIndex:3];
[enc setBuffer:scalebuf
offset:ds4_gpu_tensor_offset(scale_scratch)
atIndex:4];
[enc setThreadgroupMemoryLength:(bc_out ? 8192u : 6144u) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(
((NSUInteger)n_rows + 31u) / 32u,
((NSUInteger)out_dim + 63u) / 64u,
1)
threadsPerThreadgroup:MTLSizeMake(128, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(
cb, owned, "HC RMS scale F16 projection")) {
return 0;
}
}
return 1;
}
int ds4_gpu_rms_norm_weight_tensor(
ds4_gpu_tensor *out,
const ds4_gpu_tensor *x,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint32_t n,
float eps) {
return ds4_gpu_rms_norm_weight_rows_tensor(out, x, model_map, model_size, weight_offset, n, 1, eps);
}
static int ds4_gpu_rms_norm_weight_rows_impl(
ds4_gpu_tensor *out,
const ds4_gpu_tensor *x,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint32_t n,
uint32_t rows,
float eps,
int round_bf16) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (n == 0 || rows == 0 || (n & 3u) != 0) return 0;
id<MTLComputePipelineState> pipeline = g_rms_norm_pipeline;
if (round_bf16) {
static id<MTLComputePipelineState> rounded;
if (!rounded) rounded = ds4_gpu_get_pipeline("kernel_rms_norm_mul_f32_4_bf16");
pipeline = rounded;
}
if (!pipeline) return 0;
@autoreleasepool {
id<MTLBuffer> xbuf = ds4_gpu_tensor_buffer(x);
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out);
const uint64_t row_bytes = (uint64_t)n * sizeof(float);
const uint64_t bytes = row_bytes * rows;
if (!xbuf || !outbuf ||
ds4_gpu_tensor_bytes(x) < bytes ||
ds4_gpu_tensor_bytes(out) < bytes) {
fprintf(stderr, "ds4: Metal weighted RMS norm received undersized activation buffers\n");
return 0;
}
if (weight_offset > model_size || row_bytes > model_size - weight_offset) {
fprintf(stderr, "ds4: Metal weighted RMS norm range is outside the mapped model\n");
return 0;
}
uint64_t inner_offset = 0;
id<MTLBuffer> wbuf = ds4_gpu_wrap_model_range(model_map,
model_size,
weight_offset,
row_bytes,
&inner_offset);
if (!wbuf) return 0;
ds4_gpu_rms_norm_args args = ds4_gpu_make_rms_norm_args(n, rows, eps);
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:1];
[enc setBuffer:wbuf offset:(NSUInteger)inner_offset atIndex:2];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:3];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(out) atIndex:4];
[enc setThreadgroupMemoryLength:32u * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(rows, 1, 1)
threadsPerThreadgroup:MTLSizeMake(ds4_gpu_rms_norm_threads(n), 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "weighted RMS norm")) return 0;
}
return 1;
}
int ds4_gpu_rms_norm_weight_rows_tensor(
ds4_gpu_tensor *out,
const ds4_gpu_tensor *x,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint32_t n,
uint32_t rows,
float eps) {
return ds4_gpu_rms_norm_weight_rows_impl(out, x, model_map, model_size, weight_offset, n, rows, eps, 0);
}
int ds4_gpu_rms_norm_weight_bf16_tensor(
ds4_gpu_tensor *out,
const ds4_gpu_tensor *x,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint32_t n,
float eps) {
return ds4_gpu_rms_norm_weight_rows_impl(out, x, model_map, model_size, weight_offset, n, 1, eps, 1);
}
int ds4_gpu_add_rms_norm_weight_tensor(
ds4_gpu_tensor *norm_out,
ds4_gpu_tensor *sum_out,
const ds4_gpu_tensor *a,
const ds4_gpu_tensor *b,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint32_t n,
float eps) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!norm_out || !sum_out || !a || !b || n == 0 || (n & 3u) != 0) return 0;
@autoreleasepool {
id<MTLBuffer> abuf = ds4_gpu_tensor_buffer(a);
id<MTLBuffer> bbuf = ds4_gpu_tensor_buffer(b);
id<MTLBuffer> sumbuf = ds4_gpu_tensor_buffer(sum_out);
id<MTLBuffer> normbuf = ds4_gpu_tensor_buffer(norm_out);
const uint64_t row_bytes = (uint64_t)n * sizeof(float);
if (!abuf || !bbuf || !sumbuf || !normbuf ||
ds4_gpu_tensor_bytes(a) < row_bytes ||
ds4_gpu_tensor_bytes(b) < row_bytes ||
ds4_gpu_tensor_bytes(sum_out) < row_bytes ||
ds4_gpu_tensor_bytes(norm_out) < row_bytes) {
fprintf(stderr, "ds4: Metal add+RMS norm received undersized activation buffers\n");
return 0;
}
if (weight_offset > model_size || row_bytes > model_size - weight_offset) {
fprintf(stderr, "ds4: Metal add+RMS norm range is outside the mapped model\n");
return 0;
}
uint64_t inner_offset = 0;
id<MTLBuffer> wbuf = ds4_gpu_wrap_model_range(model_map,
model_size,
weight_offset,
row_bytes,
&inner_offset);
if (!wbuf) return 0;
ds4_gpu_rms_norm_args args = ds4_gpu_make_rms_norm_args(n, 1, eps);
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:g_add_rms_norm_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:abuf offset:ds4_gpu_tensor_offset(a) atIndex:1];
[enc setBuffer:bbuf offset:ds4_gpu_tensor_offset(b) atIndex:2];
[enc setBuffer:wbuf offset:(NSUInteger)inner_offset atIndex:3];
[enc setBuffer:sumbuf offset:ds4_gpu_tensor_offset(sum_out) atIndex:4];
[enc setBuffer:normbuf offset:ds4_gpu_tensor_offset(norm_out) atIndex:5];
[enc setThreadgroupMemoryLength:32u * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(1, 1, 1)
threadsPerThreadgroup:MTLSizeMake(ds4_gpu_rms_norm_threads(n), 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "add+RMS norm")) return 0;
}
return 1;
}
int ds4_gpu_dsv4_qkv_rms_norm_rows_tensor(
ds4_gpu_tensor *q_out,
const ds4_gpu_tensor *q,
const void *model_map,
uint64_t model_size,
uint64_t q_weight_offset,
uint32_t q_n,
ds4_gpu_tensor *kv_out,
const ds4_gpu_tensor *kv,
uint64_t kv_weight_offset,
uint32_t kv_n,
uint32_t rows,
float eps) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!q_out || !q || !kv_out || !kv || q_n == 0 || kv_n == 0 || rows == 0 ||
(q_n & 3u) != 0 || (kv_n & 3u) != 0) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> qbuf = ds4_gpu_tensor_buffer(q);
id<MTLBuffer> qoutbuf = ds4_gpu_tensor_buffer(q_out);
id<MTLBuffer> kvbuf = ds4_gpu_tensor_buffer(kv);
id<MTLBuffer> kvoutbuf = ds4_gpu_tensor_buffer(kv_out);
const uint64_t q_row_bytes = (uint64_t)q_n * sizeof(float);
const uint64_t kv_row_bytes = (uint64_t)kv_n * sizeof(float);
if (!qbuf || !qoutbuf || !kvbuf || !kvoutbuf ||
ds4_gpu_tensor_bytes(q) < q_row_bytes * rows ||
ds4_gpu_tensor_bytes(q_out) < q_row_bytes * rows ||
ds4_gpu_tensor_bytes(kv) < kv_row_bytes * rows ||
ds4_gpu_tensor_bytes(kv_out) < kv_row_bytes * rows) {
fprintf(stderr, "ds4: Metal fused q/kv RMS norm received undersized activation buffers\n");
return 0;
}
if (q_weight_offset > model_size || q_row_bytes > model_size - q_weight_offset ||
kv_weight_offset > model_size || kv_row_bytes > model_size - kv_weight_offset) {
fprintf(stderr, "ds4: Metal fused q/kv RMS norm weight range is outside the mapped model\n");
return 0;
}
uint64_t q_inner_offset = 0;
uint64_t kv_inner_offset = 0;
id<MTLBuffer> q_wbuf = ds4_gpu_wrap_model_range(model_map, model_size,
q_weight_offset, q_row_bytes,
&q_inner_offset);
if (!q_wbuf) return 0;
id<MTLBuffer> kv_wbuf = ds4_gpu_wrap_model_range(model_map, model_size,
kv_weight_offset, kv_row_bytes,
&kv_inner_offset);
if (!kv_wbuf) return 0;
ds4_gpu_qkv_rms_norm_args args = {
.q_n = (int32_t)q_n,
.q_n4 = (int32_t)(q_n / 4u),
.kv_n = (int32_t)kv_n,
.kv_n4 = (int32_t)(kv_n / 4u),
.q_row_stride = q_row_bytes,
.kv_row_stride = kv_row_bytes,
.eps = eps,
};
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:g_dsv4_qkv_rms_norm_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:qbuf offset:ds4_gpu_tensor_offset(q) atIndex:1];
[enc setBuffer:q_wbuf offset:(NSUInteger)q_inner_offset atIndex:2];
[enc setBuffer:qoutbuf offset:ds4_gpu_tensor_offset(q_out) atIndex:3];
[enc setBuffer:kvbuf offset:ds4_gpu_tensor_offset(kv) atIndex:4];
[enc setBuffer:kv_wbuf offset:(NSUInteger)kv_inner_offset atIndex:5];
[enc setBuffer:kvoutbuf offset:ds4_gpu_tensor_offset(kv_out) atIndex:6];
[enc setThreadgroupMemoryLength:32u * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(rows, 2, 1)
threadsPerThreadgroup:MTLSizeMake(ds4_gpu_rms_norm_threads(q_n), 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "fused q/kv RMS norm")) return 0;
}
return 1;
}
/* Decode-only triple fusion: q/kv RMS norm + KV RoPE tail + FP8/raw store.
* Same arithmetic, order and rounding as the three separate dispatches (see
* metal/norm.metal); gated and verified against full-vocabulary logits.
* Returns 1 on success, 0 on unsupported shape (caller falls back). */
/* Deferred kv norm/RoPE/FP8 task: the q/kv norm dispatch runs its q task
* only and the kv task is folded into the KV staging kernel of the same
* layer (ds4_gpu_kv_norm_task_flush runs it standalone if no staging
* consumed it). */
static struct {
int pending;
id<MTLBuffer> kv, kv_out, kv_w, q, q_out, q_w, raw;
NSUInteger kv_off, kv_out_off, kv_w_off, q_off, q_out_off, q_w_off, raw_off;
ds4_gpu_qkv_rms_norm_args nargs;
ds4_gpu_rope_affine_pair_args rope;
ds4_gpu_dsv4_kv_fp8_store_args store;
NSUInteger norm_threads;
} g_kv_task;
static int g_qkv_norm_defer_kv;
void ds4_gpu_dsv4_qkv_norm_defer_kv_next(void) {
g_qkv_norm_defer_kv = 1;
}
int ds4_gpu_kv_norm_task_pending(void) {
return g_kv_task.pending;
}
/* Run a deferred kv task standalone (the full q/kv norm kernel; the q task
* recomputes identical values). Used when no KV staging consumed it. */
int ds4_gpu_kv_norm_task_flush(void) {
if (!g_kv_task.pending) return 1;
g_kv_task.pending = 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_pipeline("kernel_dsv4_qkv_rms_norm_kv_rope_fp8_store_f32");
if (!pipeline) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&g_kv_task.nargs length:sizeof(g_kv_task.nargs) atIndex:0];
[enc setBytes:&g_kv_task.rope length:sizeof(g_kv_task.rope) atIndex:1];
[enc setBytes:&g_kv_task.store length:sizeof(g_kv_task.store) atIndex:2];
[enc setBuffer:g_kv_task.q offset:g_kv_task.q_off atIndex:3];
[enc setBuffer:g_kv_task.q_w offset:g_kv_task.q_w_off atIndex:4];
[enc setBuffer:g_kv_task.q_out offset:g_kv_task.q_out_off atIndex:5];
[enc setBuffer:g_kv_task.kv offset:g_kv_task.kv_off atIndex:6];
[enc setBuffer:g_kv_task.kv_w offset:g_kv_task.kv_w_off atIndex:7];
[enc setBuffer:g_kv_task.kv_out offset:g_kv_task.kv_out_off atIndex:8];
[enc setBuffer:g_kv_task.raw offset:g_kv_task.raw_off atIndex:9];
[enc setThreadgroupMemoryLength:(32u + 64u) * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(1, 2, 1)
threadsPerThreadgroup:MTLSizeMake(g_kv_task.norm_threads, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_finish_command_buffer(cb, owned, "deferred kv norm task");
}
/* Run the deferred kv task in a concurrent encoder left open for the
* caller's next dispatch (the q_b projection), so its ~9 us of serial work
* hides under that stream. End closes the section. */
int ds4_gpu_kv_norm_task_begin_concurrent(void) {
if (!g_kv_task.pending || !g_batch_cb || g_batch_encoder_concurrent) return 0;
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_pipeline("kernel_dsv4_qkv_rms_norm_kv_rope_fp8_store_f32");
if (!pipeline) return 0;
g_kv_task.pending = 0;
ds4_gpu_close_batch_encoder();
g_batch_encoder_concurrent = YES;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(g_batch_cb);
if (!enc || enc.dispatchType != MTLDispatchTypeConcurrent) {
ds4_gpu_close_batch_encoder();
g_batch_encoder_concurrent = NO;
g_kv_task.pending = 1;
return 0;
}
[enc setComputePipelineState:pipeline];
[enc setBytes:&g_kv_task.nargs length:sizeof(g_kv_task.nargs) atIndex:0];
[enc setBytes:&g_kv_task.rope length:sizeof(g_kv_task.rope) atIndex:1];
[enc setBytes:&g_kv_task.store length:sizeof(g_kv_task.store) atIndex:2];
[enc setBuffer:g_kv_task.q offset:g_kv_task.q_off atIndex:3];
[enc setBuffer:g_kv_task.q_w offset:g_kv_task.q_w_off atIndex:4];
[enc setBuffer:g_kv_task.q_out offset:g_kv_task.q_out_off atIndex:5];
[enc setBuffer:g_kv_task.kv offset:g_kv_task.kv_off atIndex:6];
[enc setBuffer:g_kv_task.kv_w offset:g_kv_task.kv_w_off atIndex:7];
[enc setBuffer:g_kv_task.kv_out offset:g_kv_task.kv_out_off atIndex:8];
[enc setBuffer:g_kv_task.raw offset:g_kv_task.raw_off atIndex:9];
[enc setThreadgroupMemoryLength:(32u + 64u) * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(1, 2, 1)
threadsPerThreadgroup:MTLSizeMake(g_kv_task.norm_threads, 1, 1)];
ds4_gpu_end_compute_encoder(g_batch_cb, enc);
return 1;
}
void ds4_gpu_kv_norm_task_end_concurrent(void) {
if (!g_batch_encoder_concurrent) return;
ds4_gpu_close_batch_encoder();
g_batch_encoder_concurrent = NO;
}
int ds4_gpu_dsv4_qkv_rms_norm_kv_rope_fp8_store_tensor(
ds4_gpu_tensor *q_out,
const ds4_gpu_tensor *q,
const void *model_map,
uint64_t model_size,
uint64_t q_weight_offset,
uint32_t q_n,
ds4_gpu_tensor *kv_out,
const ds4_gpu_tensor *kv,
uint64_t kv_weight_offset,
uint32_t kv_n,
ds4_gpu_tensor *raw_cache,
uint64_t raw_cap,
uint32_t raw_row,
uint32_t n_rot,
uint32_t pos0,
uint32_t n_ctx_orig,
float freq_base,
float freq_scale,
float ext_factor,
float attn_factor,
float beta_fast,
float beta_slow,
float eps) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!q_out || !q || !kv_out || !kv || !raw_cache ||
q_n == 0 || kv_n == 0 || (q_n & 3u) != 0 || (kv_n & 3u) != 0 ||
n_rot > kv_n || (n_rot & 1u) != 0) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> qbuf = ds4_gpu_tensor_buffer(q);
id<MTLBuffer> qoutbuf = ds4_gpu_tensor_buffer(q_out);
id<MTLBuffer> kvbuf = ds4_gpu_tensor_buffer(kv);
id<MTLBuffer> kvoutbuf = ds4_gpu_tensor_buffer(kv_out);
id<MTLBuffer> rawbuf = ds4_gpu_tensor_buffer(raw_cache);
const uint64_t q_row_bytes = (uint64_t)q_n * sizeof(float);
const uint64_t kv_row_bytes = (uint64_t)kv_n * sizeof(float);
const uint64_t raw_bytes = raw_cap * kv_row_bytes;
if (!qbuf || !qoutbuf || !kvbuf || !kvoutbuf || !rawbuf ||
ds4_gpu_tensor_bytes(q) < q_row_bytes ||
ds4_gpu_tensor_bytes(q_out) < q_row_bytes ||
ds4_gpu_tensor_bytes(kv) < kv_row_bytes ||
ds4_gpu_tensor_bytes(kv_out) < kv_row_bytes ||
ds4_gpu_tensor_bytes(raw_cache) < raw_bytes ||
(uint64_t)raw_row >= raw_cap) {
fprintf(stderr, "ds4: Metal fused q/kv norm RoPE store received undersized buffers\n");
return 0;
}
if (q_weight_offset > model_size || q_row_bytes > model_size - q_weight_offset ||
kv_weight_offset > model_size || kv_row_bytes > model_size - kv_weight_offset) {
fprintf(stderr, "ds4: Metal fused q/kv norm RoPE store weight range is outside the mapped model\n");
return 0;
}
uint64_t q_inner_offset = 0;
uint64_t kv_inner_offset = 0;
id<MTLBuffer> q_wbuf = ds4_gpu_wrap_model_range(model_map, model_size,
q_weight_offset, q_row_bytes,
&q_inner_offset);
if (!q_wbuf) return 0;
id<MTLBuffer> kv_wbuf = ds4_gpu_wrap_model_range(model_map, model_size,
kv_weight_offset, kv_row_bytes,
&kv_inner_offset);
if (!kv_wbuf) return 0;
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_pipeline("kernel_dsv4_qkv_rms_norm_kv_rope_fp8_store_f32");
if (!pipeline) return 0;
ds4_gpu_qkv_rms_norm_args args = {
.q_n = (int32_t)q_n,
.q_n4 = (int32_t)(q_n / 4u),
.kv_n = (int32_t)kv_n,
.kv_n4 = (int32_t)(kv_n / 4u),
.q_row_stride = q_row_bytes,
.kv_row_stride = kv_row_bytes,
.eps = eps,
};
ds4_gpu_rope_affine_pair_args rope = {
.row_bytes = kv_row_bytes,
.token_bytes = kv_row_bytes,
.head_dim = (int32_t)kv_n,
.n_dims = (int32_t)n_rot,
.n_ctx_orig = (int32_t)n_ctx_orig,
.inverse = 0,
.pos0 = pos0,
.pos_step = 1,
.freq_base = freq_base,
.freq_scale = freq_scale,
.ext_factor = ext_factor,
.attn_factor = attn_factor,
.beta_fast = beta_fast,
.beta_slow = beta_slow,
};
ds4_gpu_dsv4_kv_fp8_store_args store = {
.head_dim = (int32_t)kv_n,
.n_rot = (int32_t)n_rot,
.raw_row = (int32_t)raw_row,
};
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBytes:&rope length:sizeof(rope) atIndex:1];
[enc setBytes:&store length:sizeof(store) atIndex:2];
[enc setBuffer:qbuf offset:ds4_gpu_tensor_offset(q) atIndex:3];
[enc setBuffer:q_wbuf offset:(NSUInteger)q_inner_offset atIndex:4];
[enc setBuffer:qoutbuf offset:ds4_gpu_tensor_offset(q_out) atIndex:5];
[enc setBuffer:kvbuf offset:ds4_gpu_tensor_offset(kv) atIndex:6];
[enc setBuffer:kv_wbuf offset:(NSUInteger)kv_inner_offset atIndex:7];
[enc setBuffer:kvoutbuf offset:ds4_gpu_tensor_offset(kv_out) atIndex:8];
[enc setBuffer:rawbuf offset:ds4_gpu_tensor_offset(raw_cache) atIndex:9];
[enc setThreadgroupMemoryLength:(32u + 64u) * sizeof(float) atIndex:0];
const NSUInteger norm_threads = ds4_gpu_rms_norm_threads(q_n);
const bool defer_kv = g_qkv_norm_defer_kv && !g_kv_task.pending;
g_qkv_norm_defer_kv = 0;
if (defer_kv) {
args.tasks = 1u; /* q task now; kv task later, concurrently with q_b */
[enc setBytes:&args length:sizeof(args) atIndex:0];
}
[enc dispatchThreadgroups:MTLSizeMake(1, defer_kv ? 1 : 2, 1)
threadsPerThreadgroup:MTLSizeMake(norm_threads, 1, 1)];
if (defer_kv) {
g_kv_task.pending = 1;
g_kv_task.kv = kvbuf; g_kv_task.kv_off = ds4_gpu_tensor_offset(kv);
g_kv_task.kv_out = kvoutbuf; g_kv_task.kv_out_off = ds4_gpu_tensor_offset(kv_out);
g_kv_task.kv_w = kv_wbuf; g_kv_task.kv_w_off = (NSUInteger)kv_inner_offset;
g_kv_task.q = qbuf; g_kv_task.q_off = ds4_gpu_tensor_offset(q);
g_kv_task.q_out = qoutbuf; g_kv_task.q_out_off = ds4_gpu_tensor_offset(q_out);
g_kv_task.q_w = q_wbuf; g_kv_task.q_w_off = (NSUInteger)q_inner_offset;
g_kv_task.raw = rawbuf; g_kv_task.raw_off = ds4_gpu_tensor_offset(raw_cache);
g_kv_task.nargs = args;
g_kv_task.nargs.tasks = 2u; /* kv task only */
g_kv_task.rope = rope;
g_kv_task.store = store;
g_kv_task.norm_threads = norm_threads;
}
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "fused q/kv norm RoPE store")) return 0;
}
return 1;
}
int ds4_gpu_head_rms_norm_tensor(
ds4_gpu_tensor *x,
uint32_t n_tok,
uint32_t n_head,
uint32_t head_dim,
float eps) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!x || n_tok == 0 || n_head == 0 || head_dim == 0 || (head_dim & 3u) != 0) return 0;
@autoreleasepool {
id<MTLBuffer> xbuf = ds4_gpu_tensor_buffer(x);
const uint64_t bytes = (uint64_t)n_tok * n_head * head_dim * sizeof(float);
if (!xbuf || ds4_gpu_tensor_bytes(x) < bytes) {
fprintf(stderr, "ds4: Metal head RMS norm received undersized activation buffer\n");
return 0;
}
ds4_gpu_rms_norm_args args = ds4_gpu_make_rms_norm_3d_args(head_dim, n_head, n_tok, eps);
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:g_rms_norm_plain_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:1];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:2];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:3];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:4];
[enc setThreadgroupMemoryLength:32u * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(n_head, n_tok, 1)
threadsPerThreadgroup:MTLSizeMake(ds4_gpu_rms_norm_pipeline_threads(head_dim, g_rms_norm_plain_pipeline), 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "head RMS norm")) return 0;
}
return 1;
}
int ds4_gpu_rope_tail_tensor(
ds4_gpu_tensor *x,
uint32_t n_tok,
uint32_t n_head,
uint32_t head_dim,
uint32_t n_rot,
uint32_t pos0,
uint32_t n_ctx_orig,
bool inverse,
float freq_base,
float freq_scale,
float ext_factor,
float attn_factor,
float beta_fast,
float beta_slow) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!x || n_tok == 0 || n_head == 0 || head_dim == 0 || n_rot > head_dim || (n_rot & 1u) != 0) {
return 0;
}
if (n_rot == 0) return 1;
@autoreleasepool {
id<MTLBuffer> xbuf = ds4_gpu_tensor_buffer(x);
const uint64_t bytes = (uint64_t)n_tok * n_head * head_dim * sizeof(float);
if (!xbuf || ds4_gpu_tensor_bytes(x) < bytes) {
fprintf(stderr, "ds4: Metal RoPE received undersized activation buffer\n");
return 0;
}
ds4_gpu_rope_tail_batch_args args = ds4_gpu_make_rope_tail_args(
n_tok, n_head, head_dim, n_rot, n_ctx_orig, inverse,
freq_base, freq_scale, ext_factor, attn_factor, beta_fast, beta_slow);
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
if (!ds4_gpu_encode_rope_tail_inplace(cb,
xbuf,
ds4_gpu_tensor_offset(x),
&args,
n_tok,
n_head,
head_dim,
pos0,
1)) {
return 0;
}
if (!ds4_gpu_finish_command_buffer(cb, owned, "RoPE tail")) return 0;
}
return 1;
}
int ds4_gpu_head_rms_norm_rope_tail_tensor(
ds4_gpu_tensor *x,
uint32_t n_tok,
uint32_t n_head,
uint32_t head_dim,
uint32_t n_rot,
uint32_t pos0,
uint32_t n_ctx_orig,
bool inverse,
float freq_base,
float freq_scale,
float ext_factor,
float attn_factor,
float beta_fast,
float beta_slow,
float eps) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!x || n_tok == 0u || n_head == 0u || head_dim == 0u ||
(head_dim & 3u) != 0u || n_rot > head_dim ||
pos0 > (uint32_t)INT32_MAX - n_tok) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> xbuf = ds4_gpu_tensor_buffer(x);
const uint64_t bytes =
(uint64_t)n_tok * n_head * head_dim * sizeof(float);
if (!xbuf || ds4_gpu_tensor_bytes(x) < bytes) {
fprintf(stderr,
"ds4: Metal fused head norm/RoPE received undersized activation buffer\n");
return 0;
}
id<MTLComputePipelineState> pipeline =
g_use_dsv4_head_rms_norm_rope_tail_pipeline
? g_dsv4_head_rms_norm_rope_tail_pipeline
: ds4_gpu_get_pipeline(
"kernel_dsv4_head_rms_norm_rope_tail_f32");
if (!pipeline) return 0;
struct {
int32_t n_head;
int32_t head_dim;
int32_t head_dim4;
int32_t n_dims;
int32_t n_ctx_orig;
int32_t pos0;
int32_t inverse;
float eps;
float freq_base;
float freq_scale;
float ext_factor;
float attn_factor;
float beta_fast;
float beta_slow;
} args = {
.n_head = (int32_t)n_head,
.head_dim = (int32_t)head_dim,
.head_dim4 = (int32_t)(head_dim / 4u),
.n_dims = (int32_t)n_rot,
.n_ctx_orig = (int32_t)n_ctx_orig,
.pos0 = (int32_t)pos0,
.inverse = inverse ? 1 : 0,
.eps = eps,
.freq_base = freq_base,
.freq_scale = freq_scale,
.ext_factor = ext_factor,
.attn_factor = attn_factor,
.beta_fast = beta_fast,
.beta_slow = beta_slow,
};
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:1];
[enc setThreadgroupMemoryLength:32u * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(n_head, n_tok, 1)
threadsPerThreadgroup:MTLSizeMake(
ds4_gpu_rms_norm_pipeline_threads(head_dim, pipeline),
1,
1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(
cb, owned, "fused head norm/RoPE")) {
return 0;
}
}
return 1;
}
int ds4_gpu_attn_q_b_f16_head_rms_rope_tail_tensor(
ds4_gpu_tensor *out,
ds4_gpu_tensor *q_half,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint64_t in_dim,
uint64_t out_dim,
const ds4_gpu_tensor *x,
uint32_t n_tok,
uint32_t n_head,
uint32_t head_dim,
uint32_t n_rot,
uint32_t pos0,
uint32_t n_ctx_orig,
bool inverse,
float freq_base,
float freq_scale,
float ext_factor,
float attn_factor,
float beta_fast,
float beta_slow,
float eps) {
(void)out; (void)q_half; (void)model_map; (void)model_size;
(void)weight_offset; (void)in_dim; (void)out_dim; (void)x;
(void)n_tok; (void)n_head; (void)head_dim; (void)n_rot; (void)pos0;
(void)n_ctx_orig; (void)inverse; (void)freq_base; (void)freq_scale;
(void)ext_factor; (void)attn_factor; (void)beta_fast; (void)beta_slow;
(void)eps;
return 0;
}
int ds4_gpu_dsv4_fp8_kv_quantize_tensor(
ds4_gpu_tensor *x,
uint32_t n_tok,
uint32_t head_dim,
uint32_t n_rot) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!x || n_tok == 0 || head_dim == 0 || n_rot > head_dim) return 0;
if (n_rot == head_dim) return 1;
@autoreleasepool {
id<MTLBuffer> xbuf = ds4_gpu_tensor_buffer(x);
const uint64_t bytes = (uint64_t)n_tok * head_dim * sizeof(float);
if (!xbuf || ds4_gpu_tensor_bytes(x) < bytes) {
fprintf(stderr, "ds4: Metal DSV4 FP8 KV quantize received undersized activation buffer\n");
return 0;
}
ds4_gpu_dsv4_fp8_kv_quantize_args args = {
.ne00 = head_dim,
.ne01 = n_tok,
.ne02 = 1,
.ne03 = 1,
.nb00 = sizeof(float),
.nb01 = (uint64_t)head_dim * sizeof(float),
.nb02 = (uint64_t)n_tok * head_dim * sizeof(float),
.nb03 = (uint64_t)n_tok * head_dim * sizeof(float),
.nb0 = sizeof(float),
.nb1 = (uint64_t)head_dim * sizeof(float),
.nb2 = (uint64_t)n_tok * head_dim * sizeof(float),
.nb3 = (uint64_t)n_tok * head_dim * sizeof(float),
.n_rot = (int32_t)n_rot,
};
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:g_dsv4_fp8_kv_quantize_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:1];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:2];
[enc setThreadgroupMemoryLength:64u * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(n_tok, 1, 1)
threadsPerThreadgroup:MTLSizeMake(64, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "DSV4 FP8 KV quantize")) return 0;
}
return 1;
}
int ds4_gpu_dsv4_indexer_qat_tensor(
ds4_gpu_tensor *x,
uint32_t n_rows,
uint32_t head_dim) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!x || n_rows == 0 || head_dim != 128u) return 0;
@autoreleasepool {
id<MTLBuffer> xbuf = ds4_gpu_tensor_buffer(x);
const uint64_t bytes = (uint64_t)n_rows * head_dim * sizeof(float);
if (!xbuf || ds4_gpu_tensor_bytes(x) < bytes) {
fprintf(stderr, "ds4: Metal DSV4 indexer QAT received undersized activation buffer\n");
return 0;
}
ds4_gpu_dsv4_indexer_qat_args args = {
.n_rows = n_rows,
.head_dim = head_dim,
.row_stride = (uint64_t)head_dim * sizeof(float),
};
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:g_dsv4_indexer_qat_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:1];
[enc setThreadgroupMemoryLength:256u * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(n_rows, 1, 1)
threadsPerThreadgroup:MTLSizeMake(128, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "DSV4 indexer Hadamard+FP4")) return 0;
}
return 1;
}
static void ds4_gpu_set_rows_thread_shape(
uint32_t width,
NSUInteger *nth_out,
NSUInteger *nrptg_out) {
const NSUInteger nk0 = width ? (NSUInteger)width : 1u;
const NSUInteger max_threads = g_set_rows_f32_i32_pipeline
? (NSUInteger)g_set_rows_f32_i32_pipeline.maxTotalThreadsPerThreadgroup
: 1024u;
NSUInteger nth = 32u;
while (nth < nk0 && nth < max_threads) {
nth *= 2u;
}
NSUInteger nrptg = 1u;
if (nth > nk0) {
nrptg = (nth + nk0 - 1u) / nk0;
nth = nk0;
if (nrptg * nth > max_threads) {
nrptg--;
}
}
if (nth > nk0) nth = nk0;
if (nth == 0u) nth = 1u;
if (nrptg == 0u) nrptg = 1u;
*nth_out = nth;
*nrptg_out = nrptg;
}
static int ds4_gpu_encode_f16_round_copy_for_raw_store(
id<MTLCommandBuffer> cb,
const ds4_gpu_tensor *src,
uint32_t n) {
id<MTLBuffer> srcbuf = ds4_gpu_tensor_buffer(src);
const uint64_t src_bytes = (uint64_t)n * sizeof(float);
if (!srcbuf || ds4_gpu_tensor_bytes(src) < src_bytes) {
fprintf(stderr, "ds4: Metal raw KV store received undersized source buffer\n");
return 0;
}
if (!ds4_gpu_ensure_scratch_buffer(&g_f16_round_scratch_buffer,
&g_f16_round_scratch_bytes,
(NSUInteger)n * sizeof(uint16_t),
"ds4_f16_round_scratch") ||
!ds4_gpu_ensure_scratch_buffer(&g_raw_store_round_buffer,
&g_raw_store_round_bytes,
(NSUInteger)n * sizeof(float),
"ds4_raw_store_round")) {
return 0;
}
if (!ds4_gpu_encode_cpy_f32_f16_1d(cb,
srcbuf,
ds4_gpu_tensor_offset(src),
g_f16_round_scratch_buffer,
0,
n)) {
return 0;
}
return ds4_gpu_encode_cpy_f16_f32_1d(cb,
g_f16_round_scratch_buffer,
0,
g_raw_store_round_buffer,
0,
n);
}
static int ds4_gpu_encode_set_rows_f32_i32(
id<MTLCommandBuffer> cb,
ds4_gpu_tensor *dst,
id<MTLBuffer> srcbuf,
NSUInteger src_off,
const int32_t *rows,
uint32_t n_rows,
uint32_t dst_rows,
uint32_t width) {
id<MTLBuffer> dstbuf = ds4_gpu_tensor_buffer(dst);
const uint64_t dst_bytes = (uint64_t)dst_rows * width * sizeof(float);
const uint64_t src_bytes = (uint64_t)n_rows * width * sizeof(float);
if (!dstbuf || !srcbuf || !rows || n_rows == 0 || width == 0 ||
ds4_gpu_tensor_bytes(dst) < dst_bytes ||
src_bytes > NSUIntegerMax - src_off) {
fprintf(stderr, "ds4: Metal DS4 set_rows received invalid buffers\n");
return 0;
}
const uint64_t row_bytes = (uint64_t)width * sizeof(float);
const uint64_t rows_bytes = (uint64_t)n_rows * sizeof(int32_t);
ds4_gpu_set_rows_args args = {
.nk0 = (int32_t)width,
.ne01 = (int32_t)n_rows,
.nb01 = row_bytes,
.nb02 = (uint64_t)n_rows * row_bytes,
.nb03 = (uint64_t)n_rows * row_bytes,
.ne11 = 1,
.ne12 = 1,
.nb10 = sizeof(int32_t),
.nb11 = rows_bytes,
.nb12 = rows_bytes,
.nb1 = row_bytes,
.nb2 = (uint64_t)dst_rows * row_bytes,
.nb3 = (uint64_t)dst_rows * row_bytes,
};
NSUInteger nth;
NSUInteger nrptg;
ds4_gpu_set_rows_thread_shape(width, &nth, &nrptg);
id<MTLBuffer> rowsbuf = nil;
if (rows_bytes > 4096u) {
rowsbuf = ds4_gpu_new_transient_buffer((NSUInteger)rows_bytes, "ds4_set_rows_indices");
if (!rowsbuf) return 0;
memcpy([rowsbuf contents], rows, (NSUInteger)rows_bytes);
}
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:g_set_rows_f32_i32_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:srcbuf offset:src_off atIndex:1];
if (rowsbuf) {
[enc setBuffer:rowsbuf offset:0 atIndex:2];
} else {
[enc setBytes:rows length:(NSUInteger)rows_bytes atIndex:2];
}
[enc setBuffer:dstbuf offset:ds4_gpu_tensor_offset(dst) atIndex:3];
[enc dispatchThreadgroups:MTLSizeMake(((NSUInteger)n_rows + nrptg - 1u) / nrptg, 1, 1)
threadsPerThreadgroup:MTLSizeMake(nth, nrptg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_add_f32_1d(
id<MTLCommandBuffer> cb,
id<MTLBuffer> a,
NSUInteger a_off,
id<MTLBuffer> b,
NSUInteger b_off,
id<MTLBuffer> out,
NSUInteger out_off,
uint32_t n) {
if (!cb || !a || !b || !out || n == 0) return 0;
ds4_gpu_add_flat_args args = { .n = n };
NSUInteger nth = g_add2_pipeline.maxTotalThreadsPerThreadgroup;
if (nth > 256u) nth = 256u;
if (nth > (NSUInteger)n) nth = (NSUInteger)n;
if (nth == 0u) nth = 1u;
const NSUInteger groups = ((NSUInteger)n + nth - 1u) / nth;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:g_add2_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:a offset:a_off atIndex:1];
[enc setBuffer:b offset:b_off atIndex:2];
[enc setBuffer:out offset:out_off atIndex:3];
[enc dispatchThreadgroups:MTLSizeMake(groups, 1, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
int ds4_gpu_store_raw_kv_tensor(
ds4_gpu_tensor *raw_cache,
const ds4_gpu_tensor *kv,
uint32_t raw_cap,
uint32_t row,
uint32_t head_dim) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!raw_cache || !kv || raw_cap == 0 || row >= raw_cap || head_dim == 0 || raw_cap > INT32_MAX) return 0;
@autoreleasepool {
const uint64_t raw_bytes = (uint64_t)raw_cap * head_dim * sizeof(float);
if (ds4_gpu_tensor_bytes(raw_cache) < raw_bytes) {
fprintf(stderr, "ds4: Metal raw KV store received undersized destination buffer\n");
return 0;
}
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
const int32_t row_i32 = (int32_t)row;
if (!ds4_gpu_encode_f16_round_copy_for_raw_store(cb, kv, head_dim) ||
!ds4_gpu_encode_set_rows_f32_i32(cb, raw_cache,
g_raw_store_round_buffer,
0,
&row_i32,
1,
raw_cap,
head_dim)) {
return 0;
}
if (!ds4_gpu_finish_command_buffer(cb, owned, "raw KV DS4 set_rows store")) return 0;
}
return 1;
}
/* Release decode fused KV finalizer. Reference paths are selected by the C
* graph driver; this Objective-C entry point always means "use the fused
* Metal kernel." */
/* The fused KV RoPE/FP8 kernel replicates the affine decode RoPE specialisation,
* so it is only valid where that specialisation is the path in use. */
int ds4_gpu_kv_rope_fp8_fuse_available(void) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (g_dsv4_kv_rope_fp8_store_pipeline == nil) return 0;
if (g_rope_tail_inplace_pair_affine_pipeline == nil) return 0;
if (getenv("DS4_METAL_DISABLE_INPLACE_ROPE_PAIR") != NULL) return 0;
if (getenv("DS4_METAL_DISABLE_AFFINE_ROPE_PAIR") != NULL) return 0;
if (!ds4_gpu_device_name_contains("M3") && !ds4_gpu_device_name_contains("M5")) return 0;
return 1;
}
/* Decode-only fusion: one dispatch does the KV RoPE tail and the FP8/raw
* finalizer that previously cost two. Same arithmetic, same order; gated and
* verified against full-vocabulary logits. */
int ds4_gpu_kv_rope_fp8_store_raw_tensor(
ds4_gpu_tensor *kv,
ds4_gpu_tensor *raw_cache,
uint32_t raw_cap,
uint32_t row,
uint32_t head_dim,
uint32_t n_rot,
uint32_t pos0,
uint32_t n_ctx_orig,
bool inverse,
float freq_base,
float freq_scale,
float ext_factor,
float attn_factor,
float beta_fast,
float beta_slow) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!kv || !raw_cache || raw_cap == 0 || row >= raw_cap || head_dim == 0 ||
n_rot > head_dim || (n_rot & 1u) != 0 || raw_cap > INT32_MAX ||
g_dsv4_kv_rope_fp8_store_pipeline == nil) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> kvbuf = ds4_gpu_tensor_buffer(kv);
id<MTLBuffer> rawbuf = ds4_gpu_tensor_buffer(raw_cache);
const uint64_t kv_bytes = (uint64_t)head_dim * sizeof(float);
const uint64_t raw_bytes = (uint64_t)raw_cap * head_dim * sizeof(float);
if (!kvbuf || !rawbuf ||
ds4_gpu_tensor_bytes(kv) < kv_bytes ||
ds4_gpu_tensor_bytes(raw_cache) < raw_bytes) {
fprintf(stderr, "ds4: Metal fused KV RoPE/FP8 store received undersized buffers\n");
return 0;
}
ds4_gpu_dsv4_kv_fp8_store_args args = {
.head_dim = (int32_t)head_dim,
.n_rot = (int32_t)n_rot,
.raw_row = (int32_t)row,
};
const uint64_t row_bytes = (uint64_t)head_dim * sizeof(float);
ds4_gpu_rope_affine_pair_args rope = {
.row_bytes = row_bytes,
.token_bytes = row_bytes,
.head_dim = (int32_t)head_dim,
.n_dims = (int32_t)n_rot,
.n_ctx_orig = (int32_t)n_ctx_orig,
.inverse = inverse ? 1 : 0,
.pos0 = pos0,
.pos_step = 1,
.freq_base = freq_base,
.freq_scale = freq_scale,
.ext_factor = ext_factor,
.attn_factor = attn_factor,
.beta_fast = beta_fast,
.beta_slow = beta_slow,
};
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:g_dsv4_kv_rope_fp8_store_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBytes:&rope length:sizeof(rope) atIndex:1];
[enc setBuffer:kvbuf offset:ds4_gpu_tensor_offset(kv) atIndex:2];
[enc setBuffer:rawbuf offset:ds4_gpu_tensor_offset(raw_cache) atIndex:3];
[enc setThreadgroupMemoryLength:64u * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(1, 1, 1)
threadsPerThreadgroup:MTLSizeMake(64, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "KV RoPE/FP8 store fused")) return 0;
}
return 1;
}
int ds4_gpu_kv_fp8_store_raw_tensor(
ds4_gpu_tensor *kv,
ds4_gpu_tensor *raw_cache,
uint32_t raw_cap,
uint32_t row,
uint32_t head_dim,
uint32_t n_rot) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!kv || !raw_cache || raw_cap == 0 || row >= raw_cap || head_dim == 0 ||
n_rot > head_dim || raw_cap > INT32_MAX) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> kvbuf = ds4_gpu_tensor_buffer(kv);
id<MTLBuffer> rawbuf = ds4_gpu_tensor_buffer(raw_cache);
const uint64_t kv_bytes = (uint64_t)head_dim * sizeof(float);
const uint64_t raw_bytes = (uint64_t)raw_cap * head_dim * sizeof(float);
if (!kvbuf || !rawbuf ||
ds4_gpu_tensor_bytes(kv) < kv_bytes ||
ds4_gpu_tensor_bytes(raw_cache) < raw_bytes) {
fprintf(stderr, "ds4: Metal fused KV FP8/raw-store received undersized buffers\n");
return 0;
}
ds4_gpu_dsv4_kv_fp8_store_args args = {
.head_dim = (int32_t)head_dim,
.n_rot = (int32_t)n_rot,
.raw_row = (int32_t)row,
};
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:g_dsv4_kv_fp8_store_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:kvbuf offset:ds4_gpu_tensor_offset(kv) atIndex:1];
[enc setBuffer:rawbuf offset:ds4_gpu_tensor_offset(raw_cache) atIndex:2];
[enc setThreadgroupMemoryLength:64u * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(1, 1, 1)
threadsPerThreadgroup:MTLSizeMake(64, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "KV FP8/raw-store fused")) return 0;
}
return 1;
}
int ds4_gpu_store_raw_kv_batch_tensor(
ds4_gpu_tensor *raw_cache,
const ds4_gpu_tensor *kv,
uint32_t raw_cap,
uint32_t pos0,
uint32_t n_tokens,
uint32_t head_dim) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!raw_cache || !kv || raw_cap == 0 || n_tokens == 0 || head_dim == 0 || raw_cap > INT32_MAX) return 0;
@autoreleasepool {
const uint64_t raw_bytes = (uint64_t)raw_cap * head_dim * sizeof(float);
if (ds4_gpu_tensor_bytes(raw_cache) < raw_bytes) {
fprintf(stderr, "ds4: Metal raw KV batch store received undersized destination buffer\n");
return 0;
}
int32_t rows_stack[512];
int32_t *rows = rows_stack;
if (n_tokens > (uint32_t)(sizeof(rows_stack) / sizeof(rows_stack[0]))) {
rows = malloc((size_t)n_tokens * sizeof(*rows));
if (!rows) {
fprintf(stderr, "ds4: failed to allocate raw KV set_rows index list\n");
return 0;
}
}
for (uint32_t t = 0; t < n_tokens; t++) {
rows[t] = (int32_t)((pos0 + t) % raw_cap);
}
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) {
if (rows != rows_stack) free(rows);
return 0;
}
const uint64_t n = (uint64_t)n_tokens * head_dim;
const int ok = n <= UINT32_MAX &&
ds4_gpu_encode_f16_round_copy_for_raw_store(cb, kv, (uint32_t)n) &&
ds4_gpu_encode_set_rows_f32_i32(cb, raw_cache,
g_raw_store_round_buffer,
0,
rows,
n_tokens,
raw_cap,
head_dim);
if (rows != rows_stack) free(rows);
if (!ok) return 0;
if (!ds4_gpu_finish_command_buffer(cb, owned, "raw KV batch DS4 set_rows store")) return 0;
}
return 1;
}
typedef struct {
uint32_t n_total;
uint32_t width;
uint32_t ratio;
uint32_t pos0;
} ds4_gpu_cpy_tile_rows_args;
/* dst[t*width + e] = ape[((pos0 + t) % ratio)*width + e] for t < n_tokens,
* converting f16 -> f32 when ape_type == 1. One dispatch for the whole
* batch (the previous per-segment copies were n_tokens/ratio encoders). */
static int ds4_gpu_encode_cpy_ape_tile_rows(
id<MTLCommandBuffer> cb,
id<MTLBuffer> ape,
NSUInteger ape_off,
uint32_t ape_type,
id<MTLBuffer> dst,
uint32_t width,
uint32_t ratio,
uint32_t pos0,
uint32_t n_tokens) {
if (!cb || !ape || !dst || width == 0 || ratio == 0 || n_tokens == 0) return 0;
const uint64_t n_total64 = (uint64_t)n_tokens * width;
if (n_total64 > UINT32_MAX) return 0;
id<MTLComputePipelineState> pipeline = ds4_gpu_get_pipeline(
ape_type == 1u ? "kernel_cpy_tile_rows_f16_f32" : "kernel_cpy_tile_rows_f32_f32");
if (!pipeline) return 0;
ds4_gpu_cpy_tile_rows_args args = { (uint32_t)n_total64, width, ratio, pos0 };
const NSUInteger nth = 256;
const NSUInteger groups = ((NSUInteger)args.n_total + nth - 1u) / nth;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:ape offset:ape_off atIndex:1];
[enc setBuffer:dst offset:0 atIndex:2];
[enc dispatchThreadgroups:MTLSizeMake(groups, 1, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_compressor_score_with_ape(
id<MTLCommandBuffer> cb,
id<MTLBuffer> score_src,
NSUInteger score_src_offset,
id<MTLBuffer> score_dst,
NSUInteger score_dst_offset,
id<MTLBuffer> apebuf,
NSUInteger ape_offset,
uint32_t ape_type,
uint32_t width,
uint32_t ratio,
uint32_t pos0,
uint32_t n_tokens) {
if (!cb || !score_src || !score_dst || !apebuf ||
width == 0 || ratio == 0 || n_tokens == 0 ||
(ape_type != 0u && ape_type != 1u)) {
return 0;
}
const uint64_t total_elems64 = (uint64_t)n_tokens * width;
if (total_elems64 > UINT32_MAX) {
fprintf(stderr, "ds4: Metal compressor APE add received too many elements\n");
return 0;
}
const uint32_t total_elems = (uint32_t)total_elems64;
const bool use_fused =
ds4_gpu_device_name_contains("M3") &&
getenv("DS4_METAL_DISABLE_COMPRESSOR_APE_ADD") == NULL;
if (use_fused) {
id<MTLComputePipelineState> pipeline = ds4_gpu_get_pipeline(
ape_type == 1u ? "kernel_dsv4_compressor_score_ape_f16"
: "kernel_dsv4_compressor_score_ape_f32");
if (pipeline) {
ds4_gpu_dsv4_compressor_score_ape_args args = {
.width = width,
.ratio = ratio,
.pos0 = pos0,
.n_tokens = n_tokens,
};
NSUInteger nth = pipeline.maxTotalThreadsPerThreadgroup;
if (nth > 256u) nth = 256u;
if (nth == 0) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:score_src offset:score_src_offset atIndex:1];
[enc setBuffer:apebuf offset:ape_offset atIndex:2];
[enc setBuffer:score_dst offset:score_dst_offset atIndex:3];
[enc dispatchThreadgroups:MTLSizeMake(
((NSUInteger)total_elems + nth - 1u) / nth, 1, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
}
const NSUInteger scratch_bytes = (NSUInteger)total_elems * sizeof(float);
if (!ds4_gpu_ensure_scratch_buffer(&g_compressor_store_ape_buffer,
&g_compressor_store_ape_bytes,
scratch_bytes,
"ds4_compressor_store_ape")) {
return 0;
}
if (!ds4_gpu_encode_cpy_ape_tile_rows(cb, apebuf, ape_offset, ape_type,
g_compressor_store_ape_buffer,
width, ratio, pos0, n_tokens)) {
return 0;
}
return ds4_gpu_encode_add_f32_1d(cb,
score_src,
score_src_offset,
g_compressor_store_ape_buffer,
0,
score_dst,
score_dst_offset,
total_elems);
}
static int ds4_gpu_encode_compressor_set_rows_projected(
id<MTLCommandBuffer> cb,
ds4_gpu_tensor *state_kv,
ds4_gpu_tensor *state_score,
id<MTLBuffer> kvbuf,
NSUInteger kv_offset,
id<MTLBuffer> scorebuf,
NSUInteger score_offset,
id<MTLBuffer> apebuf,
NSUInteger ape_offset,
uint32_t ape_type,
uint32_t width,
uint32_t ratio,
uint32_t pos0,
const int32_t *rows,
uint32_t n_rows,
uint32_t state_rows) {
if (!cb || !state_kv || !state_score || !kvbuf || !scorebuf ||
!apebuf || !rows || width == 0 || n_rows == 0 || state_rows == 0) {
return 0;
}
const NSUInteger score_scratch_bytes = (NSUInteger)n_rows * width * sizeof(float);
if (!ds4_gpu_ensure_scratch_buffer(&g_compressor_store_score_buffer,
&g_compressor_store_score_bytes,
score_scratch_bytes,
"ds4_compressor_store_score")) {
return 0;
}
return ds4_gpu_encode_compressor_score_with_ape(cb,
scorebuf,
score_offset,
g_compressor_store_score_buffer,
0,
apebuf,
ape_offset,
ape_type,
width,
ratio,
pos0,
n_rows) &&
ds4_gpu_encode_set_rows_f32_i32(cb,
state_kv,
kvbuf,
kv_offset,
rows,
n_rows,
state_rows,
width) &&
ds4_gpu_encode_set_rows_f32_i32(cb,
state_score,
g_compressor_store_score_buffer,
0,
rows,
n_rows,
state_rows,
width);
}
static int ds4_gpu_compressor_store_one_tensor(
const ds4_gpu_tensor *kv,
const ds4_gpu_tensor *sc,
ds4_gpu_tensor *state_kv,
ds4_gpu_tensor *state_score,
const void *model_map,
uint64_t model_size,
uint64_t ape_offset,
uint32_t ape_type,
uint32_t width,
uint32_t ratio,
uint32_t pos) {
if (!kv || !sc || !state_kv || !state_score || !model_map ||
width == 0 || ratio == 0 || (ape_type != 0u && ape_type != 1u)) {
return 0;
}
id<MTLComputePipelineState> pipeline =
ds4_gpu_hot_pipeline(g_dsv4_compressor_store_one_pipeline,
"kernel_dsv4_compressor_store_one");
if (!pipeline) return 0;
const uint32_t state_rows = ratio == 4u ? 2u * ratio : ratio;
const uint64_t elem_ape = ape_type == 1u ? 2u : 4u;
const uint64_t row_bytes = (uint64_t)width * sizeof(float);
const uint64_t state_bytes = (uint64_t)state_rows * row_bytes;
const uint64_t ape_bytes = (uint64_t)width * ratio * elem_ape;
if (ape_offset > model_size || ape_bytes > model_size - ape_offset ||
ds4_gpu_tensor_bytes(kv) < row_bytes ||
ds4_gpu_tensor_bytes(sc) < row_bytes ||
ds4_gpu_tensor_bytes(state_kv) < state_bytes ||
ds4_gpu_tensor_bytes(state_score) < state_bytes) {
return 0;
}
uint64_t ape_inner = 0;
id<MTLBuffer> apebuf = ds4_gpu_wrap_model_range(model_map, model_size,
ape_offset, ape_bytes,
&ape_inner);
id<MTLBuffer> kvbuf = ds4_gpu_tensor_buffer(kv);
id<MTLBuffer> scbuf = ds4_gpu_tensor_buffer(sc);
id<MTLBuffer> statekvbuf = ds4_gpu_tensor_buffer(state_kv);
id<MTLBuffer> statescbuf = ds4_gpu_tensor_buffer(state_score);
if (!apebuf || !kvbuf || !scbuf || !statekvbuf || !statescbuf) return 0;
ds4_gpu_dsv4_compressor_store_one_args args = {
.width = width,
.ratio = ratio,
.pos = pos,
.ape_type = ape_type,
};
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
const NSUInteger nth = 256u;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:kvbuf offset:ds4_gpu_tensor_offset(kv) atIndex:1];
[enc setBuffer:scbuf offset:ds4_gpu_tensor_offset(sc) atIndex:2];
[enc setBuffer:apebuf offset:(NSUInteger)ape_inner atIndex:3];
[enc setBuffer:statekvbuf offset:ds4_gpu_tensor_offset(state_kv) atIndex:4];
[enc setBuffer:statescbuf offset:ds4_gpu_tensor_offset(state_score) atIndex:5];
[enc dispatchThreadgroups:MTLSizeMake(((NSUInteger)width + nth - 1u) / nth, 1, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_finish_command_buffer(cb, owned, "compressor one-row store");
}
int ds4_gpu_compressor_store_batch_tensor(
const ds4_gpu_tensor *kv,
const ds4_gpu_tensor *sc,
ds4_gpu_tensor *state_kv,
ds4_gpu_tensor *state_score,
const void *model_map,
uint64_t model_size,
uint64_t ape_offset,
uint32_t ape_type,
uint32_t head_dim,
uint32_t ratio,
uint32_t pos0,
uint32_t n_tokens) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!kv || !sc || !state_kv || !state_score || !model_map ||
head_dim == 0 || ratio == 0 || n_tokens == 0 ||
(ape_type != 0u && ape_type != 1u)) {
return 0;
}
@autoreleasepool {
const uint32_t coff = ratio == 4u ? 2u : 1u;
const uint32_t width = coff * head_dim;
const uint32_t state_rows = coff * ratio;
const uint64_t elem_ape = ape_type == 1u ? 2u : 4u;
const uint64_t kv_bytes = (uint64_t)n_tokens * width * sizeof(float);
const uint64_t state_bytes = (uint64_t)state_rows * width * sizeof(float);
const uint64_t ape_bytes = (uint64_t)width * ratio * elem_ape;
if (ape_offset > model_size || ape_bytes > model_size - ape_offset) {
fprintf(stderr, "ds4: Metal compressor batch APE range is outside the mapped model\n");
return 0;
}
id<MTLBuffer> kvbuf = ds4_gpu_tensor_buffer(kv);
id<MTLBuffer> scbuf = ds4_gpu_tensor_buffer(sc);
if (!kvbuf || !scbuf ||
ds4_gpu_tensor_bytes(kv) < kv_bytes ||
ds4_gpu_tensor_bytes(sc) < kv_bytes ||
ds4_gpu_tensor_bytes(state_kv) < state_bytes ||
ds4_gpu_tensor_bytes(state_score) < state_bytes) {
fprintf(stderr, "ds4: Metal compressor batch store received undersized buffers\n");
return 0;
}
uint64_t ape_inner = 0;
id<MTLBuffer> apebuf = ds4_gpu_wrap_model_range(model_map, model_size, ape_offset, ape_bytes, &ape_inner);
if (!apebuf) return 0;
const uint64_t total_elems64 = (uint64_t)n_tokens * width;
if (total_elems64 > UINT32_MAX || state_rows > INT32_MAX) {
fprintf(stderr, "ds4: Metal compressor batch store received too many elements\n");
return 0;
}
const uint32_t total_elems = (uint32_t)total_elems64;
const NSUInteger scratch_bytes = (NSUInteger)total_elems * sizeof(float);
if (!ds4_gpu_ensure_scratch_buffer(&g_compressor_store_ape_buffer,
&g_compressor_store_ape_bytes,
scratch_bytes,
"ds4_compressor_store_ape") ||
!ds4_gpu_ensure_scratch_buffer(&g_compressor_store_score_buffer,
&g_compressor_store_score_bytes,
scratch_bytes,
"ds4_compressor_store_score")) {
return 0;
}
int32_t rows_stack[16];
int32_t *rows = rows_stack;
if (n_tokens > (uint32_t)(sizeof(rows_stack) / sizeof(rows_stack[0]))) {
rows = malloc((size_t)n_tokens * sizeof(*rows));
if (!rows) {
fprintf(stderr, "ds4: failed to allocate compressor set_rows index list\n");
return 0;
}
}
for (uint32_t t = 0; t < n_tokens; t++) {
const uint32_t pos_mod = (pos0 + t) % ratio;
rows[t] = (int32_t)(ratio == 4u ? ratio + pos_mod : pos_mod);
}
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) {
if (rows != rows_stack) free(rows);
return 0;
}
int ok = ds4_gpu_encode_cpy_ape_tile_rows(cb, apebuf, (NSUInteger)ape_inner, ape_type,
g_compressor_store_ape_buffer,
width, ratio, pos0, n_tokens);
if (ok) {
ok = ds4_gpu_encode_add_f32_1d(cb,
scbuf,
ds4_gpu_tensor_offset(sc),
g_compressor_store_ape_buffer,
0,
g_compressor_store_score_buffer,
0,
total_elems);
}
if (ok) {
ok = ds4_gpu_encode_set_rows_f32_i32(cb,
state_kv,
kvbuf,
ds4_gpu_tensor_offset(kv),
rows,
n_tokens,
state_rows,
width);
}
if (ok) {
ok = ds4_gpu_encode_set_rows_f32_i32(cb,
state_score,
g_compressor_store_score_buffer,
0,
rows,
n_tokens,
state_rows,
width);
}
if (rows != rows_stack) free(rows);
if (!ok) return 0;
if (!ds4_gpu_finish_command_buffer(cb, owned, "compressor batch DS4 store")) return 0;
}
return 1;
}
static ds4_gpu_bin_args ds4_gpu_make_bin_contiguous_3d_args(
uint32_t cols,
uint32_t rows,
uint32_t planes) {
const uint64_t row_bytes = (uint64_t)cols * sizeof(float);
const uint64_t plane_bytes = (uint64_t)rows * row_bytes;
return (ds4_gpu_bin_args) {
.ne00 = (int32_t)cols,
.ne01 = (int32_t)rows,
.ne02 = (int32_t)planes,
.ne03 = 1,
.nb00 = sizeof(float),
.nb01 = row_bytes,
.nb02 = plane_bytes,
.nb03 = (uint64_t)planes * plane_bytes,
.ne10 = (int32_t)cols,
.ne11 = (int32_t)rows,
.ne12 = (int32_t)planes,
.ne13 = 1,
.nb10 = sizeof(float),
.nb11 = row_bytes,
.nb12 = plane_bytes,
.nb13 = (uint64_t)planes * plane_bytes,
.ne0 = (int32_t)cols,
.ne1 = (int32_t)rows,
.ne2 = (int32_t)planes,
.ne3 = 1,
.nb0 = sizeof(float),
.nb1 = row_bytes,
.nb2 = plane_bytes,
.nb3 = (uint64_t)planes * plane_bytes,
.offs = 0,
.o1 = { 0 },
};
}
static int ds4_gpu_encode_softmax_f32_contiguous(
id<MTLCommandBuffer> cb,
id<MTLBuffer> src,
NSUInteger src_off,
id<MTLBuffer> dst,
NSUInteger dst_off,
uint32_t width,
uint32_t rows,
uint32_t planes) {
if (!cb || !src || !dst || width == 0 || rows == 0 || planes == 0) return 0;
const uint64_t row_bytes = (uint64_t)width * sizeof(float);
const uint64_t plane_bytes = (uint64_t)rows * row_bytes;
ds4_gpu_softmax_args args = {
.ne00 = (int32_t)width,
.ne01 = (int32_t)rows,
.ne02 = (int32_t)planes,
.nb01 = row_bytes,
.nb02 = plane_bytes,
.nb03 = (uint64_t)planes * plane_bytes,
.ne11 = (int32_t)width,
.ne12 = (int32_t)rows,
.ne13 = (int32_t)planes,
.nb11 = row_bytes,
.nb12 = plane_bytes,
.nb13 = (uint64_t)planes * plane_bytes,
.nb1 = row_bytes,
.nb2 = plane_bytes,
.nb3 = (uint64_t)planes * plane_bytes,
.scale = 1.0f,
.max_bias = 0.0f,
.m0 = 0.0f,
.m1 = 0.0f,
.n_head_log2 = 1,
};
id<MTLComputePipelineState> pipeline =
(width % 4u) == 0 ? g_soft_max_f32_4_pipeline : g_soft_max_f32_pipeline;
if (!pipeline) return 0;
NSUInteger nth = 32u;
if ((width % 4u) == 0) {
while (nth < (NSUInteger)(width / 4u) &&
nth * (NSUInteger)rows * (NSUInteger)planes < 256u) {
nth *= 2u;
}
} else {
while (nth < (NSUInteger)width &&
nth * (NSUInteger)rows * (NSUInteger)planes < 256u) {
nth *= 2u;
}
}
const NSUInteger max_threads = pipeline.maxTotalThreadsPerThreadgroup;
if (nth > max_threads) nth = max_threads;
if (nth == 0) nth = 1u;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:src offset:src_off atIndex:1];
[enc setBuffer:src offset:src_off atIndex:2];
[enc setBuffer:src offset:src_off atIndex:3];
[enc setBuffer:dst offset:dst_off atIndex:4];
[enc setThreadgroupMemoryLength:32u * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(rows, planes, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_ensure_compressor_pool_ggml_scratch(
uint32_t n_rows,
uint32_t head_dim) {
const uint64_t pack_elems = (uint64_t)n_rows * head_dim;
if (n_rows == 0u || head_dim == 0u ||
pack_elems > NSUIntegerMax / sizeof(float)) {
return 0;
}
const NSUInteger pack_bytes =
(NSUInteger)pack_elems * sizeof(float);
return
ds4_gpu_ensure_scratch_buffer(&g_compressor_pool_product_buffer,
&g_compressor_pool_product_bytes,
pack_bytes,
"ds4_compressor_pool_product") &&
ds4_gpu_ensure_scratch_buffer(&g_compressor_pool_score_cont_buffer,
&g_compressor_pool_score_cont_bytes,
pack_bytes,
"ds4_compressor_pool_score_cont") &&
ds4_gpu_ensure_scratch_buffer(&g_compressor_pool_softmax_buffer,
&g_compressor_pool_softmax_bytes,
pack_bytes,
"ds4_compressor_pool_softmax");
}
static int ds4_gpu_encode_dsv4_softmax_pool_one_comp_ggml_reduce(
id<MTLCommandBuffer> cb,
ds4_gpu_tensor *out,
uint32_t n_rows,
uint32_t head_dim,
bool decode_ratio4_one_comp) {
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out);
if (!cb || !outbuf || n_rows == 0u || head_dim == 0u ||
ds4_gpu_tensor_bytes(out) < (uint64_t)head_dim * sizeof(float) ||
!ds4_gpu_ensure_compressor_pool_ggml_scratch(n_rows, head_dim)) {
return 0;
}
const bool use_pre_m5_exact_reduction_fusion_default =
!g_ssd_streaming_mode &&
(ds4_gpu_device_is_pre_m5_apple_silicon() ||
ds4_gpu_device_is_m5_apple_silicon()) &&
getenv("DS4_METAL_DISABLE_PRE_M5_COMPRESSOR_EXACT_REDUCTION_FUSION") == NULL;
const bool request_exact_reduction_fusion =
decode_ratio4_one_comp &&
n_rows == 8u &&
(head_dim == 128u || head_dim == 512u) &&
use_pre_m5_exact_reduction_fusion_default;
id<MTLComputePipelineState> exact_reduction_fusion_pipeline = nil;
if (request_exact_reduction_fusion) {
exact_reduction_fusion_pipeline = ds4_gpu_hot_pipeline(
g_dsv4_compressor_exact_softmax_product_pipeline,
"kernel_dsv4_compressor_exact_softmax_product_ratio4");
}
const bool use_exact_reduction_fusion =
exact_reduction_fusion_pipeline != nil &&
exact_reduction_fusion_pipeline.threadExecutionWidth == 32u &&
exact_reduction_fusion_pipeline.maxTotalThreadsPerThreadgroup >= 32u;
if (use_exact_reduction_fusion) {
if (getenv(
"DS4_METAL_TEST_POISON_COMPRESSOR_EXACT_REDUCTION_SCRATCH") != NULL) {
const uint32_t poison_bits = 0x7fc01234u;
float poison;
memcpy(&poison, &poison_bits, sizeof(poison));
if (!ds4_gpu_encode_fill_f32_rows(cb,
g_compressor_pool_softmax_buffer,
0,
n_rows,
head_dim,
poison)) {
return 0;
}
}
ds4_gpu_dsv4_compressor_pack_ratio4_args args = {
.head_dim = head_dim,
.n_comp = 1u,
.replay = 1u,
.n_threads = 32u,
};
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:exact_reduction_fusion_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:g_compressor_pool_product_buffer offset:0 atIndex:1];
[enc setBuffer:g_compressor_pool_score_cont_buffer offset:0 atIndex:2];
[enc setBuffer:g_compressor_pool_softmax_buffer offset:0 atIndex:3];
[enc setBuffer:g_compressor_pool_product_buffer offset:0 atIndex:4];
[enc setThreadgroupMemoryLength:32u * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(head_dim, 1, 1)
threadsPerThreadgroup:MTLSizeMake(32, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_encode_sum_rows_f32(cb,
g_compressor_pool_product_buffer,
0,
outbuf,
ds4_gpu_tensor_offset(out),
n_rows,
head_dim);
}
/*
* Preserve the exact graph reduction sequence and its contiguous
* [head_dim, n_rows] scratch layout. Only the preceding copy/transpose
* may be fused by callers.
*/
ds4_gpu_bin_args mul_args =
ds4_gpu_make_bin_contiguous_3d_args(n_rows, head_dim, 1);
return
ds4_gpu_encode_softmax_f32_contiguous(cb,
g_compressor_pool_score_cont_buffer,
0,
g_compressor_pool_softmax_buffer,
0,
n_rows,
head_dim,
1) &&
ds4_gpu_encode_bin_f32_rows(cb,
g_mul_pipeline,
&mul_args,
g_compressor_pool_product_buffer,
0,
g_compressor_pool_softmax_buffer,
0,
g_compressor_pool_product_buffer,
0) &&
ds4_gpu_encode_sum_rows_f32(cb,
g_compressor_pool_product_buffer,
0,
outbuf,
ds4_gpu_tensor_offset(out),
n_rows,
head_dim);
}
static int ds4_gpu_encode_dsv4_softmax_pool_one_comp_ggml(
id<MTLCommandBuffer> cb,
ds4_gpu_tensor *out,
id<MTLBuffer> kvbuf,
NSUInteger kv_offset,
uint64_t kv_nb0,
uint64_t kv_nb1,
uint64_t kv_nb2,
id<MTLBuffer> scorebuf,
NSUInteger score_offset,
uint64_t score_nb0,
uint64_t score_nb1,
uint64_t score_nb2,
uint32_t n_rows,
uint32_t head_dim) {
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out);
if (!cb || !outbuf || !kvbuf || !scorebuf || n_rows == 0 || head_dim == 0 ||
ds4_gpu_tensor_bytes(out) < (uint64_t)head_dim * sizeof(float)) {
return 0;
}
if (!ds4_gpu_ensure_compressor_pool_ggml_scratch(n_rows, head_dim)) {
return 0;
}
const uint64_t cont_row_stride = (uint64_t)n_rows * sizeof(float);
const uint64_t cont_plane_stride = (uint64_t)head_dim * cont_row_stride;
return
ds4_gpu_encode_cpy_f32_f32_3d_src_strided(cb,
kvbuf,
kv_offset,
g_compressor_pool_product_buffer,
0,
n_rows,
head_dim,
1,
kv_nb0,
kv_nb1,
kv_nb2,
cont_row_stride,
cont_plane_stride) &&
ds4_gpu_encode_cpy_f32_f32_3d_src_strided(cb,
scorebuf,
score_offset,
g_compressor_pool_score_cont_buffer,
0,
n_rows,
head_dim,
1,
score_nb0,
score_nb1,
score_nb2,
cont_row_stride,
cont_plane_stride) &&
ds4_gpu_encode_dsv4_softmax_pool_one_comp_ggml_reduce(cb,
out,
n_rows,
head_dim,
false);
}
static int ds4_gpu_encode_dsv4_softmax_pool(
id<MTLCommandBuffer> cb,
ds4_gpu_tensor *out,
id<MTLBuffer> kvbuf,
NSUInteger kv_offset,
uint64_t kv_nb0,
uint64_t kv_nb1,
uint64_t kv_nb2,
id<MTLBuffer> scorebuf,
NSUInteger score_offset,
uint64_t score_nb0,
uint64_t score_nb1,
uint64_t score_nb2,
uint32_t n_rows,
uint32_t head_dim,
uint32_t n_comp) {
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out);
if (!cb || !outbuf || !kvbuf || !scorebuf ||
n_rows == 0 || head_dim == 0 || n_comp == 0 ||
ds4_gpu_tensor_bytes(out) < (uint64_t)head_dim * n_comp * sizeof(float)) {
return 0;
}
if (n_comp == 1) {
return ds4_gpu_encode_dsv4_softmax_pool_one_comp_ggml(cb,
out,
kvbuf,
kv_offset,
kv_nb0,
kv_nb1,
kv_nb2,
scorebuf,
score_offset,
score_nb0,
score_nb1,
score_nb2,
n_rows,
head_dim);
}
ds4_gpu_dsv4_softmax_pool_args args = {
.ne00 = (int64_t)n_rows,
.ne01 = (int64_t)head_dim,
.ne02 = (int64_t)n_comp,
.nb00 = kv_nb0,
.nb01 = kv_nb1,
.nb02 = kv_nb2,
.nb10 = score_nb0,
.nb11 = score_nb1,
.nb12 = score_nb2,
.ne0 = (int64_t)head_dim,
.ne1 = (int64_t)n_comp,
.nb0 = sizeof(float),
.nb1 = (uint64_t)head_dim * sizeof(float),
};
const uint64_t n = (uint64_t)head_dim * n_comp;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:g_dsv4_softmax_pool_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:kvbuf offset:kv_offset atIndex:1];
[enc setBuffer:scorebuf offset:score_offset atIndex:2];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(out) atIndex:3];
[enc dispatchThreadgroups:MTLSizeMake(((NSUInteger)n + 255u) / 256u, 1, 1)
threadsPerThreadgroup:MTLSizeMake(256, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_concat_f32_dim1(
id<MTLCommandBuffer> cb,
id<MTLBuffer> src0,
NSUInteger src0_offset,
uint32_t src0_rows,
uint64_t src0_row_stride,
id<MTLBuffer> src1,
NSUInteger src1_offset,
uint32_t src1_rows,
uint64_t src1_row_stride,
id<MTLBuffer> dst,
NSUInteger dst_offset,
uint32_t cols,
uint64_t dst_row_stride) {
if (!cb || !src0 || !src1 || !dst || cols == 0 || src0_rows == 0 || src1_rows == 0) {
return 0;
}
const uint32_t rows = src0_rows + src1_rows;
const uint64_t src0_plane = (uint64_t)src0_rows * src0_row_stride;
const uint64_t src1_plane = (uint64_t)src1_rows * src1_row_stride;
const uint64_t dst_plane = (uint64_t)rows * dst_row_stride;
ds4_gpu_concat_args args = {
.ne00 = (int32_t)cols,
.ne01 = (int32_t)src0_rows,
.ne02 = 1,
.ne03 = 1,
.nb00 = sizeof(float),
.nb01 = src0_row_stride,
.nb02 = src0_plane,
.nb03 = src0_plane,
.ne10 = (int32_t)cols,
.ne11 = (int32_t)src1_rows,
.ne12 = 1,
.ne13 = 1,
.nb10 = sizeof(float),
.nb11 = src1_row_stride,
.nb12 = src1_plane,
.nb13 = src1_plane,
.ne0 = (int32_t)cols,
.ne1 = (int32_t)rows,
.ne2 = 1,
.ne3 = 1,
.nb0 = sizeof(float),
.nb1 = dst_row_stride,
.nb2 = dst_plane,
.nb3 = dst_plane,
.dim = 1,
};
NSUInteger nth = cols < 1024u ? (NSUInteger)cols : 1024u;
const NSUInteger max_threads = g_concat_pipeline.maxTotalThreadsPerThreadgroup;
if (nth > max_threads) nth = max_threads;
if (nth == 0) nth = 1;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:g_concat_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:src0 offset:src0_offset atIndex:1];
[enc setBuffer:src1 offset:src1_offset atIndex:2];
[enc setBuffer:dst offset:dst_offset atIndex:3];
[enc dispatchThreadgroups:MTLSizeMake(rows, 1, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_compressor_pack_ratio4_fusion_mode(uint32_t head_dim) {
const bool default_shape = head_dim == 128u || head_dim == 512u;
if (getenv("DS4_METAL_DISABLE_COMPRESSOR_RATIO4_PACK_FUSION") != NULL ||
!(ds4_gpu_device_name_contains("M3") && default_shape)) {
return 0;
}
if (g_dsv4_compressor_pack_ratio4_pipeline == nil) {
return 0;
}
return 1;
}
static int ds4_gpu_compressor_ratio4_decode_pack_mode(uint32_t head_dim) {
const bool default_shape = head_dim == 128u || head_dim == 512u;
const bool default_pre_m5 =
(ds4_gpu_device_is_pre_m5_apple_silicon() ||
ds4_gpu_device_is_m5_apple_silicon()) && default_shape;
if (getenv("DS4_METAL_DISABLE_PRE_M5_COMPRESSOR_RATIO4_DECODE_PACK_FUSION") != NULL ||
!default_pre_m5) {
return 0;
}
if (g_dsv4_compressor_pack_ratio4_decode_ggml_pipeline == nil) {
return 0;
}
return 1;
}
static bool ds4_gpu_buffer_ranges_overlap(
id<MTLBuffer> a,
NSUInteger a_offset,
uint64_t a_bytes,
id<MTLBuffer> b,
NSUInteger b_offset,
uint64_t b_bytes) {
if (a != b || a_bytes == 0u || b_bytes == 0u) return false;
if (a_offset <= b_offset) {
return a_bytes > (uint64_t)(b_offset - a_offset);
}
return b_bytes > (uint64_t)(a_offset - b_offset);
}
static int ds4_gpu_compressor_ratio4_direct_pool_mode(
uint32_t head_dim,
uint32_t n_comp) {
// One compressed row intentionally uses the legacy GGML reduction graph.
if (n_comp <= 1u) return 0;
const bool default_shape = head_dim == 128u || head_dim == 512u;
if (getenv("DS4_METAL_DISABLE_COMPRESSOR_RATIO4_DIRECT_POOL") != NULL ||
!(ds4_gpu_device_name_contains("M3") && default_shape)) {
return 0;
}
if (g_dsv4_softmax_pool_ratio4_direct_pipeline == nil) {
return 0;
}
return 1;
}
static int ds4_gpu_encode_compressor_ratio4_direct_pool(
id<MTLCommandBuffer> cb,
ds4_gpu_tensor *out,
id<MTLBuffer> kvbuf,
NSUInteger kv_offset,
id<MTLBuffer> scorebuf,
NSUInteger score_offset,
id<MTLBuffer> statekvbuf,
NSUInteger state_kv_offset,
id<MTLBuffer> statescbuf,
NSUInteger state_score_offset,
uint32_t head_dim,
uint32_t n_comp,
bool replay) {
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out);
const uint64_t n = (uint64_t)head_dim * n_comp;
if (!cb || !outbuf || !kvbuf || !scorebuf || !statekvbuf || !statescbuf ||
head_dim == 0u || n_comp <= 1u || n > UINT32_MAX ||
ds4_gpu_tensor_bytes(out) < n * sizeof(float)) {
return 0;
}
id<MTLComputePipelineState> pipeline = ds4_gpu_hot_pipeline(
g_dsv4_softmax_pool_ratio4_direct_pipeline,
"kernel_dsv4_softmax_pool_ratio4_direct");
if (!pipeline) return 0;
ds4_gpu_dsv4_softmax_pool_ratio4_direct_args args = {
.n_rows = 8,
.head_dim = head_dim,
.n_comp = n_comp,
.replay = replay ? 1u : 0u,
.pad = 0u,
};
NSUInteger nth = 256u;
if (nth > pipeline.maxTotalThreadsPerThreadgroup) {
nth = pipeline.maxTotalThreadsPerThreadgroup;
}
if (nth == 0u) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:kvbuf offset:kv_offset atIndex:1];
[enc setBuffer:scorebuf offset:score_offset atIndex:2];
[enc setBuffer:statekvbuf offset:state_kv_offset atIndex:3];
[enc setBuffer:statescbuf offset:state_score_offset atIndex:4];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(out) atIndex:5];
[enc dispatchThreadgroups:MTLSizeMake(((NSUInteger)n + nth - 1u) / nth, 1, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_compressor_pack_ratio4(
id<MTLCommandBuffer> cb,
id<MTLBuffer> kvbuf,
NSUInteger kv_offset,
id<MTLBuffer> scorebuf,
NSUInteger score_offset,
id<MTLBuffer> statekvbuf,
NSUInteger state_kv_offset,
id<MTLBuffer> statescbuf,
NSUInteger state_score_offset,
uint32_t head_dim,
uint32_t n_comp,
bool replay) {
if (!cb || !kvbuf || !scorebuf || !statekvbuf || !statescbuf ||
!g_compressor_pool_kv_buffer || !g_compressor_pool_score_buffer ||
head_dim == 0 || n_comp == 0 || head_dim > UINT32_MAX / 2u) {
return 0;
}
const uint64_t total_elems64 = (uint64_t)n_comp * 8u * head_dim;
if (total_elems64 > UINT32_MAX || n_comp > UINT32_MAX / 8u) return 0;
id<MTLComputePipelineState> pipeline = ds4_gpu_hot_pipeline(
g_dsv4_compressor_pack_ratio4_pipeline,
"kernel_dsv4_compressor_pack_ratio4");
if (!pipeline) return 0;
NSUInteger nth = head_dim;
if (nth > 256u) nth = 256u;
if (nth > pipeline.maxTotalThreadsPerThreadgroup) {
nth = pipeline.maxTotalThreadsPerThreadgroup;
}
if (nth == 0) return 0;
ds4_gpu_dsv4_compressor_pack_ratio4_args args = {
.head_dim = head_dim,
.n_comp = n_comp,
.replay = replay ? 1u : 0u,
.n_threads = (uint32_t)nth,
};
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:kvbuf offset:kv_offset atIndex:1];
[enc setBuffer:scorebuf offset:score_offset atIndex:2];
[enc setBuffer:statekvbuf offset:state_kv_offset atIndex:3];
[enc setBuffer:statescbuf offset:state_score_offset atIndex:4];
[enc setBuffer:g_compressor_pool_kv_buffer offset:0 atIndex:5];
[enc setBuffer:g_compressor_pool_score_buffer offset:0 atIndex:6];
[enc dispatchThreadgroups:MTLSizeMake(n_comp, 8u, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_compressor_ratio4_decode_pack_ggml(
id<MTLCommandBuffer> cb,
ds4_gpu_tensor *out,
id<MTLBuffer> statekvbuf,
NSUInteger state_kv_offset,
id<MTLBuffer> statescbuf,
NSUInteger state_score_offset,
uint32_t head_dim) {
if (!cb || !out || !statekvbuf || !statescbuf || head_dim == 0u ||
!ds4_gpu_ensure_compressor_pool_ggml_scratch(8u, head_dim)) {
return 0;
}
const bool exact_pool_enabled =
ds4_gpu_ported_m5_decode_feature_enabled(
"DS4_METAL_DISABLE_PRE_M5_COMPRESSOR_EXACT_POOL_RATIO4",
"DS4_METAL_DISABLE_M5_COMPRESSOR_EXACT_POOL_RATIO4");
if ((head_dim == 128u || head_dim == 512u) && exact_pool_enabled) {
id<MTLComputePipelineState> exact_pool_pipeline = ds4_gpu_hot_pipeline(
g_dsv4_compressor_exact_pool_ratio4_pipeline,
"kernel_dsv4_compressor_exact_pool_ratio4_decode_ggml");
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out);
const NSUInteger out_offset = ds4_gpu_tensor_offset(out);
const uint64_t state_bytes = 16ull * head_dim * sizeof(float);
const uint64_t out_bytes = (uint64_t)head_dim * sizeof(float);
const bool overlap =
ds4_gpu_buffer_ranges_overlap(outbuf, out_offset, out_bytes,
statekvbuf, state_kv_offset,
state_bytes) ||
ds4_gpu_buffer_ranges_overlap(outbuf, out_offset, out_bytes,
statescbuf, state_score_offset,
state_bytes);
if (exact_pool_pipeline && outbuf && !overlap &&
exact_pool_pipeline.threadExecutionWidth == 32u &&
exact_pool_pipeline.maxTotalThreadsPerThreadgroup >= 32u) {
if (getenv(
"DS4_METAL_TEST_POISON_COMPRESSOR_EXACT_REDUCTION_SCRATCH") != NULL) {
const uint32_t poison_bits = 0x7fc01234u;
float poison;
memcpy(&poison, &poison_bits, sizeof(poison));
if (!ds4_gpu_encode_fill_f32_rows(cb,
g_compressor_pool_softmax_buffer,
0,
8u,
head_dim,
poison)) {
return 0;
}
}
ds4_gpu_dsv4_compressor_pack_ratio4_args args = {
.head_dim = head_dim,
.n_comp = 1u,
.replay = 1u,
.n_threads = 32u,
};
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:exact_pool_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:statekvbuf offset:state_kv_offset atIndex:1];
[enc setBuffer:statescbuf offset:state_score_offset atIndex:2];
[enc setBuffer:g_compressor_pool_softmax_buffer offset:0 atIndex:3];
[enc setBuffer:g_compressor_pool_product_buffer offset:0 atIndex:4];
[enc setBuffer:outbuf offset:out_offset atIndex:5];
[enc setThreadgroupMemoryLength:32u * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(head_dim, 1, 1)
threadsPerThreadgroup:MTLSizeMake(32, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
}
id<MTLComputePipelineState> pipeline = ds4_gpu_hot_pipeline(
g_dsv4_compressor_pack_ratio4_decode_ggml_pipeline,
"kernel_dsv4_compressor_pack_ratio4_decode_ggml");
if (!pipeline) return 0;
NSUInteger nth = head_dim;
if (nth > 256u) nth = 256u;
if (nth > pipeline.maxTotalThreadsPerThreadgroup) {
nth = pipeline.maxTotalThreadsPerThreadgroup;
}
if (nth == 0u) return 0;
ds4_gpu_dsv4_compressor_pack_ratio4_args args = {
.head_dim = head_dim,
.n_comp = 1u,
.replay = 1u,
.n_threads = (uint32_t)nth,
};
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:statekvbuf offset:state_kv_offset atIndex:1];
[enc setBuffer:statescbuf offset:state_score_offset atIndex:2];
[enc setBuffer:g_compressor_pool_product_buffer offset:0 atIndex:3];
[enc setBuffer:g_compressor_pool_score_cont_buffer offset:0 atIndex:4];
[enc dispatchThreadgroups:MTLSizeMake(8u, 1, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_encode_dsv4_softmax_pool_one_comp_ggml_reduce(cb,
out,
8u,
head_dim,
true);
}
static int ds4_gpu_encode_compressor_pool(
id<MTLCommandBuffer> cb,
ds4_gpu_tensor *out,
const ds4_gpu_tensor *state_kv,
const ds4_gpu_tensor *state_score,
uint32_t head_dim,
uint32_t ratio,
bool decode_one_token) {
id<MTLBuffer> statekvbuf = ds4_gpu_tensor_buffer(state_kv);
id<MTLBuffer> statescbuf = ds4_gpu_tensor_buffer(state_score);
if (!cb || !out || !statekvbuf || !statescbuf || head_dim == 0 || ratio == 0) return 0;
const uint32_t coff = ratio == 4u ? 2u : 1u;
const uint32_t width = coff * head_dim;
const uint32_t rows = coff * ratio;
const uint64_t state_bytes = (uint64_t)width * rows * sizeof(float);
if (ds4_gpu_tensor_bytes(state_kv) < state_bytes ||
ds4_gpu_tensor_bytes(state_score) < state_bytes) {
return 0;
}
if (ratio != 4u) {
const uint64_t row_stride = (uint64_t)width * sizeof(float);
return ds4_gpu_encode_dsv4_softmax_pool(cb,
out,
statekvbuf,
ds4_gpu_tensor_offset(state_kv),
row_stride,
sizeof(float),
(uint64_t)rows * row_stride,
statescbuf,
ds4_gpu_tensor_offset(state_score),
row_stride,
sizeof(float),
(uint64_t)rows * row_stride,
ratio,
head_dim,
1);
}
const int decode_pack_mode = decode_one_token
? ds4_gpu_compressor_ratio4_decode_pack_mode(head_dim)
: 0;
if (decode_pack_mode < 0) return 0;
if (decode_pack_mode > 0) {
return ds4_gpu_encode_compressor_ratio4_decode_pack_ggml(
cb,
out,
statekvbuf,
ds4_gpu_tensor_offset(state_kv),
statescbuf,
ds4_gpu_tensor_offset(state_score),
head_dim);
}
const NSUInteger packed_bytes = (NSUInteger)8u * head_dim * sizeof(float);
if (!ds4_gpu_ensure_scratch_buffer(&g_compressor_pool_kv_buffer,
&g_compressor_pool_kv_bytes,
packed_bytes,
"ds4_compressor_pool_kv") ||
!ds4_gpu_ensure_scratch_buffer(&g_compressor_pool_score_buffer,
&g_compressor_pool_score_bytes,
packed_bytes,
"ds4_compressor_pool_score")) {
return 0;
}
const uint64_t state_row_stride = (uint64_t)width * sizeof(float);
const uint64_t pool_row_stride = (uint64_t)head_dim * sizeof(float);
const NSUInteger curr_offset = (NSUInteger)4u * state_row_stride +
(NSUInteger)head_dim * sizeof(float);
if (!ds4_gpu_encode_concat_f32_dim1(cb,
statekvbuf,
ds4_gpu_tensor_offset(state_kv),
4,
state_row_stride,
statekvbuf,
ds4_gpu_tensor_offset(state_kv) + curr_offset,
4,
state_row_stride,
g_compressor_pool_kv_buffer,
0,
head_dim,
pool_row_stride) ||
!ds4_gpu_encode_concat_f32_dim1(cb,
statescbuf,
ds4_gpu_tensor_offset(state_score),
4,
state_row_stride,
statescbuf,
ds4_gpu_tensor_offset(state_score) + curr_offset,
4,
state_row_stride,
g_compressor_pool_score_buffer,
0,
head_dim,
pool_row_stride)) {
return 0;
}
return ds4_gpu_encode_dsv4_softmax_pool(cb,
out,
g_compressor_pool_kv_buffer,
0,
pool_row_stride,
sizeof(float),
packed_bytes,
g_compressor_pool_score_buffer,
0,
pool_row_stride,
sizeof(float),
packed_bytes,
8,
head_dim,
1);
}
static int ds4_gpu_encode_compressor_shift_ratio4(
id<MTLCommandBuffer> cb,
ds4_gpu_tensor *state_kv,
ds4_gpu_tensor *state_score,
uint32_t width) {
id<MTLBuffer> statekvbuf = ds4_gpu_tensor_buffer(state_kv);
id<MTLBuffer> statescbuf = ds4_gpu_tensor_buffer(state_score);
if (!cb || !statekvbuf || !statescbuf || !g_dsv4_ratio4_shift_pipeline || width == 0) return 0;
ds4_gpu_dsv4_ratio4_shift_args args = { .width = width };
const uint32_t n = 4u * width;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:g_dsv4_ratio4_shift_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:statekvbuf offset:ds4_gpu_tensor_offset(state_kv) atIndex:1];
[enc setBuffer:statescbuf offset:ds4_gpu_tensor_offset(state_score) atIndex:2];
[enc dispatchThreadgroups:MTLSizeMake(((NSUInteger)n + 255u) / 256u, 1, 1)
threadsPerThreadgroup:MTLSizeMake(256, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
int ds4_gpu_compressor_prefill_tensor(
ds4_gpu_tensor *comp_cache,
ds4_gpu_tensor *state_kv,
ds4_gpu_tensor *state_score,
const ds4_gpu_tensor *kv,
const ds4_gpu_tensor *sc,
const void *model_map,
uint64_t model_size,
uint64_t ape_offset,
uint32_t ape_type,
uint64_t norm_offset,
uint32_t norm_type,
uint32_t head_dim,
uint32_t ratio,
uint32_t pos0,
uint32_t n_tokens,
uint32_t n_rot,
uint32_t n_ctx_orig,
bool quantize_fp8,
float freq_base,
float freq_scale,
float ext_factor,
float attn_factor,
float beta_fast,
float beta_slow,
float rms_eps) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!comp_cache || !state_kv || !state_score || !kv || !sc || !model_map ||
head_dim == 0 || ratio == 0 || n_tokens == 0 ||
n_rot > head_dim || (n_rot & 1u) != 0 ||
(ape_type != 0u && ape_type != 1u) ||
norm_type != 0u) {
return 0;
}
@autoreleasepool {
const uint32_t coff = ratio == 4u ? 2u : 1u;
const uint32_t width = coff * head_dim;
const uint32_t state_rows = coff * ratio;
const uint32_t n_comp = n_tokens / ratio;
const uint32_t cutoff = n_comp * ratio;
const uint32_t rem = n_tokens - cutoff;
const uint64_t elem_ape = ape_type == 1u ? 2u : 4u;
const uint64_t kv_bytes = (uint64_t)n_tokens * width * sizeof(float);
const uint64_t state_bytes = (uint64_t)state_rows * width * sizeof(float);
const uint64_t comp_bytes = (uint64_t)n_comp * head_dim * sizeof(float);
const uint64_t ape_bytes = (uint64_t)width * ratio * elem_ape;
const uint64_t norm_bytes = (uint64_t)head_dim * sizeof(float);
if (ape_offset > model_size || ape_bytes > model_size - ape_offset ||
norm_offset > model_size || norm_bytes > model_size - norm_offset) {
fprintf(stderr, "ds4: Metal compressor prefill tensor range is outside the mapped model\n");
return 0;
}
id<MTLBuffer> kvbuf = ds4_gpu_tensor_buffer(kv);
id<MTLBuffer> scbuf = ds4_gpu_tensor_buffer(sc);
id<MTLBuffer> compbuf = ds4_gpu_tensor_buffer(comp_cache);
id<MTLBuffer> statekvbuf = ds4_gpu_tensor_buffer(state_kv);
id<MTLBuffer> statescbuf = ds4_gpu_tensor_buffer(state_score);
if (!kvbuf || !scbuf || !compbuf || !statekvbuf || !statescbuf ||
ds4_gpu_tensor_bytes(kv) < kv_bytes ||
ds4_gpu_tensor_bytes(sc) < kv_bytes ||
ds4_gpu_tensor_bytes(state_kv) < state_bytes ||
ds4_gpu_tensor_bytes(state_score) < state_bytes ||
(n_comp && ds4_gpu_tensor_bytes(comp_cache) < comp_bytes)) {
fprintf(stderr, "ds4: Metal compressor prefill received undersized buffers\n");
return 0;
}
uint64_t ape_inner = 0;
id<MTLBuffer> apebuf = ds4_gpu_wrap_model_range(model_map, model_size, ape_offset, ape_bytes, &ape_inner);
if (!apebuf) return 0;
const bool had_batch = g_batch_cb != nil;
if (!had_batch && ds4_gpu_begin_commands() == 0) return 0;
int ok = 1;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb || owned) ok = 0;
if (ok) {
ok = ds4_gpu_encode_fill_f32_rows(cb,
statekvbuf,
ds4_gpu_tensor_offset(state_kv),
width,
state_rows,
0.0f) &&
ds4_gpu_encode_fill_f32_rows(cb,
statescbuf,
ds4_gpu_tensor_offset(state_score),
width,
state_rows,
ds4_gpu_negative_infinity());
}
if (ok && ratio == 4u) {
int32_t rows_prev[4] = { 0, 1, 2, 3 };
const int have_prev = cutoff >= ratio ? 1 : 0;
const uint32_t prev_start = rem == 0 ? cutoff - ratio : cutoff - ratio;
if (have_prev) {
ok = ds4_gpu_encode_compressor_set_rows_projected(cb,
state_kv,
state_score,
kvbuf,
ds4_gpu_tensor_offset(kv) +
(NSUInteger)prev_start * width * sizeof(float),
scbuf,
ds4_gpu_tensor_offset(sc) +
(NSUInteger)prev_start * width * sizeof(float),
apebuf,
(NSUInteger)ape_inner,
ape_type,
width,
ratio,
pos0 + prev_start,
rows_prev,
4,
state_rows);
}
if (ok && rem != 0) {
int32_t rows_cur[4];
for (uint32_t i = 0; i < rem; i++) rows_cur[i] = (int32_t)(ratio + i);
ok = ds4_gpu_encode_compressor_set_rows_projected(cb,
state_kv,
state_score,
kvbuf,
ds4_gpu_tensor_offset(kv) +
(NSUInteger)cutoff * width * sizeof(float),
scbuf,
ds4_gpu_tensor_offset(sc) +
(NSUInteger)cutoff * width * sizeof(float),
apebuf,
(NSUInteger)ape_inner,
ape_type,
width,
ratio,
pos0 + cutoff,
rows_cur,
rem,
state_rows);
}
} else if (ok && rem != 0) {
int32_t rows[128];
if (rem > (uint32_t)(sizeof(rows) / sizeof(rows[0]))) {
fprintf(stderr, "ds4: Metal compressor prefill remainder exceeds local row list\n");
ok = 0;
} else {
for (uint32_t i = 0; i < rem; i++) rows[i] = (int32_t)i;
ok = ds4_gpu_encode_compressor_set_rows_projected(cb,
state_kv,
state_score,
kvbuf,
ds4_gpu_tensor_offset(kv) +
(NSUInteger)cutoff * width * sizeof(float),
scbuf,
ds4_gpu_tensor_offset(sc) +
(NSUInteger)cutoff * width * sizeof(float),
apebuf,
(NSUInteger)ape_inner,
ape_type,
width,
ratio,
pos0 + cutoff,
rows,
rem,
state_rows);
}
}
if (ok && n_comp != 0) {
const NSUInteger score_bytes = (NSUInteger)cutoff * width * sizeof(float);
if (!ds4_gpu_ensure_scratch_buffer(&g_compressor_store_score_buffer,
&g_compressor_store_score_bytes,
score_bytes,
"ds4_compressor_store_score")) {
ok = 0;
}
if (ok) {
ok = ds4_gpu_encode_compressor_score_with_ape(cb,
scbuf,
ds4_gpu_tensor_offset(sc),
g_compressor_store_score_buffer,
0,
apebuf,
(NSUInteger)ape_inner,
ape_type,
width,
ratio,
pos0,
cutoff);
}
if (ok && ratio == 4u) {
const int direct_pool_mode =
ds4_gpu_compressor_ratio4_direct_pool_mode(head_dim, n_comp);
if (ok && direct_pool_mode < 0) ok = 0;
const uint64_t direct_output_bytes =
(uint64_t)n_comp * head_dim * sizeof(float);
const uint64_t direct_input_bytes =
(uint64_t)n_comp * 4u * width * sizeof(float);
const bool direct_pool_overlap = direct_pool_mode > 0 &&
ds4_gpu_buffer_ranges_overlap(
compbuf,
ds4_gpu_tensor_offset(comp_cache),
direct_output_bytes,
kvbuf,
ds4_gpu_tensor_offset(kv),
direct_input_bytes);
const bool use_direct_pool =
direct_pool_mode > 0 && !direct_pool_overlap;
const NSUInteger pack_bytes = (NSUInteger)n_comp * 8u * head_dim * sizeof(float);
if (ok && !use_direct_pool &&
(!ds4_gpu_ensure_scratch_buffer(&g_compressor_pool_kv_buffer,
&g_compressor_pool_kv_bytes,
pack_bytes,
"ds4_compressor_pool_kv") ||
!ds4_gpu_ensure_scratch_buffer(&g_compressor_pool_score_buffer,
&g_compressor_pool_score_bytes,
pack_bytes,
"ds4_compressor_pool_score"))) {
ok = 0;
}
const int pack_fusion_mode = use_direct_pool ? 0 :
ds4_gpu_compressor_pack_ratio4_fusion_mode(head_dim);
if (ok && pack_fusion_mode < 0) ok = 0;
const bool use_pack_fusion = pack_fusion_mode > 0;
if (ok && use_direct_pool) {
ok = ds4_gpu_encode_compressor_ratio4_direct_pool(
cb,
comp_cache,
kvbuf,
ds4_gpu_tensor_offset(kv),
g_compressor_store_score_buffer,
0,
statekvbuf,
ds4_gpu_tensor_offset(state_kv),
statescbuf,
ds4_gpu_tensor_offset(state_score),
head_dim,
n_comp,
false);
}
if (ok && use_pack_fusion) {
ok = ds4_gpu_encode_compressor_pack_ratio4(
cb,
kvbuf,
ds4_gpu_tensor_offset(kv),
g_compressor_store_score_buffer,
0,
statekvbuf,
ds4_gpu_tensor_offset(state_kv),
statescbuf,
ds4_gpu_tensor_offset(state_score),
head_dim,
n_comp,
false);
}
if (ok && !use_direct_pool && !use_pack_fusion) {
ok = ds4_gpu_encode_fill_f32_rows(cb,
g_compressor_pool_kv_buffer,
0,
head_dim,
8u * n_comp,
0.0f) &&
ds4_gpu_encode_fill_f32_rows(cb,
g_compressor_pool_score_buffer,
0,
head_dim,
8u * n_comp,
ds4_gpu_negative_infinity());
}
if (ok && !use_direct_pool && !use_pack_fusion) {
const uint64_t src_row_stride = (uint64_t)width * sizeof(float);
const uint64_t src_plane_stride = (uint64_t)ratio * src_row_stride;
const uint64_t dst_row_stride = (uint64_t)head_dim * sizeof(float);
const uint64_t dst_plane_stride = 8ull * dst_row_stride;
ok = ds4_gpu_encode_cpy_f32_f32_3d(cb,
kvbuf,
ds4_gpu_tensor_offset(kv) +
(NSUInteger)head_dim * sizeof(float),
g_compressor_pool_kv_buffer,
(NSUInteger)4u * head_dim * sizeof(float),
head_dim,
ratio,
n_comp,
src_row_stride,
src_plane_stride,
dst_row_stride,
dst_plane_stride) &&
ds4_gpu_encode_cpy_f32_f32_3d(cb,
g_compressor_store_score_buffer,
(NSUInteger)head_dim * sizeof(float),
g_compressor_pool_score_buffer,
(NSUInteger)4u * head_dim * sizeof(float),
head_dim,
ratio,
n_comp,
src_row_stride,
src_plane_stride,
dst_row_stride,
dst_plane_stride);
}
if (ok && !use_direct_pool && !use_pack_fusion && n_comp > 1u) {
const uint64_t src_row_stride = (uint64_t)width * sizeof(float);
const uint64_t src_plane_stride = (uint64_t)ratio * src_row_stride;
const uint64_t dst_row_stride = (uint64_t)head_dim * sizeof(float);
const uint64_t dst_plane_stride = 8ull * dst_row_stride;
ok = ds4_gpu_encode_cpy_f32_f32_3d(cb,
kvbuf,
ds4_gpu_tensor_offset(kv),
g_compressor_pool_kv_buffer,
dst_plane_stride,
head_dim,
ratio,
n_comp - 1u,
src_row_stride,
src_plane_stride,
dst_row_stride,
dst_plane_stride) &&
ds4_gpu_encode_cpy_f32_f32_3d(cb,
g_compressor_store_score_buffer,
0,
g_compressor_pool_score_buffer,
dst_plane_stride,
head_dim,
ratio,
n_comp - 1u,
src_row_stride,
src_plane_stride,
dst_row_stride,
dst_plane_stride);
}
if (ok && !use_direct_pool) {
ok = ds4_gpu_encode_dsv4_softmax_pool(cb,
comp_cache,
g_compressor_pool_kv_buffer,
0,
(uint64_t)head_dim * sizeof(float),
sizeof(float),
8ull * head_dim * sizeof(float),
g_compressor_pool_score_buffer,
0,
(uint64_t)head_dim * sizeof(float),
sizeof(float),
8ull * head_dim * sizeof(float),
8,
head_dim,
n_comp);
}
} else if (ok) {
const uint64_t row_stride = (uint64_t)width * sizeof(float);
ok = ds4_gpu_encode_dsv4_softmax_pool(cb,
comp_cache,
kvbuf,
ds4_gpu_tensor_offset(kv),
row_stride,
sizeof(float),
(uint64_t)ratio * row_stride,
g_compressor_store_score_buffer,
0,
row_stride,
sizeof(float),
(uint64_t)ratio * row_stride,
ratio,
head_dim,
n_comp);
}
}
if (ok && n_comp != 0) {
ok = ds4_gpu_rms_norm_weight_rows_tensor(comp_cache,
comp_cache,
model_map,
model_size,
norm_offset,
head_dim,
n_comp,
rms_eps) != 0;
}
if (ok && n_comp != 0 && n_rot != 0) {
ds4_gpu_rope_tail_batch_args rope_args = ds4_gpu_make_rope_tail_args(
n_comp, 1, head_dim, n_rot, n_ctx_orig, false,
freq_base, freq_scale, ext_factor, attn_factor, beta_fast, beta_slow);
cb = ds4_gpu_command_buffer(&owned);
ok = cb && !owned &&
ds4_gpu_encode_rope_tail_inplace(cb,
compbuf,
ds4_gpu_tensor_offset(comp_cache),
&rope_args,
n_comp,
1,
head_dim,
pos0,
ratio);
}
if (ok && n_comp != 0 && quantize_fp8) {
ok = ds4_gpu_dsv4_fp8_kv_quantize_tensor(comp_cache, n_comp, head_dim, n_rot) != 0;
}
if (!had_batch) {
const int end_ok = ds4_gpu_end_commands();
ok = end_ok && ok;
}
return ok ? 1 : 0;
}
}
int ds4_gpu_compressor_prefill_ratio4_replay_tensor(
ds4_gpu_tensor *comp_cache,
ds4_gpu_tensor *state_kv,
ds4_gpu_tensor *state_score,
const ds4_gpu_tensor *kv,
const ds4_gpu_tensor *sc,
const void *model_map,
uint64_t model_size,
uint64_t ape_offset,
uint32_t ape_type,
uint64_t norm_offset,
uint32_t norm_type,
uint32_t head_dim,
uint32_t pos0,
uint32_t n_tokens,
uint32_t n_rot,
uint32_t n_ctx_orig,
bool quantize_fp8,
float freq_base,
float freq_scale,
float ext_factor,
float attn_factor,
float beta_fast,
float beta_slow,
float rms_eps) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!comp_cache || !state_kv || !state_score || !kv || !sc || !model_map ||
head_dim == 0 || n_tokens == 0 || (n_tokens & 3u) != 0 || (pos0 & 3u) != 0 ||
n_rot > head_dim || (n_rot & 1u) != 0 ||
(ape_type != 0u && ape_type != 1u) ||
norm_type != 0u) {
return 0;
}
@autoreleasepool {
const uint32_t ratio = 4u;
const uint32_t width = 2u * head_dim;
const uint32_t state_rows = 8u;
const uint32_t n_comp = n_tokens / ratio;
const uint64_t elem_ape = ape_type == 1u ? 2u : 4u;
const uint64_t kv_bytes = (uint64_t)n_tokens * width * sizeof(float);
const uint64_t state_bytes = (uint64_t)state_rows * width * sizeof(float);
const uint64_t comp_bytes = (uint64_t)n_comp * head_dim * sizeof(float);
const uint64_t ape_bytes = (uint64_t)width * ratio * elem_ape;
const uint64_t norm_bytes = (uint64_t)head_dim * sizeof(float);
if (ape_offset > model_size || ape_bytes > model_size - ape_offset ||
norm_offset > model_size || norm_bytes > model_size - norm_offset) {
fprintf(stderr, "ds4: Metal compressor replay tensor range is outside the mapped model\n");
return 0;
}
id<MTLBuffer> kvbuf = ds4_gpu_tensor_buffer(kv);
id<MTLBuffer> scbuf = ds4_gpu_tensor_buffer(sc);
id<MTLBuffer> compbuf = ds4_gpu_tensor_buffer(comp_cache);
id<MTLBuffer> statekvbuf = ds4_gpu_tensor_buffer(state_kv);
id<MTLBuffer> statescbuf = ds4_gpu_tensor_buffer(state_score);
if (!kvbuf || !scbuf || !compbuf || !statekvbuf || !statescbuf ||
ds4_gpu_tensor_bytes(kv) < kv_bytes ||
ds4_gpu_tensor_bytes(sc) < kv_bytes ||
ds4_gpu_tensor_bytes(state_kv) < state_bytes ||
ds4_gpu_tensor_bytes(state_score) < state_bytes ||
ds4_gpu_tensor_bytes(comp_cache) < comp_bytes) {
fprintf(stderr, "ds4: Metal compressor replay received undersized buffers\n");
return 0;
}
uint64_t ape_inner = 0;
id<MTLBuffer> apebuf = ds4_gpu_wrap_model_range(model_map, model_size, ape_offset, ape_bytes, &ape_inner);
if (!apebuf) return 0;
const bool had_batch = g_batch_cb != nil;
if (!had_batch && ds4_gpu_begin_commands() == 0) return 0;
int ok = 1;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb || owned) ok = 0;
const NSUInteger score_bytes = (NSUInteger)n_tokens * width * sizeof(float);
const NSUInteger pack_bytes = (NSUInteger)n_comp * 8u * head_dim * sizeof(float);
const int direct_pool_mode =
ds4_gpu_compressor_ratio4_direct_pool_mode(head_dim, n_comp);
if (ok && direct_pool_mode < 0) ok = 0;
const uint64_t direct_output_bytes =
(uint64_t)n_comp * head_dim * sizeof(float);
const uint64_t direct_input_bytes =
(uint64_t)n_tokens * width * sizeof(float);
const uint64_t direct_state_bytes =
(uint64_t)4u * width * sizeof(float);
const bool direct_pool_overlap = direct_pool_mode > 0 &&
(ds4_gpu_buffer_ranges_overlap(
compbuf,
ds4_gpu_tensor_offset(comp_cache),
direct_output_bytes,
kvbuf,
ds4_gpu_tensor_offset(kv),
direct_input_bytes) ||
ds4_gpu_buffer_ranges_overlap(
compbuf,
ds4_gpu_tensor_offset(comp_cache),
direct_output_bytes,
statekvbuf,
ds4_gpu_tensor_offset(state_kv),
direct_state_bytes) ||
ds4_gpu_buffer_ranges_overlap(
compbuf,
ds4_gpu_tensor_offset(comp_cache),
direct_output_bytes,
statescbuf,
ds4_gpu_tensor_offset(state_score),
direct_state_bytes));
const bool use_direct_pool =
direct_pool_mode > 0 && !direct_pool_overlap;
if (ok && (!ds4_gpu_ensure_scratch_buffer(&g_compressor_store_score_buffer,
&g_compressor_store_score_bytes,
score_bytes,
"ds4_compressor_store_score") ||
(!use_direct_pool &&
(!ds4_gpu_ensure_scratch_buffer(&g_compressor_pool_kv_buffer,
&g_compressor_pool_kv_bytes,
pack_bytes,
"ds4_compressor_pool_kv") ||
!ds4_gpu_ensure_scratch_buffer(&g_compressor_pool_score_buffer,
&g_compressor_pool_score_bytes,
pack_bytes,
"ds4_compressor_pool_score"))))) {
ok = 0;
}
if (ok) {
ok = ds4_gpu_encode_compressor_score_with_ape(cb,
scbuf,
ds4_gpu_tensor_offset(sc),
g_compressor_store_score_buffer,
0,
apebuf,
(NSUInteger)ape_inner,
ape_type,
width,
ratio,
pos0,
n_tokens);
}
const uint64_t src_row_stride = (uint64_t)width * sizeof(float);
const uint64_t src_plane_stride = (uint64_t)ratio * src_row_stride;
const uint64_t dst_row_stride = (uint64_t)head_dim * sizeof(float);
const uint64_t dst_plane_stride = 8ull * dst_row_stride;
const NSUInteger state_off = ds4_gpu_tensor_offset(state_kv);
const NSUInteger state_score_off = ds4_gpu_tensor_offset(state_score);
const int pack_fusion_mode = use_direct_pool ? 0 :
ds4_gpu_compressor_pack_ratio4_fusion_mode(head_dim);
if (ok && pack_fusion_mode < 0) ok = 0;
const bool use_pack_fusion = pack_fusion_mode > 0;
if (ok && use_direct_pool) {
ok = ds4_gpu_encode_compressor_ratio4_direct_pool(
cb,
comp_cache,
kvbuf,
ds4_gpu_tensor_offset(kv),
g_compressor_store_score_buffer,
0,
statekvbuf,
state_off,
statescbuf,
state_score_off,
head_dim,
n_comp,
true);
}
if (ok && use_pack_fusion) {
ok = ds4_gpu_encode_compressor_pack_ratio4(
cb,
kvbuf,
ds4_gpu_tensor_offset(kv),
g_compressor_store_score_buffer,
0,
statekvbuf,
state_off,
statescbuf,
state_score_off,
head_dim,
n_comp,
true);
}
if (ok && !use_direct_pool && !use_pack_fusion) {
ok = ds4_gpu_encode_fill_f32_rows(cb,
g_compressor_pool_kv_buffer,
0,
head_dim,
8u * n_comp,
0.0f) &&
ds4_gpu_encode_fill_f32_rows(cb,
g_compressor_pool_score_buffer,
0,
head_dim,
8u * n_comp,
ds4_gpu_negative_infinity());
}
if (ok && !use_direct_pool && !use_pack_fusion) {
/*
* The aligned nonzero ratio-4 path replays the current ubatch
* compressor, but seeds the first compressed row with the previous
* compressor state. Rows 0..3 are the previous half, rows 4..7 are
* the current half.
*/
ok = ds4_gpu_encode_cpy_f32_f32_3d(cb,
statekvbuf,
state_off,
g_compressor_pool_kv_buffer,
0,
head_dim,
ratio,
1,
src_row_stride,
(uint64_t)ratio * src_row_stride,
dst_row_stride,
dst_plane_stride) &&
ds4_gpu_encode_cpy_f32_f32_3d(cb,
statescbuf,
state_score_off,
g_compressor_pool_score_buffer,
0,
head_dim,
ratio,
1,
src_row_stride,
(uint64_t)ratio * src_row_stride,
dst_row_stride,
dst_plane_stride);
}
if (ok && !use_direct_pool && !use_pack_fusion) {
ok = ds4_gpu_encode_cpy_f32_f32_3d(cb,
kvbuf,
ds4_gpu_tensor_offset(kv) +
(NSUInteger)head_dim * sizeof(float),
g_compressor_pool_kv_buffer,
(NSUInteger)4u * head_dim * sizeof(float),
head_dim,
ratio,
n_comp,
src_row_stride,
src_plane_stride,
dst_row_stride,
dst_plane_stride) &&
ds4_gpu_encode_cpy_f32_f32_3d(cb,
g_compressor_store_score_buffer,
(NSUInteger)head_dim * sizeof(float),
g_compressor_pool_score_buffer,
(NSUInteger)4u * head_dim * sizeof(float),
head_dim,
ratio,
n_comp,
src_row_stride,
src_plane_stride,
dst_row_stride,
dst_plane_stride);
}
if (ok && !use_direct_pool && !use_pack_fusion && n_comp > 1u) {
ok = ds4_gpu_encode_cpy_f32_f32_3d(cb,
kvbuf,
ds4_gpu_tensor_offset(kv),
g_compressor_pool_kv_buffer,
dst_plane_stride,
head_dim,
ratio,
n_comp - 1u,
src_row_stride,
src_plane_stride,
dst_row_stride,
dst_plane_stride) &&
ds4_gpu_encode_cpy_f32_f32_3d(cb,
g_compressor_store_score_buffer,
0,
g_compressor_pool_score_buffer,
dst_plane_stride,
head_dim,
ratio,
n_comp - 1u,
src_row_stride,
src_plane_stride,
dst_row_stride,
dst_plane_stride);
}
if (ok && !use_direct_pool) {
ok = ds4_gpu_encode_dsv4_softmax_pool(cb,
comp_cache,
g_compressor_pool_kv_buffer,
0,
dst_row_stride,
sizeof(float),
dst_plane_stride,
g_compressor_pool_score_buffer,
0,
dst_row_stride,
sizeof(float),
dst_plane_stride,
8,
head_dim,
n_comp);
}
if (ok) {
ok = ds4_gpu_rms_norm_weight_rows_tensor(comp_cache,
comp_cache,
model_map,
model_size,
norm_offset,
head_dim,
n_comp,
rms_eps) != 0;
}
if (ok && n_rot != 0) {
ds4_gpu_rope_tail_batch_args rope_args = ds4_gpu_make_rope_tail_args(
n_comp, 1, head_dim, n_rot, n_ctx_orig, false,
freq_base, freq_scale, ext_factor, attn_factor, beta_fast, beta_slow);
cb = ds4_gpu_command_buffer(&owned);
ok = cb && !owned &&
ds4_gpu_encode_rope_tail_inplace(cb,
compbuf,
ds4_gpu_tensor_offset(comp_cache),
&rope_args,
n_comp,
1,
head_dim,
pos0,
ratio);
}
if (ok && quantize_fp8) {
ok = ds4_gpu_dsv4_fp8_kv_quantize_tensor(comp_cache, n_comp, head_dim, n_rot) != 0;
}
if (ok) {
ok = ds4_gpu_encode_fill_f32_rows(cb,
statekvbuf,
state_off,
width,
state_rows,
0.0f) &&
ds4_gpu_encode_fill_f32_rows(cb,
statescbuf,
state_score_off,
width,
state_rows,
ds4_gpu_negative_infinity());
}
if (ok) {
int32_t rows_prev[4] = { 0, 1, 2, 3 };
const uint32_t prev_start = n_tokens - ratio;
ok = ds4_gpu_encode_compressor_set_rows_projected(cb,
state_kv,
state_score,
kvbuf,
ds4_gpu_tensor_offset(kv) +
(NSUInteger)prev_start * width * sizeof(float),
scbuf,
ds4_gpu_tensor_offset(sc) +
(NSUInteger)prev_start * width * sizeof(float),
apebuf,
(NSUInteger)ape_inner,
ape_type,
width,
ratio,
pos0 + prev_start,
rows_prev,
ratio,
state_rows);
}
if (!had_batch) {
const int end_ok = ds4_gpu_end_commands();
ok = end_ok && ok;
}
return ok ? 1 : 0;
}
}
int ds4_gpu_compressor_prefill_state_ratio4_tensor(
ds4_gpu_tensor *state_kv,
ds4_gpu_tensor *state_score,
const ds4_gpu_tensor *kv_tail,
const ds4_gpu_tensor *sc_tail,
const void *model_map,
uint64_t model_size,
uint64_t ape_offset,
uint32_t ape_type,
uint32_t head_dim,
uint32_t pos0) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!state_kv || !state_score || !kv_tail || !sc_tail || !model_map ||
head_dim == 0 || (ape_type != 0u && ape_type != 1u)) {
return 0;
}
@autoreleasepool {
const uint32_t ratio = 4u;
const uint32_t width = 2u * head_dim;
const uint32_t state_rows = 8u;
const uint64_t elem_ape = ape_type == 1u ? 2u : 4u;
const uint64_t tail_bytes = (uint64_t)ratio * width * sizeof(float);
const uint64_t state_bytes = (uint64_t)state_rows * width * sizeof(float);
const uint64_t ape_bytes = (uint64_t)ratio * width * elem_ape;
if (ape_offset > model_size || ape_bytes > model_size - ape_offset) {
fprintf(stderr, "ds4: Metal compressor prefill-state APE range is outside the mapped model\n");
return 0;
}
id<MTLBuffer> kvbuf = ds4_gpu_tensor_buffer(kv_tail);
id<MTLBuffer> scbuf = ds4_gpu_tensor_buffer(sc_tail);
id<MTLBuffer> statekvbuf = ds4_gpu_tensor_buffer(state_kv);
id<MTLBuffer> statescbuf = ds4_gpu_tensor_buffer(state_score);
if (!kvbuf || !scbuf || !statekvbuf || !statescbuf ||
ds4_gpu_tensor_bytes(kv_tail) < tail_bytes ||
ds4_gpu_tensor_bytes(sc_tail) < tail_bytes ||
ds4_gpu_tensor_bytes(state_kv) < state_bytes ||
ds4_gpu_tensor_bytes(state_score) < state_bytes) {
fprintf(stderr, "ds4: Metal compressor prefill-state received undersized buffers\n");
return 0;
}
uint64_t ape_inner = 0;
id<MTLBuffer> apebuf = ds4_gpu_wrap_model_range(model_map, model_size, ape_offset, ape_bytes, &ape_inner);
if (!apebuf) return 0;
const bool had_batch = g_batch_cb != nil;
if (!had_batch && ds4_gpu_begin_commands() == 0) return 0;
int ok = 1;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb || owned) ok = 0;
if (ok) {
ok = ds4_gpu_encode_fill_f32_rows(cb,
statekvbuf,
ds4_gpu_tensor_offset(state_kv),
width,
state_rows,
0.0f) &&
ds4_gpu_encode_fill_f32_rows(cb,
statescbuf,
ds4_gpu_tensor_offset(state_score),
width,
state_rows,
ds4_gpu_negative_infinity());
}
if (ok) {
int32_t rows[4] = { 0, 1, 2, 3 };
ok = ds4_gpu_encode_compressor_set_rows_projected(cb,
state_kv,
state_score,
kvbuf,
ds4_gpu_tensor_offset(kv_tail),
scbuf,
ds4_gpu_tensor_offset(sc_tail),
apebuf,
(NSUInteger)ape_inner,
ape_type,
width,
ratio,
pos0,
rows,
ratio,
state_rows);
}
if (!had_batch) {
const int end_ok = ds4_gpu_end_commands();
ok = end_ok && ok;
}
return ok ? 1 : 0;
}
}
int ds4_gpu_compressor_update_tensor(
const ds4_gpu_tensor *kv_cur,
const ds4_gpu_tensor *sc_cur,
ds4_gpu_tensor *state_kv,
ds4_gpu_tensor *state_score,
ds4_gpu_tensor *comp_cache,
const void *model_map,
uint64_t model_size,
uint64_t ape_offset,
uint32_t ape_type,
uint64_t norm_offset,
uint32_t norm_type,
uint32_t head_dim,
uint32_t ratio,
uint32_t pos,
uint32_t comp_row,
uint32_t n_rot,
uint32_t n_ctx_orig,
float freq_base,
float freq_scale,
float ext_factor,
float attn_factor,
float beta_fast,
float beta_slow,
float rms_eps,
bool state_already_stored,
bool decode_one_token,
bool defer_finalize) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!kv_cur || !sc_cur || !state_kv || !state_score || !comp_cache ||
!model_map || head_dim == 0 || ratio == 0 ||
n_rot > head_dim || (n_rot & 1u) != 0 ||
(ape_type != 0u && ape_type != 1u) ||
norm_type != 0u) {
return 0;
}
@autoreleasepool {
const uint32_t coff = ratio == 4u ? 2u : 1u;
const uint32_t width = coff * head_dim;
const uint32_t state_rows = coff * ratio;
const uint32_t emit = ((pos + 1u) % ratio) == 0u ? 1u : 0u;
const uint64_t elem_ape = ape_type == 1u ? 2u : 4u;
const uint64_t kv_bytes = (uint64_t)width * sizeof(float);
const uint64_t state_bytes = (uint64_t)state_rows * width * sizeof(float);
const uint64_t comp_bytes = (uint64_t)(comp_row + (emit ? 1u : 0u)) * head_dim * sizeof(float);
const uint64_t ape_bytes = (uint64_t)width * ratio * elem_ape;
const uint64_t norm_bytes = (uint64_t)head_dim * sizeof(float);
if (ape_offset > model_size || ape_bytes > model_size - ape_offset ||
norm_offset > model_size || norm_bytes > model_size - norm_offset) {
fprintf(stderr, "ds4: Metal compressor tensor range is outside the mapped model\n");
return 0;
}
id<MTLBuffer> kvbuf = ds4_gpu_tensor_buffer(kv_cur);
id<MTLBuffer> scbuf = ds4_gpu_tensor_buffer(sc_cur);
id<MTLBuffer> compbuf = ds4_gpu_tensor_buffer(comp_cache);
if (!kvbuf || !scbuf || !compbuf ||
ds4_gpu_tensor_bytes(kv_cur) < kv_bytes ||
ds4_gpu_tensor_bytes(sc_cur) < kv_bytes ||
ds4_gpu_tensor_bytes(state_kv) < state_bytes ||
ds4_gpu_tensor_bytes(state_score) < state_bytes ||
(emit && ds4_gpu_tensor_bytes(comp_cache) < comp_bytes)) {
fprintf(stderr, "ds4: Metal compressor update received undersized buffers\n");
return 0;
}
if (!state_already_stored) {
const bool use_store_one =
getenv("DS4_METAL_DISABLE_COMPRESSOR_STORE_ONE") == NULL;
const int store_ok = use_store_one
? ds4_gpu_compressor_store_one_tensor(kv_cur,
sc_cur,
state_kv,
state_score,
model_map,
model_size,
ape_offset,
ape_type,
width,
ratio,
pos)
: ds4_gpu_compressor_store_batch_tensor(kv_cur,
sc_cur,
state_kv,
state_score,
model_map,
model_size,
ape_offset,
ape_type,
head_dim,
ratio,
pos,
1);
if (!store_ok) {
return 0;
}
}
if (!emit) return 1;
ds4_gpu_tensor *comp_row_view = ds4_gpu_tensor_view(
comp_cache,
(uint64_t)comp_row * head_dim * sizeof(float),
(uint64_t)head_dim * sizeof(float));
if (!comp_row_view) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
int ok = cb &&
ds4_gpu_encode_compressor_pool(cb,
comp_row_view,
state_kv,
state_score,
head_dim,
ratio,
decode_one_token);
if (ok) ok = ds4_gpu_finish_command_buffer(cb, owned, "compressor DS4 softmax pool");
/* When the caller runs the fused finalize dispatch afterwards, the
* pooled row is consumed un-normalized there; skip the standalone
* norm+rope so the work happens exactly once. The state shift still
* runs: it only moves the rolling state rows. */
if (ok && !defer_finalize) {
ok = ds4_gpu_rms_norm_weight_rows_tensor(comp_row_view,
comp_row_view,
model_map,
model_size,
norm_offset,
head_dim,
1,
rms_eps) != 0;
}
if (ok && !defer_finalize) {
const uint32_t comp_pos = pos + 1u - ratio;
ok = ds4_gpu_rope_tail_tensor(comp_row_view,
1,
1,
head_dim,
n_rot,
comp_pos,
n_ctx_orig,
false,
freq_base,
freq_scale,
ext_factor,
attn_factor,
beta_fast,
beta_slow) != 0;
}
if (ok && ratio == 4u && !defer_finalize) {
cb = ds4_gpu_command_buffer(&owned);
ok = cb &&
ds4_gpu_encode_compressor_shift_ratio4(cb,
state_kv,
state_score,
width);
if (ok) ok = ds4_gpu_finish_command_buffer(cb, owned, "compressor ratio4 state shift");
}
ds4_gpu_tensor_free(comp_row_view);
if (!ok) return 0;
}
return 1;
}
static int ds4_gpu_encode_fill_f32_rows(
id<MTLCommandBuffer> cb,
id<MTLBuffer> buf,
NSUInteger offset,
uint32_t width,
uint32_t rows,
float value) {
if (!cb || !buf || width == 0 || rows == 0 || (width & 3u) != 0) return 0;
ds4_gpu_unary_args args = ds4_gpu_make_unary_rows_args(width, rows, 1, 0.0f, 0.0f);
args.val = value;
NSUInteger nth_max = g_unary_fill_pipeline.maxTotalThreadsPerThreadgroup;
if (nth_max > 256u) nth_max = 256u;
NSUInteger nth = (NSUInteger)args.ne00;
if (nth > nth_max) nth = nth_max;
if (nth == 0) nth = 1u;
const NSUInteger nk0 = ((NSUInteger)args.ne00 + nth - 1u) / nth;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:g_unary_fill_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:buf offset:offset atIndex:1];
[enc setBuffer:buf offset:offset atIndex:2];
[enc dispatchThreadgroups:MTLSizeMake(nk0 * (NSUInteger)args.ne01,
(NSUInteger)args.ne02,
(NSUInteger)args.ne03)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_matmul_q8_0_kslice_exact_rows(
ds4_gpu_tensor *out, const void *model_map, uint64_t model_size,
uint64_t weight_offset, uint64_t full_in_dim, uint64_t k_off,
uint64_t k_cnt, uint64_t out_dim, const ds4_gpu_tensor *x,
uint64_t x_elem_off, uint32_t n_rows);
static int ds4_gpu_matmul_q8_0_kslice_rows_impl(
ds4_gpu_tensor *out, const void *model_map, uint64_t model_size,
uint64_t weight_offset, uint64_t full_in_dim, uint64_t out_dim,
uint64_t k_off, uint64_t k_cnt, const ds4_gpu_tensor *x,
uint64_t n_rows, uint64_t x_stride, uint64_t x_off);
static int ds4_gpu_attention_output_q8_batch_impl(
ds4_gpu_tensor *out,
ds4_gpu_tensor *low,
ds4_gpu_tensor *group_tmp,
ds4_gpu_tensor *low_tmp,
const void *model_map,
uint64_t model_size,
uint64_t out_a_offset,
uint64_t out_b_offset,
uint64_t group_dim,
uint64_t rank,
uint32_t n_groups,
uint64_t out_dim,
const ds4_gpu_tensor *heads,
uint32_t n_tokens,
bool round_low_bf16,
uint64_t full_low_dim,
uint64_t low_offset) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!out || !low || !group_tmp || !low_tmp || !heads || !model_map ||
group_dim == 0 || rank == 0 || n_groups == 0 || out_dim == 0 || n_tokens == 0 ||
group_dim > UINT32_MAX || rank > UINT32_MAX || out_dim > UINT32_MAX) {
return 0;
}
@autoreleasepool {
const uint64_t low_dim = (uint64_t)n_groups * rank;
if (!full_low_dim) full_low_dim = low_dim;
if ((group_dim % 32u) != 0 || (low_dim % 32u) != 0 || low_dim > UINT32_MAX ||
full_low_dim > UINT32_MAX || (full_low_dim % 32u) || (low_offset % 32u) ||
low_offset > full_low_dim || low_dim > full_low_dim - low_offset) {
fprintf(stderr, "ds4: Metal attention output batch received invalid q8 dimensions\n");
return 0;
}
const uint64_t row_a_bytes = (group_dim / 32u) * 34u;
const uint64_t row_b_bytes = (full_low_dim / 32u) * 34u;
const uint64_t out_a_bytes = (uint64_t)n_groups * rank * row_a_bytes;
const uint64_t out_b_bytes = out_dim * row_b_bytes;
if (out_a_offset > model_size || out_a_bytes > model_size - out_a_offset ||
out_b_offset > model_size || out_b_bytes > model_size - out_b_offset) {
fprintf(stderr, "ds4: Metal attention output batch weights are outside the mapped model\n");
return 0;
}
const uint64_t heads_bytes = (uint64_t)n_tokens * n_groups * group_dim * sizeof(float);
const uint64_t low_bytes = (uint64_t)n_tokens * low_dim * sizeof(float);
const uint64_t out_bytes = (uint64_t)n_tokens * out_dim * sizeof(float);
if (ds4_gpu_tensor_bytes(heads) < heads_bytes ||
ds4_gpu_tensor_bytes(low) < low_bytes ||
ds4_gpu_tensor_bytes(out) < out_bytes) {
fprintf(stderr, "ds4: Metal attention output batch received undersized buffers\n");
return 0;
}
(void)group_tmp;
(void)low_tmp;
const bool use_direct_low =
n_tokens < 32u && getenv("DS4_METAL_DISABLE_ATTN_OUT_LOW_DIRECT") == NULL;
/* The exported TensorOps attention-output kernel is a 64-token tile.
* Keep this on full tiles only; smaller multiples of 32 use the legacy
* path instead of relying on cooperative tensor partial RHS bounds. */
const bool use_mpp_low =
n_tokens >= 32u &&
(n_tokens % DS4_METAL_ATTN_OUT_MPP_TILE_N) == 0 &&
ds4_gpu_use_mpp_attn_out_low_matmul();
const NSUInteger ids_bytes = (NSUInteger)n_tokens * (NSUInteger)n_groups * sizeof(int32_t);
id<MTLBuffer> group_ids_buffer = nil;
if (!use_direct_low && !use_mpp_low) {
if (getenv("DS4_METAL_DISABLE_ATTN_OUT_IDS_CACHE") != NULL) {
group_ids_buffer =
ds4_gpu_new_transient_buffer(ids_bytes, "attention output group ids");
if (!group_ids_buffer) {
return 0;
}
} else {
if (!ds4_gpu_ensure_scratch_buffer(&g_attn_out_group_ids_buffer,
&g_attn_out_group_ids_bytes,
ids_bytes,
"ds4_attention_output_group_ids")) {
return 0;
}
group_ids_buffer = g_attn_out_group_ids_buffer;
}
int32_t *ids = (int32_t *)[group_ids_buffer contents];
for (uint32_t t = 0; t < n_tokens; t++) {
for (uint32_t group = 0; group < n_groups; group++) {
ids[(uint64_t)t * n_groups + group] = (int32_t)group;
}
}
}
uint64_t out_a_inner = 0;
id<MTLBuffer> out_a_buf =
ds4_gpu_wrap_model_range(model_map, model_size,
out_a_offset, out_a_bytes,
&out_a_inner);
if (!out_a_buf) return 0;
const bool had_batch = g_batch_cb != nil;
if (!had_batch && ds4_gpu_begin_commands() == 0) return 0;
bool ok = true;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb || owned) {
ok = false;
}
const bool attn_out_profile =
getenv("DS4_METAL_ATTN_OUT_STAGE_PROFILE") != NULL && g_batch_cb != nil;
if (ok && attn_out_profile) {
if (ds4_gpu_end_commands() == 0 || ds4_gpu_begin_commands() == 0) {
ok = false;
} else {
cb = ds4_gpu_command_buffer(&owned);
if (!cb || owned) ok = false;
}
}
double attn_out_t0 = attn_out_profile ? ds4_gpu_now_ms() : 0.0;
#define DS4_METAL_PROFILE_ATTN_OUT_STAGE(name) do { \
if (ok && attn_out_profile) { \
if (ds4_gpu_end_commands() == 0) { \
ok = false; \
} else { \
const double now_ms = ds4_gpu_now_ms(); \
fprintf(stderr, \
"ds4: Metal attention output stage tokens=%u %s=%.3f ms\n", \
n_tokens, (name), now_ms - attn_out_t0); \
attn_out_t0 = now_ms; \
if (ds4_gpu_begin_commands() == 0) { \
ok = false; \
} else { \
cb = ds4_gpu_command_buffer(&owned); \
if (!cb || owned) ok = false; \
} \
} \
} \
} while (0)
if (ok) {
/*
* Batched attention-output projections switch from the vector
* kernel to the SIMD matrix kernel once the batch has at least 32
* tokens. This preserves the single-token generation path while
* keeping prefill accumulation stable.
*/
if (use_mpp_low) {
ds4_gpu_mul_mm_id_args mm_args =
ds4_gpu_make_mul_mm_id_args((uint32_t)group_dim,
(uint32_t)rank,
n_groups,
row_a_bytes,
(uint64_t)rank * row_a_bytes,
n_groups,
n_groups,
n_tokens);
/*
* Direct RHS lets MPP read the dense low-rank activation tile
* directly from device memory instead of staging a second
* threadgroup tile. The retained attention-output path is the
* 64-token direct-RHS kernel; the older staged-RHS and 32-token
* variants were not kept as alternate runtime modes.
*/
const char *attn_out_pipeline_name =
"kernel_attn_out_low_q8_0_mpp_direct_rhs_n64";
id<MTLComputePipelineState> mm_pipeline =
ds4_gpu_get_mul_mm_id_pipeline(attn_out_pipeline_name, false);
ok = ds4_gpu_encode_attn_out_low_mpp(cb,
mm_pipeline,
&mm_args,
out_a_buf,
(NSUInteger)out_a_inner,
ds4_gpu_tensor_buffer(heads),
ds4_gpu_tensor_offset(heads),
ds4_gpu_tensor_buffer(low),
ds4_gpu_tensor_offset(low)) != 0;
if (!ok) {
ds4_gpu_warn_mpp_fallback();
if (ds4_gpu_mul_mm_id_map0_name(n_groups) != NULL) {
if (getenv("DS4_METAL_DISABLE_ATTN_OUT_IDS_CACHE") != NULL) {
group_ids_buffer =
ds4_gpu_new_transient_buffer(ids_bytes, "attention output group ids");
} else if (ds4_gpu_ensure_scratch_buffer(&g_attn_out_group_ids_buffer,
&g_attn_out_group_ids_bytes,
ids_bytes,
"ds4_attention_output_group_ids")) {
group_ids_buffer = g_attn_out_group_ids_buffer;
}
if (group_ids_buffer) {
int32_t *ids = (int32_t *)[group_ids_buffer contents];
for (uint32_t t = 0; t < n_tokens; t++) {
for (uint32_t group = 0; group < n_groups; group++) {
ids[(uint64_t)t * n_groups + group] = (int32_t)group;
}
}
ds4_gpu_mul_mm_id_map_args map_args =
ds4_gpu_make_mul_mm_id_map_args((uint32_t)group_dim,
n_groups,
n_groups,
n_groups,
n_tokens);
id<MTLComputePipelineState> map_pipeline =
ds4_gpu_get_pipeline(ds4_gpu_mul_mm_id_map0_name(n_groups));
id<MTLComputePipelineState> fallback_pipeline =
ds4_gpu_get_mul_mm_id_pipeline("kernel_mul_mm_id_q8_0_f32", false);
ok = ds4_gpu_encode_mul_mm_id(cb,
map_pipeline,
fallback_pipeline,
&map_args,
&mm_args,
out_a_buf,
(NSUInteger)out_a_inner,
ds4_gpu_tensor_buffer(heads),
ds4_gpu_tensor_offset(heads),
ds4_gpu_tensor_buffer(low),
ds4_gpu_tensor_offset(low),
group_ids_buffer,
0) != 0;
}
}
}
} else if (n_tokens >= 32u && ds4_gpu_mul_mm_id_map0_name(n_groups) != NULL) {
ds4_gpu_mul_mm_id_map_args map_args =
ds4_gpu_make_mul_mm_id_map_args((uint32_t)group_dim,
n_groups,
n_groups,
n_groups,
n_tokens);
ds4_gpu_mul_mm_id_args mm_args =
ds4_gpu_make_mul_mm_id_args((uint32_t)group_dim,
(uint32_t)rank,
n_groups,
row_a_bytes,
(uint64_t)rank * row_a_bytes,
n_groups,
n_groups,
n_tokens);
id<MTLComputePipelineState> map_pipeline =
ds4_gpu_get_pipeline(ds4_gpu_mul_mm_id_map0_name(n_groups));
id<MTLComputePipelineState> mm_pipeline =
ds4_gpu_get_mul_mm_id_pipeline("kernel_mul_mm_id_q8_0_f32", false);
ok = ds4_gpu_encode_mul_mm_id(cb,
map_pipeline,
mm_pipeline,
&map_args,
&mm_args,
out_a_buf,
(NSUInteger)out_a_inner,
ds4_gpu_tensor_buffer(heads),
ds4_gpu_tensor_offset(heads),
ds4_gpu_tensor_buffer(low),
ds4_gpu_tensor_offset(low),
group_ids_buffer,
0) != 0;
} else if (use_direct_low) {
ds4_gpu_mul_mv_id_args args = {
.nei0 = (int32_t)n_groups,
.nei1 = (int32_t)n_tokens,
.nbi1 = 0,
.ne00 = (int32_t)group_dim,
.ne01 = (int32_t)rank,
.ne02 = (int32_t)n_groups,
.nb00 = 34,
.nb01 = row_a_bytes,
.nb02 = (uint64_t)rank * row_a_bytes,
.ne10 = (int32_t)group_dim,
.ne11 = (int32_t)n_groups,
.ne12 = (int32_t)n_tokens,
.ne13 = 1,
.nb10 = sizeof(float),
.nb11 = (uint64_t)group_dim * sizeof(float),
.nb12 = (uint64_t)n_groups * group_dim * sizeof(float),
.ne0 = (int32_t)rank,
.ne1 = (int32_t)n_groups,
.nb1 = (uint64_t)rank * sizeof(float),
.nr0 = 2,
};
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_mul_mv_pipeline("kernel_dsv4_attn_out_low_q8_0_f32", 4);
ok = ds4_gpu_encode_attn_out_low_q8_direct(cb,
pipeline,
&args,
out_a_buf,
(NSUInteger)out_a_inner,
ds4_gpu_tensor_buffer(heads),
ds4_gpu_tensor_offset(heads),
ds4_gpu_tensor_buffer(low),
ds4_gpu_tensor_offset(low),
32u * 2u * sizeof(float),
4,
true) != 0;
} else {
ds4_gpu_mul_mv_id_args args = {
.nei0 = (int32_t)n_groups,
.nei1 = (int32_t)n_tokens,
.nbi1 = (uint64_t)n_groups * sizeof(int32_t),
.ne00 = (int32_t)group_dim,
.ne01 = (int32_t)rank,
.ne02 = (int32_t)n_groups,
.nb00 = 34,
.nb01 = row_a_bytes,
.nb02 = (uint64_t)rank * row_a_bytes,
.ne10 = (int32_t)group_dim,
.ne11 = (int32_t)n_groups,
.ne12 = (int32_t)n_tokens,
.ne13 = 1,
.nb10 = sizeof(float),
.nb11 = (uint64_t)group_dim * sizeof(float),
.nb12 = (uint64_t)n_groups * group_dim * sizeof(float),
.ne0 = (int32_t)rank,
.ne1 = (int32_t)n_groups,
.nb1 = (uint64_t)rank * sizeof(float),
.nr0 = 2,
};
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_mul_mv_pipeline("kernel_mul_mv_id_q8_0_f32", 4);
ok = ds4_gpu_encode_mul_mv_id(cb,
pipeline,
&args,
out_a_buf,
(NSUInteger)out_a_inner,
ds4_gpu_tensor_buffer(heads),
ds4_gpu_tensor_offset(heads),
ds4_gpu_tensor_buffer(low),
ds4_gpu_tensor_offset(low),
group_ids_buffer,
0,
32u * 2u * sizeof(float),
4,
true) != 0;
}
}
DS4_METAL_PROFILE_ATTN_OUT_STAGE("low_proj");
if (ok && round_low_bf16)
ok = ds4_gpu_dsv41_quantize(low, (uint32_t)low_dim, n_tokens, DS4_V41_BF16);
if (ok && full_low_dim != low_dim) {
const uint32_t matrix_rows = ds4_gpu_mpp_available() ? n_tokens / 32u * 32u : 0;
if (matrix_rows) {
ok = ds4_gpu_matmul_q8_0_kslice_rows_impl(out, model_map, model_size,
out_b_offset, full_low_dim, out_dim, low_offset, low_dim,
low, matrix_rows, low_dim, 0) != 0;
}
/* Older GPUs use the exact matvec reduction for every row.
* Dispatch them together instead of encoding one kernel per token. */
if (ok && matrix_rows < n_tokens) {
const uint32_t rows = n_tokens - matrix_rows;
ds4_gpu_tensor *tail = ds4_gpu_tensor_view(out,
(uint64_t)matrix_rows * out_dim * sizeof(float),
(uint64_t)rows * out_dim * sizeof(float));
ok = tail && ds4_gpu_matmul_q8_0_kslice_exact_rows(tail, model_map, model_size,
out_b_offset, full_low_dim, low_offset, low_dim, out_dim,
low, (uint64_t)matrix_rows * low_dim, rows);
ds4_gpu_tensor_free(tail);
}
} else if (ok) {
ok = ds4_gpu_matmul_q8_0_tensor(out, model_map, model_size,
out_b_offset,
low_dim, out_dim, low, n_tokens) != 0;
}
DS4_METAL_PROFILE_ATTN_OUT_STAGE("out_proj");
if (!had_batch) {
ok = ds4_gpu_end_commands() != 0 && ok;
}
#undef DS4_METAL_PROFILE_ATTN_OUT_STAGE
return ok ? 1 : 0;
}
}
int ds4_gpu_attention_output_q8_batch_tensor(
ds4_gpu_tensor *out, ds4_gpu_tensor *low,
ds4_gpu_tensor *group_tmp, ds4_gpu_tensor *low_tmp,
const void *model_map, uint64_t model_size,
uint64_t out_a_offset, uint64_t out_b_offset,
uint64_t group_dim, uint64_t rank, uint32_t n_groups, uint64_t out_dim,
const ds4_gpu_tensor *heads, uint32_t n_tokens) {
return ds4_gpu_attention_output_q8_batch_impl(out, low, group_tmp, low_tmp,
model_map, model_size, out_a_offset, out_b_offset, group_dim, rank,
n_groups, out_dim, heads, n_tokens, false, 0, 0);
}
int ds4_gpu_dsv41_attention_output_batch(
ds4_gpu_tensor *out, ds4_gpu_tensor *low,
const void *model_map, uint64_t model_size,
uint64_t out_a_offset, uint64_t out_b_offset,
const ds4_gpu_tensor *heads, uint32_t n_tokens) {
return ds4_gpu_attention_output_q8_batch_impl(out, low, low, low,
model_map, model_size, out_a_offset, out_b_offset, 4096, 1024,
8, 5120, heads, n_tokens, true, 0, 0);
}
int ds4_gpu_dsv41_attention_output_tp_batch(
ds4_gpu_tensor *out, ds4_gpu_tensor *low,
const void *model_map, uint64_t model_size,
uint64_t out_a_offset, uint64_t out_b_offset,
const ds4_gpu_tensor *heads, uint32_t n_tokens, uint32_t tp_rank) {
const uint64_t shard_bytes = UINT64_C(4) * 1024 * (4096 / 32 * 34);
if (tp_rank > 1 || out_a_offset > model_size ||
shard_bytes * 2u > model_size - out_a_offset) return 0;
return ds4_gpu_attention_output_q8_batch_impl(out, low, low, low,
model_map, model_size, out_a_offset + tp_rank * shard_bytes, out_b_offset,
4096, 1024, 4, 5120, heads, n_tokens, true, 8192, tp_rank * 4096u);
}
int ds4_gpu_attention_output_q4_K_batch_tensor(
ds4_gpu_tensor *out,
ds4_gpu_tensor *low,
ds4_gpu_tensor *group_tmp,
ds4_gpu_tensor *low_tmp,
const void *model_map,
uint64_t model_size,
uint64_t out_a_offset,
uint64_t out_b_offset,
uint32_t out_b_type,
uint64_t group_dim,
uint64_t rank,
uint32_t n_groups,
uint64_t out_dim,
const ds4_gpu_tensor *heads,
uint32_t n_tokens) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!out || !low || !heads || !model_map ||
group_dim == 0 || rank == 0 || n_groups == 0 || out_dim == 0 || n_tokens == 0 ||
group_dim > UINT32_MAX || rank > UINT32_MAX || out_dim > UINT32_MAX) {
return 0;
}
if (n_tokens < 32u) return 0;
@autoreleasepool {
const uint64_t low_dim = (uint64_t)n_groups * rank;
if ((group_dim % 256u) != 0 || (low_dim % 256u) != 0 || low_dim > UINT32_MAX) {
return 0;
}
uint64_t row_a_bytes = 0;
uint64_t row_b_bytes = 0;
if (!ds4_gpu_quant_row_bytes(DS4_METAL_TENSOR_Q4_K, (uint32_t)group_dim, &row_a_bytes) ||
!ds4_gpu_quant_row_bytes(out_b_type, (uint32_t)low_dim, &row_b_bytes)) {
return 0;
}
const uint64_t out_a_bytes = (uint64_t)n_groups * rank * row_a_bytes;
const uint64_t out_b_bytes = out_dim * row_b_bytes;
if (out_a_offset > model_size || out_a_bytes > model_size - out_a_offset ||
out_b_offset > model_size || out_b_bytes > model_size - out_b_offset) {
fprintf(stderr, "ds4: Metal Q4 attention output batch weights are outside the mapped model\n");
return 0;
}
const uint64_t heads_bytes = (uint64_t)n_tokens * n_groups * group_dim * sizeof(float);
const uint64_t low_bytes = (uint64_t)n_tokens * low_dim * sizeof(float);
const uint64_t out_bytes = (uint64_t)n_tokens * out_dim * sizeof(float);
if (ds4_gpu_tensor_bytes(heads) < heads_bytes ||
ds4_gpu_tensor_bytes(low) < low_bytes ||
ds4_gpu_tensor_bytes(out) < out_bytes) {
fprintf(stderr, "ds4: Metal Q4 attention output batch received undersized buffers\n");
return 0;
}
(void)group_tmp;
(void)low_tmp;
const uint64_t padded_n_tokens_u64 =
((uint64_t)n_tokens + DS4_METAL_ATTN_OUT_MPP_TILE_N - 1u) /
DS4_METAL_ATTN_OUT_MPP_TILE_N * DS4_METAL_ATTN_OUT_MPP_TILE_N;
const uint32_t padded_n_tokens =
padded_n_tokens_u64 <= UINT32_MAX ? (uint32_t)padded_n_tokens_u64 : n_tokens;
/*
* TensorOps requires complete 64-row tiles. For a substantial final
* prefill chunk, compute harmless extra rows when the caller's
* workspace already covers them; the following projection consumes
* only the real rows. Small tails stay on the legacy kernel because
* padding overhead outweighs the faster arithmetic there.
*/
const bool use_mpp_padding =
n_tokens >= 256u &&
padded_n_tokens > n_tokens &&
ds4_gpu_tensor_bytes(heads) >=
(uint64_t)padded_n_tokens * n_groups * group_dim * sizeof(float) &&
ds4_gpu_tensor_bytes(low) >=
(uint64_t)padded_n_tokens * low_dim * sizeof(float);
bool use_mpp_low =
((n_tokens % DS4_METAL_ATTN_OUT_MPP_TILE_N) == 0 ||
use_mpp_padding) &&
ds4_gpu_use_mpp_attn_out_low_matmul();
id<MTLComputePipelineState> mpp_low_pipeline = nil;
if (use_mpp_low) {
mpp_low_pipeline = ds4_gpu_get_mul_mm_id_pipeline(
"kernel_attn_out_low_q4_K_mpp_direct_rhs_n64", false);
if (!mpp_low_pipeline) {
ds4_gpu_warn_mpp_fallback();
use_mpp_low = false;
}
}
const NSUInteger ids_bytes = (NSUInteger)n_tokens * (NSUInteger)n_groups * sizeof(int32_t);
id<MTLBuffer> group_ids_buffer = nil;
if (!use_mpp_low) {
if (getenv("DS4_METAL_DISABLE_ATTN_OUT_IDS_CACHE") != NULL) {
group_ids_buffer =
ds4_gpu_new_transient_buffer(ids_bytes, "attention output Q4 group ids");
} else if (ds4_gpu_ensure_scratch_buffer(&g_attn_out_group_ids_buffer,
&g_attn_out_group_ids_bytes,
ids_bytes,
"ds4_attention_output_group_ids")) {
group_ids_buffer = g_attn_out_group_ids_buffer;
}
if (!group_ids_buffer) return 0;
int32_t *ids = (int32_t *)[group_ids_buffer contents];
for (uint32_t t = 0; t < n_tokens; t++) {
for (uint32_t group = 0; group < n_groups; group++) {
ids[(uint64_t)t * n_groups + group] = (int32_t)group;
}
}
}
uint64_t out_a_inner = 0;
id<MTLBuffer> out_a_buf =
ds4_gpu_wrap_model_range(model_map, model_size,
out_a_offset, out_a_bytes,
&out_a_inner);
if (!out_a_buf) return 0;
const bool had_batch = g_batch_cb != nil;
if (!had_batch && ds4_gpu_begin_commands() == 0) return 0;
bool ok = true;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb || owned) ok = false;
if (ok) {
ds4_gpu_mul_mm_id_args mm_args =
ds4_gpu_make_mul_mm_id_args((uint32_t)group_dim,
(uint32_t)rank,
n_groups,
row_a_bytes,
(uint64_t)rank * row_a_bytes,
n_groups,
n_groups,
use_mpp_low && use_mpp_padding
? padded_n_tokens
: n_tokens);
if (use_mpp_low) {
ok = ds4_gpu_encode_attn_out_low_mpp(
cb,
mpp_low_pipeline,
&mm_args,
out_a_buf,
(NSUInteger)out_a_inner,
ds4_gpu_tensor_buffer(heads),
ds4_gpu_tensor_offset(heads),
ds4_gpu_tensor_buffer(low),
ds4_gpu_tensor_offset(low)) != 0;
} else {
ds4_gpu_mul_mm_id_map_args map_args =
ds4_gpu_make_mul_mm_id_map_args((uint32_t)group_dim,
n_groups,
n_groups,
n_groups,
n_tokens);
id<MTLComputePipelineState> map_pipeline =
ds4_gpu_get_pipeline(ds4_gpu_mul_mm_id_map0_name(n_groups));
id<MTLComputePipelineState> mm_pipeline =
ds4_gpu_get_mul_mm_id_pipeline("kernel_mul_mm_id_q4_K_f32", false);
ok = ds4_gpu_encode_mul_mm_id(cb,
map_pipeline,
mm_pipeline,
&map_args,
&mm_args,
out_a_buf,
(NSUInteger)out_a_inner,
ds4_gpu_tensor_buffer(heads),
ds4_gpu_tensor_offset(heads),
ds4_gpu_tensor_buffer(low),
ds4_gpu_tensor_offset(low),
group_ids_buffer,
0) != 0;
}
}
if (ok) {
ok = ds4_gpu_matmul_quant_tensor(out,
model_map,
model_size,
out_b_offset,
out_b_type,
low_dim,
out_dim,
low,
n_tokens) != 0;
}
if (!had_batch) {
ok = ds4_gpu_end_commands() != 0 && ok;
}
return ok ? 1 : 0;
}
}
int ds4_gpu_attention_output_q8_batch_f16_tensor(
ds4_gpu_tensor *out_h,
ds4_gpu_tensor *low,
const void *model_map,
uint64_t model_size,
uint64_t out_a_offset,
uint64_t out_b_offset,
uint64_t group_dim,
uint64_t rank,
uint32_t n_groups,
uint64_t out_dim,
const ds4_gpu_tensor *heads,
uint32_t n_tokens) {
(void)out_h; (void)low; (void)model_map; (void)model_size;
(void)out_a_offset; (void)out_b_offset; (void)group_dim; (void)rank;
(void)n_groups; (void)out_dim; (void)heads; (void)n_tokens;
return 0;
}
static int ds4_gpu_matmul_q8_0_kslice_exact_rows(
ds4_gpu_tensor *out,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint64_t full_in_dim,
uint64_t k_off,
uint64_t k_cnt,
uint64_t out_dim,
const ds4_gpu_tensor *x,
uint64_t x_elem_off,
uint32_t n_rows) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if ((full_in_dim & 31u) != 0 || (k_off & 31u) != 0 || (k_cnt & 31u) != 0 ||
!n_rows || n_rows > INT32_MAX || k_cnt == 0 || k_off > full_in_dim ||
k_cnt > full_in_dim - k_off ||
full_in_dim > INT32_MAX || out_dim > INT32_MAX ||
x_elem_off > UINT64_MAX / sizeof(float) - (uint64_t)n_rows * k_cnt) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> xbuf = ds4_gpu_tensor_buffer(x);
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out);
if (!xbuf || !outbuf ||
ds4_gpu_tensor_bytes(x) < (x_elem_off + (uint64_t)n_rows * k_cnt) * sizeof(float) ||
ds4_gpu_tensor_bytes(out) < (uint64_t)n_rows * out_dim * sizeof(float)) {
fprintf(stderr, "ds4: Metal Q8_0 kslice matmul received undersized buffers\n");
return 0;
}
const uint64_t row_bytes = (full_in_dim / 32u) * 34u;
const uint64_t weight_bytes = out_dim * row_bytes;
if (weight_offset > model_size || weight_bytes > model_size - weight_offset) {
fprintf(stderr, "ds4: Metal Q8_0 kslice weights are outside the mapped model\n");
return 0;
}
uint64_t inner = 0;
id<MTLBuffer> wbuf = ds4_gpu_wrap_model_range(model_map, model_size,
weight_offset, weight_bytes, &inner);
if (!wbuf) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
/* Same matvec kernel as the full projection: ne00 bounds the k loop
* while nb01/nb02 keep the full-row stride, so each row reads only
* the owned k window. */
ds4_gpu_q8_0_matvec_args mv_args = ds4_gpu_make_q8_0_mv_args(full_in_dim, out_dim);
mv_args.ne00 = (int32_t)k_cnt;
mv_args.ne10 = (int32_t)k_cnt;
mv_args.ne11 = (int32_t)n_rows;
mv_args.ne1 = (int32_t)n_rows;
mv_args.nb11 = k_cnt * sizeof(float);
mv_args.nb12 = (uint64_t)n_rows * k_cnt * sizeof(float);
mv_args.nb13 = mv_args.nb12;
ds4_gpu_mv_dispatch mv_dispatch = ds4_gpu_make_q8_0_mv_dispatch();
if (out_dim > 65536u) mv_dispatch.nsg = 8;
/* Reuse the input across more output rows without changing the K
* traversal or SIMD reduction used by TP attention partials. */
if (n_rows >= 32u && full_in_dim == 8192u && k_cnt == 4096u &&
out_dim == 5120u && mv_dispatch.nsg == 2 && ds4_gpu_tp_world_is_two() &&
ds4_gpu_device_name_contains("M3 Ultra") &&
!getenv("DS4_METAL_DISABLE_V41_PREFILL_Q8_ROWS8")) {
mv_dispatch.function_name = "kernel_mul_mv_q8_0_f32_rows8";
mv_dispatch.nr0 = 8;
mv_dispatch.smem = 32u * 8u * sizeof(float);
}
mv_args.nr0 = mv_dispatch.nr0;
/* TP partial producer: publish the gate's checked flag from the
* same kernel (last-arriving threadgroup) when requested. */
uint32_t fold_slot = 0, fold_value = 0;
const int fold = n_rows == 1u && !owned &&
ds4_gpu_tp_flag_fold_take(out, out_dim * sizeof(float), &fold_slot, &fold_value);
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_mul_mv_pipeline(
fold ? "kernel_dsv4_mul_mv_q8_0_f32_tp_flag_checked"
: mv_dispatch.function_name,
mv_dispatch.nsg);
if (!pipeline) return 0;
const NSUInteger ntg =
((NSUInteger)out_dim + (NSUInteger)mv_dispatch.nr0 - 1u) / (NSUInteger)mv_dispatch.nr0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&mv_args length:sizeof(mv_args) atIndex:0];
[enc setBuffer:wbuf offset:(NSUInteger)(inner + (k_off / 32u) * 34u) atIndex:1];
[enc setBuffer:xbuf
offset:(NSUInteger)(ds4_gpu_tensor_offset(x) + x_elem_off * sizeof(float))
atIndex:2];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(out) atIndex:3];
if (fold) {
const uint32_t ntg32 = (uint32_t)ntg;
[enc setBuffer:g_tp_slab_buffer
offset:(NSUInteger)(g_tp_slab_buffer_off + g_tp_gpu_flags_off + (uint64_t)fold_slot * 4u)
atIndex:4];
[enc setBuffer:g_tp_check_buffer offset:(NSUInteger)fold_slot * 4u atIndex:5];
[enc setBytes:&fold_value length:sizeof(fold_value) atIndex:6];
[enc setBuffer:g_tp_fold_ctl offset:(NSUInteger)fold_slot * 8u atIndex:7];
[enc setBytes:&ntg32 length:sizeof(ntg32) atIndex:8];
[enc setThreadgroupMemoryLength:16 atIndex:1];
}
[enc setThreadgroupMemoryLength:mv_dispatch.smem atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(ntg, n_rows, 1)
threadsPerThreadgroup:MTLSizeMake(32, (NSUInteger)mv_dispatch.nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (fold) g_tp_flag_prepublished_seq = g_tp_seq + 1u;
if (!ds4_gpu_finish_command_buffer(cb, owned, "Q8_0 kslice matvec")) {
return 0;
}
return 1;
}
}
int ds4_gpu_matmul_q8_0_kslice_tensor(
ds4_gpu_tensor *out, const void *model_map, uint64_t model_size,
uint64_t weight_offset, uint64_t full_in_dim, uint64_t k_off,
uint64_t k_cnt, uint64_t out_dim, const ds4_gpu_tensor *x,
uint64_t x_elem_off) {
return ds4_gpu_matmul_q8_0_kslice_exact_rows(out, model_map, model_size,
weight_offset, full_in_dim, k_off, k_cnt, out_dim, x, x_elem_off, 1);
}
static int ds4_gpu_matmul_q8_0_kslice_rows_impl(
ds4_gpu_tensor *out,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint64_t full_in_dim,
uint64_t out_dim,
uint64_t k_off,
uint64_t k_cnt,
const ds4_gpu_tensor *x,
uint64_t n_rows,
uint64_t x_stride,
uint64_t x_off) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!ds4_gpu_mpp_available() ||
!out || !x || !model_map ||
(full_in_dim % 64u) != 0 || (out_dim % 64u) != 0 ||
(k_off % 32u) != 0 || (k_cnt % 64u) != 0 ||
k_cnt == 0 || k_off > full_in_dim || k_cnt > full_in_dim - k_off ||
x_stride > UINT32_MAX || x_off > x_stride || k_cnt > x_stride - x_off ||
(n_rows % 32u) != 0 || n_rows == 0 ||
full_in_dim > UINT32_MAX || out_dim > UINT32_MAX ||
n_rows > UINT32_MAX) {
return 0;
}
@autoreleasepool {
const uint64_t row_bytes = (full_in_dim / 32u) * 34u;
const uint64_t weight_bytes = out_dim * row_bytes;
const uint64_t x_bytes = n_rows * x_stride * sizeof(float);
const uint64_t out_bytes = n_rows * out_dim * sizeof(float);
id<MTLBuffer> xbuf = ds4_gpu_tensor_buffer(x);
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out);
if (!xbuf || !outbuf ||
ds4_gpu_tensor_bytes(x) < x_bytes ||
ds4_gpu_tensor_bytes(out) < out_bytes ||
weight_offset > model_size ||
weight_bytes > model_size - weight_offset) {
return 0;
}
uint64_t inner_offset = 0;
id<MTLBuffer> wbuf = ds4_gpu_wrap_model_range(model_map,
model_size,
weight_offset,
weight_bytes,
&inner_offset);
if (!wbuf) return 0;
uint64_t tile_rows = 32u;
if ((n_rows % 128u) == 0) tile_rows = 128u;
else if ((n_rows % 64u) == 0) tile_rows = 64u;
const char *fn = tile_rows == 128u
? "kernel_mul_mm_q8_0_f32_nax_direct_rhs_n128"
: (tile_rows == 64u
? "kernel_mul_mm_q8_0_f32_nax_direct_rhs_n64"
: "kernel_mul_mm_q8_0_f32_nax_direct_rhs");
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_mul_mm_pipeline(fn, false, false);
if (!pipeline) return 0;
ds4_gpu_mul_mm_args args =
ds4_gpu_make_mm_args(k_cnt, out_dim, n_rows, row_bytes);
args.nb01 = row_bytes;
args.nb02 = row_bytes * out_dim;
args.nb03 = row_bytes * out_dim;
args.nb11 = x_stride * sizeof(float);
args.nb12 = n_rows * x_stride * sizeof(float);
args.nb13 = args.nb12;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:wbuf
offset:(NSUInteger)(inner_offset + (k_off / 32u) * 34u)
atIndex:1];
[enc setBuffer:xbuf
offset:(NSUInteger)(ds4_gpu_tensor_offset(x) +
x_off * sizeof(float))
atIndex:2];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(out) atIndex:3];
[enc setThreadgroupMemoryLength:2u * 64u * 32u * sizeof(uint16_t)
atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)(n_rows / tile_rows),
(NSUInteger)(out_dim / 64u),
1)
threadsPerThreadgroup:MTLSizeMake(128, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_finish_command_buffer(cb,
owned,
"Q8_0 NAX kslice rows matmul");
}
}
int ds4_gpu_matmul_q8_0_kslice_rows_tensor(
ds4_gpu_tensor *out, const void *model_map, uint64_t model_size,
uint64_t weight_offset, uint64_t full_in_dim, uint64_t out_dim,
uint64_t k_off, uint64_t k_cnt, const ds4_gpu_tensor *x,
uint64_t n_rows) {
return ds4_gpu_matmul_q8_0_kslice_rows_impl(out, model_map, model_size,
weight_offset, full_in_dim, out_dim, k_off, k_cnt, x, n_rows,
full_in_dim, k_off);
}
int ds4_gpu_matmul_quant_kslice_tensor(
ds4_gpu_tensor *out,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint32_t weight_type,
uint64_t full_in_dim,
uint64_t k_off,
uint64_t k_cnt,
uint64_t out_dim,
const ds4_gpu_tensor *x,
uint64_t x_elem_off) {
if (weight_type == DS4_METAL_TENSOR_Q8_0) {
return ds4_gpu_matmul_q8_0_kslice_tensor(out,
model_map,
model_size,
weight_offset,
full_in_dim,
k_off,
k_cnt,
out_dim,
x,
x_elem_off);
}
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!out || !x || !model_map ||
full_in_dim == 0 || k_cnt == 0 || out_dim == 0 ||
k_off + k_cnt > full_in_dim ||
full_in_dim > UINT32_MAX || k_cnt > UINT32_MAX ||
out_dim > UINT32_MAX) {
return 0;
}
uint64_t block_elems = 0;
uint64_t block_bytes = 0;
if (weight_type == DS4_METAL_TENSOR_Q4_K) {
block_elems = 256u;
block_bytes = 144u;
} else if (weight_type == DS4_METAL_TENSOR_Q4_0) {
block_elems = 32u;
block_bytes = 18u;
} else {
fprintf(stderr, "ds4: Metal quant kslice received unsupported type %u\n", weight_type);
return 0;
}
if ((full_in_dim % block_elems) != 0 ||
(k_off % block_elems) != 0 ||
(k_cnt % block_elems) != 0) {
fprintf(stderr, "ds4: Metal quant kslice dimensions are not block aligned\n");
return 0;
}
@autoreleasepool {
id<MTLBuffer> xbuf = ds4_gpu_tensor_buffer(x);
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out);
if (!xbuf || !outbuf ||
ds4_gpu_tensor_bytes(x) < (x_elem_off + k_cnt) * sizeof(float) ||
ds4_gpu_tensor_bytes(out) < out_dim * sizeof(float)) {
fprintf(stderr, "ds4: Metal quant kslice matmul received undersized buffers\n");
return 0;
}
const uint64_t row_bytes = (full_in_dim / block_elems) * block_bytes;
const uint64_t weight_bytes = out_dim * row_bytes;
if (weight_offset > model_size || weight_bytes > model_size - weight_offset) {
fprintf(stderr, "ds4: Metal quant kslice weights are outside the mapped model\n");
return 0;
}
uint64_t inner = 0;
id<MTLBuffer> wbuf = ds4_gpu_wrap_model_range(model_map, model_size,
weight_offset, weight_bytes, &inner);
if (!wbuf) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
if (weight_type == DS4_METAL_TENSOR_Q4_K &&
(k_cnt % 256u) == 0 &&
getenv("DS4_METAL_DISABLE_Q4_MV_CLASSIC") == NULL) {
const int16_t nsg = 2;
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_mul_mv_ext_pipeline("kernel_mul_mv_q4_K_dense_f32", nsg, 8);
if (pipeline) {
ds4_gpu_q8_0_matvec_args args = {
.ne00 = (int32_t)k_cnt,
.ne01 = (int32_t)out_dim,
.ne02 = 1,
.nb00 = 1,
.nb01 = row_bytes,
.nb02 = row_bytes * out_dim,
.nb03 = row_bytes * out_dim,
.ne10 = (int32_t)k_cnt,
.ne11 = 1,
.ne12 = 1,
.nb10 = sizeof(float),
.nb11 = k_cnt * sizeof(float),
.nb12 = k_cnt * sizeof(float),
.nb13 = k_cnt * sizeof(float),
.ne0 = (int32_t)out_dim,
.ne1 = 1,
.nr0 = 2,
.r2 = 1,
.r3 = 1,
};
const uint64_t rows_ptg = (uint64_t)nsg * 2u;
const uint64_t w_skip = (k_off / block_elems) * block_bytes;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:wbuf offset:(NSUInteger)(inner + w_skip) atIndex:1];
[enc setBuffer:xbuf
offset:(NSUInteger)(ds4_gpu_tensor_offset(x) + x_elem_off * sizeof(float))
atIndex:2];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(out) atIndex:3];
[enc setThreadgroupMemoryLength:32 atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(((NSUInteger)out_dim + rows_ptg - 1u) / rows_ptg,
1,
1)
threadsPerThreadgroup:MTLSizeMake(32, (NSUInteger)nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "Q4_K kslice matvec")) return 0;
return 1;
}
}
fprintf(stderr, "ds4: Metal quant kslice has no kernel for type %u\n",
weight_type);
return 0;
}
}
int ds4_gpu_attention_output_q8_tp_tensor(
ds4_gpu_tensor *out,
ds4_gpu_tensor *low,
const void *model_map,
uint64_t model_size,
uint64_t out_a_offset,
uint64_t out_b_offset,
uint64_t group_dim,
uint64_t rank,
uint32_t n_groups_total,
uint32_t group0,
uint32_t group_cnt,
uint64_t out_dim,
const ds4_gpu_tensor *heads) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!out || !low || !heads || !model_map ||
group_dim == 0 || rank == 0 || group_cnt == 0 ||
group0 + group_cnt > n_groups_total ||
(group_dim % 32u) != 0 || ((rank * group_cnt) % 32u) != 0 ||
group_dim > UINT32_MAX || rank > UINT32_MAX || out_dim > UINT32_MAX) {
return 0;
}
@autoreleasepool {
const uint64_t low_dim_total = (uint64_t)n_groups_total * rank;
const uint64_t row_a_bytes = (group_dim / 32u) * 34u;
const uint64_t a_group_bytes = rank * row_a_bytes;
const uint64_t out_a_bytes = (uint64_t)n_groups_total * a_group_bytes;
if (out_a_offset > model_size || out_a_bytes > model_size - out_a_offset) {
fprintf(stderr, "ds4: Metal TP attention output weights are outside the mapped model\n");
return 0;
}
/* The heads buffer holds only the owned groups, compact at its
* base (the head slice keeps q/attention output halves packed). */
if (ds4_gpu_tensor_bytes(heads) < (uint64_t)group_cnt * group_dim * sizeof(float) ||
ds4_gpu_tensor_bytes(low) < (uint64_t)group_cnt * rank * sizeof(float) ||
ds4_gpu_tensor_bytes(out) < out_dim * sizeof(float)) {
fprintf(stderr, "ds4: Metal TP attention output received undersized buffers\n");
return 0;
}
uint64_t out_a_inner = 0;
id<MTLBuffer> out_a_buf =
ds4_gpu_wrap_model_range(model_map, model_size,
out_a_offset, out_a_bytes, &out_a_inner);
if (!out_a_buf) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb || owned) return 0;
/* Low projection for the owned groups only: identical dispatch to
* the single-node direct path with the weight base, heads input and
* group count shifted to the slice. The owned low half lands
* compactly at low[0 .. group_cnt*rank). */
ds4_gpu_mul_mv_id_args args = {
.nei0 = (int32_t)group_cnt,
.nei1 = 1,
.nbi1 = 0,
.ne00 = (int32_t)group_dim,
.ne01 = (int32_t)rank,
.ne02 = (int32_t)group_cnt,
.nb00 = 34,
.nb01 = row_a_bytes,
.nb02 = a_group_bytes,
.ne10 = (int32_t)group_dim,
.ne11 = (int32_t)group_cnt,
.ne12 = 1,
.ne13 = 1,
.nb10 = sizeof(float),
.nb11 = (uint64_t)group_dim * sizeof(float),
.nb12 = (uint64_t)group_cnt * group_dim * sizeof(float),
.ne0 = (int32_t)rank,
.ne1 = (int32_t)group_cnt,
.nb1 = (uint64_t)rank * sizeof(float),
.nr0 = 2,
};
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_mul_mv_pipeline("kernel_dsv4_attn_out_low_q8_0_f32", 4);
int ok = ds4_gpu_encode_attn_out_low_q8_direct(cb,
pipeline,
&args,
out_a_buf,
(NSUInteger)(out_a_inner + (uint64_t)group0 * a_group_bytes),
ds4_gpu_tensor_buffer(heads),
ds4_gpu_tensor_offset(heads),
ds4_gpu_tensor_buffer(low),
ds4_gpu_tensor_offset(low),
32u * 2u * sizeof(float),
4,
true);
if (!ok) return 0;
/* Expand projection over the owned k window only; the result is this
* rank's partial attention block output. */
return ds4_gpu_matmul_q8_0_kslice_tensor(out, model_map, model_size,
out_b_offset,
low_dim_total,
(uint64_t)group0 * rank,
(uint64_t)group_cnt * rank,
out_dim, low, 0);
}
}
int ds4_gpu_attention_output_low_q8_tensor(
ds4_gpu_tensor *low,
const void *model_map,
uint64_t model_size,
uint64_t out_a_offset,
uint64_t group_dim,
uint64_t rank,
uint32_t n_groups,
const ds4_gpu_tensor *heads) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!low || !heads || !model_map || group_dim == 0 || rank == 0 ||
n_groups == 0 || group_dim > UINT32_MAX || rank > UINT32_MAX) {
return 0;
}
@autoreleasepool {
const uint64_t low_dim = (uint64_t)n_groups * rank;
if ((group_dim % 32u) != 0 || low_dim > UINT32_MAX) {
fprintf(stderr, "ds4: Metal attention output low received invalid q8 dimensions\n");
return 0;
}
const uint64_t row_a_bytes = (group_dim / 32u) * 34u;
const uint64_t out_a_bytes = (uint64_t)n_groups * rank * row_a_bytes;
if (out_a_offset > model_size || out_a_bytes > model_size - out_a_offset) {
fprintf(stderr, "ds4: Metal attention output low weights are outside the mapped model\n");
return 0;
}
const uint64_t heads_bytes = (uint64_t)n_groups * group_dim * sizeof(float);
const uint64_t low_bytes = low_dim * sizeof(float);
if (ds4_gpu_tensor_bytes(heads) < heads_bytes ||
ds4_gpu_tensor_bytes(low) < low_bytes) {
fprintf(stderr, "ds4: Metal attention output low received undersized buffers\n");
return 0;
}
uint64_t out_a_inner = 0;
id<MTLBuffer> out_a_buf =
ds4_gpu_wrap_model_range(model_map, model_size,
out_a_offset, out_a_bytes,
&out_a_inner);
if (!out_a_buf) return 0;
const bool had_batch = g_batch_cb != nil;
if (!had_batch && ds4_gpu_begin_commands() == 0) return 0;
bool ok = true;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb || owned) {
ok = false;
}
if (ok) {
ds4_gpu_mul_mv_id_args args = {
.nei0 = (int32_t)n_groups,
.nei1 = 1,
.nbi1 = 0,
.ne00 = (int32_t)group_dim,
.ne01 = (int32_t)rank,
.ne02 = (int32_t)n_groups,
.nb00 = 34,
.nb01 = row_a_bytes,
.nb02 = (uint64_t)rank * row_a_bytes,
.ne10 = (int32_t)group_dim,
.ne11 = (int32_t)n_groups,
.ne12 = 1,
.ne13 = 1,
.nb10 = sizeof(float),
.nb11 = (uint64_t)group_dim * sizeof(float),
.nb12 = (uint64_t)n_groups * group_dim * sizeof(float),
.ne0 = (int32_t)rank,
.ne1 = (int32_t)n_groups,
.nb1 = (uint64_t)rank * sizeof(float),
.nr0 = 2,
};
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_mul_mv_pipeline("kernel_dsv4_attn_out_low_q8_0_f32", 4);
ok = ds4_gpu_encode_attn_out_low_q8_direct(cb,
pipeline,
&args,
out_a_buf,
(NSUInteger)out_a_inner,
ds4_gpu_tensor_buffer(heads),
ds4_gpu_tensor_offset(heads),
ds4_gpu_tensor_buffer(low),
ds4_gpu_tensor_offset(low),
32u * 2u * sizeof(float),
4,
true) != 0;
}
if (!had_batch) {
ok = ds4_gpu_end_commands() != 0 && ok;
}
return ok ? 1 : 0;
}
}
int ds4_gpu_attention_output_low_q8_rows_exact_tensor(
ds4_gpu_tensor *low,
const void *model_map,
uint64_t model_size,
uint64_t out_a_offset,
uint64_t group_dim,
uint64_t rank,
uint32_t n_groups_total,
uint32_t group0,
uint32_t group_cnt,
const ds4_gpu_tensor *heads,
uint32_t n_rows) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!low || !heads || !model_map || group_dim == 0 || rank == 0 ||
n_groups_total == 0 || group_cnt == 0 || group0 > n_groups_total ||
group_cnt > n_groups_total - group0 || n_rows == 0 ||
(uint64_t)n_rows * group_cnt > 65535u || (group_dim % 32u) != 0 ||
group_dim > UINT32_MAX || rank > UINT32_MAX) {
return 0;
}
@autoreleasepool {
const uint64_t row_a_bytes = (group_dim / 32u) * 34u;
const uint64_t a_offset = out_a_offset + (uint64_t)group0 * rank * row_a_bytes;
const uint64_t out_a_bytes = (uint64_t)group_cnt * rank * row_a_bytes;
if (a_offset > model_size || out_a_bytes > model_size - a_offset ||
ds4_gpu_tensor_bytes(heads) <
(uint64_t)n_rows * n_groups_total * group_dim * sizeof(float) ||
ds4_gpu_tensor_bytes(low) < (uint64_t)n_rows * group_cnt * rank * sizeof(float)) {
return 0;
}
uint64_t out_a_inner = 0;
id<MTLBuffer> out_a_buf = ds4_gpu_wrap_model_range(model_map, model_size,
a_offset, out_a_bytes, &out_a_inner);
if (!out_a_buf) return 0;
const bool had_batch = g_batch_cb != nil;
if (!had_batch && ds4_gpu_begin_commands() == 0) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
bool ok = cb && !owned;
if (ok) {
/* the rows kernels take every token of one group per threadgroup */
const bool rows_kernel = n_rows >= 2u && n_rows <= 8u;
ds4_gpu_mul_mv_id_args args = {
.nei0 = (int32_t)group_cnt,
.nei1 = rows_kernel ? 1 : (int32_t)n_rows,
.nbi1 = 0,
.ne00 = (int32_t)group_dim,
.ne01 = (int32_t)rank,
.ne02 = (int32_t)group_cnt,
.nb00 = 34,
.nb01 = row_a_bytes,
.nb02 = (uint64_t)rank * row_a_bytes,
.ne10 = (int32_t)group_dim,
.ne11 = (int32_t)group_cnt,
.ne12 = 1,
.ne13 = 1,
.nb10 = sizeof(float),
.nb11 = (uint64_t)group_dim * sizeof(float),
.nb12 = (uint64_t)n_groups_total * group_dim * sizeof(float),
.ne0 = (int32_t)rank,
.ne1 = (int32_t)group_cnt,
.nb1 = (uint64_t)rank * sizeof(float),
.nr0 = 2,
};
const NSUInteger groups = rows_kernel ? n_rows : 1u;
const int mma_off = ds4_gpu_v41_rows_mma_off();
const bool mma_kernel = !mma_off && n_rows >= 3u && n_rows <= 8u &&
(group_dim % 256u) == 0 && (rank % 8u) == 0;
if (mma_kernel) { args.nr0 = 8; args.ne12 = (int32_t)n_rows; }
id<MTLComputePipelineState> pipeline = mma_kernel ?
ds4_gpu_get_pipeline("kernel_dsv4_attn_out_low_q8_0_f32_mma_rows8") :
ds4_gpu_get_mul_mv_pipeline(rows_kernel ?
"kernel_dsv4_attn_out_low_q8_0_f32_rows" : "kernel_dsv4_attn_out_low_q8_0_f32", 4);
ok = pipeline && ds4_gpu_encode_attn_out_low_q8_direct(cb, pipeline, &args,
out_a_buf, (NSUInteger)out_a_inner,
ds4_gpu_tensor_buffer(heads),
ds4_gpu_tensor_offset(heads) + (NSUInteger)group0 * group_dim * sizeof(float),
ds4_gpu_tensor_buffer(low), ds4_gpu_tensor_offset(low),
mma_kernel ? (8u * 2u * 8u * 32u + 8u * 64u) * sizeof(float) : 32u * 2u * sizeof(float) * groups,
mma_kernel ? 8u : 4u * groups, true) != 0;
}
if (!had_batch) ok = ds4_gpu_end_commands() != 0 && ok;
return ok ? 1 : 0;
}
}
int ds4_gpu_attention_output_low_q4_K_slice_tensor(
ds4_gpu_tensor *low,
const void *model_map,
uint64_t model_size,
uint64_t out_a_offset,
uint64_t group_dim,
uint64_t rank,
uint32_t group0,
uint32_t group_cnt,
const ds4_gpu_tensor *heads) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!low || !heads || !model_map || group_dim == 0 || rank == 0 ||
group_cnt == 0 || group_dim > UINT32_MAX || rank > UINT32_MAX) {
return 0;
}
@autoreleasepool {
if ((group_dim % 256u) != 0) {
fprintf(stderr, "ds4: Metal attention output low received invalid Q4_K dimensions\n");
return 0;
}
uint64_t row_a_bytes = 0;
if (!ds4_gpu_quant_row_bytes(DS4_METAL_TENSOR_Q4_K,
(uint32_t)group_dim,
&row_a_bytes)) {
return 0;
}
if (rank > UINT64_MAX / row_a_bytes) return 0;
const uint64_t group_weight_bytes = rank * row_a_bytes;
if (group0 > UINT64_MAX / group_weight_bytes ||
group_cnt > UINT64_MAX / group_weight_bytes) {
return 0;
}
const uint64_t group_skip = (uint64_t)group0 * group_weight_bytes;
const uint64_t out_a_bytes = (uint64_t)group_cnt * group_weight_bytes;
if (out_a_offset > UINT64_MAX - group_skip ||
out_a_offset + group_skip > model_size ||
out_a_bytes > model_size - (out_a_offset + group_skip)) {
fprintf(stderr, "ds4: Metal Q4 attention output low weights are outside the mapped model\n");
return 0;
}
const uint64_t heads_bytes = (uint64_t)group_cnt * group_dim * sizeof(float);
const uint64_t low_bytes = (uint64_t)group_cnt * rank * sizeof(float);
if (ds4_gpu_tensor_bytes(heads) < heads_bytes ||
ds4_gpu_tensor_bytes(low) < low_bytes) {
fprintf(stderr, "ds4: Metal Q4 attention output low received undersized buffers\n");
return 0;
}
uint64_t out_a_inner = 0;
id<MTLBuffer> out_a_buf =
ds4_gpu_wrap_model_range(model_map, model_size,
out_a_offset + group_skip,
out_a_bytes,
&out_a_inner);
if (!out_a_buf) return 0;
const bool had_batch = g_batch_cb != nil;
if (!had_batch && ds4_gpu_begin_commands() == 0) return 0;
bool ok = true;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb || owned) {
ok = false;
}
if (ok) {
ds4_gpu_mul_mv_id_args args = {
.nei0 = (int32_t)group_cnt,
.nei1 = 1,
.nbi1 = 0,
.ne00 = (int32_t)group_dim,
.ne01 = (int32_t)rank,
.ne02 = (int32_t)group_cnt,
.nb00 = 1,
.nb01 = row_a_bytes,
.nb02 = group_weight_bytes,
.ne10 = (int32_t)group_dim,
.ne11 = (int32_t)group_cnt,
.ne12 = 1,
.ne13 = 1,
.nb10 = sizeof(float),
.nb11 = (uint64_t)group_dim * sizeof(float),
.nb12 = (uint64_t)group_cnt * group_dim * sizeof(float),
.ne0 = (int32_t)rank,
.ne1 = (int32_t)group_cnt,
.nb1 = (uint64_t)rank * sizeof(float),
.nr0 = 2,
};
const NSUInteger nsg = 2;
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_mul_mv_pipeline("kernel_dsv4_attn_out_low_q4_K_f32", (int16_t)nsg);
ok = ds4_gpu_encode_attn_out_low_q8_direct(cb,
pipeline,
&args,
out_a_buf,
(NSUInteger)out_a_inner,
ds4_gpu_tensor_buffer(heads),
ds4_gpu_tensor_offset(heads),
ds4_gpu_tensor_buffer(low),
ds4_gpu_tensor_offset(low),
32u,
nsg,
false) != 0;
}
if (!had_batch) {
ok = ds4_gpu_end_commands() != 0 && ok;
}
return ok ? 1 : 0;
}
}
static NSUInteger ds4_gpu_align_up_ns(NSUInteger value, NSUInteger align) {
return (value + align - 1u) & ~(align - 1u);
}
static int ds4_gpu_encode_cpy_f32_f32_3d(
id<MTLCommandBuffer> cb,
id<MTLBuffer> src,
NSUInteger src_off,
id<MTLBuffer> dst,
NSUInteger dst_off,
uint32_t cols,
uint32_t rows,
uint32_t planes,
uint64_t src_row_stride,
uint64_t src_plane_stride,
uint64_t dst_row_stride,
uint64_t dst_plane_stride) {
if (!cb || !src || !dst || cols == 0 || rows == 0 || planes == 0) return 0;
ds4_gpu_cpy_args args = {
.nk0 = (int64_t)cols,
.ne00 = (int64_t)cols,
.ne01 = (int64_t)rows,
.ne02 = (int64_t)planes,
.ne03 = 1,
.nb00 = sizeof(float),
.nb01 = src_row_stride,
.nb02 = src_plane_stride,
.nb03 = (uint64_t)planes * src_plane_stride,
.ne0 = (int64_t)cols,
.ne1 = (int64_t)rows,
.ne2 = (int64_t)planes,
.ne3 = 1,
.nb0 = sizeof(float),
.nb1 = dst_row_stride,
.nb2 = dst_plane_stride,
.nb3 = (uint64_t)planes * dst_plane_stride,
};
const NSUInteger nth = ds4_gpu_cpy_threads(cols, g_cpy_f32_f32_pipeline);
const NSUInteger col_groups = ((NSUInteger)cols + nth - 1u) / nth;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:g_cpy_f32_f32_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:src offset:src_off atIndex:1];
[enc setBuffer:dst offset:dst_off atIndex:2];
[enc dispatchThreadgroups:MTLSizeMake(col_groups * rows, planes, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_cpy_f32_f32_3d_src_strided(
id<MTLCommandBuffer> cb,
id<MTLBuffer> src,
NSUInteger src_off,
id<MTLBuffer> dst,
NSUInteger dst_off,
uint32_t cols,
uint32_t rows,
uint32_t planes,
uint64_t src_col_stride,
uint64_t src_row_stride,
uint64_t src_plane_stride,
uint64_t dst_row_stride,
uint64_t dst_plane_stride) {
if (!cb || !src || !dst || cols == 0 || rows == 0 || planes == 0) return 0;
ds4_gpu_cpy_args args = {
.nk0 = (int64_t)cols,
.ne00 = (int64_t)cols,
.ne01 = (int64_t)rows,
.ne02 = (int64_t)planes,
.ne03 = 1,
.nb00 = src_col_stride,
.nb01 = src_row_stride,
.nb02 = src_plane_stride,
.nb03 = (uint64_t)planes * src_plane_stride,
.ne0 = (int64_t)cols,
.ne1 = (int64_t)rows,
.ne2 = (int64_t)planes,
.ne3 = 1,
.nb0 = sizeof(float),
.nb1 = dst_row_stride,
.nb2 = dst_plane_stride,
.nb3 = (uint64_t)planes * dst_plane_stride,
};
const NSUInteger nth = ds4_gpu_cpy_threads(cols, g_cpy_f32_f32_pipeline);
const NSUInteger col_groups = ((NSUInteger)cols + nth - 1u) / nth;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:g_cpy_f32_f32_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:src offset:src_off atIndex:1];
[enc setBuffer:dst offset:dst_off atIndex:2];
[enc dispatchThreadgroups:MTLSizeMake(col_groups * rows, planes, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_cpy_f32_f16_1d(
id<MTLCommandBuffer> cb,
id<MTLBuffer> src,
NSUInteger src_off,
id<MTLBuffer> dst,
NSUInteger dst_off,
uint32_t n) {
if (!cb || !src || !dst || n == 0) return 0;
const int use_contiguous =
ds4_gpu_env_bool("DS4_METAL_DISABLE_CONTIG_F32_F16_COPY") <= 0;
id<MTLComputePipelineState> pipeline = use_contiguous
? g_cpy_contig_f32_f16_pipeline
: g_cpy_f32_f16_pipeline;
const NSUInteger work_items = use_contiguous
? ((NSUInteger)n + 3u) / 4u
: (NSUInteger)n;
const NSUInteger nth = ds4_gpu_cpy_threads((uint32_t)work_items, pipeline);
const NSUInteger groups = (work_items + nth - 1u) / nth;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
if (use_contiguous) {
[enc setBytes:&n length:sizeof(n) atIndex:0];
} else {
ds4_gpu_cpy_args args =
ds4_gpu_make_cpy_1d_args(n, sizeof(float), sizeof(uint16_t));
[enc setBytes:&args length:sizeof(args) atIndex:0];
}
[enc setBuffer:src offset:src_off atIndex:1];
[enc setBuffer:dst offset:dst_off atIndex:2];
[enc dispatchThreadgroups:MTLSizeMake(groups, 1, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_cpy_f32_f16_2d(
id<MTLCommandBuffer> cb,
id<MTLBuffer> src,
NSUInteger src_off,
id<MTLBuffer> dst,
NSUInteger dst_off,
uint32_t cols,
uint32_t rows,
uint64_t src_row_stride,
uint64_t dst_row_stride) {
if (!cb || !src || !dst || cols == 0 || rows == 0) return 0;
ds4_gpu_cpy_args args = {
.nk0 = (int64_t)cols,
.ne00 = (int64_t)cols,
.ne01 = (int64_t)rows,
.ne02 = 1,
.ne03 = 1,
.nb00 = sizeof(float),
.nb01 = src_row_stride,
.nb02 = (uint64_t)rows * src_row_stride,
.nb03 = (uint64_t)rows * src_row_stride,
.ne0 = (int64_t)cols,
.ne1 = (int64_t)rows,
.ne2 = 1,
.ne3 = 1,
.nb0 = sizeof(uint16_t),
.nb1 = dst_row_stride,
.nb2 = (uint64_t)rows * dst_row_stride,
.nb3 = (uint64_t)rows * dst_row_stride,
};
const NSUInteger nth = ds4_gpu_cpy_threads(cols, g_cpy_f32_f16_pipeline);
const NSUInteger col_groups = ((NSUInteger)cols + nth - 1u) / nth;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:g_cpy_f32_f16_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:src offset:src_off atIndex:1];
[enc setBuffer:dst offset:dst_off atIndex:2];
[enc dispatchThreadgroups:MTLSizeMake(col_groups * rows, 1, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_cpy_f32_f16_3d(
id<MTLCommandBuffer> cb,
id<MTLBuffer> src,
NSUInteger src_off,
id<MTLBuffer> dst,
NSUInteger dst_off,
uint32_t cols,
uint32_t rows,
uint32_t planes,
uint64_t src_row_stride,
uint64_t src_plane_stride,
uint64_t dst_row_stride,
uint64_t dst_plane_stride) {
if (!cb || !src || !dst || cols == 0 || rows == 0 || planes == 0) return 0;
ds4_gpu_cpy_args args = {
.nk0 = (int64_t)cols,
.ne00 = (int64_t)cols,
.ne01 = (int64_t)rows,
.ne02 = (int64_t)planes,
.ne03 = 1,
.nb00 = sizeof(float),
.nb01 = src_row_stride,
.nb02 = src_plane_stride,
.nb03 = (uint64_t)planes * src_plane_stride,
.ne0 = (int64_t)cols,
.ne1 = (int64_t)rows,
.ne2 = (int64_t)planes,
.ne3 = 1,
.nb0 = sizeof(uint16_t),
.nb1 = dst_row_stride,
.nb2 = dst_plane_stride,
.nb3 = (uint64_t)planes * dst_plane_stride,
};
const NSUInteger nth = ds4_gpu_cpy_threads(cols, g_cpy_f32_f16_pipeline);
const NSUInteger col_groups = ((NSUInteger)cols + nth - 1u) / nth;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:g_cpy_f32_f16_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:src offset:src_off atIndex:1];
[enc setBuffer:dst offset:dst_off atIndex:2];
[enc dispatchThreadgroups:MTLSizeMake(col_groups * rows, planes, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_cpy_f16_f16_3d(
id<MTLCommandBuffer> cb,
id<MTLBuffer> src,
NSUInteger src_off,
id<MTLBuffer> dst,
NSUInteger dst_off,
uint32_t cols,
uint32_t rows,
uint32_t planes,
uint64_t src_row_stride,
uint64_t src_plane_stride,
uint64_t dst_row_stride,
uint64_t dst_plane_stride) {
if (!cb || !src || !dst || cols == 0 || rows == 0 || planes == 0) return 0;
ds4_gpu_cpy_args args = {
.nk0 = (int64_t)cols,
.ne00 = (int64_t)cols,
.ne01 = (int64_t)rows,
.ne02 = (int64_t)planes,
.ne03 = 1,
.nb00 = sizeof(uint16_t),
.nb01 = src_row_stride,
.nb02 = src_plane_stride,
.nb03 = (uint64_t)planes * src_plane_stride,
.ne0 = (int64_t)cols,
.ne1 = (int64_t)rows,
.ne2 = (int64_t)planes,
.ne3 = 1,
.nb0 = sizeof(uint16_t),
.nb1 = dst_row_stride,
.nb2 = dst_plane_stride,
.nb3 = (uint64_t)planes * dst_plane_stride,
};
const NSUInteger nth = ds4_gpu_cpy_threads(cols, g_cpy_f16_f16_pipeline);
const NSUInteger col_groups = ((NSUInteger)cols + nth - 1u) / nth;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:g_cpy_f16_f16_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:src offset:src_off atIndex:1];
[enc setBuffer:dst offset:dst_off atIndex:2];
[enc dispatchThreadgroups:MTLSizeMake(col_groups * rows, planes, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_cpy_f16_f32_1d(
id<MTLCommandBuffer> cb,
id<MTLBuffer> src,
NSUInteger src_off,
id<MTLBuffer> dst,
NSUInteger dst_off,
uint32_t n) {
if (!cb || !src || !dst || n == 0) return 0;
const int use_contiguous =
ds4_gpu_env_bool("DS4_METAL_DISABLE_CONTIG_F32_F16_COPY") <= 0;
id<MTLComputePipelineState> pipeline = use_contiguous
? g_cpy_contig_f16_f32_pipeline
: g_cpy_f16_f32_pipeline;
const NSUInteger work_items = use_contiguous
? ((NSUInteger)n + 3u) / 4u
: (NSUInteger)n;
const NSUInteger nth = ds4_gpu_cpy_threads((uint32_t)work_items, pipeline);
const NSUInteger groups = (work_items + nth - 1u) / nth;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
if (use_contiguous) {
[enc setBytes:&n length:sizeof(n) atIndex:0];
} else {
ds4_gpu_cpy_args args =
ds4_gpu_make_cpy_1d_args(n, sizeof(uint16_t), sizeof(float));
[enc setBytes:&args length:sizeof(args) atIndex:0];
}
[enc setBuffer:src offset:src_off atIndex:1];
[enc setBuffer:dst offset:dst_off atIndex:2];
[enc dispatchThreadgroups:MTLSizeMake(groups, 1, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_copy_to_f16_1d(
id<MTLCommandBuffer> cb,
id<MTLBuffer> src,
NSUInteger src_off,
bool src_is_f16,
id<MTLBuffer> dst,
NSUInteger dst_off,
uint32_t n) {
if (!cb || !src || !dst) return 0;
if (n == 0) return 1;
if (!src_is_f16) {
return ds4_gpu_encode_cpy_f32_f16_1d(cb, src, src_off, dst, dst_off, n);
}
if (ds4_gpu_env_bool("DS4_METAL_DISABLE_CONTIG_F16_F16_COPY") <= 0) {
const NSUInteger work_items = ((NSUInteger)n + 3u) / 4u;
const NSUInteger nth = ds4_gpu_cpy_threads(
(uint32_t)work_items,
g_cpy_contig_f16_f16_pipeline);
const NSUInteger groups = (work_items + nth - 1u) / nth;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:g_cpy_contig_f16_f16_pipeline];
[enc setBytes:&n length:sizeof(n) atIndex:0];
[enc setBuffer:src offset:src_off atIndex:1];
[enc setBuffer:dst offset:dst_off atIndex:2];
[enc dispatchThreadgroups:MTLSizeMake(groups, 1, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
if (g_batch_cb && cb == g_batch_cb) ds4_gpu_close_batch_encoder();
id<MTLBlitCommandEncoder> blit = ds4_gpu_blit_encoder(cb, "f16_copy", 1u);
if (!blit) return 0;
[blit copyFromBuffer:src
sourceOffset:src_off
toBuffer:dst
destinationOffset:dst_off
size:(NSUInteger)n * sizeof(uint16_t)];
[blit endEncoding];
return 1;
}
static int ds4_gpu_encode_copy_raw_ring_to_f16(
id<MTLCommandBuffer> cb,
id<MTLBuffer> raw,
NSUInteger raw_offset,
uint32_t raw_cap,
uint32_t raw_start,
uint32_t n_raw,
uint32_t head_dim,
id<MTLBuffer> dst,
NSUInteger dst_offset) {
if (!cb || !raw || !dst || raw_cap == 0 || raw_start >= raw_cap ||
n_raw == 0 || n_raw > raw_cap || head_dim == 0) {
return 0;
}
const NSUInteger raw_row_bytes = (NSUInteger)head_dim * sizeof(float);
const NSUInteger dst_row_bytes = (NSUInteger)head_dim * sizeof(uint16_t);
const uint32_t tail_rows = raw_cap - raw_start < n_raw
? raw_cap - raw_start
: n_raw;
const uint32_t head_rows = n_raw - tail_rows;
const uint64_t tail_count = (uint64_t)tail_rows * head_dim;
const uint64_t head_count = (uint64_t)head_rows * head_dim;
const uint64_t raw_inner = (uint64_t)raw_start * raw_row_bytes;
const uint64_t dst_inner = (uint64_t)tail_rows * dst_row_bytes;
if (tail_count > UINT32_MAX || head_count > UINT32_MAX ||
raw_inner > NSUIntegerMax - raw_offset ||
dst_inner > NSUIntegerMax - dst_offset) {
return 0;
}
if (tail_rows &&
!ds4_gpu_encode_cpy_f32_f16_1d(
cb,
raw,
raw_offset + (NSUInteger)raw_inner,
dst,
dst_offset,
(uint32_t)tail_count)) {
return 0;
}
if (head_rows &&
!ds4_gpu_encode_cpy_f32_f16_1d(
cb,
raw,
raw_offset,
dst,
dst_offset + (NSUInteger)dst_inner,
(uint32_t)head_count)) {
return 0;
}
return 1;
}
static int ds4_gpu_encode_flash_kv_stage_f16(
id<MTLCommandBuffer> cb,
id<MTLBuffer> raw,
NSUInteger raw_offset,
uint32_t raw_cap,
uint32_t raw_start,
uint32_t n_raw,
id<MTLBuffer> comp,
NSUInteger comp_offset,
bool comp_is_f16,
id<MTLBuffer> ids,
NSUInteger ids_offset,
uint32_t n_comp,
uint32_t head_dim,
id<MTLBuffer> dst,
NSUInteger dst_offset,
id<MTLBuffer> mask,
NSUInteger mask_offset,
id<MTLBuffer> pad,
NSUInteger pad_offset,
bool fuse_pad,
bool shared_pad,
bool *did_fuse_pad) {
if (did_fuse_pad) *did_fuse_pad = false;
if (!cb || !raw || !comp || !dst || raw_cap == 0 ||
raw_start >= raw_cap || n_raw == 0 || n_raw > raw_cap ||
n_comp == 0 || head_dim == 0) {
return 0;
}
const bool require =
getenv("DS4_METAL_REQUIRE_GATHERED_KV_STAGE") != NULL;
const bool supported_shape =
(comp_is_f16 || ids != nil) && head_dim == 512u && raw_cap <= UINT32_MAX / 128u;
const uint64_t row_vecs64 = 128u;
const uint64_t comp_count64 = (uint64_t)n_comp * head_dim;
const uint64_t dst_comp_inner64 =
(uint64_t)n_raw * head_dim * sizeof(uint16_t);
if (comp_count64 > UINT32_MAX ||
dst_comp_inner64 > NSUIntegerMax - dst_offset) {
return 0;
}
const uint64_t total_vecs64 =
((uint64_t)n_raw + n_comp) * row_vecs64;
const bool valid_grid =
total_vecs64 != 0 && total_vecs64 <= UINT32_MAX;
id<MTLComputePipelineState> stage_pipeline = ids ?
ds4_gpu_get_pipeline("kernel_dsv41_flash_kv_stage") : g_flash_kv_stage_f16_pipeline;
const bool eligible =
supported_shape && valid_grid && !g_quality_mode &&
getenv("DS4_METAL_DISABLE_GATHERED_KV_STAGE") == NULL &&
stage_pipeline != nil &&
(ds4_gpu_device_name_contains("M3") ||
ds4_gpu_device_name_contains("M5"));
const bool component_disabled = eligible &&
(ds4_gpu_env_bool("DS4_METAL_DISABLE_CONTIG_F32_F16_COPY") > 0 ||
ds4_gpu_env_bool("DS4_METAL_DISABLE_CONTIG_F16_F16_COPY") > 0);
const bool use_fusion = eligible && !component_disabled;
const bool use_pad_fusion =
use_fusion && fuse_pad && mask != nil && pad != nil &&
getenv("DS4_METAL_DISABLE_GATHERED_KV_PAD_FUSION") == NULL;
if (require && supported_shape && !use_fusion) {
fprintf(stderr,
"ds4: required Metal gathered KV staging kernel was not selected\n");
return 0;
}
if (use_fusion) {
ds4_gpu_flash_kv_stage_f16_args args = {
.raw_cap = raw_cap,
.raw_start = raw_start,
.n_raw = n_raw,
.n_comp = n_comp,
.pad_rows = use_pad_fusion ? 32u : 0u,
.shared_pad = use_pad_fusion && shared_pad ? 1u : 0u,
};
const NSUInteger total_vecs = (NSUInteger)total_vecs64 +
(use_pad_fusion ? 32u * 128u + 32u : 0u);
NSUInteger nth =
stage_pipeline.maxTotalThreadsPerThreadgroup;
if (nth > 256u) nth = 256u;
if (nth > total_vecs) nth = total_vecs;
if (nth == 0) return 0;
const NSUInteger groups = (total_vecs + nth - 1u) / nth;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
if (g_kv_task.pending) {
/* Safety net: a deferred kv task nothing consumed runs before the
* staging reads the cache row. */
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_kv_norm_task_flush()) return 0;
enc = ds4_gpu_compute_encoder(cb);
}
[enc setComputePipelineState:stage_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:raw offset:raw_offset atIndex:1];
[enc setBuffer:comp offset:comp_offset atIndex:2];
[enc setBuffer:dst offset:dst_offset atIndex:3];
[enc setBuffer:(use_pad_fusion ? mask : dst)
offset:(use_pad_fusion ? mask_offset : dst_offset)
atIndex:4];
[enc setBuffer:(use_pad_fusion ? pad : dst)
offset:(use_pad_fusion ? pad_offset : dst_offset)
atIndex:5];
if (ids) [enc setBuffer:ids offset:ids_offset atIndex:6];
[enc dispatchThreadgroups:MTLSizeMake(groups, 1, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (did_fuse_pad) *did_fuse_pad = use_pad_fusion;
return 1;
}
if (ids) return 0; /* gathered rows have no separate copy path */
if (!ds4_gpu_encode_copy_raw_ring_to_f16(cb,
raw,
raw_offset,
raw_cap,
raw_start,
n_raw,
head_dim,
dst,
dst_offset)) {
return 0;
}
return ds4_gpu_encode_copy_to_f16_1d(
cb,
comp,
comp_offset,
comp_is_f16,
dst,
dst_offset + (NSUInteger)dst_comp_inner64,
(uint32_t)comp_count64);
}
int ds4_gpu_flash_kv_stage_f16_tensor(
ds4_gpu_tensor *dst,
const ds4_gpu_tensor *raw,
uint32_t raw_cap,
uint32_t raw_start,
uint32_t n_raw,
const ds4_gpu_tensor *comp,
uint32_t comp_is_f16,
uint32_t n_comp,
uint32_t head_dim) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!dst || !raw || !comp || raw_cap == 0 || raw_start >= raw_cap ||
n_raw == 0 || n_raw > raw_cap || n_comp == 0 ||
comp_is_f16 == 0 || head_dim != 512u) {
return 0;
}
@autoreleasepool {
const uint64_t raw_bytes =
(uint64_t)raw_cap * head_dim * sizeof(float);
const uint64_t comp_bytes =
(uint64_t)n_comp * head_dim *
(comp_is_f16 ? sizeof(uint16_t) : sizeof(float));
const uint64_t dst_bytes =
((uint64_t)n_raw + n_comp) * head_dim * sizeof(uint16_t);
id<MTLBuffer> rawbuf = ds4_gpu_tensor_buffer(raw);
id<MTLBuffer> compbuf = ds4_gpu_tensor_buffer(comp);
id<MTLBuffer> dstbuf = ds4_gpu_tensor_buffer(dst);
if (!rawbuf || !compbuf || !dstbuf ||
ds4_gpu_tensor_bytes(raw) < raw_bytes ||
ds4_gpu_tensor_bytes(comp) < comp_bytes ||
ds4_gpu_tensor_bytes(dst) < dst_bytes) {
fprintf(stderr,
"ds4: Metal gathered KV staging received undersized buffers\n");
return 0;
}
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
if (!ds4_gpu_encode_flash_kv_stage_f16(
cb,
rawbuf,
ds4_gpu_tensor_offset(raw),
raw_cap,
raw_start,
n_raw,
compbuf,
ds4_gpu_tensor_offset(comp),
comp_is_f16 != 0,
nil,
0,
n_comp,
head_dim,
dstbuf,
ds4_gpu_tensor_offset(dst),
nil,
0,
nil,
0,
false,
false,
NULL)) {
return 0;
}
if (!ds4_gpu_finish_command_buffer(
cb, owned, "gathered KV staging")) {
return 0;
}
}
return 1;
}
static int ds4_gpu_encode_fill_f16_1d(
id<MTLCommandBuffer> cb,
id<MTLBuffer> buf,
NSUInteger offset,
uint32_t n,
float value) {
if (!cb || !buf || n == 0) return 0;
ds4_gpu_unary_args args = ds4_gpu_make_unary_rows_args(n, 1, 0, 0.0f, 0.0f);
args.val = value;
NSUInteger nth = (NSUInteger)n;
const NSUInteger max_threads = g_unary_fill_f16_pipeline.maxTotalThreadsPerThreadgroup;
if (nth > max_threads) nth = max_threads;
if (nth > 256u) nth = 256u;
if (nth == 0) nth = 1u;
const NSUInteger groups = ((NSUInteger)n + nth - 1u) / nth;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:g_unary_fill_f16_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:buf offset:offset atIndex:1];
[enc setBuffer:buf offset:offset atIndex:2];
[enc dispatchThreadgroups:MTLSizeMake(groups, 1, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_flash_attention_raw_heads(
id<MTLCommandBuffer> cb,
ds4_gpu_tensor *heads,
id<MTLBuffer> sinks_buf,
NSUInteger sinks_offset,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *raw_kv,
uint32_t n_raw,
uint32_t raw_cap,
uint32_t raw_start,
uint32_t n_head,
uint32_t head_dim) {
if (head_dim != 512 || n_head == 0 || n_raw == 0 || raw_cap < n_raw) {
return 0;
}
id<MTLBuffer> qbuf = ds4_gpu_tensor_buffer(q);
id<MTLBuffer> rawbuf = ds4_gpu_tensor_buffer(raw_kv);
id<MTLBuffer> headsbuf = ds4_gpu_tensor_buffer(heads);
const uint64_t q_bytes = (uint64_t)n_head * head_dim * sizeof(float);
const uint64_t raw_bytes = (uint64_t)raw_cap * head_dim * sizeof(float);
const uint64_t heads_bytes = q_bytes;
if (!qbuf || !rawbuf || !headsbuf || !sinks_buf ||
ds4_gpu_tensor_bytes(q) < q_bytes ||
ds4_gpu_tensor_bytes(raw_kv) < raw_bytes ||
ds4_gpu_tensor_bytes(heads) < heads_bytes) {
fprintf(stderr, "ds4: Metal DS4 FlashAttention received undersized buffers\n");
return 0;
}
const uint32_t ncpsg = 32;
const uint32_t nwg = 32;
const uint32_t nsg = ds4_gpu_flash_attn_vec_nsg(n_raw, nwg, ncpsg);
const NSUInteger row_bytes = (NSUInteger)head_dim * sizeof(float);
const NSUInteger row_bytes_f16 = (NSUInteger)head_dim * sizeof(uint16_t);
const NSUInteger mask_bytes = (NSUInteger)n_raw * sizeof(uint16_t);
const NSUInteger kv_bytes = (NSUInteger)n_raw * row_bytes_f16;
const NSUInteger pad_bytes = 2u * (NSUInteger)ncpsg * row_bytes_f16 +
(NSUInteger)ncpsg * sizeof(uint16_t);
const NSUInteger nrows = (NSUInteger)n_head;
const NSUInteger tmp_bytes = nrows * (NSUInteger)head_dim * (NSUInteger)nwg * sizeof(float) +
nrows * (2u * (NSUInteger)nwg) * sizeof(float);
const bool use_persistent_zero_mask =
ds4_gpu_device_is_pre_m5_apple_silicon() &&
getenv("DS4_METAL_DISABLE_PRE_M5_DECODE_RAW_ZERO_ATTN_MASK") == NULL &&
!g_quality_mode &&
!g_ssd_streaming_mode &&
g_tp_split_world == 1;
id<MTLBuffer> mask_buffer = nil;
if (use_persistent_zero_mask &&
ds4_gpu_ensure_zero_attention_mask(mask_bytes)) {
mask_buffer = g_flash_attn_zero_mask_buffer;
} else {
mask_buffer =
ds4_gpu_new_transient_buffer(mask_bytes, "ds4_flash_attn_mask");
if (mask_buffer) memset([mask_buffer contents], 0, mask_bytes);
}
if (!mask_buffer ||
!ds4_gpu_ensure_scratch_buffer(&g_flash_attn_kv_buffer,
&g_flash_attn_kv_bytes,
kv_bytes,
"ds4_flash_attn_kv_f16") ||
!ds4_gpu_ensure_scratch_buffer(&g_flash_attn_pad_buffer,
&g_flash_attn_pad_bytes,
pad_bytes,
"ds4_flash_attn_pad") ||
!ds4_gpu_ensure_scratch_buffer(&g_flash_attn_tmp_buffer,
&g_flash_attn_tmp_bytes,
tmp_bytes,
"ds4_flash_attn_tmp")) {
return 0;
}
id<MTLComputePipelineState> pad_pipeline = nil;
if ((n_raw % ncpsg) != 0) {
pad_pipeline = ds4_gpu_get_flash_attn_pad_pipeline(true, (int32_t)ncpsg);
if (!pad_pipeline) return 0;
}
id<MTLComputePipelineState> vec_pipeline =
ds4_gpu_get_flash_attn_vec_pipeline("kernel_flash_attn_ext_vec_f16_dk512_dv512",
true, true, false, false, (n_raw % ncpsg) != 0,
false,
(int32_t)head_dim,
(int32_t)head_dim,
(int32_t)nsg,
(int32_t)nwg);
id<MTLComputePipelineState> reduce_pipeline =
ds4_gpu_get_flash_attn_reduce_pipeline((int32_t)head_dim, (int32_t)nwg);
if (!vec_pipeline || !reduce_pipeline) return 0;
if (!ds4_gpu_encode_copy_raw_ring_to_f16(cb,
rawbuf,
ds4_gpu_tensor_offset(raw_kv),
raw_cap,
raw_start,
n_raw,
head_dim,
g_flash_attn_kv_buffer,
0)) {
return 0;
}
if ((n_raw % ncpsg) != 0) {
ds4_gpu_flash_attn_pad_args pad_args = {
.ne11 = (int32_t)n_raw,
.ne_12_2 = 1,
.ne_12_3 = 1,
.nb11 = row_bytes_f16,
.nb12 = (uint64_t)n_raw * row_bytes_f16,
.nb13 = (uint64_t)n_raw * row_bytes_f16,
.nb21 = row_bytes_f16,
.nb22 = (uint64_t)n_raw * row_bytes_f16,
.nb23 = (uint64_t)n_raw * row_bytes_f16,
.ne31 = 1,
.ne32 = 1,
.ne33 = 1,
.nb31 = mask_bytes,
.nb32 = mask_bytes,
.nb33 = mask_bytes,
};
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pad_pipeline];
[enc setBytes:&pad_args length:sizeof(pad_args) atIndex:0];
[enc setBuffer:g_flash_attn_kv_buffer offset:0 atIndex:1];
[enc setBuffer:g_flash_attn_kv_buffer offset:0 atIndex:2];
[enc setBuffer:mask_buffer offset:0 atIndex:3];
[enc setBuffer:g_flash_attn_pad_buffer offset:0 atIndex:4];
[enc dispatchThreadgroups:MTLSizeMake(ncpsg, 1, 1)
threadsPerThreadgroup:MTLSizeMake(32, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
}
ds4_gpu_flash_attn_vec_args vec_args = {
.ne01 = 1,
.ne02 = (int32_t)n_head,
.ne03 = 1,
.nb01 = (uint64_t)n_head * row_bytes,
.nb02 = row_bytes,
.nb03 = (uint64_t)n_head * row_bytes,
.ne11 = (int32_t)n_raw,
.ne_12_2 = 1,
.ne_12_3 = 1,
.ns10 = (int32_t)head_dim,
.nb11 = row_bytes_f16,
.nb12 = (uint64_t)n_raw * row_bytes_f16,
.nb13 = (uint64_t)n_raw * row_bytes_f16,
.ns20 = (int32_t)head_dim,
.nb21 = row_bytes_f16,
.nb22 = (uint64_t)n_raw * row_bytes_f16,
.nb23 = (uint64_t)n_raw * row_bytes_f16,
.ne31 = 1,
.ne32 = 1,
.ne33 = 1,
.nb31 = mask_bytes,
.nb32 = mask_bytes,
.nb33 = mask_bytes,
.ne1 = (int32_t)n_head,
.ne2 = 1,
.ne3 = 1,
.scale = 1.0f / sqrtf((float)head_dim),
.max_bias = 0.0f,
.m0 = 0.0f,
.m1 = 0.0f,
.n_head_log2 = 0,
.logit_softcap = 0.0f,
};
const NSUInteger shared_elems = (ds4_gpu_align_up_ns(head_dim, 128u) +
4u * ncpsg +
2u * ds4_gpu_align_up_ns(head_dim, 128u)) * nsg;
const NSUInteger shared_bytes = ds4_gpu_align_up_ns(shared_elems * (sizeof(float) / 2u), 16u);
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:vec_pipeline];
[enc setBytes:&vec_args length:sizeof(vec_args) atIndex:0];
[enc setBuffer:qbuf offset:ds4_gpu_tensor_offset(q) atIndex:1];
[enc setBuffer:g_flash_attn_kv_buffer offset:0 atIndex:2];
[enc setBuffer:g_flash_attn_kv_buffer offset:0 atIndex:3];
[enc setBuffer:mask_buffer offset:0 atIndex:4];
[enc setBuffer:sinks_buf offset:sinks_offset atIndex:5];
[enc setBuffer:g_flash_attn_pad_buffer offset:0 atIndex:6];
[enc setBuffer:g_flash_attn_tmp_buffer offset:0 atIndex:7];
[enc setThreadgroupMemoryLength:shared_bytes atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(1, n_head, nwg)
threadsPerThreadgroup:MTLSizeMake(32, nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
ds4_gpu_flash_attn_reduce_args reduce_args = {
.nrows = (int32_t)nrows,
};
enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:reduce_pipeline];
[enc setBytes:&reduce_args length:sizeof(reduce_args) atIndex:0];
[enc setBuffer:g_flash_attn_tmp_buffer offset:0 atIndex:1];
[enc setBuffer:headsbuf offset:ds4_gpu_tensor_offset(heads) atIndex:2];
[enc dispatchThreadgroups:MTLSizeMake(nrows, 1, 1)
threadsPerThreadgroup:MTLSizeMake(32u * nwg, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
/* Rectangular causal/window mask for raw prefill: q covers rows
* [q_row0, q_row0 + n_q) of an n_kv-token block whose keys all live in
* rows [0, n_kv). The square prefill case is q_row0 == 0, n_q == n_kv. */
static void ds4_gpu_fill_raw_prefill_mask(
uint16_t *mask,
uint32_t q_row0,
uint32_t n_q,
uint32_t n_kv,
uint32_t window) {
const uint16_t neg_inf_half = 0xfc00u;
for (uint32_t q = 0; q < n_q; q++) {
const uint32_t qpos = q_row0 + q;
uint16_t *row = mask + (uint64_t)q * n_kv;
for (uint32_t k = 0; k < n_kv; k++) {
const bool causal = k <= qpos;
const bool in_window = window == 0 || qpos - k < window;
row[k] = causal && in_window ? 0u : neg_inf_half;
}
}
}
static void ds4_gpu_fill_glm_prefill_mask(
uint16_t *mask,
uint32_t pos0,
uint32_t n_tokens,
uint32_t cache_len) {
const uint16_t neg_inf_half = 0xfc00u;
for (uint32_t q = 0; q < n_tokens; q++) {
const uint32_t qpos = pos0 + q;
uint16_t *row = mask + (uint64_t)q * cache_len;
for (uint32_t k = 0; k < cache_len; k++) {
row[k] = k <= qpos ? 0u : neg_inf_half;
}
}
}
static id<MTLBuffer> ds4_gpu_glm_prefill_mask_buffer(
uint32_t pos0,
uint32_t n_tokens,
uint32_t cache_len,
NSUInteger mask_bytes) {
const int same_shape =
g_glm_flash_attn_mask_valid &&
g_glm_flash_attn_mask_buffer &&
g_glm_flash_attn_mask_bytes >= mask_bytes &&
g_glm_flash_attn_mask_pos0 == pos0 &&
g_glm_flash_attn_mask_tokens == n_tokens &&
g_glm_flash_attn_mask_cache_len == cache_len;
if (same_shape) return g_glm_flash_attn_mask_buffer;
if (g_glm_flash_attn_mask_buffer) {
[g_transient_buffers addObject:g_glm_flash_attn_mask_buffer];
g_glm_flash_attn_mask_buffer = nil;
}
g_glm_flash_attn_mask_bytes = 0;
g_glm_flash_attn_mask_valid = 0;
if (!ds4_gpu_ensure_scratch_buffer(&g_glm_flash_attn_mask_buffer,
&g_glm_flash_attn_mask_bytes,
mask_bytes,
"ds4_glm_flash_attn_mask")) {
return nil;
}
ds4_gpu_fill_glm_prefill_mask((uint16_t *)[g_glm_flash_attn_mask_buffer contents],
pos0,
n_tokens,
cache_len);
g_glm_flash_attn_mask_pos0 = pos0;
g_glm_flash_attn_mask_tokens = n_tokens;
g_glm_flash_attn_mask_cache_len = cache_len;
g_glm_flash_attn_mask_valid = 1;
return g_glm_flash_attn_mask_buffer;
}
static void ds4_gpu_fill_raw_decode_batch_mask(
uint16_t *mask,
uint32_t n_tokens,
uint32_t n_raw,
uint32_t pos0,
uint32_t window) {
const uint16_t neg_inf_half = 0xfc00u;
const uint32_t last_pos = pos0 + n_tokens - 1u;
/* The caller has already copied the SWA ring into logical order when it
* wraps, so key row k represents first_raw_pos + k. */
const uint32_t first_raw_pos = last_pos + 1u - n_raw;
for (uint32_t q = 0; q < n_tokens; q++) {
const uint32_t qpos = pos0 + q;
uint16_t *row = mask + (uint64_t)q * n_raw;
for (uint32_t k = 0; k < n_raw; k++) {
const uint32_t kpos = first_raw_pos + k;
const bool causal = kpos <= qpos;
const bool in_window = causal && (window == 0 || qpos - kpos < window);
row[k] = causal && in_window ? 0u : neg_inf_half;
}
}
}
static void ds4_gpu_fill_mixed_decode_batch_mask(
uint16_t *mask,
uint32_t n_tokens,
uint32_t n_raw,
uint32_t n_comp,
uint32_t pos0,
uint32_t window,
uint32_t ratio) {
const uint16_t neg_inf_half = 0xfc00u;
const uint32_t n_keys = n_raw + n_comp;
const uint32_t last_pos = pos0 + n_tokens - 1u;
/* Raw keys are laid out by logical position; compressed keys follow them. */
const uint32_t first_raw_pos = last_pos + 1u - n_raw;
for (uint32_t q = 0; q < n_tokens; q++) {
const uint32_t qpos = pos0 + q;
uint16_t *row = mask + (uint64_t)q * n_keys;
for (uint32_t k = 0; k < n_raw; k++) {
const uint32_t kpos = first_raw_pos + k;
const bool causal = kpos <= qpos;
const bool in_window = causal && (window == 0 || qpos - kpos < window);
row[k] = causal && in_window ? 0u : neg_inf_half;
}
const uint32_t n_visible = (qpos + 1u) / ratio;
for (uint32_t c = 0; c < n_comp; c++) {
row[n_raw + c] = c < n_visible ? 0u : neg_inf_half;
}
}
}
static bool ds4_gpu_fill_visual_mixed_batch_mask(
uint16_t *mask,
const int32_t *tokens,
uint32_t vocab_size,
uint32_t n_tokens,
uint32_t n_raw,
uint32_t n_comp,
uint32_t pos0,
uint32_t window,
uint32_t ratio) {
if (!mask || !tokens || vocab_size == 0 || n_tokens == 0 || n_raw == 0) {
return false;
}
const uint16_t neg_inf_half = 0xfc00u;
const uint32_t n_keys = n_raw + n_comp;
const uint32_t first_raw_pos = pos0 + n_tokens - n_raw;
uint32_t *bounds = malloc((size_t)n_tokens * 2u * sizeof(*bounds));
if (!bounds || !ds4_deepseek4_attention_bounds(
(const int *)tokens, n_tokens, vocab_size,
pos0, n_raw, window, bounds)) {
free(bounds);
return false;
}
for (uint32_t q = 0; q < n_tokens; q++) {
const uint32_t qpos = pos0 + q;
uint16_t *row = mask + (uint64_t)q * n_keys;
for (uint32_t k = 0; k < n_raw; k++) {
const uint32_t kpos = first_raw_pos + k;
row[k] = kpos >= bounds[2u * q] &&
kpos <= bounds[2u * q + 1u]
? 0u : neg_inf_half;
}
const uint32_t n_visible = ratio ? (qpos + 1u) / ratio : 0u;
for (uint32_t c = 0; c < n_comp; c++) {
row[n_raw + c] = c < n_visible ? 0u : neg_inf_half;
}
}
free(bounds);
return true;
}
/* Rectangular causal/window + compressed-key visibility mask: q covers rows
* [q_row0, q_row0 + n_q) of an n_tokens-token chunk whose raw keys all stay
* resident, followed by n_comp compressed keys. The square prefill case is
* q_row0 == 0, n_q == n_tokens. */
static void ds4_gpu_fill_static_mixed_prefill_mask(
uint16_t *mask,
uint32_t q_row0,
uint32_t n_q,
uint32_t n_tokens,
uint32_t n_comp,
uint32_t window,
uint32_t ratio) {
const uint16_t neg_inf_half = 0xfc00u;
const uint32_t n_keys = n_tokens + n_comp;
for (uint32_t q = 0; q < n_q; q++) {
const uint32_t qpos = q_row0 + q;
uint16_t *row = mask + (uint64_t)q * n_keys;
for (uint32_t k = 0; k < n_tokens; k++) {
const bool causal = k <= qpos;
const bool in_window = window == 0 || qpos - k < window;
row[k] = causal && in_window ? 0u : neg_inf_half;
}
const uint32_t n_visible = (qpos + 1u) / ratio;
for (uint32_t c = 0; c < n_comp; c++) {
row[n_tokens + c] = c < n_visible ? 0u : neg_inf_half;
}
}
}
/* Static-mixed prefill FlashAttention over a rectangular problem: q holds
* n_q query rows for token positions [q_row0, q_row0 + n_q) of the chunk,
* while the keys stay full (all n_tokens raw rows plus n_comp compressed
* rows). The classic square prefill is q_row0 == 0, n_q == n_tokens. */
static int ds4_gpu_encode_flash_attention_prefill_static_mixed_heads_nonvec_long(
id<MTLCommandBuffer> __strong *cbp,
ds4_gpu_tensor *heads,
id<MTLBuffer> sinks_buf,
NSUInteger sinks_offset,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *raw_kv,
const ds4_gpu_tensor *comp_kv,
uint32_t comp_kv_f16,
const ds4_gpu_tensor *comp_mask,
uint32_t use_comp_mask,
uint32_t q_row0,
uint32_t n_q,
uint32_t n_tokens,
uint32_t n_comp,
uint32_t window,
uint32_t ratio,
uint32_t n_head,
uint32_t head_dim) {
if (!cbp || !*cbp) return 0;
id<MTLCommandBuffer> cb = *cbp;
if (head_dim != 512 || n_head == 0 || n_q == 0 || n_tokens == 0 || ratio == 0) {
return 0;
}
const uint32_t n_keys = n_tokens + n_comp;
id<MTLBuffer> qbuf = ds4_gpu_tensor_buffer(q);
id<MTLBuffer> rawbuf = ds4_gpu_tensor_buffer(raw_kv);
id<MTLBuffer> compbuf = n_comp ? ds4_gpu_tensor_buffer(comp_kv) : rawbuf;
id<MTLBuffer> maskbuf = use_comp_mask ? ds4_gpu_tensor_buffer(comp_mask) : rawbuf;
id<MTLBuffer> headsbuf = ds4_gpu_tensor_buffer(heads);
const uint64_t q_bytes = (uint64_t)n_q * n_head * head_dim * sizeof(float);
const uint64_t raw_bytes = (uint64_t)n_tokens * head_dim * sizeof(float);
const uint64_t comp_bytes = (uint64_t)n_comp * head_dim *
(comp_kv_f16 ? sizeof(uint16_t) : sizeof(float));
const uint64_t comp_mask_bytes = use_comp_mask
? (uint64_t)n_comp * (q_row0 + n_q) * sizeof(float) : 0u;
if (!qbuf || !rawbuf || !compbuf || !maskbuf || !headsbuf || !sinks_buf ||
ds4_gpu_tensor_bytes(q) < q_bytes ||
ds4_gpu_tensor_bytes(raw_kv) < raw_bytes ||
(n_comp && ds4_gpu_tensor_bytes(comp_kv) < comp_bytes) ||
(use_comp_mask && ds4_gpu_tensor_bytes(comp_mask) < comp_mask_bytes) ||
ds4_gpu_tensor_bytes(heads) < q_bytes) {
fprintf(stderr, "ds4: Metal prefill static mixed DS4 non-vector FlashAttention received undersized buffers\n");
return 0;
}
const uint32_t nqptg = 8;
const uint32_t ncpsg = 64;
const uint32_t nsg = head_dim >= 512 ? 8u : 4u;
const bool has_kvpad = (n_keys % ncpsg) != 0;
const bool bc_mask = (n_q % nqptg) != 0;
const NSUInteger row_bytes = (NSUInteger)head_dim * sizeof(float);
const NSUInteger row_bytes_f16 = (NSUInteger)head_dim * sizeof(uint16_t);
const NSUInteger mask_bytes = (NSUInteger)n_keys * (NSUInteger)n_q * sizeof(uint16_t);
const NSUInteger kv_bytes = (NSUInteger)n_keys * row_bytes_f16;
const NSUInteger pad_bytes = has_kvpad
? (NSUInteger)ncpsg * (2u * row_bytes_f16 + (NSUInteger)n_q * sizeof(uint16_t))
: 1u;
const NSUInteger nblk0 = ((NSUInteger)n_keys + ncpsg - 1u) / ncpsg;
const NSUInteger nblk1 = ((NSUInteger)n_q + nqptg - 1u) / nqptg;
const NSUInteger blk_bytes = ds4_gpu_align_up_ns(nblk0 * nblk1, 32u);
const uint32_t mask_cache_kind = ratio == 4u
? DS4_GPU_PREFILL_MASK_CACHE_RATIO4
: (ratio == 128u ? DS4_GPU_PREFILL_MASK_CACHE_RATIO128 : 0u);
bool mask_cache_created = false;
ds4_gpu_zero_prefix_prefill_mask_cache_entry *mask_cache =
use_comp_mask == 0u && mask_cache_kind != 0u &&
q_row0 == 0u && n_q == n_tokens
? ds4_gpu_get_zero_prefix_prefill_mask_cache(mask_cache_kind,
n_tokens,
n_comp,
n_keys,
window,
ratio,
nqptg,
ncpsg,
has_kvpad,
bc_mask,
mask_bytes,
blk_bytes,
&mask_cache_created)
: NULL;
id<MTLBuffer> mask_buffer = mask_cache
? mask_cache->mask
: ds4_gpu_new_transient_buffer(mask_bytes, "ds4_flash_attn_mask");
if (mask_cache && mask_cache_created) {
ds4_gpu_fill_static_mixed_prefill_mask((uint16_t *)[mask_buffer contents],
0u,
n_tokens,
n_tokens,
n_comp,
window,
ratio);
mask_cache->valid = true;
}
if (!mask_buffer ||
!ds4_gpu_ensure_scratch_buffer(&g_flash_attn_kv_buffer,
&g_flash_attn_kv_bytes,
kv_bytes,
"ds4_flash_attn_kv_f16") ||
!ds4_gpu_ensure_scratch_buffer(&g_flash_attn_pad_buffer,
&g_flash_attn_pad_bytes,
pad_bytes,
"ds4_flash_attn_pad") ||
(!mask_cache &&
!ds4_gpu_ensure_scratch_buffer(&g_flash_attn_blk_buffer,
&g_flash_attn_blk_bytes,
blk_bytes,
"ds4_flash_attn_blk"))) {
return 0;
}
id<MTLBuffer> blk_buffer = mask_cache
? mask_cache->blk
: g_flash_attn_blk_buffer;
const bool flash_stage_profile =
getenv("DS4_METAL_FLASH_ATTN_STAGE_PROFILE") != NULL && g_batch_cb != nil;
double flash_stage_t0 = 0.0;
if (flash_stage_profile) {
if (ds4_gpu_end_commands() == 0 || ds4_gpu_begin_commands() == 0) {
return 0;
}
int profile_owned = 0;
cb = ds4_gpu_command_buffer(&profile_owned);
if (!cb || profile_owned) return 0;
*cbp = cb;
flash_stage_t0 = ds4_gpu_now_ms();
}
#define DS4_METAL_PROFILE_FLASH_ATTN_STAGE(name) do { \
if (flash_stage_profile) { \
if (!ds4_gpu_flash_attn_stage_profile_boundary(cbp, \
"static_mixed_nonvec", (name), n_q, n_comp, n_keys, \
n_head, head_dim, window, ratio, &flash_stage_t0)) { \
return 0; \
} \
cb = *cbp; \
} \
} while (0)
if (!ds4_gpu_encode_cpy_f32_f16_1d(cb,
rawbuf,
ds4_gpu_tensor_offset(raw_kv),
g_flash_attn_kv_buffer,
0,
n_tokens * head_dim)) {
return 0;
}
DS4_METAL_PROFILE_FLASH_ATTN_STAGE("copy_raw");
if (n_comp &&
!ds4_gpu_encode_copy_to_f16_1d(cb,
compbuf,
ds4_gpu_tensor_offset(comp_kv),
comp_kv_f16 != 0,
g_flash_attn_kv_buffer,
(NSUInteger)n_tokens * row_bytes_f16,
n_comp * head_dim)) {
return 0;
}
if (n_comp) {
DS4_METAL_PROFILE_FLASH_ATTN_STAGE("copy_comp");
}
if (!mask_cache) {
ds4_gpu_fill_static_mixed_prefill_mask((uint16_t *)[mask_buffer contents],
q_row0,
n_q,
n_tokens,
n_comp,
window,
ratio);
}
DS4_METAL_PROFILE_FLASH_ATTN_STAGE("mask_fill");
if (use_comp_mask && n_comp != 0) {
if (!ds4_gpu_encode_cpy_f32_f16_2d(cb,
maskbuf,
ds4_gpu_tensor_offset(comp_mask) +
(NSUInteger)((uint64_t)q_row0 * n_comp * sizeof(float)),
mask_buffer,
(NSUInteger)n_tokens * sizeof(uint16_t),
n_comp,
n_q,
(uint64_t)n_comp * sizeof(float),
(uint64_t)n_keys * sizeof(uint16_t))) {
return 0;
}
DS4_METAL_PROFILE_FLASH_ATTN_STAGE("mask_comp_copy");
}
id<MTLComputePipelineState> pad_pipeline = nil;
if (has_kvpad) {
pad_pipeline = ds4_gpu_get_flash_attn_pad_pipeline(true, (int32_t)ncpsg);
if (!pad_pipeline) return 0;
}
id<MTLComputePipelineState> blk_pipeline =
ds4_gpu_get_flash_attn_blk_pipeline((int32_t)nqptg, (int32_t)ncpsg);
id<MTLComputePipelineState> attn_pipeline =
ds4_gpu_get_flash_attn_pipeline("kernel_flash_attn_ext_f16_dk512_dv512",
true, true, false, false, has_kvpad, bc_mask,
(int32_t)head_dim,
(int32_t)head_dim,
(int32_t)nsg);
if (!blk_pipeline || !attn_pipeline) return 0;
if (has_kvpad) {
ds4_gpu_flash_attn_pad_args pad_args = {
.ne11 = (int32_t)n_keys,
.ne_12_2 = 1,
.ne_12_3 = 1,
.nb11 = row_bytes_f16,
.nb12 = (uint64_t)n_keys * row_bytes_f16,
.nb13 = (uint64_t)n_keys * row_bytes_f16,
.nb21 = row_bytes_f16,
.nb22 = (uint64_t)n_keys * row_bytes_f16,
.nb23 = (uint64_t)n_keys * row_bytes_f16,
.ne31 = (int32_t)n_q,
.ne32 = 1,
.ne33 = 1,
.nb31 = (uint64_t)n_keys * sizeof(uint16_t),
.nb32 = mask_bytes,
.nb33 = mask_bytes,
};
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pad_pipeline];
[enc setBytes:&pad_args length:sizeof(pad_args) atIndex:0];
[enc setBuffer:g_flash_attn_kv_buffer offset:0 atIndex:1];
[enc setBuffer:g_flash_attn_kv_buffer offset:0 atIndex:2];
[enc setBuffer:mask_buffer offset:0 atIndex:3];
[enc setBuffer:g_flash_attn_pad_buffer offset:0 atIndex:4];
[enc dispatchThreadgroups:MTLSizeMake(ncpsg, 1, 1)
threadsPerThreadgroup:MTLSizeMake(32, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
DS4_METAL_PROFILE_FLASH_ATTN_STAGE("pad");
}
ds4_gpu_flash_attn_blk_args blk_args = {
.ne01 = (int32_t)n_q,
.ne30 = (int32_t)n_keys,
.ne31 = (int32_t)n_q,
.ne32 = 1,
.ne33 = 1,
.nb31 = (uint64_t)n_keys * sizeof(uint16_t),
.nb32 = mask_bytes,
.nb33 = mask_bytes,
};
id<MTLComputeCommandEncoder> enc = nil;
if (!mask_cache || !mask_cache->blk_ready) {
enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:blk_pipeline];
[enc setBytes:&blk_args length:sizeof(blk_args) atIndex:0];
[enc setBuffer:mask_buffer offset:0 atIndex:1];
[enc setBuffer:blk_buffer offset:0 atIndex:2];
[enc dispatchThreadgroups:MTLSizeMake(nblk0, nblk1, 1)
threadsPerThreadgroup:MTLSizeMake(32, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (mask_cache) mask_cache->blk_ready = true;
}
DS4_METAL_PROFILE_FLASH_ATTN_STAGE("block_map");
ds4_gpu_flash_attn_vec_args args = {
.ne01 = (int32_t)n_q,
.ne02 = (int32_t)n_head,
.ne03 = 1,
.nb01 = (uint64_t)n_head * row_bytes,
.nb02 = row_bytes,
.nb03 = (uint64_t)n_q * n_head * row_bytes,
.ne11 = (int32_t)n_keys,
.ne_12_2 = 1,
.ne_12_3 = 1,
.ns10 = (int32_t)head_dim,
.nb11 = row_bytes_f16,
.nb12 = (uint64_t)n_keys * row_bytes_f16,
.nb13 = (uint64_t)n_keys * row_bytes_f16,
.ns20 = (int32_t)head_dim,
.nb21 = row_bytes_f16,
.nb22 = (uint64_t)n_keys * row_bytes_f16,
.nb23 = (uint64_t)n_keys * row_bytes_f16,
.ne31 = (int32_t)n_q,
.ne32 = 1,
.ne33 = 1,
.nb31 = (uint64_t)n_keys * sizeof(uint16_t),
.nb32 = mask_bytes,
.nb33 = mask_bytes,
.ne1 = (int32_t)n_head,
.ne2 = (int32_t)n_q,
.ne3 = 1,
.scale = 1.0f / sqrtf((float)head_dim),
.max_bias = 0.0f,
.m0 = 0.0f,
.m1 = 0.0f,
.n_head_log2 = 0,
.logit_softcap = 0.0f,
};
const NSUInteger padded_v = ds4_gpu_align_up_ns(head_dim, 64u);
const NSUInteger shared_elems = (NSUInteger)nqptg *
((NSUInteger)head_dim + 2u * padded_v + 2u * (2u * (NSUInteger)ncpsg));
const NSUInteger shared_bytes = ds4_gpu_align_up_ns(shared_elems * (sizeof(float) / 2u), 16u);
enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:attn_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:qbuf offset:ds4_gpu_tensor_offset(q) atIndex:1];
[enc setBuffer:g_flash_attn_kv_buffer offset:0 atIndex:2];
[enc setBuffer:g_flash_attn_kv_buffer offset:0 atIndex:3];
[enc setBuffer:mask_buffer offset:0 atIndex:4];
[enc setBuffer:sinks_buf offset:sinks_offset atIndex:5];
[enc setBuffer:g_flash_attn_pad_buffer offset:0 atIndex:6];
[enc setBuffer:blk_buffer offset:0 atIndex:7];
[enc setBuffer:headsbuf offset:ds4_gpu_tensor_offset(heads) atIndex:8];
[enc setThreadgroupMemoryLength:shared_bytes atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(nblk1, n_head, 1)
threadsPerThreadgroup:MTLSizeMake(32, nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
DS4_METAL_PROFILE_FLASH_ATTN_STAGE("attention");
#undef DS4_METAL_PROFILE_FLASH_ATTN_STAGE
return 1;
}
static int ds4_gpu_encode_flash_attention_prefill_static_mixed_heads_vec(
id<MTLCommandBuffer> __strong *cbp,
ds4_gpu_tensor *heads,
id<MTLBuffer> sinks_buf,
NSUInteger sinks_offset,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *raw_kv,
const ds4_gpu_tensor *comp_kv,
uint32_t comp_kv_f16,
const ds4_gpu_tensor *comp_mask,
uint32_t use_comp_mask,
uint32_t n_tokens,
uint32_t n_comp,
uint32_t window,
uint32_t ratio,
uint32_t n_head,
uint32_t head_dim) {
if (!cbp || !*cbp) return 0;
id<MTLCommandBuffer> cb = *cbp;
if (head_dim != 512 || n_head == 0 || n_tokens == 0 || ratio == 0) {
return 0;
}
const uint32_t n_keys = n_tokens + n_comp;
id<MTLBuffer> qbuf = ds4_gpu_tensor_buffer(q);
id<MTLBuffer> rawbuf = ds4_gpu_tensor_buffer(raw_kv);
id<MTLBuffer> compbuf = n_comp ? ds4_gpu_tensor_buffer(comp_kv) : rawbuf;
id<MTLBuffer> maskbuf = use_comp_mask ? ds4_gpu_tensor_buffer(comp_mask) : rawbuf;
id<MTLBuffer> headsbuf = ds4_gpu_tensor_buffer(heads);
const uint64_t q_bytes = (uint64_t)n_tokens * n_head * head_dim * sizeof(float);
const uint64_t raw_bytes = (uint64_t)n_tokens * head_dim * sizeof(float);
const uint64_t comp_bytes = (uint64_t)n_comp * head_dim *
(comp_kv_f16 ? sizeof(uint16_t) : sizeof(float));
const uint64_t comp_mask_bytes = use_comp_mask ? (uint64_t)n_comp * n_tokens * sizeof(float) : 0u;
if (!qbuf || !rawbuf || !compbuf || !maskbuf || !headsbuf || !sinks_buf ||
ds4_gpu_tensor_bytes(q) < q_bytes ||
ds4_gpu_tensor_bytes(raw_kv) < raw_bytes ||
(n_comp && ds4_gpu_tensor_bytes(comp_kv) < comp_bytes) ||
(use_comp_mask && ds4_gpu_tensor_bytes(comp_mask) < comp_mask_bytes) ||
ds4_gpu_tensor_bytes(heads) < q_bytes) {
fprintf(stderr, "ds4: Metal prefill static mixed DS4 FlashAttention received undersized buffers\n");
return 0;
}
const uint32_t ncpsg = 32;
const uint32_t nwg = 32;
const uint32_t nsg = ds4_gpu_flash_attn_vec_nsg(n_keys, nwg, ncpsg);
const NSUInteger row_bytes = (NSUInteger)head_dim * sizeof(float);
const NSUInteger row_bytes_f16 = (NSUInteger)head_dim * sizeof(uint16_t);
const NSUInteger mask_bytes = (NSUInteger)n_keys * (NSUInteger)n_tokens * sizeof(uint16_t);
const NSUInteger kv_bytes = (NSUInteger)n_keys * row_bytes_f16;
const bool has_kvpad = (n_keys % ncpsg) != 0;
const NSUInteger pad_bytes = has_kvpad
? (NSUInteger)ncpsg * (2u * row_bytes_f16 + (NSUInteger)n_tokens * sizeof(uint16_t))
: 1u;
const NSUInteger nrows = (NSUInteger)n_tokens * n_head;
const NSUInteger tmp_bytes = nrows * (NSUInteger)head_dim * (NSUInteger)nwg * sizeof(float) +
nrows * (2u * (NSUInteger)nwg) * sizeof(float);
id<MTLBuffer> mask_buffer =
ds4_gpu_new_transient_buffer(mask_bytes, "ds4_flash_attn_mask");
if (!mask_buffer ||
!ds4_gpu_ensure_scratch_buffer(&g_flash_attn_kv_buffer,
&g_flash_attn_kv_bytes,
kv_bytes,
"ds4_flash_attn_kv") ||
!ds4_gpu_ensure_scratch_buffer(&g_flash_attn_pad_buffer,
&g_flash_attn_pad_bytes,
pad_bytes,
"ds4_flash_attn_pad") ||
!ds4_gpu_ensure_scratch_buffer(&g_flash_attn_tmp_buffer,
&g_flash_attn_tmp_bytes,
tmp_bytes,
"ds4_flash_attn_tmp")) {
return 0;
}
const bool flash_stage_profile =
getenv("DS4_METAL_FLASH_ATTN_STAGE_PROFILE") != NULL && g_batch_cb != nil;
double flash_stage_t0 = 0.0;
if (flash_stage_profile) {
if (ds4_gpu_end_commands() == 0 || ds4_gpu_begin_commands() == 0) {
return 0;
}
int profile_owned = 0;
cb = ds4_gpu_command_buffer(&profile_owned);
if (!cb || profile_owned) return 0;
*cbp = cb;
flash_stage_t0 = ds4_gpu_now_ms();
}
#define DS4_METAL_PROFILE_FLASH_ATTN_STAGE(name) do { \
if (flash_stage_profile) { \
if (!ds4_gpu_flash_attn_stage_profile_boundary(cbp, \
"static_mixed_vec", (name), n_tokens, n_comp, n_keys, \
n_head, head_dim, window, ratio, &flash_stage_t0)) { \
return 0; \
} \
cb = *cbp; \
} \
} while (0)
if (!ds4_gpu_encode_cpy_f32_f16_1d(cb,
rawbuf,
ds4_gpu_tensor_offset(raw_kv),
g_flash_attn_kv_buffer,
0,
n_tokens * head_dim)) {
return 0;
}
DS4_METAL_PROFILE_FLASH_ATTN_STAGE("copy_raw");
if (n_comp) {
if (!ds4_gpu_encode_copy_to_f16_1d(cb,
compbuf,
ds4_gpu_tensor_offset(comp_kv),
comp_kv_f16 != 0,
g_flash_attn_kv_buffer,
(NSUInteger)n_tokens * row_bytes_f16,
n_comp * head_dim)) {
return 0;
}
DS4_METAL_PROFILE_FLASH_ATTN_STAGE("copy_comp");
}
ds4_gpu_fill_static_mixed_prefill_mask((uint16_t *)[mask_buffer contents],
0,
n_tokens,
n_tokens,
n_comp,
window,
ratio);
DS4_METAL_PROFILE_FLASH_ATTN_STAGE("mask_fill");
if (use_comp_mask && n_comp != 0) {
if (!ds4_gpu_encode_cpy_f32_f16_2d(cb,
maskbuf,
ds4_gpu_tensor_offset(comp_mask),
mask_buffer,
(NSUInteger)n_tokens * sizeof(uint16_t),
n_comp,
n_tokens,
(uint64_t)n_comp * sizeof(float),
(uint64_t)n_keys * sizeof(uint16_t))) {
return 0;
}
DS4_METAL_PROFILE_FLASH_ATTN_STAGE("mask_comp_copy");
}
id<MTLComputePipelineState> pad_pipeline = nil;
id<MTLComputeCommandEncoder> enc = nil;
if (has_kvpad) {
pad_pipeline = ds4_gpu_get_flash_attn_pad_pipeline(true, (int32_t)ncpsg);
if (!pad_pipeline) return 0;
}
id<MTLComputePipelineState> vec_pipeline =
ds4_gpu_get_flash_attn_vec_pipeline("kernel_flash_attn_ext_vec_f16_dk512_dv512",
true, true, false, false, has_kvpad,
false,
(int32_t)head_dim,
(int32_t)head_dim,
(int32_t)nsg,
(int32_t)nwg);
id<MTLComputePipelineState> reduce_pipeline =
ds4_gpu_get_flash_attn_reduce_pipeline((int32_t)head_dim, (int32_t)nwg);
if (!vec_pipeline || !reduce_pipeline) return 0;
if (has_kvpad) {
ds4_gpu_flash_attn_pad_args pad_args = {
.ne11 = (int32_t)n_keys,
.ne_12_2 = 1,
.ne_12_3 = 1,
.nb11 = row_bytes_f16,
.nb12 = (uint64_t)n_keys * row_bytes_f16,
.nb13 = (uint64_t)n_keys * row_bytes_f16,
.nb21 = row_bytes_f16,
.nb22 = (uint64_t)n_keys * row_bytes_f16,
.nb23 = (uint64_t)n_keys * row_bytes_f16,
.ne31 = (int32_t)n_tokens,
.ne32 = 1,
.ne33 = 1,
.nb31 = (uint64_t)n_keys * sizeof(uint16_t),
.nb32 = mask_bytes,
.nb33 = mask_bytes,
};
enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pad_pipeline];
[enc setBytes:&pad_args length:sizeof(pad_args) atIndex:0];
[enc setBuffer:g_flash_attn_kv_buffer offset:0 atIndex:1];
[enc setBuffer:g_flash_attn_kv_buffer offset:0 atIndex:2];
[enc setBuffer:mask_buffer offset:0 atIndex:3];
[enc setBuffer:g_flash_attn_pad_buffer offset:0 atIndex:4];
[enc dispatchThreadgroups:MTLSizeMake(ncpsg, 1, 1)
threadsPerThreadgroup:MTLSizeMake(32, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
DS4_METAL_PROFILE_FLASH_ATTN_STAGE("pad");
}
ds4_gpu_flash_attn_vec_args vec_args = {
.ne01 = (int32_t)n_tokens,
.ne02 = (int32_t)n_head,
.ne03 = 1,
.nb01 = (uint64_t)n_head * row_bytes,
.nb02 = row_bytes,
.nb03 = (uint64_t)n_tokens * n_head * row_bytes,
.ne11 = (int32_t)n_keys,
.ne_12_2 = 1,
.ne_12_3 = 1,
.ns10 = (int32_t)head_dim,
.nb11 = row_bytes_f16,
.nb12 = (uint64_t)n_keys * row_bytes_f16,
.nb13 = (uint64_t)n_keys * row_bytes_f16,
.ns20 = (int32_t)head_dim,
.nb21 = row_bytes_f16,
.nb22 = (uint64_t)n_keys * row_bytes_f16,
.nb23 = (uint64_t)n_keys * row_bytes_f16,
.ne31 = (int32_t)n_tokens,
.ne32 = 1,
.ne33 = 1,
.nb31 = (uint64_t)n_keys * sizeof(uint16_t),
.nb32 = mask_bytes,
.nb33 = mask_bytes,
.ne1 = (int32_t)n_head,
.ne2 = (int32_t)n_tokens,
.ne3 = 1,
.scale = 1.0f / sqrtf((float)head_dim),
.max_bias = 0.0f,
.m0 = 0.0f,
.m1 = 0.0f,
.n_head_log2 = 0,
.logit_softcap = 0.0f,
};
const NSUInteger shared_elems = (ds4_gpu_align_up_ns(head_dim, 128u) +
4u * ncpsg +
2u * ds4_gpu_align_up_ns(head_dim, 128u)) * nsg;
const NSUInteger shared_bytes = ds4_gpu_align_up_ns(shared_elems * (sizeof(float) / 2u), 16u);
enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:vec_pipeline];
[enc setBytes:&vec_args length:sizeof(vec_args) atIndex:0];
[enc setBuffer:qbuf offset:ds4_gpu_tensor_offset(q) atIndex:1];
[enc setBuffer:g_flash_attn_kv_buffer offset:0 atIndex:2];
[enc setBuffer:g_flash_attn_kv_buffer offset:0 atIndex:3];
[enc setBuffer:mask_buffer offset:0 atIndex:4];
[enc setBuffer:sinks_buf offset:sinks_offset atIndex:5];
[enc setBuffer:g_flash_attn_pad_buffer offset:0 atIndex:6];
[enc setBuffer:g_flash_attn_tmp_buffer offset:0 atIndex:7];
[enc setThreadgroupMemoryLength:shared_bytes atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(n_tokens, n_head, nwg)
threadsPerThreadgroup:MTLSizeMake(32, nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
DS4_METAL_PROFILE_FLASH_ATTN_STAGE("attention_vec");
ds4_gpu_flash_attn_reduce_args reduce_args = {
.nrows = (int32_t)nrows,
};
enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:reduce_pipeline];
[enc setBytes:&reduce_args length:sizeof(reduce_args) atIndex:0];
[enc setBuffer:g_flash_attn_tmp_buffer offset:0 atIndex:1];
[enc setBuffer:headsbuf offset:ds4_gpu_tensor_offset(heads) atIndex:2];
[enc dispatchThreadgroups:MTLSizeMake(nrows, 1, 1)
threadsPerThreadgroup:MTLSizeMake(32u * nwg, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
DS4_METAL_PROFILE_FLASH_ATTN_STAGE("attention_reduce");
#undef DS4_METAL_PROFILE_FLASH_ATTN_STAGE
return 1;
}
static int ds4_gpu_encode_flash_attention_prefill_static_mixed_heads_nonvec(
id<MTLCommandBuffer> __strong *cbp,
ds4_gpu_tensor *heads,
id<MTLBuffer> sinks_buf,
NSUInteger sinks_offset,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *raw_kv,
const ds4_gpu_tensor *comp_kv,
uint32_t comp_kv_f16,
const ds4_gpu_tensor *comp_mask,
uint32_t use_comp_mask,
uint32_t q_row0,
uint32_t n_q,
uint32_t n_tokens,
uint32_t n_comp,
uint32_t window,
uint32_t ratio,
uint32_t n_head,
uint32_t head_dim) {
/* The vector sibling below only handles the small square case; every
* rectangular (TP row-split) problem goes through the long path. */
if (n_tokens >= 20 || q_row0 != 0 || n_q != n_tokens) {
return ds4_gpu_encode_flash_attention_prefill_static_mixed_heads_nonvec_long(cbp,
heads,
sinks_buf,
sinks_offset,
q,
raw_kv,
comp_kv,
comp_kv_f16,
comp_mask,
use_comp_mask,
q_row0,
n_q,
n_tokens,
n_comp,
window,
ratio,
n_head,
head_dim);
}
return ds4_gpu_encode_flash_attention_prefill_static_mixed_heads_vec(cbp,
heads,
sinks_buf,
sinks_offset,
q,
raw_kv,
comp_kv,
comp_kv_f16,
comp_mask,
use_comp_mask,
n_tokens,
n_comp,
window,
ratio,
n_head,
head_dim);
}
/* Raw prefill FlashAttention over a rectangular problem: q holds n_q query
* rows that correspond to token positions [q_row0, q_row0 + n_q) of the
* chunk, raw_kv holds all n_kv key rows, and heads receives one output row
* per query row. The classic square prefill is q_row0 == 0, n_q == n_kv. */
static int ds4_gpu_encode_flash_attention_prefill_raw_heads_nonvec(
id<MTLCommandBuffer> __strong *cbp,
ds4_gpu_tensor *heads,
id<MTLBuffer> sinks_buf,
NSUInteger sinks_offset,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *raw_kv,
uint32_t q_row0,
uint32_t n_q,
uint32_t n_kv,
uint32_t window,
uint32_t n_head,
uint32_t head_dim,
bool raw_kv_f16,
bool has_sinks,
float scale) {
if (!cbp || !*cbp) return 0;
id<MTLCommandBuffer> cb = *cbp;
if (head_dim != 512 || n_head == 0 || n_q == 0 || n_kv == 0) {
return 0;
}
id<MTLBuffer> qbuf = ds4_gpu_tensor_buffer(q);
id<MTLBuffer> rawbuf = ds4_gpu_tensor_buffer(raw_kv);
id<MTLBuffer> headsbuf = ds4_gpu_tensor_buffer(heads);
const uint64_t q_bytes = (uint64_t)n_q * n_head * head_dim * sizeof(float);
const uint64_t raw_bytes = (uint64_t)n_kv * head_dim *
(raw_kv_f16 ? sizeof(uint16_t) : sizeof(float));
if (!qbuf || !rawbuf || !headsbuf || (has_sinks && !sinks_buf) ||
ds4_gpu_tensor_bytes(q) < q_bytes ||
ds4_gpu_tensor_bytes(raw_kv) < raw_bytes ||
ds4_gpu_tensor_bytes(heads) < q_bytes) {
fprintf(stderr, "ds4: Metal prefill shared-KV FlashAttention received undersized buffers\n");
return 0;
}
const uint32_t nqptg = 8;
const uint32_t ncpsg = 64;
const uint32_t nsg = head_dim >= 512 ? 8u : 4u;
const bool has_kvpad = (n_kv % ncpsg) != 0;
const bool bc_mask = (n_q % nqptg) != 0;
const NSUInteger row_bytes = (NSUInteger)head_dim * sizeof(float);
const NSUInteger row_bytes_f16 = (NSUInteger)head_dim * sizeof(uint16_t);
const NSUInteger mask_bytes = (NSUInteger)n_q * (NSUInteger)n_kv * sizeof(uint16_t);
const NSUInteger kv_bytes = (NSUInteger)n_kv * row_bytes_f16;
const NSUInteger pad_bytes = has_kvpad
? (NSUInteger)ncpsg * (2u * row_bytes_f16 + (NSUInteger)n_q * sizeof(uint16_t))
: 1u;
const NSUInteger nblk0 = ((NSUInteger)n_kv + ncpsg - 1u) / ncpsg;
const NSUInteger nblk1 = ((NSUInteger)n_q + nqptg - 1u) / nqptg;
const NSUInteger blk_bytes = ds4_gpu_align_up_ns(nblk0 * nblk1, 32u);
bool mask_cache_created = false;
ds4_gpu_zero_prefix_prefill_mask_cache_entry *mask_cache =
q_row0 == 0u && n_q == n_kv
? ds4_gpu_get_zero_prefix_prefill_mask_cache(
DS4_GPU_PREFILL_MASK_CACHE_RAW,
n_kv,
0u,
n_kv,
window,
0u,
nqptg,
ncpsg,
has_kvpad,
bc_mask,
mask_bytes,
blk_bytes,
&mask_cache_created)
: NULL;
id<MTLBuffer> mask_buffer = mask_cache
? mask_cache->mask
: ds4_gpu_new_transient_buffer(mask_bytes, "ds4_flash_attn_mask");
if (mask_cache && mask_cache_created) {
ds4_gpu_fill_raw_prefill_mask((uint16_t *)[mask_buffer contents],
0u, n_kv, n_kv, window);
mask_cache->valid = true;
}
if (!mask_buffer ||
!ds4_gpu_ensure_scratch_buffer(&g_flash_attn_kv_buffer,
&g_flash_attn_kv_bytes,
kv_bytes,
"ds4_flash_attn_kv_f16") ||
!ds4_gpu_ensure_scratch_buffer(&g_flash_attn_pad_buffer,
&g_flash_attn_pad_bytes,
pad_bytes,
"ds4_flash_attn_pad") ||
(!mask_cache &&
!ds4_gpu_ensure_scratch_buffer(&g_flash_attn_blk_buffer,
&g_flash_attn_blk_bytes,
blk_bytes,
"ds4_flash_attn_blk"))) {
return 0;
}
id<MTLBuffer> blk_buffer = mask_cache
? mask_cache->blk
: g_flash_attn_blk_buffer;
const bool flash_stage_profile =
getenv("DS4_METAL_FLASH_ATTN_STAGE_PROFILE") != NULL && g_batch_cb != nil;
double flash_stage_t0 = 0.0;
if (flash_stage_profile) {
if (ds4_gpu_end_commands() == 0 || ds4_gpu_begin_commands() == 0) {
return 0;
}
int profile_owned = 0;
cb = ds4_gpu_command_buffer(&profile_owned);
if (!cb || profile_owned) return 0;
*cbp = cb;
flash_stage_t0 = ds4_gpu_now_ms();
}
#define DS4_METAL_PROFILE_FLASH_ATTN_STAGE(name) do { \
if (flash_stage_profile) { \
if (!ds4_gpu_flash_attn_stage_profile_boundary(cbp, \
"raw_nonvec", (name), n_q, 0, n_kv, \
n_head, head_dim, window, 0, &flash_stage_t0)) { \
return 0; \
} \
cb = *cbp; \
} \
} while (0)
if (!mask_cache) {
ds4_gpu_fill_raw_prefill_mask((uint16_t *)[mask_buffer contents],
q_row0, n_q, n_kv, window);
}
DS4_METAL_PROFILE_FLASH_ATTN_STAGE("mask_fill");
id<MTLComputePipelineState> pad_pipeline = nil;
if (has_kvpad) {
pad_pipeline = ds4_gpu_get_flash_attn_pad_pipeline(true, (int32_t)ncpsg);
if (!pad_pipeline) return 0;
}
id<MTLComputePipelineState> blk_pipeline =
ds4_gpu_get_flash_attn_blk_pipeline((int32_t)nqptg, (int32_t)ncpsg);
id<MTLComputePipelineState> attn_pipeline =
ds4_gpu_get_flash_attn_pipeline("kernel_flash_attn_ext_f16_dk512_dv512",
true, has_sinks, false, false, has_kvpad, bc_mask,
(int32_t)head_dim,
(int32_t)head_dim,
(int32_t)nsg);
if (!blk_pipeline || !attn_pipeline) return 0;
if (!ds4_gpu_encode_copy_to_f16_1d(cb,
rawbuf,
ds4_gpu_tensor_offset(raw_kv),
raw_kv_f16,
g_flash_attn_kv_buffer,
0,
n_kv * head_dim)) {
return 0;
}
DS4_METAL_PROFILE_FLASH_ATTN_STAGE("copy_raw");
if (has_kvpad) {
ds4_gpu_flash_attn_pad_args pad_args = {
.ne11 = (int32_t)n_kv,
.ne_12_2 = 1,
.ne_12_3 = 1,
.nb11 = row_bytes_f16,
.nb12 = (uint64_t)n_kv * row_bytes_f16,
.nb13 = (uint64_t)n_kv * row_bytes_f16,
.nb21 = row_bytes_f16,
.nb22 = (uint64_t)n_kv * row_bytes_f16,
.nb23 = (uint64_t)n_kv * row_bytes_f16,
.ne31 = (int32_t)n_q,
.ne32 = 1,
.ne33 = 1,
.nb31 = (uint64_t)n_kv * sizeof(uint16_t),
.nb32 = mask_bytes,
.nb33 = mask_bytes,
};
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pad_pipeline];
[enc setBytes:&pad_args length:sizeof(pad_args) atIndex:0];
[enc setBuffer:g_flash_attn_kv_buffer offset:0 atIndex:1];
[enc setBuffer:g_flash_attn_kv_buffer offset:0 atIndex:2];
[enc setBuffer:mask_buffer offset:0 atIndex:3];
[enc setBuffer:g_flash_attn_pad_buffer offset:0 atIndex:4];
[enc dispatchThreadgroups:MTLSizeMake(ncpsg, 1, 1)
threadsPerThreadgroup:MTLSizeMake(32, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
DS4_METAL_PROFILE_FLASH_ATTN_STAGE("pad");
}
ds4_gpu_flash_attn_blk_args blk_args = {
.ne01 = (int32_t)n_q,
.ne30 = (int32_t)n_kv,
.ne31 = (int32_t)n_q,
.ne32 = 1,
.ne33 = 1,
.nb31 = (uint64_t)n_kv * sizeof(uint16_t),
.nb32 = mask_bytes,
.nb33 = mask_bytes,
};
id<MTLComputeCommandEncoder> enc = nil;
if (!mask_cache || !mask_cache->blk_ready) {
enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:blk_pipeline];
[enc setBytes:&blk_args length:sizeof(blk_args) atIndex:0];
[enc setBuffer:mask_buffer offset:0 atIndex:1];
[enc setBuffer:blk_buffer offset:0 atIndex:2];
[enc dispatchThreadgroups:MTLSizeMake(nblk0, nblk1, 1)
threadsPerThreadgroup:MTLSizeMake(32, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (mask_cache) mask_cache->blk_ready = true;
}
DS4_METAL_PROFILE_FLASH_ATTN_STAGE("block_map");
ds4_gpu_flash_attn_vec_args args = {
.ne01 = (int32_t)n_q,
.ne02 = (int32_t)n_head,
.ne03 = 1,
.nb01 = (uint64_t)n_head * row_bytes,
.nb02 = row_bytes,
.nb03 = (uint64_t)n_q * n_head * row_bytes,
.ne11 = (int32_t)n_kv,
.ne_12_2 = 1,
.ne_12_3 = 1,
.ns10 = (int32_t)head_dim,
.nb11 = row_bytes_f16,
.nb12 = (uint64_t)n_kv * row_bytes_f16,
.nb13 = (uint64_t)n_kv * row_bytes_f16,
.ns20 = (int32_t)head_dim,
.nb21 = row_bytes_f16,
.nb22 = (uint64_t)n_kv * row_bytes_f16,
.nb23 = (uint64_t)n_kv * row_bytes_f16,
.ne31 = (int32_t)n_q,
.ne32 = 1,
.ne33 = 1,
.nb31 = (uint64_t)n_kv * sizeof(uint16_t),
.nb32 = mask_bytes,
.nb33 = mask_bytes,
.ne1 = (int32_t)n_head,
.ne2 = (int32_t)n_q,
.ne3 = 1,
.scale = scale,
.max_bias = 0.0f,
.m0 = 0.0f,
.m1 = 0.0f,
.n_head_log2 = 0,
.logit_softcap = 0.0f,
};
const NSUInteger padded_v = ds4_gpu_align_up_ns(head_dim, 64u);
const NSUInteger shared_elems = (NSUInteger)nqptg *
((NSUInteger)head_dim + 2u * padded_v + 2u * (2u * (NSUInteger)ncpsg));
const NSUInteger shared_bytes = ds4_gpu_align_up_ns(shared_elems * (sizeof(float) / 2u), 16u);
enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:attn_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:qbuf offset:ds4_gpu_tensor_offset(q) atIndex:1];
[enc setBuffer:g_flash_attn_kv_buffer offset:0 atIndex:2];
[enc setBuffer:g_flash_attn_kv_buffer offset:0 atIndex:3];
[enc setBuffer:mask_buffer offset:0 atIndex:4];
[enc setBuffer:has_sinks ? sinks_buf : qbuf
offset:has_sinks ? sinks_offset : ds4_gpu_tensor_offset(q)
atIndex:5];
[enc setBuffer:g_flash_attn_pad_buffer offset:0 atIndex:6];
[enc setBuffer:blk_buffer offset:0 atIndex:7];
[enc setBuffer:headsbuf offset:ds4_gpu_tensor_offset(heads) atIndex:8];
[enc setThreadgroupMemoryLength:shared_bytes atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(nblk1, n_head, 1)
threadsPerThreadgroup:MTLSizeMake(32, nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
DS4_METAL_PROFILE_FLASH_ATTN_STAGE("attention");
#undef DS4_METAL_PROFILE_FLASH_ATTN_STAGE
return 1;
}
static int ds4_gpu_encode_flash_attention_prefill_raw_heads(
id<MTLCommandBuffer> __strong *cbp,
ds4_gpu_tensor *heads,
id<MTLBuffer> sinks_buf,
NSUInteger sinks_offset,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *raw_kv,
uint32_t q_row0,
uint32_t n_q,
uint32_t n_kv,
uint32_t window,
uint32_t n_head,
uint32_t head_dim) {
if (!cbp || !*cbp) return 0;
id<MTLCommandBuffer> cb = *cbp;
if (head_dim != 512 || n_head == 0 || n_q == 0 || n_kv == 0) {
return 0;
}
/* The vector sibling below only handles the small square case; every
* rectangular (TP row-split) problem goes through the non-vector path. */
if (n_kv >= 20 || q_row0 != 0 || n_q != n_kv) {
return ds4_gpu_encode_flash_attention_prefill_raw_heads_nonvec(cbp,
heads,
sinks_buf,
sinks_offset,
q,
raw_kv,
q_row0,
n_q,
n_kv,
window,
n_head,
head_dim,
false,
true,
1.0f / sqrtf((float)head_dim));
}
const uint32_t n_tokens = n_q;
id<MTLBuffer> qbuf = ds4_gpu_tensor_buffer(q);
id<MTLBuffer> rawbuf = ds4_gpu_tensor_buffer(raw_kv);
id<MTLBuffer> headsbuf = ds4_gpu_tensor_buffer(heads);
const uint64_t q_bytes = (uint64_t)n_tokens * n_head * head_dim * sizeof(float);
const uint64_t raw_bytes = (uint64_t)n_tokens * head_dim * sizeof(float);
if (!qbuf || !rawbuf || !headsbuf || !sinks_buf ||
ds4_gpu_tensor_bytes(q) < q_bytes ||
ds4_gpu_tensor_bytes(raw_kv) < raw_bytes ||
ds4_gpu_tensor_bytes(heads) < q_bytes) {
fprintf(stderr, "ds4: Metal prefill raw DS4 FlashAttention received undersized buffers\n");
return 0;
}
const uint32_t ncpsg = 32;
const uint32_t nwg = 32;
const uint32_t nsg = ds4_gpu_flash_attn_vec_nsg(n_tokens, nwg, ncpsg);
const NSUInteger row_bytes = (NSUInteger)head_dim * sizeof(float);
const NSUInteger row_bytes_f16 = (NSUInteger)head_dim * sizeof(uint16_t);
const NSUInteger mask_bytes = (NSUInteger)n_tokens * (NSUInteger)n_tokens * sizeof(uint16_t);
const NSUInteger kv_f16_offset = 0;
const NSUInteger kv_f16_bytes = (NSUInteger)n_tokens * row_bytes_f16;
const NSUInteger pad_bytes = 2u * (NSUInteger)ncpsg * row_bytes_f16 +
(NSUInteger)ncpsg * (NSUInteger)n_tokens * sizeof(uint16_t);
const NSUInteger nrows = (NSUInteger)n_tokens * n_head;
const NSUInteger tmp_bytes = nrows * (NSUInteger)head_dim * (NSUInteger)nwg * sizeof(float) +
nrows * (2u * (NSUInteger)nwg) * sizeof(float);
id<MTLBuffer> mask_buffer =
ds4_gpu_new_transient_buffer(mask_bytes, "ds4_flash_attn_mask");
if (!mask_buffer ||
!ds4_gpu_ensure_scratch_buffer(&g_flash_attn_pad_buffer,
&g_flash_attn_pad_bytes,
pad_bytes,
"ds4_flash_attn_pad") ||
!ds4_gpu_ensure_scratch_buffer(&g_flash_attn_kv_buffer,
&g_flash_attn_kv_bytes,
kv_f16_bytes,
"ds4_flash_attn_kv_f16") ||
!ds4_gpu_ensure_scratch_buffer(&g_flash_attn_tmp_buffer,
&g_flash_attn_tmp_bytes,
tmp_bytes,
"ds4_flash_attn_tmp")) {
return 0;
}
const bool flash_stage_profile =
getenv("DS4_METAL_FLASH_ATTN_STAGE_PROFILE") != NULL && g_batch_cb != nil;
double flash_stage_t0 = 0.0;
if (flash_stage_profile) {
if (ds4_gpu_end_commands() == 0 || ds4_gpu_begin_commands() == 0) {
return 0;
}
int profile_owned = 0;
cb = ds4_gpu_command_buffer(&profile_owned);
if (!cb || profile_owned) return 0;
*cbp = cb;
flash_stage_t0 = ds4_gpu_now_ms();
}
#define DS4_METAL_PROFILE_FLASH_ATTN_STAGE(name) do { \
if (flash_stage_profile) { \
if (!ds4_gpu_flash_attn_stage_profile_boundary(cbp, \
"raw_vec", (name), n_tokens, 0, n_tokens, \
n_head, head_dim, window, 0, &flash_stage_t0)) { \
return 0; \
} \
cb = *cbp; \
} \
} while (0)
ds4_gpu_fill_raw_prefill_mask((uint16_t *)[mask_buffer contents], 0, n_tokens, n_tokens, window);
DS4_METAL_PROFILE_FLASH_ATTN_STAGE("mask_fill");
id<MTLComputePipelineState> pad_pipeline = nil;
if ((n_tokens % ncpsg) != 0) {
pad_pipeline = ds4_gpu_get_flash_attn_pad_pipeline(true, (int32_t)ncpsg);
if (!pad_pipeline) return 0;
}
id<MTLComputePipelineState> vec_pipeline =
ds4_gpu_get_flash_attn_vec_pipeline("kernel_flash_attn_ext_vec_f16_dk512_dv512",
true, true, false, false, true,
false,
(int32_t)head_dim,
(int32_t)head_dim,
(int32_t)nsg,
(int32_t)nwg);
id<MTLComputePipelineState> reduce_pipeline =
ds4_gpu_get_flash_attn_reduce_pipeline((int32_t)head_dim, (int32_t)nwg);
if (!vec_pipeline || !reduce_pipeline) return 0;
if (!ds4_gpu_encode_cpy_f32_f16_1d(cb,
rawbuf,
ds4_gpu_tensor_offset(raw_kv),
g_flash_attn_kv_buffer,
kv_f16_offset,
n_tokens * head_dim)) {
return 0;
}
DS4_METAL_PROFILE_FLASH_ATTN_STAGE("copy_raw");
if ((n_tokens % ncpsg) != 0) {
ds4_gpu_flash_attn_pad_args pad_args = {
.ne11 = (int32_t)n_tokens,
.ne_12_2 = 1,
.ne_12_3 = 1,
.nb11 = row_bytes_f16,
.nb12 = (uint64_t)n_tokens * row_bytes_f16,
.nb13 = (uint64_t)n_tokens * row_bytes_f16,
.nb21 = row_bytes_f16,
.nb22 = (uint64_t)n_tokens * row_bytes_f16,
.nb23 = (uint64_t)n_tokens * row_bytes_f16,
.ne31 = (int32_t)n_tokens,
.ne32 = 1,
.ne33 = 1,
.nb31 = (uint64_t)n_tokens * sizeof(uint16_t),
.nb32 = mask_bytes,
.nb33 = mask_bytes,
};
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pad_pipeline];
[enc setBytes:&pad_args length:sizeof(pad_args) atIndex:0];
[enc setBuffer:g_flash_attn_kv_buffer offset:kv_f16_offset atIndex:1];
[enc setBuffer:g_flash_attn_kv_buffer offset:kv_f16_offset atIndex:2];
[enc setBuffer:mask_buffer offset:0 atIndex:3];
[enc setBuffer:g_flash_attn_pad_buffer offset:0 atIndex:4];
[enc dispatchThreadgroups:MTLSizeMake(ncpsg, 1, 1)
threadsPerThreadgroup:MTLSizeMake(32, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
DS4_METAL_PROFILE_FLASH_ATTN_STAGE("pad");
}
ds4_gpu_flash_attn_vec_args vec_args = {
.ne01 = (int32_t)n_tokens,
.ne02 = (int32_t)n_head,
.ne03 = 1,
.nb01 = (uint64_t)n_head * row_bytes,
.nb02 = row_bytes,
.nb03 = (uint64_t)n_tokens * n_head * row_bytes,
.ne11 = (int32_t)n_tokens,
.ne_12_2 = 1,
.ne_12_3 = 1,
.ns10 = (int32_t)head_dim,
.nb11 = row_bytes_f16,
.nb12 = (uint64_t)n_tokens * row_bytes_f16,
.nb13 = (uint64_t)n_tokens * row_bytes_f16,
.ns20 = (int32_t)head_dim,
.nb21 = row_bytes_f16,
.nb22 = (uint64_t)n_tokens * row_bytes_f16,
.nb23 = (uint64_t)n_tokens * row_bytes_f16,
.ne31 = (int32_t)n_tokens,
.ne32 = 1,
.ne33 = 1,
.nb31 = (uint64_t)n_tokens * sizeof(uint16_t),
.nb32 = mask_bytes,
.nb33 = mask_bytes,
.ne1 = (int32_t)n_head,
.ne2 = (int32_t)n_tokens,
.ne3 = 1,
.scale = 1.0f / sqrtf((float)head_dim),
.max_bias = 0.0f,
.m0 = 0.0f,
.m1 = 0.0f,
.n_head_log2 = 0,
.logit_softcap = 0.0f,
};
const NSUInteger shared_elems = (ds4_gpu_align_up_ns(head_dim, 128u) +
4u * ncpsg +
2u * ds4_gpu_align_up_ns(head_dim, 128u)) * nsg;
const NSUInteger shared_bytes = ds4_gpu_align_up_ns(shared_elems * (sizeof(float) / 2u), 16u);
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:vec_pipeline];
[enc setBytes:&vec_args length:sizeof(vec_args) atIndex:0];
[enc setBuffer:qbuf offset:ds4_gpu_tensor_offset(q) atIndex:1];
[enc setBuffer:g_flash_attn_kv_buffer offset:kv_f16_offset atIndex:2];
[enc setBuffer:g_flash_attn_kv_buffer offset:kv_f16_offset atIndex:3];
[enc setBuffer:mask_buffer offset:0 atIndex:4];
[enc setBuffer:sinks_buf offset:sinks_offset atIndex:5];
[enc setBuffer:g_flash_attn_pad_buffer offset:0 atIndex:6];
[enc setBuffer:g_flash_attn_tmp_buffer offset:0 atIndex:7];
[enc setThreadgroupMemoryLength:shared_bytes atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(n_tokens, n_head, nwg)
threadsPerThreadgroup:MTLSizeMake(32, nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
DS4_METAL_PROFILE_FLASH_ATTN_STAGE("attention_vec");
ds4_gpu_flash_attn_reduce_args reduce_args = {
.nrows = (int32_t)nrows,
};
enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:reduce_pipeline];
[enc setBytes:&reduce_args length:sizeof(reduce_args) atIndex:0];
[enc setBuffer:g_flash_attn_tmp_buffer offset:0 atIndex:1];
[enc setBuffer:headsbuf offset:ds4_gpu_tensor_offset(heads) atIndex:2];
[enc dispatchThreadgroups:MTLSizeMake(nrows, 1, 1)
threadsPerThreadgroup:MTLSizeMake(32u * nwg, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
DS4_METAL_PROFILE_FLASH_ATTN_STAGE("attention_reduce");
#undef DS4_METAL_PROFILE_FLASH_ATTN_STAGE
return 1;
}
static int ds4_gpu_encode_flash_attention_gathered_heads(
id<MTLCommandBuffer> cb,
ds4_gpu_tensor *heads,
id<MTLBuffer> sinks_buf,
NSUInteger sinks_offset,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *raw_kv,
uint32_t n_raw,
uint32_t raw_cap,
uint32_t raw_start,
const ds4_gpu_tensor *comp_kv,
uint32_t comp_kv_f16,
const ds4_gpu_tensor *comp_ids,
uint32_t n_comp,
const ds4_gpu_tensor *comp_mask,
uint32_t use_mask,
uint32_t n_head,
uint32_t head_dim) {
const uint32_t n_keys = n_raw + n_comp;
if (head_dim != 512 || n_head == 0 || n_raw == 0 || n_keys == 0 ||
raw_cap < n_raw || n_keys < n_raw) {
return 0;
}
id<MTLBuffer> qbuf = ds4_gpu_tensor_buffer(q);
id<MTLBuffer> rawbuf = ds4_gpu_tensor_buffer(raw_kv);
id<MTLBuffer> compbuf = n_comp ? ds4_gpu_tensor_buffer(comp_kv) : nil;
id<MTLBuffer> idsbuf = comp_ids ? ds4_gpu_tensor_buffer(comp_ids) : nil;
if (comp_ids && (!idsbuf || ds4_gpu_tensor_bytes(comp_ids) < (uint64_t)n_comp * sizeof(int32_t))) return 0;
id<MTLBuffer> headsbuf = ds4_gpu_tensor_buffer(heads);
id<MTLBuffer> maskbuf = use_mask ? ds4_gpu_tensor_buffer(comp_mask) : nil;
const uint64_t q_bytes = (uint64_t)n_head * head_dim * sizeof(float);
const uint64_t raw_bytes = (uint64_t)raw_cap * head_dim * sizeof(float);
const uint64_t comp_bytes = (uint64_t)n_comp * head_dim *
(comp_kv_f16 ? sizeof(uint16_t) : sizeof(float));
const uint64_t comp_mask_bytes = use_mask ? (uint64_t)n_comp * sizeof(float) : 0u;
if (!qbuf || !rawbuf || !headsbuf || !sinks_buf ||
(n_comp && !compbuf) ||
(use_mask && !maskbuf) ||
ds4_gpu_tensor_bytes(q) < q_bytes ||
ds4_gpu_tensor_bytes(raw_kv) < raw_bytes ||
(n_comp && ds4_gpu_tensor_bytes(comp_kv) < comp_bytes) ||
ds4_gpu_tensor_bytes(heads) < q_bytes ||
(use_mask && ds4_gpu_tensor_bytes(comp_mask) < comp_mask_bytes)) {
fprintf(stderr, "ds4: Metal gathered DS4 FlashAttention received undersized buffers\n");
return 0;
}
const uint32_t ncpsg = 32;
/* Split-K work groups per head. 32 keys per group is the smallest
* useful unit; at short contexts 32 groups meant 4 keys each plus a
* 32-way reduce. DS4_METAL_FLASH_NWG=N forces a value (0 = adaptive). */
static int nwg_env = -1;
if (nwg_env < 0) {
const char *e = getenv("DS4_METAL_FLASH_NWG");
nwg_env = e ? atoi(e) : 0;
}
uint32_t nwg = 32;
if (nwg_env > 0) {
nwg = (uint32_t)nwg_env;
} else {
/* 32 always: 8 groups at 128 keys saved 1 us in attention but made the
* later concurrent FFN block ~50 us slower (unexplained, measured);
* DS4_METAL_FLASH_NWG overrides for experiments. */
nwg = 32u;
}
if (nwg > 32u) nwg = 32u;
if (nwg < 1u) nwg = 1u;
const uint32_t nsg = ds4_gpu_flash_attn_vec_nsg(n_keys, nwg, ncpsg);
const NSUInteger row_bytes = (NSUInteger)head_dim * sizeof(float);
const NSUInteger row_bytes_f16 = (NSUInteger)head_dim * sizeof(uint16_t);
const NSUInteger mask_bytes = (NSUInteger)n_keys * sizeof(uint16_t);
const NSUInteger kv_bytes = (NSUInteger)n_keys * row_bytes_f16;
const NSUInteger pad_bytes = 2u * (NSUInteger)ncpsg * row_bytes_f16 +
(NSUInteger)ncpsg * sizeof(uint16_t);
const NSUInteger nrows = (NSUInteger)n_head;
const NSUInteger tmp_bytes = nrows * (NSUInteger)head_dim * (NSUInteger)nwg * sizeof(float) +
nrows * (2u * (NSUInteger)nwg) * sizeof(float);
const bool packed_shape =
ds4_gpu_ported_m5_decode_feature_enabled(
"DS4_METAL_DISABLE_PRE_M5_FLASH_ATTN_PACKED32_REDUCE",
NULL) &&
!g_quality_mode && use_mask == 0u && comp_kv_f16 != 0u && n_comp != 0u &&
n_head == 64u && head_dim == 512u && nsg == 1u && nwg == 32u &&
n_keys <= 1024u && g_decode_attn_rope_fuse != 0 &&
g_decode_attn_rope_args.head_dim == 512 &&
g_decode_attn_rope_args.n_dims == 64 &&
g_decode_attn_rope_args.row_bytes == 2048 &&
g_decode_attn_rope_args.inverse != 0;
const bool m5_persistent_zero_mask =
ds4_gpu_device_is_m5_apple_silicon() &&
getenv("DS4_METAL_DISABLE_M5_PERSISTENT_ZERO_ATTN_MASK") == NULL &&
(!packed_shape ||
getenv("DS4_METAL_DISABLE_M5_PACKED_ZERO_MASK") == NULL);
const bool use_persistent_zero_mask =
use_mask == 0u &&
(ds4_gpu_device_name_contains("M3") ||
m5_persistent_zero_mask) &&
getenv("DS4_METAL_DISABLE_PERSISTENT_ZERO_ATTN_MASK") == NULL;
if (!(use_persistent_zero_mask
? ds4_gpu_ensure_zero_attention_mask(mask_bytes)
: ds4_gpu_ensure_scratch_buffer(&g_flash_attn_mask_buffer,
&g_flash_attn_mask_bytes,
mask_bytes,
"ds4_flash_attn_mask")) ||
!ds4_gpu_ensure_scratch_buffer(&g_flash_attn_kv_buffer,
&g_flash_attn_kv_bytes,
kv_bytes,
"ds4_flash_attn_kv") ||
!ds4_gpu_ensure_scratch_buffer(&g_flash_attn_pad_buffer,
&g_flash_attn_pad_bytes,
pad_bytes,
"ds4_flash_attn_pad") ||
!ds4_gpu_ensure_scratch_buffer(&g_flash_attn_tmp_buffer,
&g_flash_attn_tmp_bytes,
tmp_bytes,
"ds4_flash_attn_tmp")) {
return 0;
}
id<MTLBuffer> flash_mask_buffer = use_persistent_zero_mask
? g_flash_attn_zero_mask_buffer
: g_flash_attn_mask_buffer;
const bool has_kvpad = (n_keys % ncpsg) != 0;
const bool use_shared_kvpad =
has_kvpad &&
ds4_gpu_device_name_contains("M3") &&
getenv("DS4_METAL_DISABLE_SHARED_KV_PAD") == NULL;
const NSUInteger packed_threads = 8u * 32u;
const NSUInteger packed_shared_bytes =
512u * sizeof(uint16_t) +
8u * 128u * sizeof(uint16_t) +
(32u * 32u + 2u * 32u + 32u) * sizeof(float) +
32u * 33u * 4u * sizeof(float);
const bool packed_requested =
packed_shape &&
getenv("DS4_METAL_DISABLE_M5_FLASH_ATTN_PACKED32_REDUCE") == NULL;
id<MTLComputePipelineState> packed_pipeline = nil;
if (packed_requested) {
packed_pipeline = ds4_gpu_get_flash_attn_vec_pipeline(
"kernel_dsv4_flash_attn_vec_packed32_reduce_rope_f16_dk512_dv512",
true, true, false, false, has_kvpad, use_shared_kvpad,
(int32_t)head_dim, (int32_t)head_dim, 1, 32);
}
const NSUInteger max_tgmem = [g_device maxThreadgroupMemoryLength];
const bool use_packed =
packed_pipeline != nil &&
packed_pipeline.threadExecutionWidth == 32u &&
packed_pipeline.maxTotalThreadsPerThreadgroup >= packed_threads &&
(max_tgmem == 0u || max_tgmem >= packed_shared_bytes);
if (packed_requested &&
getenv("DS4_METAL_TRACE_M5_FLASH_ATTN_PACKED32_REDUCE") != NULL) {
fprintf(stderr,
"ds4: packed FA use=%d max_threads=%lu tew=%lu tgmem=%lu need=%lu\n",
use_packed ? 1 : 0,
(unsigned long)(packed_pipeline ?
packed_pipeline.maxTotalThreadsPerThreadgroup : 0u),
(unsigned long)(packed_pipeline ?
packed_pipeline.threadExecutionWidth : 0u),
(unsigned long)max_tgmem,
(unsigned long)packed_shared_bytes);
}
id<MTLComputePipelineState> vec_pipeline = nil;
id<MTLComputePipelineState> reduce_pipeline = nil;
if (!use_packed) {
vec_pipeline = ds4_gpu_get_flash_attn_vec_pipeline(
"kernel_flash_attn_ext_vec_f16_dk512_dv512",
true, true, false, false, has_kvpad, use_shared_kvpad,
(int32_t)head_dim, (int32_t)head_dim,
(int32_t)nsg, (int32_t)nwg);
reduce_pipeline =
ds4_gpu_get_flash_attn_reduce_pipeline((int32_t)head_dim,
(int32_t)nwg);
if (!vec_pipeline || !reduce_pipeline) return 0;
}
if (!use_persistent_zero_mask &&
!ds4_gpu_encode_fill_f16_1d(cb, flash_mask_buffer, 0, n_keys, 0.0f)) {
return 0;
}
if (use_mask && n_comp &&
!ds4_gpu_encode_cpy_f32_f16_1d(cb,
maskbuf,
ds4_gpu_tensor_offset(comp_mask),
flash_mask_buffer,
(NSUInteger)n_raw * sizeof(uint16_t),
n_comp)) {
return 0;
}
bool pad_fused = false;
if (!ds4_gpu_encode_flash_kv_stage_f16(
cb,
rawbuf,
ds4_gpu_tensor_offset(raw_kv),
raw_cap,
raw_start,
n_raw,
compbuf,
ds4_gpu_tensor_offset(comp_kv),
comp_kv_f16 != 0,
idsbuf,
comp_ids ? ds4_gpu_tensor_offset(comp_ids) : 0,
n_comp,
head_dim,
g_flash_attn_kv_buffer,
0,
flash_mask_buffer,
0,
g_flash_attn_pad_buffer,
0,
has_kvpad,
use_shared_kvpad,
&pad_fused)) {
return 0;
}
id<MTLComputePipelineState> pad_pipeline = nil;
if (has_kvpad && !pad_fused) {
pad_pipeline = ds4_gpu_get_flash_attn_pad_pipeline(true, (int32_t)ncpsg);
if (!pad_pipeline) return 0;
}
if (has_kvpad && !pad_fused) {
ds4_gpu_flash_attn_pad_args pad_args = {
.ne11 = (int32_t)n_keys,
.ne_12_2 = 1,
.ne_12_3 = 1,
.nb11 = row_bytes_f16,
.nb12 = (uint64_t)n_keys * row_bytes_f16,
.nb13 = (uint64_t)n_keys * row_bytes_f16,
.nb21 = row_bytes_f16,
.nb22 = (uint64_t)n_keys * row_bytes_f16,
.nb23 = (uint64_t)n_keys * row_bytes_f16,
.ne31 = 1,
.ne32 = 1,
.ne33 = 1,
.nb31 = mask_bytes,
.nb32 = mask_bytes,
.nb33 = mask_bytes,
};
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pad_pipeline];
[enc setBytes:&pad_args length:sizeof(pad_args) atIndex:0];
[enc setBuffer:g_flash_attn_kv_buffer offset:0 atIndex:1];
[enc setBuffer:g_flash_attn_kv_buffer offset:0 atIndex:2];
[enc setBuffer:flash_mask_buffer offset:0 atIndex:3];
[enc setBuffer:g_flash_attn_pad_buffer offset:0 atIndex:4];
[enc dispatchThreadgroups:MTLSizeMake(ncpsg, 1, 1)
threadsPerThreadgroup:MTLSizeMake(32, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
}
ds4_gpu_flash_attn_vec_args vec_args = {
.ne01 = 1,
.ne02 = (int32_t)n_head,
.ne03 = 1,
.nb01 = (uint64_t)n_head * row_bytes,
.nb02 = row_bytes,
.nb03 = (uint64_t)n_head * row_bytes,
.ne11 = (int32_t)n_keys,
.ne_12_2 = 1,
.ne_12_3 = 1,
.ns10 = (int32_t)head_dim,
.nb11 = row_bytes_f16,
.nb12 = (uint64_t)n_keys * row_bytes_f16,
.nb13 = (uint64_t)n_keys * row_bytes_f16,
.ns20 = (int32_t)head_dim,
.nb21 = row_bytes_f16,
.nb22 = (uint64_t)n_keys * row_bytes_f16,
.nb23 = (uint64_t)n_keys * row_bytes_f16,
.ne31 = 1,
.ne32 = 1,
.ne33 = 1,
.nb31 = mask_bytes,
.nb32 = mask_bytes,
.nb33 = mask_bytes,
.ne1 = (int32_t)n_head,
.ne2 = 1,
.ne3 = 1,
.scale = 1.0f / sqrtf((float)head_dim),
.max_bias = 0.0f,
.m0 = 0.0f,
.m1 = 0.0f,
.n_head_log2 = 0,
.logit_softcap = 0.0f,
};
const NSUInteger shared_elems = (ds4_gpu_align_up_ns(head_dim, 128u) +
4u * ncpsg +
2u * ds4_gpu_align_up_ns(head_dim, 128u)) * nsg;
const NSUInteger shared_bytes = ds4_gpu_align_up_ns(shared_elems * (sizeof(float) / 2u), 16u);
if (use_packed) {
id<MTLComputeCommandEncoder> packed_enc = ds4_gpu_compute_encoder(cb);
[packed_enc setComputePipelineState:packed_pipeline];
[packed_enc setBytes:&vec_args length:sizeof(vec_args) atIndex:0];
[packed_enc setBuffer:qbuf offset:ds4_gpu_tensor_offset(q) atIndex:1];
[packed_enc setBuffer:g_flash_attn_kv_buffer offset:0 atIndex:2];
[packed_enc setBuffer:g_flash_attn_kv_buffer offset:0 atIndex:3];
[packed_enc setBuffer:flash_mask_buffer offset:0 atIndex:4];
[packed_enc setBuffer:sinks_buf offset:sinks_offset atIndex:5];
[packed_enc setBuffer:g_flash_attn_pad_buffer offset:0 atIndex:6];
[packed_enc setBuffer:headsbuf
offset:ds4_gpu_tensor_offset(heads) atIndex:7];
[packed_enc setBytes:&g_decode_attn_rope_args
length:sizeof(g_decode_attn_rope_args) atIndex:8];
[packed_enc setThreadgroupMemoryLength:packed_shared_bytes atIndex:0];
[packed_enc dispatchThreadgroups:MTLSizeMake(nrows, 1, 1)
threadsPerThreadgroup:MTLSizeMake(packed_threads, 1, 1)];
ds4_gpu_end_compute_encoder(cb, packed_enc);
g_decode_attn_rope_fuse = 0;
g_decode_attn_rope_fuse_used = 1;
return 1;
}
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:vec_pipeline];
[enc setBytes:&vec_args length:sizeof(vec_args) atIndex:0];
[enc setBuffer:qbuf offset:ds4_gpu_tensor_offset(q) atIndex:1];
[enc setBuffer:g_flash_attn_kv_buffer offset:0 atIndex:2];
[enc setBuffer:g_flash_attn_kv_buffer offset:0 atIndex:3];
[enc setBuffer:flash_mask_buffer offset:0 atIndex:4];
[enc setBuffer:sinks_buf offset:sinks_offset atIndex:5];
[enc setBuffer:g_flash_attn_pad_buffer offset:0 atIndex:6];
[enc setBuffer:g_flash_attn_tmp_buffer offset:0 atIndex:7];
[enc setThreadgroupMemoryLength:shared_bytes atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(1, n_head, nwg)
threadsPerThreadgroup:MTLSizeMake(32, nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
ds4_gpu_flash_attn_reduce_args reduce_args = {
.nrows = (int32_t)nrows,
};
/* When ds4.c defers the inverse RoPE tail, the reduce threadgroup that owns
* a head's whole row rotates it in place, removing a dispatch per layer. */
const int fuse_rope = g_decode_attn_rope_fuse;
g_decode_attn_rope_fuse = 0;
if (fuse_rope) g_decode_attn_rope_fuse_used = 1;
id<MTLComputePipelineState> reduce_pso = reduce_pipeline;
if (fuse_rope) {
reduce_pso = ds4_gpu_get_flash_attn_reduce_rope_pipeline((int32_t)head_dim,
(int32_t)nwg);
if (!reduce_pso) return 0;
}
enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:reduce_pso];
[enc setBytes:&reduce_args length:sizeof(reduce_args) atIndex:0];
[enc setBuffer:g_flash_attn_tmp_buffer offset:0 atIndex:1];
[enc setBuffer:headsbuf offset:ds4_gpu_tensor_offset(heads) atIndex:2];
if (fuse_rope) {
[enc setBytes:&g_decode_attn_rope_args
length:sizeof(g_decode_attn_rope_args) atIndex:3];
}
[enc dispatchThreadgroups:MTLSizeMake(nrows, 1, 1)
threadsPerThreadgroup:MTLSizeMake(32u * nwg, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_flash_attention_decode_raw_batch_heads(
id<MTLCommandBuffer> cb,
ds4_gpu_tensor *heads,
id<MTLBuffer> sinks_buf,
NSUInteger sinks_offset,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *raw_kv,
uint32_t n_tokens,
uint32_t pos0,
uint32_t n_raw,
uint32_t raw_cap,
uint32_t raw_start,
uint32_t window,
uint32_t n_head,
uint32_t head_dim,
bool noncausal,
const int32_t *visual_tokens,
uint32_t vocab_size) {
if (head_dim != 512 || n_head == 0 || n_tokens == 0 ||
n_raw == 0 || raw_cap < n_raw || raw_start >= raw_cap) {
return 0;
}
id<MTLBuffer> qbuf = ds4_gpu_tensor_buffer(q);
id<MTLBuffer> rawbuf = ds4_gpu_tensor_buffer(raw_kv);
id<MTLBuffer> headsbuf = ds4_gpu_tensor_buffer(heads);
const uint64_t q_bytes = (uint64_t)n_tokens * n_head * head_dim * sizeof(float);
const uint64_t raw_bytes = (uint64_t)raw_cap * head_dim * sizeof(float);
if (!qbuf || !rawbuf || !headsbuf || !sinks_buf ||
ds4_gpu_tensor_bytes(q) < q_bytes ||
ds4_gpu_tensor_bytes(raw_kv) < raw_bytes ||
ds4_gpu_tensor_bytes(heads) < q_bytes) {
fprintf(stderr, "ds4: Metal decode raw batch FlashAttention received undersized buffers\n");
return 0;
}
const uint32_t nqptg = 8;
const uint32_t ncpsg = 64;
const uint32_t nsg = head_dim >= 512 ? 8u : 4u;
const bool has_kvpad = (n_raw % ncpsg) != 0;
const bool bc_mask = (n_tokens % nqptg) != 0;
const NSUInteger row_bytes = (NSUInteger)head_dim * sizeof(float);
const NSUInteger row_bytes_f16 = (NSUInteger)head_dim * sizeof(uint16_t);
const NSUInteger mask_bytes = (NSUInteger)n_raw * (NSUInteger)n_tokens * sizeof(uint16_t);
const NSUInteger kv_bytes = (NSUInteger)n_raw * row_bytes_f16;
const NSUInteger pad_bytes = has_kvpad
? (NSUInteger)ncpsg * (2u * row_bytes_f16 + (NSUInteger)n_tokens * sizeof(uint16_t))
: 1u;
const NSUInteger nblk0 = ((NSUInteger)n_raw + ncpsg - 1u) / ncpsg;
const NSUInteger nblk1 = ((NSUInteger)n_tokens + nqptg - 1u) / nqptg;
const NSUInteger blk_bytes = ds4_gpu_align_up_ns(nblk0 * nblk1, 32u);
id<MTLBuffer> mask_buffer =
ds4_gpu_new_transient_buffer(mask_bytes, "ds4_flash_attn_mask");
if (!mask_buffer ||
!ds4_gpu_ensure_scratch_buffer(&g_flash_attn_kv_buffer,
&g_flash_attn_kv_bytes,
kv_bytes,
"ds4_flash_attn_kv_f16") ||
!ds4_gpu_ensure_scratch_buffer(&g_flash_attn_pad_buffer,
&g_flash_attn_pad_bytes,
pad_bytes,
"ds4_flash_attn_pad") ||
!ds4_gpu_ensure_scratch_buffer(&g_flash_attn_blk_buffer,
&g_flash_attn_blk_bytes,
blk_bytes,
"ds4_flash_attn_blk")) {
return 0;
}
if (!ds4_gpu_encode_copy_raw_ring_to_f16(cb,
rawbuf,
ds4_gpu_tensor_offset(raw_kv),
raw_cap,
raw_start,
n_raw,
head_dim,
g_flash_attn_kv_buffer,
0)) {
return 0;
}
if (visual_tokens) {
if (!ds4_gpu_fill_visual_mixed_batch_mask(
(uint16_t *)[mask_buffer contents],
visual_tokens, vocab_size, n_tokens, n_raw, 0u,
pos0, window, 0u)) return 0;
} else if (noncausal) {
memset([mask_buffer contents], 0, mask_bytes);
} else {
ds4_gpu_fill_raw_decode_batch_mask((uint16_t *)[mask_buffer contents],
n_tokens,
n_raw,
pos0,
window);
}
id<MTLComputePipelineState> pad_pipeline = nil;
if (has_kvpad) {
pad_pipeline = ds4_gpu_get_flash_attn_pad_pipeline(true, (int32_t)ncpsg);
if (!pad_pipeline) return 0;
}
id<MTLComputePipelineState> blk_pipeline =
ds4_gpu_get_flash_attn_blk_pipeline((int32_t)nqptg, (int32_t)ncpsg);
id<MTLComputePipelineState> attn_pipeline =
ds4_gpu_get_flash_attn_pipeline("kernel_flash_attn_ext_f16_dk512_dv512",
true, true, false, false, has_kvpad, bc_mask,
(int32_t)head_dim,
(int32_t)head_dim,
(int32_t)nsg);
if (!blk_pipeline || !attn_pipeline) return 0;
if (has_kvpad) {
ds4_gpu_flash_attn_pad_args pad_args = {
.ne11 = (int32_t)n_raw,
.ne_12_2 = 1,
.ne_12_3 = 1,
.nb11 = row_bytes_f16,
.nb12 = (uint64_t)n_raw * row_bytes_f16,
.nb13 = (uint64_t)n_raw * row_bytes_f16,
.nb21 = row_bytes_f16,
.nb22 = (uint64_t)n_raw * row_bytes_f16,
.nb23 = (uint64_t)n_raw * row_bytes_f16,
.ne31 = (int32_t)n_tokens,
.ne32 = 1,
.ne33 = 1,
.nb31 = (uint64_t)n_raw * sizeof(uint16_t),
.nb32 = mask_bytes,
.nb33 = mask_bytes,
};
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pad_pipeline];
[enc setBytes:&pad_args length:sizeof(pad_args) atIndex:0];
[enc setBuffer:g_flash_attn_kv_buffer offset:0 atIndex:1];
[enc setBuffer:g_flash_attn_kv_buffer offset:0 atIndex:2];
[enc setBuffer:mask_buffer offset:0 atIndex:3];
[enc setBuffer:g_flash_attn_pad_buffer offset:0 atIndex:4];
[enc dispatchThreadgroups:MTLSizeMake(ncpsg, 1, 1)
threadsPerThreadgroup:MTLSizeMake(32, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
}
ds4_gpu_flash_attn_blk_args blk_args = {
.ne01 = (int32_t)n_tokens,
.ne30 = (int32_t)n_raw,
.ne31 = (int32_t)n_tokens,
.ne32 = 1,
.ne33 = 1,
.nb31 = (uint64_t)n_raw * sizeof(uint16_t),
.nb32 = mask_bytes,
.nb33 = mask_bytes,
};
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:blk_pipeline];
[enc setBytes:&blk_args length:sizeof(blk_args) atIndex:0];
[enc setBuffer:mask_buffer offset:0 atIndex:1];
[enc setBuffer:g_flash_attn_blk_buffer offset:0 atIndex:2];
[enc dispatchThreadgroups:MTLSizeMake(nblk0, nblk1, 1)
threadsPerThreadgroup:MTLSizeMake(32, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
ds4_gpu_flash_attn_vec_args args = {
.ne01 = (int32_t)n_tokens,
.ne02 = (int32_t)n_head,
.ne03 = 1,
.nb01 = (uint64_t)n_head * row_bytes,
.nb02 = row_bytes,
.nb03 = (uint64_t)n_tokens * n_head * row_bytes,
.ne11 = (int32_t)n_raw,
.ne_12_2 = 1,
.ne_12_3 = 1,
.ns10 = (int32_t)head_dim,
.nb11 = row_bytes_f16,
.nb12 = (uint64_t)n_raw * row_bytes_f16,
.nb13 = (uint64_t)n_raw * row_bytes_f16,
.ns20 = (int32_t)head_dim,
.nb21 = row_bytes_f16,
.nb22 = (uint64_t)n_raw * row_bytes_f16,
.nb23 = (uint64_t)n_raw * row_bytes_f16,
.ne31 = (int32_t)n_tokens,
.ne32 = 1,
.ne33 = 1,
.nb31 = (uint64_t)n_raw * sizeof(uint16_t),
.nb32 = mask_bytes,
.nb33 = mask_bytes,
.ne1 = (int32_t)n_head,
.ne2 = (int32_t)n_tokens,
.ne3 = 1,
.scale = 1.0f / sqrtf((float)head_dim),
.max_bias = 0.0f,
.m0 = 0.0f,
.m1 = 0.0f,
.n_head_log2 = 0,
.logit_softcap = 0.0f,
};
const NSUInteger padded_v = ds4_gpu_align_up_ns(head_dim, 64u);
const NSUInteger shared_elems = (NSUInteger)nqptg *
((NSUInteger)head_dim + 2u * padded_v + 2u * (2u * (NSUInteger)ncpsg));
const NSUInteger shared_bytes = ds4_gpu_align_up_ns(shared_elems * (sizeof(float) / 2u), 16u);
enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:attn_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:qbuf offset:ds4_gpu_tensor_offset(q) atIndex:1];
[enc setBuffer:g_flash_attn_kv_buffer offset:0 atIndex:2];
[enc setBuffer:g_flash_attn_kv_buffer offset:0 atIndex:3];
[enc setBuffer:mask_buffer offset:0 atIndex:4];
[enc setBuffer:sinks_buf offset:sinks_offset atIndex:5];
[enc setBuffer:g_flash_attn_pad_buffer offset:0 atIndex:6];
[enc setBuffer:g_flash_attn_blk_buffer offset:0 atIndex:7];
[enc setBuffer:headsbuf offset:ds4_gpu_tensor_offset(heads) atIndex:8];
[enc setThreadgroupMemoryLength:shared_bytes atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(nblk1, n_head, 1)
threadsPerThreadgroup:MTLSizeMake(32, nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_flash_attention_decode_mixed_batch_heads(
id<MTLCommandBuffer> cb,
ds4_gpu_tensor *heads,
id<MTLBuffer> sinks_buf,
NSUInteger sinks_offset,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *raw_kv,
const ds4_gpu_tensor *comp_kv,
uint32_t comp_kv_f16,
const ds4_gpu_tensor *comp_mask,
uint32_t use_comp_mask,
uint32_t n_tokens,
uint32_t pos0,
uint32_t n_raw,
uint32_t raw_cap,
uint32_t raw_start,
uint32_t n_comp,
uint32_t window,
uint32_t ratio,
uint32_t n_head,
uint32_t head_dim,
const int32_t *visual_tokens,
uint32_t vocab_size) {
if (n_comp == 0) {
return ds4_gpu_encode_flash_attention_decode_raw_batch_heads(cb,
heads,
sinks_buf,
sinks_offset,
q,
raw_kv,
n_tokens,
pos0,
n_raw,
raw_cap,
raw_start,
window,
n_head,
head_dim,
false,
visual_tokens,
vocab_size);
}
if (head_dim != 512 || n_head == 0 || n_tokens == 0 ||
n_raw == 0 || raw_cap < n_raw || raw_start >= raw_cap ||
ratio == 0 || !comp_kv || (use_comp_mask && !comp_mask)) {
return 0;
}
const uint32_t n_keys = n_raw + n_comp;
id<MTLBuffer> qbuf = ds4_gpu_tensor_buffer(q);
id<MTLBuffer> rawbuf = ds4_gpu_tensor_buffer(raw_kv);
id<MTLBuffer> compbuf = ds4_gpu_tensor_buffer(comp_kv);
id<MTLBuffer> maskbuf = use_comp_mask ? ds4_gpu_tensor_buffer(comp_mask) : rawbuf;
id<MTLBuffer> headsbuf = ds4_gpu_tensor_buffer(heads);
const uint64_t q_bytes = (uint64_t)n_tokens * n_head * head_dim * sizeof(float);
const uint64_t raw_bytes = (uint64_t)raw_cap * head_dim * sizeof(float);
const uint64_t comp_bytes = (uint64_t)n_comp * head_dim *
(comp_kv_f16 ? sizeof(uint16_t) : sizeof(float));
const uint64_t comp_mask_bytes = use_comp_mask ? (uint64_t)n_comp * n_tokens * sizeof(float) : 0u;
if (!qbuf || !rawbuf || !compbuf || !maskbuf || !headsbuf || !sinks_buf ||
ds4_gpu_tensor_bytes(q) < q_bytes ||
ds4_gpu_tensor_bytes(raw_kv) < raw_bytes ||
ds4_gpu_tensor_bytes(comp_kv) < comp_bytes ||
(use_comp_mask && ds4_gpu_tensor_bytes(comp_mask) < comp_mask_bytes) ||
ds4_gpu_tensor_bytes(heads) < q_bytes) {
fprintf(stderr, "ds4: Metal decode mixed batch FlashAttention received undersized buffers\n");
return 0;
}
const uint32_t nqptg = 8;
const uint32_t ncpsg = 64;
const uint32_t nsg = head_dim >= 512 ? 8u : 4u;
const bool has_kvpad = (n_keys % ncpsg) != 0;
const bool bc_mask = (n_tokens % nqptg) != 0;
const NSUInteger row_bytes = (NSUInteger)head_dim * sizeof(float);
const NSUInteger row_bytes_f16 = (NSUInteger)head_dim * sizeof(uint16_t);
const NSUInteger mask_bytes = (NSUInteger)n_keys * (NSUInteger)n_tokens * sizeof(uint16_t);
const NSUInteger kv_bytes = (NSUInteger)n_keys * row_bytes_f16;
const NSUInteger pad_bytes = has_kvpad
? (NSUInteger)ncpsg * (2u * row_bytes_f16 + (NSUInteger)n_tokens * sizeof(uint16_t))
: 1u;
const NSUInteger nblk0 = ((NSUInteger)n_keys + ncpsg - 1u) / ncpsg;
const NSUInteger nblk1 = ((NSUInteger)n_tokens + nqptg - 1u) / nqptg;
const NSUInteger blk_bytes = ds4_gpu_align_up_ns(nblk0 * nblk1, 32u);
id<MTLBuffer> mask_buffer =
ds4_gpu_new_transient_buffer(mask_bytes, "ds4_flash_attn_mask");
if (!mask_buffer ||
!ds4_gpu_ensure_scratch_buffer(&g_flash_attn_kv_buffer,
&g_flash_attn_kv_bytes,
kv_bytes,
"ds4_flash_attn_kv_f16") ||
!ds4_gpu_ensure_scratch_buffer(&g_flash_attn_pad_buffer,
&g_flash_attn_pad_bytes,
pad_bytes,
"ds4_flash_attn_pad") ||
!ds4_gpu_ensure_scratch_buffer(&g_flash_attn_blk_buffer,
&g_flash_attn_blk_bytes,
blk_bytes,
"ds4_flash_attn_blk")) {
return 0;
}
if (!ds4_gpu_encode_copy_raw_ring_to_f16(cb,
rawbuf,
ds4_gpu_tensor_offset(raw_kv),
raw_cap,
raw_start,
n_raw,
head_dim,
g_flash_attn_kv_buffer,
0) ||
!ds4_gpu_encode_copy_to_f16_1d(cb,
compbuf,
ds4_gpu_tensor_offset(comp_kv),
comp_kv_f16 != 0,
g_flash_attn_kv_buffer,
(NSUInteger)n_raw * row_bytes_f16,
n_comp * head_dim)) {
return 0;
}
if (visual_tokens) {
if (!ds4_gpu_fill_visual_mixed_batch_mask(
(uint16_t *)[mask_buffer contents],
visual_tokens, vocab_size, n_tokens, n_raw, n_comp,
pos0, window, ratio)) return 0;
} else {
ds4_gpu_fill_mixed_decode_batch_mask((uint16_t *)[mask_buffer contents],
n_tokens,
n_raw,
n_comp,
pos0,
window,
ratio);
}
if (use_comp_mask) {
if (!ds4_gpu_encode_cpy_f32_f16_2d(cb,
maskbuf,
ds4_gpu_tensor_offset(comp_mask),
mask_buffer,
(NSUInteger)n_raw * sizeof(uint16_t),
n_comp,
n_tokens,
(uint64_t)n_comp * sizeof(float),
(uint64_t)n_keys * sizeof(uint16_t))) {
return 0;
}
}
id<MTLComputePipelineState> pad_pipeline = nil;
if (has_kvpad) {
pad_pipeline = ds4_gpu_get_flash_attn_pad_pipeline(true, (int32_t)ncpsg);
if (!pad_pipeline) return 0;
}
id<MTLComputePipelineState> blk_pipeline =
ds4_gpu_get_flash_attn_blk_pipeline((int32_t)nqptg, (int32_t)ncpsg);
id<MTLComputePipelineState> attn_pipeline =
ds4_gpu_get_flash_attn_pipeline("kernel_flash_attn_ext_f16_dk512_dv512",
true, true, false, false, has_kvpad, bc_mask,
(int32_t)head_dim,
(int32_t)head_dim,
(int32_t)nsg);
if (!blk_pipeline || !attn_pipeline) return 0;
if (has_kvpad) {
ds4_gpu_flash_attn_pad_args pad_args = {
.ne11 = (int32_t)n_keys,
.ne_12_2 = 1,
.ne_12_3 = 1,
.nb11 = row_bytes_f16,
.nb12 = (uint64_t)n_keys * row_bytes_f16,
.nb13 = (uint64_t)n_keys * row_bytes_f16,
.nb21 = row_bytes_f16,
.nb22 = (uint64_t)n_keys * row_bytes_f16,
.nb23 = (uint64_t)n_keys * row_bytes_f16,
.ne31 = (int32_t)n_tokens,
.ne32 = 1,
.ne33 = 1,
.nb31 = (uint64_t)n_keys * sizeof(uint16_t),
.nb32 = mask_bytes,
.nb33 = mask_bytes,
};
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pad_pipeline];
[enc setBytes:&pad_args length:sizeof(pad_args) atIndex:0];
[enc setBuffer:g_flash_attn_kv_buffer offset:0 atIndex:1];
[enc setBuffer:g_flash_attn_kv_buffer offset:0 atIndex:2];
[enc setBuffer:mask_buffer offset:0 atIndex:3];
[enc setBuffer:g_flash_attn_pad_buffer offset:0 atIndex:4];
[enc dispatchThreadgroups:MTLSizeMake(ncpsg, 1, 1)
threadsPerThreadgroup:MTLSizeMake(32, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
}
ds4_gpu_flash_attn_blk_args blk_args = {
.ne01 = (int32_t)n_tokens,
.ne30 = (int32_t)n_keys,
.ne31 = (int32_t)n_tokens,
.ne32 = 1,
.ne33 = 1,
.nb31 = (uint64_t)n_keys * sizeof(uint16_t),
.nb32 = mask_bytes,
.nb33 = mask_bytes,
};
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:blk_pipeline];
[enc setBytes:&blk_args length:sizeof(blk_args) atIndex:0];
[enc setBuffer:mask_buffer offset:0 atIndex:1];
[enc setBuffer:g_flash_attn_blk_buffer offset:0 atIndex:2];
[enc dispatchThreadgroups:MTLSizeMake(nblk0, nblk1, 1)
threadsPerThreadgroup:MTLSizeMake(32, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
ds4_gpu_flash_attn_vec_args args = {
.ne01 = (int32_t)n_tokens,
.ne02 = (int32_t)n_head,
.ne03 = 1,
.nb01 = (uint64_t)n_head * row_bytes,
.nb02 = row_bytes,
.nb03 = (uint64_t)n_tokens * n_head * row_bytes,
.ne11 = (int32_t)n_keys,
.ne_12_2 = 1,
.ne_12_3 = 1,
.ns10 = (int32_t)head_dim,
.nb11 = row_bytes_f16,
.nb12 = (uint64_t)n_keys * row_bytes_f16,
.nb13 = (uint64_t)n_keys * row_bytes_f16,
.ns20 = (int32_t)head_dim,
.nb21 = row_bytes_f16,
.nb22 = (uint64_t)n_keys * row_bytes_f16,
.nb23 = (uint64_t)n_keys * row_bytes_f16,
.ne31 = (int32_t)n_tokens,
.ne32 = 1,
.ne33 = 1,
.nb31 = (uint64_t)n_keys * sizeof(uint16_t),
.nb32 = mask_bytes,
.nb33 = mask_bytes,
.ne1 = (int32_t)n_head,
.ne2 = (int32_t)n_tokens,
.ne3 = 1,
.scale = 1.0f / sqrtf((float)head_dim),
.max_bias = 0.0f,
.m0 = 0.0f,
.m1 = 0.0f,
.n_head_log2 = 0,
.logit_softcap = 0.0f,
};
const NSUInteger padded_v = ds4_gpu_align_up_ns(head_dim, 64u);
const NSUInteger shared_elems = (NSUInteger)nqptg *
((NSUInteger)head_dim + 2u * padded_v + 2u * (2u * (NSUInteger)ncpsg));
const NSUInteger shared_bytes = ds4_gpu_align_up_ns(shared_elems * (sizeof(float) / 2u), 16u);
enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:attn_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:qbuf offset:ds4_gpu_tensor_offset(q) atIndex:1];
[enc setBuffer:g_flash_attn_kv_buffer offset:0 atIndex:2];
[enc setBuffer:g_flash_attn_kv_buffer offset:0 atIndex:3];
[enc setBuffer:mask_buffer offset:0 atIndex:4];
[enc setBuffer:sinks_buf offset:sinks_offset atIndex:5];
[enc setBuffer:g_flash_attn_pad_buffer offset:0 atIndex:6];
[enc setBuffer:g_flash_attn_blk_buffer offset:0 atIndex:7];
[enc setBuffer:headsbuf offset:ds4_gpu_tensor_offset(heads) atIndex:8];
[enc setThreadgroupMemoryLength:shared_bytes atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(nblk1, n_head, 1)
threadsPerThreadgroup:MTLSizeMake(32, nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
int ds4_gpu_attention_prefill_raw_heads_range_tensor(
ds4_gpu_tensor *heads,
const void *model_map,
uint64_t model_size,
uint64_t sinks_offset,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *raw_kv,
uint32_t q_row0,
uint32_t n_q,
uint32_t n_kv,
uint32_t window,
uint32_t n_head,
uint32_t head_dim) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!heads || !q || !raw_kv || !model_map || n_q == 0 || n_kv == 0) return 0;
@autoreleasepool {
if (sinks_offset > model_size || (uint64_t)n_head * sizeof(float) > model_size - sinks_offset) {
fprintf(stderr, "ds4: Metal attention sinks range is outside the mapped model\n");
return 0;
}
uint64_t sinks_inner = 0;
id<MTLBuffer> sinks_buf = ds4_gpu_wrap_model_range(model_map, model_size,
sinks_offset,
(uint64_t)n_head * sizeof(float),
&sinks_inner);
if (!sinks_buf) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
if (!ds4_gpu_encode_flash_attention_prefill_raw_heads(&cb,
heads,
sinks_buf,
(NSUInteger)sinks_inner,
q,
raw_kv,
q_row0,
n_q,
n_kv,
window,
n_head,
head_dim)) {
return 0;
}
if (!ds4_gpu_finish_command_buffer(cb, owned, "graph prefill raw attention heads")) return 0;
}
return 1;
}
int ds4_gpu_attention_prefill_raw_heads_tensor(
ds4_gpu_tensor *heads,
const void *model_map,
uint64_t model_size,
uint64_t sinks_offset,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *raw_kv,
uint32_t n_tokens,
uint32_t window,
uint32_t n_head,
uint32_t head_dim) {
return ds4_gpu_attention_prefill_raw_heads_range_tensor(heads,
model_map,
model_size,
sinks_offset,
q,
raw_kv,
0,
n_tokens,
n_tokens,
window,
n_head,
head_dim);
}
int ds4_gpu_glm_attention_dense_compact_lora_causal_tensor(
ds4_gpu_tensor *lora_out,
const ds4_gpu_tensor *qk_low,
const ds4_gpu_tensor *kv_lora_cache,
uint32_t q_row0,
uint32_t n_q,
uint32_t n_kv,
uint32_t cache_cap,
bool cache_f16,
uint32_t n_head,
uint32_t kv_lora_dim,
uint32_t qk_dim) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!lora_out || !qk_low || !kv_lora_cache ||
n_q == 0 || n_kv == 0 || n_kv > cache_cap ||
q_row0 >= n_kv || n_q > n_kv - q_row0 ||
n_head == 0 || kv_lora_dim != 512u || qk_dim == 0) {
return 0;
}
@autoreleasepool {
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
if (!ds4_gpu_encode_flash_attention_prefill_raw_heads_nonvec(
&cb,
lora_out,
nil,
0,
qk_low,
kv_lora_cache,
q_row0,
n_q,
n_kv,
0,
n_head,
kv_lora_dim,
cache_f16,
false,
1.0f / sqrtf((float)qk_dim))) {
return 0;
}
if (!ds4_gpu_finish_command_buffer(
cb, owned, "GLM dense compact prefill attention")) {
return 0;
}
}
return 1;
}
int ds4_gpu_attention_decode_raw_batch_heads_tensor(
ds4_gpu_tensor *heads,
const void *model_map,
uint64_t model_size,
uint64_t sinks_offset,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *raw_kv,
uint32_t n_tokens,
uint32_t pos0,
uint32_t n_raw,
uint32_t raw_cap,
uint32_t raw_start,
uint32_t window,
uint32_t n_head,
uint32_t head_dim) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!heads || !q || !raw_kv || !model_map || n_tokens == 0 ||
n_raw == 0 || raw_cap < n_raw || raw_start >= raw_cap) {
return 0;
}
@autoreleasepool {
if (sinks_offset > model_size || (uint64_t)n_head * sizeof(float) > model_size - sinks_offset) {
fprintf(stderr, "ds4: Metal attention sinks range is outside the mapped model\n");
return 0;
}
uint64_t sinks_inner = 0;
id<MTLBuffer> sinks_buf = ds4_gpu_wrap_model_range(model_map, model_size,
sinks_offset,
(uint64_t)n_head * sizeof(float),
&sinks_inner);
if (!sinks_buf) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
if (!ds4_gpu_encode_flash_attention_decode_raw_batch_heads(cb,
heads,
sinks_buf,
(NSUInteger)sinks_inner,
q,
raw_kv,
n_tokens,
pos0,
n_raw,
raw_cap,
raw_start,
window,
n_head,
head_dim,
false,
NULL,
0)) {
return 0;
}
if (!ds4_gpu_finish_command_buffer(cb, owned, "graph decode raw batch attention heads")) return 0;
}
return 1;
}
int ds4_gpu_attention_noncausal_raw_batch_heads_tensor(
ds4_gpu_tensor *heads,
const void *model_map,
uint64_t model_size,
uint64_t sinks_offset,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *raw_kv,
uint32_t n_tokens,
uint32_t n_raw,
uint32_t raw_cap,
uint32_t raw_start,
uint32_t n_head,
uint32_t head_dim) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!heads || !q || !raw_kv || !model_map ||
n_tokens == 0 || n_raw == 0 || raw_cap < n_raw ||
raw_start >= raw_cap || n_head == 0 || head_dim == 0) {
return 0;
}
@autoreleasepool {
const uint64_t sink_bytes = (uint64_t)n_head * sizeof(float);
if (sinks_offset > model_size || sink_bytes > model_size - sinks_offset) {
fprintf(stderr, "ds4: Metal noncausal attention sinks range is outside the mapped model\n");
return 0;
}
uint64_t sinks_inner = 0;
id<MTLBuffer> sinks_buf = ds4_gpu_wrap_model_range(model_map, model_size,
sinks_offset,
sink_bytes,
&sinks_inner);
if (!sinks_buf) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
if (!ds4_gpu_encode_flash_attention_decode_raw_batch_heads(cb,
heads,
sinks_buf,
(NSUInteger)sinks_inner,
q,
raw_kv,
n_tokens,
0,
n_raw,
raw_cap,
raw_start,
0,
n_head,
head_dim,
true,
NULL,
0)) {
return 0;
}
if (!ds4_gpu_finish_command_buffer(cb, owned, "graph noncausal raw batch attention heads")) return 0;
}
return 1;
}
int ds4_gpu_attention_decode_mixed_batch_heads_tensor(
ds4_gpu_tensor *heads,
const void *model_map,
uint64_t model_size,
uint64_t sinks_offset,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *raw_kv,
const ds4_gpu_tensor *comp_kv,
uint32_t comp_kv_f16,
const ds4_gpu_tensor *comp_mask,
uint32_t use_comp_mask,
uint32_t n_tokens,
uint32_t pos0,
uint32_t n_raw,
uint32_t raw_cap,
uint32_t raw_start,
uint32_t n_comp,
uint32_t window,
uint32_t ratio,
uint32_t n_head,
uint32_t head_dim) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!heads || !q || !raw_kv || !model_map || n_tokens == 0 ||
n_raw == 0 || raw_cap < n_raw || raw_start >= raw_cap ||
ratio == 0 || (n_comp != 0 && !comp_kv) ||
(use_comp_mask != 0 && !comp_mask)) {
return 0;
}
@autoreleasepool {
if (sinks_offset > model_size || (uint64_t)n_head * sizeof(float) > model_size - sinks_offset) {
fprintf(stderr, "ds4: Metal attention sinks range is outside the mapped model\n");
return 0;
}
uint64_t sinks_inner = 0;
id<MTLBuffer> sinks_buf = ds4_gpu_wrap_model_range(model_map, model_size,
sinks_offset,
(uint64_t)n_head * sizeof(float),
&sinks_inner);
if (!sinks_buf) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
if (!ds4_gpu_encode_flash_attention_decode_mixed_batch_heads(cb,
heads,
sinks_buf,
(NSUInteger)sinks_inner,
q,
raw_kv,
comp_kv,
comp_kv_f16,
comp_mask,
use_comp_mask,
n_tokens,
pos0,
n_raw,
raw_cap,
raw_start,
n_comp,
window,
ratio,
n_head,
head_dim,
NULL,
0)) {
return 0;
}
if (!ds4_gpu_finish_command_buffer(cb, owned, "graph decode mixed batch attention heads")) return 0;
}
return 1;
}
int ds4_gpu_attention_visual_mixed_batch_heads_tensor(
ds4_gpu_tensor *heads,
const void *model_map,
uint64_t model_size,
uint64_t sinks_offset,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *raw_kv,
const ds4_gpu_tensor *comp_kv,
uint32_t comp_kv_f16,
const ds4_gpu_tensor *comp_mask,
uint32_t use_comp_mask,
const int32_t *tokens,
uint32_t vocab_size,
uint32_t n_tokens,
uint32_t pos0,
uint32_t n_raw,
uint32_t raw_cap,
uint32_t raw_start,
uint32_t n_comp,
uint32_t window,
uint32_t ratio,
uint32_t n_head,
uint32_t head_dim) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!heads || !q || !raw_kv || !model_map || !tokens || vocab_size == 0 ||
n_tokens == 0 || n_raw == 0 || raw_cap < n_raw || raw_start >= raw_cap ||
(n_comp != 0 && (!comp_kv || ratio == 0)) ||
(use_comp_mask != 0 && !comp_mask)) return 0;
@autoreleasepool {
const uint64_t sink_bytes = (uint64_t)n_head * sizeof(float);
if (sinks_offset > model_size || sink_bytes > model_size - sinks_offset) {
fprintf(stderr, "ds4: Metal visual attention sinks are outside the mapped model\n");
return 0;
}
uint64_t sinks_inner = 0;
id<MTLBuffer> sinks_buf = ds4_gpu_wrap_model_range(
model_map, model_size, sinks_offset, sink_bytes, &sinks_inner);
if (!sinks_buf) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb || !ds4_gpu_encode_flash_attention_decode_mixed_batch_heads(
cb, heads, sinks_buf, (NSUInteger)sinks_inner,
q, raw_kv, comp_kv, comp_kv_f16,
comp_mask, use_comp_mask,
n_tokens, pos0, n_raw, raw_cap, raw_start,
n_comp, window, ratio, n_head, head_dim,
tokens, vocab_size)) return 0;
if (!ds4_gpu_finish_command_buffer(
cb, owned, "graph visual mixed batch attention heads")) return 0;
}
return 1;
}
/* A DSpark verify pass runs; its rows take decode's attention path. */
static int g_v41_verify_rows;
void ds4_gpu_dsv41_verify_rows(int on) { g_v41_verify_rows = on; }
int ds4_gpu_attention_indexed_mixed_batch_heads_tensor(
ds4_gpu_tensor *heads,
const void *model_map,
uint64_t model_size,
uint64_t sinks_offset,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *raw_kv,
const ds4_gpu_tensor *comp_kv,
uint32_t comp_kv_f16,
const ds4_gpu_tensor *topk,
uint32_t n_tokens,
uint32_t pos0,
uint32_t n_raw,
uint32_t raw_cap,
uint32_t raw_start,
uint32_t n_comp,
uint32_t top_k,
uint32_t window,
uint32_t ratio,
uint32_t n_head,
uint32_t head_dim) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!heads || !model_map || !q || !raw_kv || !comp_kv || !topk ||
n_tokens == 0 || n_raw == 0 || raw_cap < n_raw || raw_start >= raw_cap ||
n_comp == 0 || top_k == 0 || top_k > n_comp || (top_k & (top_k - 1u)) != 0 ||
ratio == 0 || n_head == 0 || head_dim != 512) {
return 0;
}
@autoreleasepool {
if (sinks_offset > model_size || (uint64_t)n_head * sizeof(float) > model_size - sinks_offset) {
fprintf(stderr, "ds4: Metal indexed attention sinks range is outside the mapped model\n");
return 0;
}
const uint64_t row_bytes = (uint64_t)head_dim * sizeof(float);
const uint64_t row_bytes_f16 = (uint64_t)head_dim * sizeof(uint16_t);
const uint64_t q_bytes = (uint64_t)n_tokens * n_head * row_bytes;
const uint64_t raw_bytes = (uint64_t)raw_cap * row_bytes;
const uint64_t comp_bytes = (uint64_t)n_comp * (comp_kv_f16 ? row_bytes_f16 : row_bytes);
const uint64_t topk_bytes = (uint64_t)top_k * n_tokens * sizeof(int32_t);
id<MTLBuffer> qbuf = ds4_gpu_tensor_buffer(q);
id<MTLBuffer> rawbuf = ds4_gpu_tensor_buffer(raw_kv);
id<MTLBuffer> compbuf = ds4_gpu_tensor_buffer(comp_kv);
id<MTLBuffer> topkbuf = ds4_gpu_tensor_buffer(topk);
id<MTLBuffer> headsbuf = ds4_gpu_tensor_buffer(heads);
if (!qbuf || !rawbuf || !compbuf || !topkbuf || !headsbuf ||
ds4_gpu_tensor_bytes(q) < q_bytes ||
ds4_gpu_tensor_bytes(raw_kv) < raw_bytes ||
ds4_gpu_tensor_bytes(comp_kv) < comp_bytes ||
ds4_gpu_tensor_bytes(topk) < topk_bytes ||
ds4_gpu_tensor_bytes(heads) < q_bytes) {
fprintf(stderr, "ds4: Metal indexed mixed attention received undersized buffers\n");
return 0;
}
uint64_t sinks_inner = 0;
id<MTLBuffer> sinks_buf = ds4_gpu_wrap_model_range(model_map, model_size,
sinks_offset,
(uint64_t)n_head * sizeof(float),
&sinks_inner);
if (!sinks_buf) return 0;
id<MTLComputePipelineState> sort_pipeline =
ds4_gpu_hot_pipeline(g_dsv4_sort_i32_rows_asc_pipeline,
"kernel_dsv4_sort_i32_rows_asc");
const bool decode_one_token = n_tokens == 1u;
/* A few rows take decode's split path and score order: one token's
* work per split keeps the GPU busy and the rows match decode steps. */
const bool decode_rows = (n_tokens == 1u || (n_tokens <= 8u && g_v41_verify_rows)) && !g_quality_mode;
const bool prefill_dual_heads =
!decode_one_token && !g_quality_mode && ds4_gpu_mpp_available() &&
(n_head == 64u || n_head == 32u) &&
top_k == 512u && window == 128u && head_dim == 512u;
const uint32_t decode_splits = decode_rows ? 12u : 1u;
const bool split_decode = decode_splits > 1u;
id<MTLComputePipelineState> attn_pipeline =
split_decode ?
ds4_gpu_hot_pipeline(
g_dsv4_indexed_attention_heads8_split_pipeline,
"kernel_dsv4_indexed_mixed_attention_heads8_split") :
decode_one_token ?
ds4_gpu_hot_pipeline(g_dsv4_indexed_attention_heads8_rb16_pipeline,
"kernel_dsv4_indexed_mixed_attention_heads8_rb16") :
prefill_dual_heads ?
ds4_gpu_hot_pipeline(g_dsv4_indexed_attention_heads16_dual_pipeline,
"kernel_dsv4_indexed_mixed_attention_heads16_dual") :
ds4_gpu_hot_pipeline(g_dsv4_indexed_attention_heads8_pipeline,
"kernel_dsv4_indexed_mixed_attention_heads8");
id<MTLComputePipelineState> split_reduce_pipeline = split_decode ?
ds4_gpu_hot_pipeline(
g_dsv4_indexed_attention_heads8_split_reduce_pipeline,
"kernel_dsv4_indexed_mixed_attention_heads8_split_reduce") :
nil;
if (!sort_pipeline || !attn_pipeline ||
(split_decode && !split_reduce_pipeline)) {
return 0;
}
if ((NSUInteger)top_k > sort_pipeline.maxTotalThreadsPerThreadgroup) {
fprintf(stderr, "ds4: Metal indexed attention top-k exceeds sort threadgroup limit\n");
return 0;
}
/*
* Fast decode attends to the same full top-k compressed rows but keeps
* them in score order, avoiding a chronological sort dispatch.
* --quality restores the sorted order for stricter reproducibility.
*/
const bool skip_decode_sort = decode_rows;
if (!skip_decode_sort &&
!ds4_gpu_ensure_scratch_buffer(&g_indexed_topk_buffer,
&g_indexed_topk_bytes,
(NSUInteger)topk_bytes,
"ds4_indexed_topk_sorted")) {
return 0;
}
ds4_gpu_dsv4_topk_mask_args sort_args = {
.ne00 = (int64_t)top_k,
.ne01 = (int64_t)n_tokens,
.nb00 = sizeof(int32_t),
.nb01 = (uint64_t)top_k * sizeof(int32_t),
.ne0 = (int64_t)top_k,
.ne1 = (int64_t)n_tokens,
.nb0 = sizeof(int32_t),
.nb1 = (uint64_t)top_k * sizeof(int32_t),
};
ds4_gpu_dsv4_indexed_attention_args attn_args = {
.n_tokens = n_tokens,
.n_head = n_head,
.n_raw = n_raw,
.raw_cap = raw_cap,
.raw_start = raw_start,
.n_comp = n_comp,
.top_k = top_k,
.pos0 = pos0,
.window = window,
.ratio = ratio,
.comp_kv_f16 = comp_kv_f16 ? 1u : 0u,
.n_splits = decode_splits,
.q_token_stride = (uint64_t)n_head * row_bytes,
.q_head_stride = row_bytes,
.raw_row_stride = row_bytes,
.comp_row_stride = comp_kv_f16 ? row_bytes_f16 : row_bytes,
.topk_token_stride = (uint64_t)top_k * sizeof(int32_t),
.dst_token_stride = (uint64_t)n_head * row_bytes,
.dst_head_stride = row_bytes,
.scale = 1.0f / sqrtf((float)head_dim),
};
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = nil;
if (!skip_decode_sort) {
enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:sort_pipeline];
[enc setBytes:&sort_args length:sizeof(sort_args) atIndex:0];
[enc setBuffer:topkbuf offset:ds4_gpu_tensor_offset(topk) atIndex:1];
[enc setBuffer:g_indexed_topk_buffer offset:0 atIndex:2];
[enc setThreadgroupMemoryLength:(NSUInteger)top_k * sizeof(int32_t) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(n_tokens, 1, 1)
threadsPerThreadgroup:MTLSizeMake(top_k, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
}
if (split_decode) {
const uint64_t nrows = (uint64_t)n_tokens * n_head;
const uint64_t partial_bytes =
nrows * head_dim * decode_splits * sizeof(float);
const uint64_t stats_bytes =
nrows * decode_splits * 2u * sizeof(float);
if (partial_bytes > NSUIntegerMax - stats_bytes ||
!ds4_gpu_ensure_scratch_buffer(
&g_flash_attn_tmp_buffer,
&g_flash_attn_tmp_bytes,
(NSUInteger)(partial_bytes + stats_bytes),
"ds4_dsv4_indexed_attention_split_tmp")) {
return 0;
}
enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:attn_pipeline];
[enc setBytes:&attn_args length:sizeof(attn_args) atIndex:0];
[enc setBuffer:qbuf offset:ds4_gpu_tensor_offset(q) atIndex:1];
[enc setBuffer:rawbuf offset:ds4_gpu_tensor_offset(raw_kv) atIndex:2];
[enc setBuffer:compbuf offset:ds4_gpu_tensor_offset(comp_kv) atIndex:3];
[enc setBuffer:skip_decode_sort ? topkbuf : g_indexed_topk_buffer
offset:skip_decode_sort ? ds4_gpu_tensor_offset(topk) : 0
atIndex:4];
[enc setBuffer:g_flash_attn_tmp_buffer offset:0 atIndex:5];
[enc setThreadgroupMemoryLength:16u * 128u * sizeof(uint16_t) * 4u
atIndex:0];
[enc dispatchThreadgroups:
MTLSizeMake((NSUInteger)n_tokens,
((NSUInteger)n_head + 7u) / 8u,
decode_splits)
threadsPerThreadgroup:MTLSizeMake(32, 8, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:split_reduce_pipeline];
[enc setBytes:&attn_args length:sizeof(attn_args) atIndex:0];
[enc setBuffer:g_flash_attn_tmp_buffer offset:0 atIndex:1];
[enc setBuffer:sinks_buf offset:(NSUInteger)sinks_inner atIndex:2];
[enc setBuffer:headsbuf offset:ds4_gpu_tensor_offset(heads) atIndex:3];
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)nrows, 1, 1)
threadsPerThreadgroup:MTLSizeMake(32, 4, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
} else {
enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:attn_pipeline];
[enc setBytes:&attn_args length:sizeof(attn_args) atIndex:0];
[enc setBuffer:qbuf offset:ds4_gpu_tensor_offset(q) atIndex:1];
[enc setBuffer:rawbuf offset:ds4_gpu_tensor_offset(raw_kv) atIndex:2];
[enc setBuffer:compbuf offset:ds4_gpu_tensor_offset(comp_kv) atIndex:3];
[enc setBuffer:skip_decode_sort ? topkbuf : g_indexed_topk_buffer
offset:skip_decode_sort ? ds4_gpu_tensor_offset(topk) : 0
atIndex:4];
[enc setBuffer:sinks_buf offset:(NSUInteger)sinks_inner atIndex:5];
[enc setBuffer:headsbuf offset:ds4_gpu_tensor_offset(heads) atIndex:6];
[enc setThreadgroupMemoryLength:(decode_one_token ? 16u : 1u) *
128u * 4u * sizeof(uint16_t)
atIndex:0];
[enc dispatchThreadgroups:
MTLSizeMake((NSUInteger)n_tokens,
((NSUInteger)n_head + (prefill_dual_heads ? 15u : 7u)) /
(prefill_dual_heads ? 16u : 8u),
1)
threadsPerThreadgroup:MTLSizeMake(32, 8, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
}
if (!ds4_gpu_finish_command_buffer(cb, owned, "graph indexed mixed attention heads")) return 0;
}
return 1;
}
int ds4_gpu_attention_prefill_static_mixed_heads_range_tensor(
ds4_gpu_tensor *heads,
const void *model_map,
uint64_t model_size,
uint64_t sinks_offset,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *raw_kv,
const ds4_gpu_tensor *comp_kv,
uint32_t comp_kv_f16,
uint32_t q_row0,
uint32_t n_q,
uint32_t n_tokens,
uint32_t n_comp,
uint32_t window,
uint32_t ratio,
uint32_t n_head,
uint32_t head_dim) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!heads || !q || !raw_kv || !model_map || n_q == 0 || n_tokens == 0 ||
ratio == 0 || (n_comp != 0 && !comp_kv)) {
return 0;
}
@autoreleasepool {
if (sinks_offset > model_size || (uint64_t)n_head * sizeof(float) > model_size - sinks_offset) {
fprintf(stderr, "ds4: Metal attention sinks range is outside the mapped model\n");
return 0;
}
uint64_t sinks_inner = 0;
id<MTLBuffer> sinks_buf = ds4_gpu_wrap_model_range(model_map, model_size,
sinks_offset,
(uint64_t)n_head * sizeof(float),
&sinks_inner);
if (!sinks_buf) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
if (!ds4_gpu_encode_flash_attention_prefill_static_mixed_heads_nonvec(&cb,
heads,
sinks_buf,
(NSUInteger)sinks_inner,
q,
raw_kv,
comp_kv,
comp_kv_f16,
NULL,
0,
q_row0,
n_q,
n_tokens,
n_comp,
window,
ratio,
n_head,
head_dim)) {
return 0;
}
if (!ds4_gpu_finish_command_buffer(cb, owned, "graph prefill static mixed attention heads")) return 0;
}
return 1;
}
int ds4_gpu_attention_prefill_static_mixed_heads_tensor(
ds4_gpu_tensor *heads,
const void *model_map,
uint64_t model_size,
uint64_t sinks_offset,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *raw_kv,
const ds4_gpu_tensor *comp_kv,
uint32_t comp_kv_f16,
uint32_t n_tokens,
uint32_t n_comp,
uint32_t window,
uint32_t ratio,
uint32_t n_head,
uint32_t head_dim) {
return ds4_gpu_attention_prefill_static_mixed_heads_range_tensor(heads,
model_map,
model_size,
sinks_offset,
q,
raw_kv,
comp_kv,
comp_kv_f16,
0,
n_tokens,
n_tokens,
n_comp,
window,
ratio,
n_head,
head_dim);
}
int ds4_gpu_attention_prefill_masked_mixed_heads_tensor(
ds4_gpu_tensor *heads,
const void *model_map,
uint64_t model_size,
uint64_t sinks_offset,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *raw_kv,
const ds4_gpu_tensor *comp_kv,
uint32_t comp_kv_f16,
const ds4_gpu_tensor *comp_mask,
uint32_t n_tokens,
uint32_t n_comp,
uint32_t window,
uint32_t ratio,
uint32_t n_head,
uint32_t head_dim) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!heads || !q || !raw_kv || !comp_kv || !comp_mask || !model_map ||
n_tokens == 0 || n_comp == 0 || ratio == 0) {
return 0;
}
@autoreleasepool {
if (sinks_offset > model_size || (uint64_t)n_head * sizeof(float) > model_size - sinks_offset) {
fprintf(stderr, "ds4: Metal attention sinks range is outside the mapped model\n");
return 0;
}
uint64_t sinks_inner = 0;
id<MTLBuffer> sinks_buf = ds4_gpu_wrap_model_range(model_map, model_size,
sinks_offset,
(uint64_t)n_head * sizeof(float),
&sinks_inner);
if (!sinks_buf) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
if (!ds4_gpu_encode_flash_attention_prefill_static_mixed_heads_nonvec(&cb,
heads,
sinks_buf,
(NSUInteger)sinks_inner,
q,
raw_kv,
comp_kv,
comp_kv_f16,
comp_mask,
1,
0,
n_tokens,
n_tokens,
n_comp,
window,
ratio,
n_head,
head_dim)) {
return 0;
}
if (!ds4_gpu_finish_command_buffer(cb, owned, "graph prefill masked mixed attention heads")) return 0;
}
return 1;
}
int ds4_gpu_attention_decode_heads_tensor(
ds4_gpu_tensor *heads,
const void *model_map,
uint64_t model_size,
uint64_t sinks_offset,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *raw_kv,
uint32_t n_raw,
uint32_t raw_cap,
uint32_t raw_start,
const ds4_gpu_tensor *comp_kv,
uint32_t comp_kv_f16,
uint32_t n_comp,
const ds4_gpu_tensor *comp_mask,
uint32_t use_mask,
uint32_t n_head,
uint32_t head_dim) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!heads || !model_map || !q || !raw_kv ||
n_raw == 0 || n_head == 0 || head_dim == 0 ||
raw_cap < n_raw || raw_start >= raw_cap ||
n_raw > UINT32_MAX - n_comp || n_raw + n_comp > 8192u ||
(n_comp != 0 && !comp_kv) ||
(use_mask != 0 && !comp_mask)) {
return 0;
}
@autoreleasepool {
const uint64_t q_bytes = (uint64_t)n_head * head_dim * sizeof(float);
const uint64_t raw_bytes = (uint64_t)raw_cap * head_dim * sizeof(float);
const uint64_t comp_bytes = (uint64_t)n_comp * head_dim *
(comp_kv_f16 ? sizeof(uint16_t) : sizeof(float));
const uint64_t sink_bytes = (uint64_t)n_head * sizeof(float);
if (sinks_offset > model_size || sink_bytes > model_size - sinks_offset) {
fprintf(stderr, "ds4: Metal graph attention heads sink range is outside the mapped model\n");
return 0;
}
id<MTLBuffer> qbuf = ds4_gpu_tensor_buffer(q);
id<MTLBuffer> rawbuf = ds4_gpu_tensor_buffer(raw_kv);
id<MTLBuffer> compbuf = n_comp ? ds4_gpu_tensor_buffer(comp_kv) : rawbuf;
id<MTLBuffer> maskbuf = use_mask ? ds4_gpu_tensor_buffer(comp_mask) : rawbuf;
id<MTLBuffer> headsbuf = ds4_gpu_tensor_buffer(heads);
const uint64_t comp_mask_bytes = use_mask ? (uint64_t)n_comp * sizeof(float) : 0u;
if (!qbuf || !rawbuf || !compbuf || !maskbuf || !headsbuf ||
ds4_gpu_tensor_bytes(q) < q_bytes ||
ds4_gpu_tensor_bytes(raw_kv) < raw_bytes ||
(n_comp && ds4_gpu_tensor_bytes(comp_kv) < comp_bytes) ||
(use_mask && ds4_gpu_tensor_bytes(comp_mask) < comp_mask_bytes) ||
ds4_gpu_tensor_bytes(heads) < q_bytes) {
fprintf(stderr, "ds4: Metal graph attention heads received undersized buffers\n");
return 0;
}
uint64_t sinks_inner = 0;
id<MTLBuffer> sinks_buf = ds4_gpu_wrap_model_range(model_map, model_size, sinks_offset, sink_bytes, &sinks_inner);
if (!sinks_buf) return 0;
if (n_comp == 0) {
/* The raw-only path stages through the same kernel when it
* stages at all; a pending deferred kv task that nothing
* consumes must run before the attention reads the cache. */
if (g_kv_task.pending && !ds4_gpu_kv_norm_task_flush()) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
if (!ds4_gpu_encode_flash_attention_raw_heads(cb,
heads,
sinks_buf,
(NSUInteger)sinks_inner,
q,
raw_kv,
n_raw,
raw_cap,
raw_start,
n_head,
head_dim)) {
return 0;
}
if (!ds4_gpu_finish_command_buffer(cb, owned, "graph raw attention heads")) return 0;
return 1;
}
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
if (!ds4_gpu_encode_flash_attention_gathered_heads(cb,
heads,
sinks_buf,
(NSUInteger)sinks_inner,
q,
raw_kv,
n_raw,
raw_cap,
raw_start,
comp_kv,
comp_kv_f16,
NULL,
n_comp,
comp_mask,
use_mask,
n_head,
head_dim)) {
return 0;
}
if (g_kv_task.pending) {
static int warned;
if (!warned) {
warned = 1;
fprintf(stderr, "ds4: deferred kv norm task was not consumed by the attention path\n");
}
if (!ds4_gpu_kv_norm_task_flush()) return 0;
}
if (!ds4_gpu_finish_command_buffer(cb, owned, "graph attention heads")) return 0;
}
return 1;
}
/* DeepSeek V4.1 decode attention over the raw ring and the compressed rows
* selected by `ids`, staged by one kernel (0: the staging kernel does not
* apply; the caller gathers into `selected` and runs the plain entry). */
int ds4_gpu_dsv41_attention_decode(ds4_gpu_tensor *heads, const void *model_map, uint64_t model_size,
uint64_t sinks_offset, const ds4_gpu_tensor *q,
const ds4_gpu_tensor *raw_kv, uint32_t n_raw, uint32_t raw_cap,
uint32_t raw_start, const ds4_gpu_tensor *comp_cache,
const ds4_gpu_tensor *ids, ds4_gpu_tensor *selected,
uint32_t n_comp, uint32_t attended, uint32_t n_head, uint32_t head_dim) {
(void)selected;
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!heads || !model_map || !q || !raw_kv || !comp_cache || !ids || !n_raw || !attended ||
attended > n_comp || raw_cap < n_raw || raw_start >= raw_cap || head_dim != 512u ||
n_raw + attended > 8192u || g_quality_mode ||
getenv("DS4_METAL_DISABLE_GATHERED_KV_STAGE") != NULL ||
!(ds4_gpu_device_name_contains("M3") || ds4_gpu_device_name_contains("M5")) ||
ds4_gpu_tensor_bytes(comp_cache) < (uint64_t)n_comp * head_dim * sizeof(float) ||
ds4_gpu_tensor_bytes(ids) < (uint64_t)attended * sizeof(int32_t) ||
ds4_gpu_tensor_bytes(q) < (uint64_t)n_head * head_dim * sizeof(float) ||
ds4_gpu_tensor_bytes(heads) < (uint64_t)n_head * head_dim * sizeof(float)) return 0;
@autoreleasepool {
const uint64_t sink_bytes = (uint64_t)n_head * sizeof(float);
if (sinks_offset > model_size || sink_bytes > model_size - sinks_offset) return 0;
uint64_t sinks_inner = 0;
id<MTLBuffer> sinks_buf = ds4_gpu_wrap_model_range(model_map, model_size, sinks_offset,
sink_bytes, &sinks_inner);
if (!sinks_buf) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
if (!ds4_gpu_encode_flash_attention_gathered_heads(cb, heads, sinks_buf, (NSUInteger)sinks_inner,
q, raw_kv, n_raw, raw_cap, raw_start, comp_cache, 0, ids, attended, NULL, 0,
n_head, head_dim)) return 0;
if (g_kv_task.pending && !ds4_gpu_kv_norm_task_flush()) return 0;
return ds4_gpu_finish_command_buffer(cb, owned, "V4.1 gathered attention");
}
}
int ds4_gpu_swiglu_tensor(
ds4_gpu_tensor *out,
const ds4_gpu_tensor *gate,
const ds4_gpu_tensor *up,
uint32_t n,
float clamp,
float weight) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!out || !gate || !up || n == 0) return 0;
if (!isfinite(clamp) || clamp < 0.0f || !isfinite(weight)) return 0;
@autoreleasepool {
id<MTLBuffer> gatebuf = ds4_gpu_tensor_buffer(gate);
id<MTLBuffer> upbuf = ds4_gpu_tensor_buffer(up);
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out);
const uint64_t bytes = (uint64_t)n * sizeof(float);
if (!gatebuf || !upbuf || !outbuf ||
ds4_gpu_tensor_bytes(gate) < bytes ||
ds4_gpu_tensor_bytes(up) < bytes ||
ds4_gpu_tensor_bytes(out) < bytes) {
fprintf(stderr, "ds4: Metal SwiGLU received undersized buffers\n");
return 0;
}
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
ds4_gpu_glu_args args = {
.ne00 = (int32_t)n,
.nb01 = (uint64_t)n * sizeof(float),
.ne10 = (int32_t)n,
.nb11 = (uint64_t)n * sizeof(float),
.ne0 = (int32_t)n,
.nb1 = (uint64_t)n * sizeof(float),
.i00 = 0,
.i10 = 0,
.alpha = weight,
.limit = clamp,
};
NSUInteger nth = g_swiglu_flat_pipeline.maxTotalThreadsPerThreadgroup;
if (nth > 256u) nth = 256u;
if (nth > (NSUInteger)n) nth = (NSUInteger)n;
if (nth == 0u) nth = 1u;
const NSUInteger groups = ((NSUInteger)n + nth - 1u) / nth;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:g_swiglu_flat_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:gatebuf offset:ds4_gpu_tensor_offset(gate) atIndex:1];
[enc setBuffer:upbuf offset:ds4_gpu_tensor_offset(up) atIndex:2];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(out) atIndex:3];
[enc dispatchThreadgroups:MTLSizeMake(groups, 1, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "SwiGLU")) return 0;
}
return 1;
}
int ds4_gpu_add_tensor(
ds4_gpu_tensor *out,
const ds4_gpu_tensor *a,
const ds4_gpu_tensor *b,
uint32_t n) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!out || !a || !b || n == 0) return 0;
@autoreleasepool {
id<MTLBuffer> abuf = ds4_gpu_tensor_buffer(a);
id<MTLBuffer> bbuf = ds4_gpu_tensor_buffer(b);
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out);
const uint64_t bytes = (uint64_t)n * sizeof(float);
if (!abuf || !bbuf || !outbuf ||
ds4_gpu_tensor_bytes(a) < bytes ||
ds4_gpu_tensor_bytes(b) < bytes ||
ds4_gpu_tensor_bytes(out) < bytes) {
fprintf(stderr, "ds4: Metal tensor add received undersized buffers\n");
return 0;
}
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
ds4_gpu_add_flat_args args = { .n = n };
NSUInteger nth = g_add2_pipeline.maxTotalThreadsPerThreadgroup;
if (nth > 256u) nth = 256u;
if (nth > (NSUInteger)n) nth = (NSUInteger)n;
if (nth == 0u) nth = 1u;
const NSUInteger groups = ((NSUInteger)n + nth - 1u) / nth;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:g_add2_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:abuf offset:ds4_gpu_tensor_offset(a) atIndex:1];
[enc setBuffer:bbuf offset:ds4_gpu_tensor_offset(b) atIndex:2];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(out) atIndex:3];
[enc dispatchThreadgroups:MTLSizeMake(groups, 1, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "tensor add")) return 0;
}
return 1;
}
int ds4_gpu_add3_tensor(
ds4_gpu_tensor *out,
const ds4_gpu_tensor *a,
const ds4_gpu_tensor *b,
const ds4_gpu_tensor *c,
uint32_t n) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!out || !a || !b || !c || n == 0) return 0;
@autoreleasepool {
id<MTLBuffer> abuf = ds4_gpu_tensor_buffer(a);
id<MTLBuffer> bbuf = ds4_gpu_tensor_buffer(b);
id<MTLBuffer> cbuf = ds4_gpu_tensor_buffer(c);
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out);
const uint64_t bytes = (uint64_t)n * sizeof(float);
if (!abuf || !bbuf || !cbuf || !outbuf ||
ds4_gpu_tensor_bytes(a) < bytes ||
ds4_gpu_tensor_bytes(b) < bytes ||
ds4_gpu_tensor_bytes(c) < bytes ||
ds4_gpu_tensor_bytes(out) < bytes) {
fprintf(stderr, "ds4: Metal tensor add3 received undersized buffers\n");
return 0;
}
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
ds4_gpu_add_flat_args args = { .n = n };
NSUInteger nth = g_add3_pipeline.maxTotalThreadsPerThreadgroup;
if (nth > 256u) nth = 256u;
if (nth > (NSUInteger)n) nth = (NSUInteger)n;
if (nth == 0u) nth = 1u;
const NSUInteger groups = ((NSUInteger)n + nth - 1u) / nth;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:g_add3_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:abuf offset:ds4_gpu_tensor_offset(a) atIndex:1];
[enc setBuffer:bbuf offset:ds4_gpu_tensor_offset(b) atIndex:2];
[enc setBuffer:cbuf offset:ds4_gpu_tensor_offset(c) atIndex:3];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(out) atIndex:4];
[enc dispatchThreadgroups:MTLSizeMake(groups, 1, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "tensor add3")) return 0;
}
return 1;
}
typedef struct {
uint32_t width;
uint32_t rows;
uint32_t layer;
uint32_t n_threads;
float scale;
} ds4_gpu_directional_steering_project_args;
int ds4_gpu_directional_steering_project_tensor(
ds4_gpu_tensor *x,
const ds4_gpu_tensor *directions,
uint32_t layer,
uint32_t width,
uint32_t rows,
float scale) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!x || !directions || width == 0 || rows == 0 || scale == 0.0f) return 0;
@autoreleasepool {
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_pipeline("kernel_dsv4_directional_steering_project_f32");
if (!pipeline) return 0;
id<MTLBuffer> xbuf = ds4_gpu_tensor_buffer(x);
id<MTLBuffer> dbuf = ds4_gpu_tensor_buffer(directions);
const uint64_t x_bytes = (uint64_t)width * rows * sizeof(float);
const uint64_t dir_bytes = (uint64_t)(layer + 1u) * width * sizeof(float);
if (!xbuf || !dbuf ||
ds4_gpu_tensor_bytes(x) < x_bytes ||
ds4_gpu_tensor_bytes(directions) < dir_bytes) {
fprintf(stderr, "ds4: Metal directional steering received undersized buffers\n");
return 0;
}
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
NSUInteger nth = pipeline.maxTotalThreadsPerThreadgroup;
if (nth > 256u) nth = 256u;
while (nth > width && nth > 1u) nth >>= 1;
if (nth == 0) nth = 1;
ds4_gpu_directional_steering_project_args args = {
.width = width,
.rows = rows,
.layer = layer,
.n_threads = (uint32_t)nth,
.scale = scale,
};
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:1];
[enc setBuffer:dbuf offset:ds4_gpu_tensor_offset(directions) atIndex:2];
[enc setThreadgroupMemoryLength:nth * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)rows, 1, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "directional steering")) return 0;
}
return 1;
}
static NSUInteger ds4_gpu_bin_threads(uint32_t width, id<MTLComputePipelineState> pipeline) {
NSUInteger nth_max = pipeline.maxTotalThreadsPerThreadgroup;
if (nth_max > 256u) nth_max = 256u;
NSUInteger nth = 1u;
while (2u * nth < (NSUInteger)width && nth < nth_max) nth *= 2u;
return nth ? nth : 1u;
}
static int ds4_gpu_encode_unary_f32_rows(
id<MTLCommandBuffer> cb,
id<MTLComputePipelineState> pipeline,
id<MTLBuffer> src,
NSUInteger src_off,
id<MTLBuffer> dst,
NSUInteger dst_off,
uint32_t width,
uint32_t rows,
int c4,
float min,
float max) {
if (!cb || !pipeline || !src || !dst || width == 0 || rows == 0) return 0;
if (c4 && (width & 3u) != 0) return 0;
ds4_gpu_unary_args args = ds4_gpu_make_unary_rows_args(width, rows, c4, 0.0f, 0.0f);
args.min = min;
args.max = max;
NSUInteger nth_max = pipeline.maxTotalThreadsPerThreadgroup;
if (nth_max > 256u) nth_max = 256u;
NSUInteger nth = (NSUInteger)args.ne00;
if (nth > nth_max) nth = nth_max;
if (nth == 0) nth = 1u;
const NSUInteger nk0 = ((NSUInteger)args.ne00 + nth - 1u) / nth;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:src offset:src_off atIndex:1];
[enc setBuffer:dst offset:dst_off atIndex:2];
[enc dispatchThreadgroups:MTLSizeMake(nk0 * (NSUInteger)args.ne01,
(NSUInteger)args.ne02,
(NSUInteger)args.ne03)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_bin_f32_rows(
id<MTLCommandBuffer> cb,
id<MTLComputePipelineState> pipeline,
const ds4_gpu_bin_args *args,
id<MTLBuffer> a,
NSUInteger a_off,
id<MTLBuffer> b,
NSUInteger b_off,
id<MTLBuffer> out,
NSUInteger out_off) {
if (!cb || !pipeline || !args || !a || !b || !out || args->ne0 <= 0 || args->ne1 <= 0) {
return 0;
}
const NSUInteger nth = ds4_gpu_bin_threads((uint32_t)args->ne0, pipeline);
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:args length:sizeof(*args) atIndex:0];
[enc setBuffer:a offset:a_off atIndex:1];
[enc setBuffer:b offset:b_off atIndex:2];
[enc setBuffer:out offset:out_off atIndex:3];
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)args->ne1,
(NSUInteger)args->ne2,
(NSUInteger)args->ne3)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static ds4_gpu_bin_args ds4_gpu_make_bin_rowwise_scalar_args(uint32_t width, uint32_t rows) {
const uint64_t lhs_row_bytes = (uint64_t)width * sizeof(float);
const uint64_t rhs_row_bytes = sizeof(float);
return (ds4_gpu_bin_args) {
.ne00 = (int32_t)width,
.ne01 = (int32_t)rows,
.ne02 = 1,
.ne03 = 1,
.nb00 = sizeof(float),
.nb01 = lhs_row_bytes,
.nb02 = (uint64_t)rows * lhs_row_bytes,
.nb03 = (uint64_t)rows * lhs_row_bytes,
.ne10 = 1,
.ne11 = (int32_t)rows,
.ne12 = 1,
.ne13 = 1,
.nb10 = sizeof(float),
.nb11 = rhs_row_bytes,
.nb12 = (uint64_t)rows * rhs_row_bytes,
.nb13 = (uint64_t)rows * rhs_row_bytes,
.ne0 = (int32_t)width,
.ne1 = (int32_t)rows,
.ne2 = 1,
.ne3 = 1,
.nb0 = sizeof(float),
.nb1 = lhs_row_bytes,
.nb2 = (uint64_t)rows * lhs_row_bytes,
.nb3 = (uint64_t)rows * lhs_row_bytes,
.offs = 0,
.o1 = { 0 },
};
}
static ds4_gpu_mul_mv_id_args ds4_gpu_make_mul_mv_id_args(
uint32_t src0_cols,
uint32_t src0_rows,
uint32_t src0_experts,
uint64_t src0_row_bytes,
uint64_t src0_expert_bytes,
uint32_t src1_expert_rows,
uint32_t selected_experts,
uint32_t n_tokens,
uint32_t nr0) {
const uint64_t src1_row_bytes = (uint64_t)src0_cols * sizeof(float);
const uint64_t src0_blocks = src0_cols / 256u;
const uint64_t src0_block_bytes = src0_blocks ? src0_row_bytes / src0_blocks : 1u;
return (ds4_gpu_mul_mv_id_args) {
.nei0 = (int32_t)selected_experts,
.nei1 = (int32_t)n_tokens,
.nbi1 = (uint64_t)selected_experts * sizeof(int32_t),
.ne00 = (int32_t)src0_cols,
.ne01 = (int32_t)src0_rows,
.ne02 = (int32_t)src0_experts,
.nb00 = src0_block_bytes,
.nb01 = src0_row_bytes,
.nb02 = src0_expert_bytes,
.ne10 = (int32_t)src0_cols,
.ne11 = (int32_t)src1_expert_rows,
.ne12 = (int32_t)n_tokens,
.ne13 = 1,
.nb10 = sizeof(float),
.nb11 = src1_row_bytes,
.nb12 = (uint64_t)src1_expert_rows * src1_row_bytes,
.ne0 = (int32_t)src0_rows,
.ne1 = (int32_t)selected_experts,
.nb1 = (uint64_t)src0_rows * sizeof(float),
.nr0 = (int32_t)nr0,
};
}
static ds4_gpu_mul_mm_id_map_args ds4_gpu_make_mul_mm_id_map_args(
uint32_t src0_cols,
uint32_t src0_experts,
uint32_t src1_expert_rows,
uint32_t selected_experts,
uint32_t n_tokens) {
const uint64_t src1_row_bytes = (uint64_t)src0_cols * sizeof(float);
return (ds4_gpu_mul_mm_id_map_args) {
.ne02 = (int32_t)src0_experts,
.ne10 = (int32_t)src0_cols,
.ne11 = (int32_t)src1_expert_rows,
.nb11 = src1_row_bytes,
.nb12 = (uint64_t)src1_expert_rows * src1_row_bytes,
.ne21 = (int32_t)n_tokens,
.ne20 = (int32_t)selected_experts,
.nb21 = (uint64_t)selected_experts * sizeof(int32_t),
};
}
static ds4_gpu_mul_mm_id_args ds4_gpu_make_mul_mm_id_args(
uint32_t src0_cols,
uint32_t src0_rows,
uint32_t src0_experts,
uint64_t src0_row_bytes,
uint64_t src0_expert_bytes,
uint32_t src1_expert_rows,
uint32_t selected_experts,
uint32_t n_tokens) {
return ds4_gpu_make_mul_mm_id_args_src1_size(src0_cols,
src0_rows,
src0_experts,
src0_row_bytes,
src0_expert_bytes,
src1_expert_rows,
selected_experts,
n_tokens,
sizeof(float));
}
static ds4_gpu_mul_mm_id_args ds4_gpu_make_mul_mm_id_args_src1_size(
uint32_t src0_cols,
uint32_t src0_rows,
uint32_t src0_experts,
uint64_t src0_row_bytes,
uint64_t src0_expert_bytes,
uint32_t src1_expert_rows,
uint32_t selected_experts,
uint32_t n_tokens,
uint32_t src1_elem_size) {
const uint64_t src1_row_bytes = (uint64_t)src0_cols * src1_elem_size;
return (ds4_gpu_mul_mm_id_args) {
.ne00 = (int32_t)src0_cols,
.ne02 = (int32_t)src0_experts,
.nb01 = src0_row_bytes,
.nb02 = src0_expert_bytes,
.nb03 = (uint64_t)src0_experts * src0_expert_bytes,
.ne11 = (int32_t)src1_expert_rows,
.nb10 = src1_elem_size,
.nb11 = src1_row_bytes,
.nb12 = (uint64_t)src1_expert_rows * src1_row_bytes,
.nb13 = (uint64_t)n_tokens * (uint64_t)src1_expert_rows * src1_row_bytes,
.ne20 = (int32_t)selected_experts,
.ne21 = (int32_t)n_tokens,
.ne0 = (int32_t)src0_rows,
.ne1 = (int32_t)selected_experts,
.r2 = 1,
.r3 = 1,
};
}
static uint32_t ds4_gpu_routed_mv_nr0(uint32_t type) {
switch (type) {
case DS4_METAL_TENSOR_Q8_0: return 2;
case DS4_METAL_TENSOR_Q8_K: return 2;
case DS4_METAL_TENSOR_Q4_K: return 2;
case DS4_METAL_TENSOR_MXFP4: return 2;
case DS4_METAL_TENSOR_Q2_K:
case DS4_METAL_TENSOR_IQ2_XXS: return 4;
default: return 0;
}
}
static const char *ds4_gpu_metal_tensor_type_name(uint32_t type) {
switch (type) {
case DS4_METAL_TENSOR_IQ2_XXS: return "iq2_xxs";
case DS4_METAL_TENSOR_Q2_K: return "q2_k";
case DS4_METAL_TENSOR_Q4_K: return "q4_k";
case DS4_METAL_TENSOR_Q5_K: return "q5_k";
case DS4_METAL_TENSOR_Q6_K: return "q6_k";
case DS4_METAL_TENSOR_MXFP4: return "mxfp4";
default: return "unknown";
}
}
static const char *ds4_gpu_trim_env_value(const char *env, size_t *len_out) {
if (len_out) *len_out = 0;
if (!env) return NULL;
while (isspace((unsigned char)*env)) env++;
size_t n = strlen(env);
while (n > 0 && isspace((unsigned char)env[n - 1])) n--;
if (len_out) *len_out = n;
return env;
}
static bool ds4_gpu_profile_layer_value_match(const char *env, uint32_t layer_index) {
size_t env_len = 0;
env = ds4_gpu_trim_env_value(env, &env_len);
if (!env || env_len == 0) return true;
if (ds4_gpu_env_value_eq(env, env_len, "all")) return true;
const char *p = env;
const char *end_env = env + env_len;
while (p < end_env) {
while (p < end_env && (*p == ' ' || *p == '\t' || *p == ',')) p++;
if (p >= end_env) break;
char *end = NULL;
const unsigned long first = strtoul(p, &end, 10);
if (end == p || end > end_env || first > UINT32_MAX) return false;
unsigned long last = first;
p = end;
if (p < end_env && *p == '-') {
p++;
last = strtoul(p, &end, 10);
if (end == p || end > end_env || last > UINT32_MAX) return false;
p = end;
}
if (first <= layer_index && layer_index <= last) return true;
while (p < end_env && (*p == ' ' || *p == '\t')) p++;
if (p < end_env && *p != ',') return false;
}
return false;
}
static bool ds4_gpu_stage_profile_enabled_for_layer(const char *flag_env_name,
const char *layer_env_name,
uint32_t layer_index) {
size_t flag_len = 0;
const char *flag = ds4_gpu_trim_env_value(getenv(flag_env_name), &flag_len);
if (!flag) return false;
size_t layer_len = 0;
const char *layer = ds4_gpu_trim_env_value(getenv(layer_env_name), &layer_len);
const bool has_layer_filter = layer && layer_len != 0;
if (flag_len != 0) {
if (ds4_gpu_env_value_eq(flag, flag_len, "0") ||
ds4_gpu_env_value_eq(flag, flag_len, "false") ||
ds4_gpu_env_value_eq(flag, flag_len, "no") ||
ds4_gpu_env_value_eq(flag, flag_len, "off")) {
return false;
}
if (!has_layer_filter &&
!ds4_gpu_env_value_eq(flag, flag_len, "1") &&
!ds4_gpu_env_value_eq(flag, flag_len, "true") &&
!ds4_gpu_env_value_eq(flag, flag_len, "yes") &&
!ds4_gpu_env_value_eq(flag, flag_len, "on") &&
!ds4_gpu_env_value_eq(flag, flag_len, "all")) {
return ds4_gpu_profile_layer_value_match(flag, layer_index);
}
}
return ds4_gpu_profile_layer_value_match(layer, layer_index);
}
static NSUInteger ds4_gpu_routed_mv_smem(uint32_t type) {
if (type == DS4_METAL_TENSOR_Q8_0) {
return 32u * 2u * sizeof(float);
}
if (type == DS4_METAL_TENSOR_IQ2_XXS) {
return 256u * sizeof(uint64_t) + 128u * sizeof(uint8_t);
}
if (type == DS4_METAL_TENSOR_MXFP4) {
return 32u * sizeof(float);
}
return 0;
}
static NSUInteger ds4_gpu_routed_mv_nsg(uint32_t type) {
return type == DS4_METAL_TENSOR_Q8_0 ? 4u : 2u;
}
static bool ds4_gpu_routed_mv_rows_per_group_is_nr0(uint32_t type) {
return type == DS4_METAL_TENSOR_Q8_0;
}
static id<MTLComputePipelineState> ds4_gpu_routed_mv_pipeline(uint32_t type) {
switch (type) {
case DS4_METAL_TENSOR_Q8_0:
return ds4_gpu_get_mul_mv_pipeline("kernel_mul_mv_id_q8_0_f32", 4);
case DS4_METAL_TENSOR_Q8_K:
return ds4_gpu_get_mul_mv_pipeline("kernel_mul_mv_id_q8_K_f32", 2);
case DS4_METAL_TENSOR_IQ2_XXS: return g_moe_mul_mv_id_iq2_xxs_pipeline;
case DS4_METAL_TENSOR_Q2_K: return g_moe_mul_mv_id_q2_k_pipeline;
case DS4_METAL_TENSOR_Q4_K: return g_moe_mul_mv_id_q4_k_pipeline;
case DS4_METAL_TENSOR_MXFP4: return g_moe_mul_mv_id_mxfp4_pipeline;
default: return nil;
}
}
/* TensorOps routed-MoE prefill uses bits 0/1/2 for gate/up/down. Unsupported
* tensor types keep their established kernels. --quality and the global
* Metal4 comparison switch retain the reference path. */
static int ds4_gpu_routed_mm_mpp_mask(void) {
return ds4_gpu_mpp_available() ? 7 : 0;
}
static id<MTLComputePipelineState> ds4_gpu_routed_mm_pipeline(uint32_t type) {
switch (type) {
case DS4_METAL_TENSOR_Q8_0:
return ds4_gpu_get_mul_mm_id_pipeline("kernel_mul_mm_id_q8_0_f32", false);
case DS4_METAL_TENSOR_Q8_K:
return ds4_gpu_get_mul_mm_id_pipeline("kernel_mul_mm_id_q8_K_f32", false);
case DS4_METAL_TENSOR_IQ2_XXS:
return ds4_gpu_get_mul_mm_id_pipeline("kernel_mul_mm_id_iq2_xxs_f32", false);
case DS4_METAL_TENSOR_Q2_K:
return ds4_gpu_get_mul_mm_id_pipeline("kernel_mul_mm_id_q2_K_f32", false);
case DS4_METAL_TENSOR_Q4_K:
return ds4_gpu_get_mul_mm_id_pipeline("kernel_mul_mm_id_q4_K_f32", false);
case DS4_METAL_TENSOR_Q5_K:
return ds4_gpu_get_mul_mm_id_pipeline("kernel_mul_mm_id_q5_K_f32", false);
case DS4_METAL_TENSOR_Q6_K:
return ds4_gpu_get_mul_mm_id_pipeline("kernel_mul_mm_id_q6_K_f32", false);
case DS4_METAL_TENSOR_MXFP4:
return ds4_gpu_get_mul_mm_id_pipeline("kernel_mul_mm_id_mxfp4_f32", false);
default:
return nil;
}
}
static id<MTLComputePipelineState> ds4_gpu_routed_mm_addr_pipeline(uint32_t type) {
switch (type) {
case DS4_METAL_TENSOR_Q2_K:
return ds4_gpu_get_mul_mm_id_pipeline("kernel_mul_mm_id_addr_q2_K_f32", false);
case DS4_METAL_TENSOR_Q4_K:
return ds4_gpu_get_mul_mm_id_pipeline("kernel_mul_mm_id_addr_q4_K_f32", false);
case DS4_METAL_TENSOR_MXFP4:
return ds4_gpu_get_mul_mm_id_pipeline("kernel_mul_mm_id_addr_mxfp4_f32", false);
default:
return nil;
}
}
static id<MTLComputePipelineState> ds4_gpu_routed_mm_f16_rhs_pipeline(uint32_t type) {
switch (type) {
case DS4_METAL_TENSOR_Q8_0:
return ds4_gpu_get_mul_mm_id_pipeline("kernel_mul_mm_id_q8_0_f16", false);
case DS4_METAL_TENSOR_Q8_K:
return ds4_gpu_get_mul_mm_id_pipeline("kernel_mul_mm_id_q8_K_f16", false);
case DS4_METAL_TENSOR_IQ2_XXS:
return ds4_gpu_get_mul_mm_id_pipeline("kernel_mul_mm_id_iq2_xxs_f16", false);
case DS4_METAL_TENSOR_Q2_K:
return ds4_gpu_get_mul_mm_id_pipeline("kernel_mul_mm_id_q2_K_f16", false);
case DS4_METAL_TENSOR_Q4_K:
return ds4_gpu_get_mul_mm_id_pipeline("kernel_mul_mm_id_q4_K_f16", false);
case DS4_METAL_TENSOR_Q5_K:
return ds4_gpu_get_mul_mm_id_pipeline("kernel_mul_mm_id_q5_K_f16", false);
case DS4_METAL_TENSOR_Q6_K:
return ds4_gpu_get_mul_mm_id_pipeline("kernel_mul_mm_id_q6_K_f16", false);
case DS4_METAL_TENSOR_MXFP4:
return ds4_gpu_get_mul_mm_id_pipeline("kernel_mul_mm_id_mxfp4_f16", false);
default:
return nil;
}
}
static id<MTLComputePipelineState> ds4_gpu_routed_mm_addr_f16_rhs_pipeline(uint32_t type) {
switch (type) {
case DS4_METAL_TENSOR_Q2_K:
return ds4_gpu_get_mul_mm_id_pipeline("kernel_mul_mm_id_addr_q2_K_f16", false);
case DS4_METAL_TENSOR_Q4_K:
return ds4_gpu_get_mul_mm_id_pipeline("kernel_mul_mm_id_addr_q4_K_f16", false);
case DS4_METAL_TENSOR_MXFP4:
return ds4_gpu_get_mul_mm_id_pipeline("kernel_mul_mm_id_addr_mxfp4_f16", false);
default:
return nil;
}
}
static int ds4_gpu_encode_mul_mv_id(
id<MTLCommandBuffer> cb,
id<MTLComputePipelineState> pipeline,
const ds4_gpu_mul_mv_id_args *args,
id<MTLBuffer> src0,
NSUInteger src0_off,
id<MTLBuffer> src1,
NSUInteger src1_off,
id<MTLBuffer> dst,
NSUInteger dst_off,
id<MTLBuffer> ids,
NSUInteger ids_off,
NSUInteger threadgroup_bytes,
NSUInteger nsg,
bool rows_per_group_is_nr0) {
if (!cb || !pipeline || !args || !src0 || !src1 || !dst || !ids ||
args->ne00 <= 0 || args->ne01 <= 0 || args->nei0 <= 0 || args->nei1 <= 0) {
return 0;
}
const NSUInteger nr0 = (NSUInteger)args->nr0;
const NSUInteger rows_per_group = rows_per_group_is_nr0 ? nr0 : nr0 * nsg;
const NSUInteger row_groups = ((NSUInteger)args->ne01 + rows_per_group - 1u) / rows_per_group;
const NSUInteger pairs = (NSUInteger)args->nei0 * (NSUInteger)args->nei1;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:args length:sizeof(*args) atIndex:0];
[enc setBuffer:src0 offset:src0_off atIndex:1];
[enc setBuffer:src1 offset:src1_off atIndex:2];
[enc setBuffer:dst offset:dst_off atIndex:3];
[enc setBuffer:ids offset:ids_off atIndex:4];
if (threadgroup_bytes != 0) {
[enc setThreadgroupMemoryLength:threadgroup_bytes atIndex:0];
}
[enc dispatchThreadgroups:MTLSizeMake(row_groups, 1, pairs)
threadsPerThreadgroup:MTLSizeMake(32, nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_attn_out_low_q8_direct(
id<MTLCommandBuffer> cb,
id<MTLComputePipelineState> pipeline,
const ds4_gpu_mul_mv_id_args *args,
id<MTLBuffer> src0,
NSUInteger src0_off,
id<MTLBuffer> src1,
NSUInteger src1_off,
id<MTLBuffer> dst,
NSUInteger dst_off,
NSUInteger threadgroup_bytes,
NSUInteger nsg,
bool rows_per_group_is_nr0) {
if (!cb || !pipeline || !args || !src0 || !src1 || !dst ||
args->ne00 <= 0 || args->ne01 <= 0 || args->nei0 <= 0 || args->nei1 <= 0) {
return 0;
}
/* Two row conventions in the classic matvec family: Q8_0 k-splits one
* nr0-row group across all nsg simdgroups (cross-simdgroup reduce), so a
* threadgroup covers nr0 rows; Q4_K gives each simdgroup its own nr0
* rows, covering nr0*nsg. Dispatching Q8 with the Q4 stride leaves
* (nsg-1)/nsg of the output rows unwritten. */
const NSUInteger rows_per_group = rows_per_group_is_nr0 ?
(NSUInteger)args->nr0 : (NSUInteger)args->nr0 * nsg;
const NSUInteger row_groups = ((NSUInteger)args->ne01 + rows_per_group - 1u) / rows_per_group;
const NSUInteger pairs = (NSUInteger)args->nei0 * (NSUInteger)args->nei1;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:args length:sizeof(*args) atIndex:0];
[enc setBuffer:src0 offset:src0_off atIndex:1];
[enc setBuffer:src1 offset:src1_off atIndex:2];
[enc setBuffer:dst offset:dst_off atIndex:3];
if (threadgroup_bytes != 0) {
[enc setThreadgroupMemoryLength:threadgroup_bytes atIndex:0];
}
[enc dispatchThreadgroups:MTLSizeMake(row_groups, 1, pairs)
threadsPerThreadgroup:MTLSizeMake(32, nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_mul_mv_id_pair(
id<MTLCommandBuffer> cb,
id<MTLComputePipelineState> pipeline,
const ds4_gpu_mul_mv_id_args *args,
id<MTLBuffer> src0_a,
NSUInteger src0_a_off,
id<MTLBuffer> src0_b,
NSUInteger src0_b_off,
id<MTLBuffer> src1,
NSUInteger src1_off,
id<MTLBuffer> dst_a,
NSUInteger dst_a_off,
id<MTLBuffer> dst_b,
NSUInteger dst_b_off,
id<MTLBuffer> ids,
NSUInteger ids_off,
NSUInteger threadgroup_bytes,
NSUInteger nsg,
bool rows_per_group_is_nr0) {
if (!cb || !pipeline || !args || !src0_a || !src0_b || !src1 || !dst_a || !dst_b || !ids ||
args->ne00 <= 0 || args->ne01 <= 0 || args->nei0 <= 0 || args->nei1 <= 0) {
return 0;
}
const NSUInteger nr0 = (NSUInteger)args->nr0;
const NSUInteger rows_per_group = rows_per_group_is_nr0 ? nr0 : nr0 * nsg;
const NSUInteger row_groups = ((NSUInteger)args->ne01 + rows_per_group - 1u) / rows_per_group;
const NSUInteger pairs = (NSUInteger)args->nei0 * (NSUInteger)args->nei1;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:args length:sizeof(*args) atIndex:0];
[enc setBuffer:src0_a offset:src0_a_off atIndex:1];
[enc setBuffer:src0_b offset:src0_b_off atIndex:2];
[enc setBuffer:src1 offset:src1_off atIndex:3];
[enc setBuffer:dst_a offset:dst_a_off atIndex:4];
[enc setBuffer:dst_b offset:dst_b_off atIndex:5];
[enc setBuffer:ids offset:ids_off atIndex:6];
if (threadgroup_bytes != 0) {
[enc setThreadgroupMemoryLength:threadgroup_bytes atIndex:0];
}
[enc dispatchThreadgroups:MTLSizeMake(row_groups, 1, pairs)
threadsPerThreadgroup:MTLSizeMake(32, nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_mul_mv_id_pair_swiglu(
id<MTLCommandBuffer> cb,
id<MTLComputePipelineState> pipeline,
const ds4_gpu_mul_mv_id_args *args,
const ds4_gpu_dsv4_moe_swiglu_weight_args *act,
id<MTLBuffer> src0_a,
NSUInteger src0_a_off,
id<MTLBuffer> src0_b,
NSUInteger src0_b_off,
id<MTLBuffer> src1,
NSUInteger src1_off,
id<MTLBuffer> dst_a,
NSUInteger dst_a_off,
id<MTLBuffer> dst_b,
NSUInteger dst_b_off,
id<MTLBuffer> dst_mid,
NSUInteger dst_mid_off,
id<MTLBuffer> ids,
NSUInteger ids_off,
id<MTLBuffer> weights,
NSUInteger weights_off,
NSUInteger threadgroup_bytes,
NSUInteger nsg,
bool rows_per_group_is_nr0) {
if (!cb || !pipeline || !args || !act ||
!src0_a || !src0_b || !src1 || !dst_a || !dst_b || !dst_mid || !ids || !weights ||
args->ne00 <= 0 || args->ne01 <= 0 || args->nei0 <= 0 || args->nei1 <= 0) {
return 0;
}
const NSUInteger nr0 = (NSUInteger)args->nr0;
const NSUInteger rows_per_group = rows_per_group_is_nr0 ? nr0 : nr0 * nsg;
const NSUInteger row_groups = ((NSUInteger)args->ne01 + rows_per_group - 1u) / rows_per_group;
const NSUInteger pairs = (NSUInteger)args->nei0 * (NSUInteger)args->nei1;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:args length:sizeof(*args) atIndex:0];
[enc setBytes:act length:sizeof(*act) atIndex:1];
[enc setBuffer:src0_a offset:src0_a_off atIndex:2];
[enc setBuffer:src0_b offset:src0_b_off atIndex:3];
[enc setBuffer:src1 offset:src1_off atIndex:4];
[enc setBuffer:dst_a offset:dst_a_off atIndex:5];
[enc setBuffer:dst_b offset:dst_b_off atIndex:6];
[enc setBuffer:dst_mid offset:dst_mid_off atIndex:7];
[enc setBuffer:ids offset:ids_off atIndex:8];
[enc setBuffer:weights offset:weights_off atIndex:9];
if (threadgroup_bytes != 0) {
[enc setThreadgroupMemoryLength:threadgroup_bytes atIndex:0];
}
[enc dispatchThreadgroups:MTLSizeMake(row_groups, 1, pairs)
threadsPerThreadgroup:MTLSizeMake(32, nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
/* A few rows' pair/SwiGLU with the distinct experts read once: the plan
* dispatch, then one threadgroup per distinct expert with a simdgroup per
* (row, slot) pair (kernel_mul_mv_id_mxfp4_pair_swiglu_dedup_f32). */
static int ds4_gpu_encode_mul_mv_id_pair_swiglu_dedup(
id<MTLCommandBuffer> cb,
const ds4_gpu_mul_mv_id_args *args,
const ds4_gpu_dsv4_moe_swiglu_weight_args *act,
id<MTLBuffer> src0_a, NSUInteger src0_a_off,
id<MTLBuffer> src0_b, NSUInteger src0_b_off,
id<MTLBuffer> src1, NSUInteger src1_off,
id<MTLBuffer> dst_a, NSUInteger dst_a_off,
id<MTLBuffer> dst_b, NSUInteger dst_b_off,
id<MTLBuffer> dst_mid, NSUInteger dst_mid_off,
id<MTLBuffer> ids, NSUInteger ids_off,
id<MTLBuffer> weights, NSUInteger weights_off,
NSUInteger threadgroup_bytes, uint32_t rows) {
static id<MTLComputePipelineState> plan_pipeline, pair_pipeline;
static id<MTLBuffer> plan;
if (!cb || !args || !act || !rows || rows > 8u || args->nei0 <= 0 || args->nei0 > 8 ||
args->nei1 != (int32_t)rows || (uint64_t)args->nei0 * rows > 64u) return 0;
if (!plan_pipeline) plan_pipeline = ds4_gpu_get_pipeline("kernel_dsv41_moe_dedup_plan");
if (!pair_pipeline) pair_pipeline = ds4_gpu_get_pipeline("kernel_mul_mv_id_mxfp4_pair_swiglu_dedup_f32");
if (!plan) plan = [g_device newBufferWithLength:4096 options:MTLResourceStorageModePrivate];
if (!plan_pipeline || !pair_pipeline || !plan) return 0;
const NSUInteger nr0 = (NSUInteger)args->nr0;
const NSUInteger row_groups = ((NSUInteger)args->ne01 + nr0 - 1u) / nr0;
const NSUInteger pairs = (NSUInteger)args->nei0 * (NSUInteger)args->nei1;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:plan_pipeline];
[enc setBytes:args length:sizeof(*args) atIndex:0];
[enc setBuffer:ids offset:ids_off atIndex:1];
[enc setBuffer:plan offset:0 atIndex:2];
[enc dispatchThreadgroups:MTLSizeMake(1, 1, 1) threadsPerThreadgroup:MTLSizeMake(32, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pair_pipeline];
[enc setBytes:args length:sizeof(*args) atIndex:0];
[enc setBytes:act length:sizeof(*act) atIndex:1];
[enc setBuffer:src0_a offset:src0_a_off atIndex:2];
[enc setBuffer:src0_b offset:src0_b_off atIndex:3];
[enc setBuffer:src1 offset:src1_off atIndex:4];
[enc setBuffer:dst_a offset:dst_a_off atIndex:5];
[enc setBuffer:dst_b offset:dst_b_off atIndex:6];
[enc setBuffer:dst_mid offset:dst_mid_off atIndex:7];
[enc setBuffer:ids offset:ids_off atIndex:8];
[enc setBuffer:weights offset:weights_off atIndex:9];
[enc setBuffer:plan offset:0 atIndex:10];
if (threadgroup_bytes != 0) [enc setThreadgroupMemoryLength:threadgroup_bytes atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(row_groups, 1, pairs)
threadsPerThreadgroup:MTLSizeMake(32, rows, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_mul_mv_table_q4_pair_swiglu(
id<MTLCommandBuffer> cb,
id<MTLComputePipelineState> pipeline,
const ds4_gpu_mul_mv_id_args *args,
const ds4_gpu_dsv4_moe_swiglu_weight_args *act,
DS4MetalQ4ExpertTable *gate_table,
DS4MetalQ4ExpertTable *up_table,
id<MTLBuffer> src1,
NSUInteger src1_off,
id<MTLBuffer> dst_a,
NSUInteger dst_a_off,
id<MTLBuffer> dst_b,
NSUInteger dst_b_off,
id<MTLBuffer> dst_mid,
NSUInteger dst_mid_off,
id<MTLBuffer> ids,
NSUInteger ids_off,
id<MTLBuffer> weights,
NSUInteger weights_off,
NSUInteger threadgroup_bytes,
NSUInteger nsg,
bool rows_per_group_is_nr0,
bool queue_residency) {
if (!cb || !pipeline || !args || !act || !gate_table || !up_table ||
!gate_table.argumentBuffer || !up_table.argumentBuffer ||
!src1 || !dst_a || !dst_b || !dst_mid || !ids || !weights ||
args->ne00 <= 0 || args->ne01 <= 0 || args->nei0 != 6 || args->nei1 <= 0 ||
args->ne02 <= 0 || args->ne02 > 384) {
return 0;
}
const NSUInteger nr0 = (NSUInteger)args->nr0;
const NSUInteger rows_per_group = rows_per_group_is_nr0 ? nr0 : nr0 * nsg;
const NSUInteger row_groups = ((NSUInteger)args->ne01 + rows_per_group - 1u) / rows_per_group;
const NSUInteger pairs = (NSUInteger)args->nei0 * (NSUInteger)args->nei1;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:args length:sizeof(*args) atIndex:0];
[enc setBytes:act length:sizeof(*act) atIndex:1];
[enc setBuffer:gate_table.argumentBuffer offset:0 atIndex:2];
[enc setBuffer:up_table.argumentBuffer offset:0 atIndex:3];
[enc setBuffer:src1 offset:src1_off atIndex:4];
[enc setBuffer:dst_a offset:dst_a_off atIndex:5];
[enc setBuffer:dst_b offset:dst_b_off atIndex:6];
[enc setBuffer:dst_mid offset:dst_mid_off atIndex:7];
[enc setBuffer:ids offset:ids_off atIndex:8];
[enc setBuffer:weights offset:weights_off atIndex:9];
if (!ds4_gpu_bind_q4_expert_table_anchors(enc, gate_table, 10, 6) ||
!ds4_gpu_bind_q4_expert_table_anchors(enc, up_table, 16, 6)) {
ds4_gpu_end_compute_encoder(cb, enc);
return 0;
}
ds4_gpu_use_q4_expert_table_resources(cb, enc, gate_table, queue_residency);
ds4_gpu_use_q4_expert_table_resources(cb, enc, up_table, queue_residency);
if (threadgroup_bytes != 0) {
[enc setThreadgroupMemoryLength:threadgroup_bytes atIndex:0];
}
[enc dispatchThreadgroups:MTLSizeMake(row_groups, 1, pairs)
threadsPerThreadgroup:MTLSizeMake(32, nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_mul_mv_addr_q4_pair_swiglu(
id<MTLCommandBuffer> cb,
id<MTLComputePipelineState> pipeline,
const ds4_gpu_mul_mv_id_args *args,
const ds4_gpu_dsv4_moe_swiglu_weight_args *act,
DS4MetalQ4ExpertTable *gate_table,
DS4MetalQ4ExpertTable *up_table,
id<MTLBuffer> src1,
NSUInteger src1_off,
id<MTLBuffer> dst_a,
NSUInteger dst_a_off,
id<MTLBuffer> dst_b,
NSUInteger dst_b_off,
id<MTLBuffer> dst_mid,
NSUInteger dst_mid_off,
id<MTLBuffer> ids,
NSUInteger ids_off,
id<MTLBuffer> weights,
NSUInteger weights_off,
NSUInteger threadgroup_bytes,
NSUInteger nsg,
bool rows_per_group_is_nr0) {
if (!cb || !pipeline || !args || !act || !gate_table || !up_table ||
!gate_table.addressBuffer || !up_table.addressBuffer ||
!src1 || !dst_a || !dst_b || !dst_mid || !ids || !weights ||
args->ne00 <= 0 || args->ne01 <= 0 || args->nei0 != 6 || args->nei1 <= 0 ||
args->ne02 <= 0 || args->ne02 > 384) {
return 0;
}
const NSUInteger nr0 = (NSUInteger)args->nr0;
const NSUInteger rows_per_group = rows_per_group_is_nr0 ? nr0 : nr0 * nsg;
const NSUInteger row_groups = ((NSUInteger)args->ne01 + rows_per_group - 1u) / rows_per_group;
const NSUInteger pairs = (NSUInteger)args->nei0 * (NSUInteger)args->nei1;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:args length:sizeof(*args) atIndex:0];
[enc setBytes:act length:sizeof(*act) atIndex:1];
[enc setBuffer:gate_table.addressBuffer offset:0 atIndex:2];
[enc setBuffer:up_table.addressBuffer offset:0 atIndex:3];
[enc setBuffer:src1 offset:src1_off atIndex:4];
[enc setBuffer:dst_a offset:dst_a_off atIndex:5];
[enc setBuffer:dst_b offset:dst_b_off atIndex:6];
[enc setBuffer:dst_mid offset:dst_mid_off atIndex:7];
[enc setBuffer:ids offset:ids_off atIndex:8];
[enc setBuffer:weights offset:weights_off atIndex:9];
if (!ds4_gpu_bind_q4_expert_table_anchors(enc, gate_table, 10, 6) ||
!ds4_gpu_bind_q4_expert_table_anchors(enc, up_table, 16, 6)) {
ds4_gpu_end_compute_encoder(cb, enc);
return 0;
}
if (getenv("DS4_METAL_Q4_ADDR_USE_RESOURCES") != NULL) {
ds4_gpu_use_q4_expert_table_resources(cb, enc, gate_table, false);
ds4_gpu_use_q4_expert_table_resources(cb, enc, up_table, false);
}
if (threadgroup_bytes != 0) {
[enc setThreadgroupMemoryLength:threadgroup_bytes atIndex:0];
}
[enc dispatchThreadgroups:MTLSizeMake(row_groups, 1, pairs)
threadsPerThreadgroup:MTLSizeMake(32, nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_mul_mv_table_q4_sum6(
id<MTLCommandBuffer> cb,
id<MTLComputePipelineState> pipeline,
const ds4_gpu_mul_mv_id_args *args,
DS4MetalQ4ExpertTable *table,
id<MTLBuffer> src1,
NSUInteger src1_off,
id<MTLBuffer> dst,
NSUInteger dst_off,
id<MTLBuffer> ids,
NSUInteger ids_off,
NSUInteger threadgroup_bytes,
NSUInteger nsg,
bool queue_residency) {
if (!cb || !pipeline || !args || !table || !table.argumentBuffer ||
!src1 || !dst || !ids ||
args->ne00 <= 0 || args->ne01 <= 0 || args->nei0 != 6 || args->nei1 <= 0 ||
args->ne02 <= 0 || args->ne02 > 384) {
return 0;
}
const NSUInteger rows_per_group = (NSUInteger)args->nr0 * nsg;
const NSUInteger row_groups = ((NSUInteger)args->ne01 + rows_per_group - 1u) / rows_per_group;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:args length:sizeof(*args) atIndex:0];
[enc setBuffer:table.argumentBuffer offset:0 atIndex:1];
[enc setBuffer:src1 offset:src1_off atIndex:2];
[enc setBuffer:dst offset:dst_off atIndex:3];
[enc setBuffer:ids offset:ids_off atIndex:4];
if (!ds4_gpu_bind_q4_expert_table_anchors(enc, table, 5, 6)) {
ds4_gpu_end_compute_encoder(cb, enc);
return 0;
}
ds4_gpu_use_q4_expert_table_resources(cb, enc, table, queue_residency);
if (threadgroup_bytes != 0) {
[enc setThreadgroupMemoryLength:threadgroup_bytes atIndex:0];
}
[enc dispatchThreadgroups:MTLSizeMake(row_groups, (NSUInteger)args->nei1, 1)
threadsPerThreadgroup:MTLSizeMake(32, nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_mul_mv_addr_q4_sum6(
id<MTLCommandBuffer> cb,
id<MTLComputePipelineState> pipeline,
const ds4_gpu_mul_mv_id_args *args,
DS4MetalQ4ExpertTable *table,
id<MTLBuffer> src1,
NSUInteger src1_off,
id<MTLBuffer> dst,
NSUInteger dst_off,
id<MTLBuffer> ids,
NSUInteger ids_off,
NSUInteger threadgroup_bytes,
NSUInteger nsg) {
if (!cb || !pipeline || !args || !table || !table.addressBuffer ||
!src1 || !dst || !ids ||
args->ne00 <= 0 || args->ne01 <= 0 || args->nei0 != 6 || args->nei1 <= 0 ||
args->ne02 <= 0 || args->ne02 > 384) {
return 0;
}
const NSUInteger rows_per_group = (NSUInteger)args->nr0 * nsg;
const NSUInteger row_groups = ((NSUInteger)args->ne01 + rows_per_group - 1u) / rows_per_group;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:args length:sizeof(*args) atIndex:0];
[enc setBuffer:table.addressBuffer offset:0 atIndex:1];
[enc setBuffer:src1 offset:src1_off atIndex:2];
[enc setBuffer:dst offset:dst_off atIndex:3];
[enc setBuffer:ids offset:ids_off atIndex:4];
if (!ds4_gpu_bind_q4_expert_table_anchors(enc, table, 5, 6)) {
ds4_gpu_end_compute_encoder(cb, enc);
return 0;
}
if (getenv("DS4_METAL_Q4_ADDR_USE_RESOURCES") != NULL) {
ds4_gpu_use_q4_expert_table_resources(cb, enc, table, false);
}
if (threadgroup_bytes != 0) {
[enc setThreadgroupMemoryLength:threadgroup_bytes atIndex:0];
}
[enc dispatchThreadgroups:MTLSizeMake(row_groups, (NSUInteger)args->nei1, 1)
threadsPerThreadgroup:MTLSizeMake(32, nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static uint32_t ds4_gpu_q4_expert_group_size(uint32_t n_total_expert) {
uint32_t group_size = 32;
const char *env = getenv("DS4_METAL_Q4_EXPERT_GROUP_SIZE");
if (env && env[0]) {
char *end = NULL;
unsigned long v = strtoul(env, &end, 10);
if (end != env && *end == '\0' && v > 0 && v <= UINT32_MAX) {
group_size = (uint32_t)v;
}
}
if (group_size == 0) group_size = 1;
if (group_size > n_total_expert) group_size = n_total_expert;
return group_size;
}
static int ds4_gpu_encode_mul_mv_group_q4_pair_swiglu(
id<MTLCommandBuffer> cb,
id<MTLComputePipelineState> pipeline,
const ds4_gpu_mul_mv_id_args *args,
const ds4_gpu_dsv4_moe_swiglu_weight_args *act,
const ds4_gpu_moe_expert_group_args *group,
id<MTLBuffer> src0_a,
NSUInteger src0_a_off,
id<MTLBuffer> src0_b,
NSUInteger src0_b_off,
id<MTLBuffer> src1,
NSUInteger src1_off,
id<MTLBuffer> dst_a,
NSUInteger dst_a_off,
id<MTLBuffer> dst_b,
NSUInteger dst_b_off,
id<MTLBuffer> dst_mid,
NSUInteger dst_mid_off,
id<MTLBuffer> ids,
NSUInteger ids_off,
id<MTLBuffer> weights,
NSUInteger weights_off,
NSUInteger threadgroup_bytes,
NSUInteger nsg,
bool rows_per_group_is_nr0) {
if (!cb || !pipeline || !args || !act || !group ||
!src0_a || !src0_b || !src1 || !dst_a || !dst_b || !dst_mid || !ids || !weights ||
args->ne00 <= 0 || args->ne01 <= 0 || args->nei0 <= 0 || args->nei1 <= 0 ||
group->expert_count == 0) {
return 0;
}
const NSUInteger nr0 = (NSUInteger)args->nr0;
const NSUInteger rows_per_group = rows_per_group_is_nr0 ? nr0 : nr0 * nsg;
const NSUInteger row_groups = ((NSUInteger)args->ne01 + rows_per_group - 1u) / rows_per_group;
const NSUInteger pairs = (NSUInteger)args->nei0 * (NSUInteger)args->nei1;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:args length:sizeof(*args) atIndex:0];
[enc setBytes:act length:sizeof(*act) atIndex:1];
[enc setBytes:group length:sizeof(*group) atIndex:2];
[enc setBuffer:src0_a offset:src0_a_off atIndex:3];
[enc setBuffer:src0_b offset:src0_b_off atIndex:4];
[enc setBuffer:src1 offset:src1_off atIndex:5];
[enc setBuffer:dst_a offset:dst_a_off atIndex:6];
[enc setBuffer:dst_b offset:dst_b_off atIndex:7];
[enc setBuffer:dst_mid offset:dst_mid_off atIndex:8];
[enc setBuffer:ids offset:ids_off atIndex:9];
[enc setBuffer:weights offset:weights_off atIndex:10];
if (threadgroup_bytes != 0) {
[enc setThreadgroupMemoryLength:threadgroup_bytes atIndex:0];
}
[enc dispatchThreadgroups:MTLSizeMake(row_groups, 1, pairs)
threadsPerThreadgroup:MTLSizeMake(32, nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_mul_mv_group_q4_sum6(
id<MTLCommandBuffer> cb,
id<MTLComputePipelineState> pipeline,
const ds4_gpu_mul_mv_id_args *args,
const ds4_gpu_moe_expert_group_args *group,
id<MTLBuffer> src0,
NSUInteger src0_off,
id<MTLBuffer> src1,
NSUInteger src1_off,
id<MTLBuffer> dst,
NSUInteger dst_off,
id<MTLBuffer> ids,
NSUInteger ids_off,
NSUInteger threadgroup_bytes,
NSUInteger nsg) {
if (!cb || !pipeline || !args || !group || !src0 || !src1 || !dst || !ids ||
args->ne00 <= 0 || args->ne01 <= 0 || args->nei0 != 6 || args->nei1 <= 0 ||
group->expert_count == 0) {
return 0;
}
const NSUInteger rows_per_group = (NSUInteger)args->nr0 * nsg;
const NSUInteger row_groups = ((NSUInteger)args->ne01 + rows_per_group - 1u) / rows_per_group;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:args length:sizeof(*args) atIndex:0];
[enc setBytes:group length:sizeof(*group) atIndex:1];
[enc setBuffer:src0 offset:src0_off atIndex:2];
[enc setBuffer:src1 offset:src1_off atIndex:3];
[enc setBuffer:dst offset:dst_off atIndex:4];
[enc setBuffer:ids offset:ids_off atIndex:5];
if (threadgroup_bytes != 0) {
[enc setThreadgroupMemoryLength:threadgroup_bytes atIndex:0];
}
[enc dispatchThreadgroups:MTLSizeMake(row_groups, (NSUInteger)args->nei1, 1)
threadsPerThreadgroup:MTLSizeMake(32, nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_mul_mv_id_sum6(
id<MTLCommandBuffer> cb,
id<MTLComputePipelineState> pipeline,
const ds4_gpu_mul_mv_id_args *args,
id<MTLBuffer> src0,
NSUInteger src0_off,
id<MTLBuffer> src1,
NSUInteger src1_off,
id<MTLBuffer> dst,
NSUInteger dst_off,
id<MTLBuffer> ids,
NSUInteger ids_off,
id<MTLBuffer> add_in,
NSUInteger add_in_off,
NSUInteger threadgroup_bytes,
NSUInteger nsg) {
if (!cb || !pipeline || !args || !src0 || !src1 || !dst || !ids ||
args->ne00 <= 0 || args->ne01 <= 0 ||
args->nei0 <= 0 || args->nei0 > 8 || args->nei1 <= 0) {
return 0;
}
const NSUInteger rows_per_group = (NSUInteger)args->nr0 * nsg;
const NSUInteger row_groups = ((NSUInteger)args->ne01 + rows_per_group - 1u) / rows_per_group;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:args length:sizeof(*args) atIndex:0];
[enc setBuffer:src0 offset:src0_off atIndex:1];
[enc setBuffer:src1 offset:src1_off atIndex:2];
[enc setBuffer:dst offset:dst_off atIndex:3];
[enc setBuffer:ids offset:ids_off atIndex:4];
[enc setBuffer:(add_in ? add_in : dst) offset:(add_in ? add_in_off : dst_off) atIndex:5];
if (threadgroup_bytes != 0) {
[enc setThreadgroupMemoryLength:threadgroup_bytes atIndex:0];
}
[enc dispatchThreadgroups:MTLSizeMake(row_groups, (NSUInteger)args->nei1, 1)
threadsPerThreadgroup:MTLSizeMake(32, nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_q4_gather_slots6(
id<MTLCommandBuffer> cb,
id<MTLComputePipelineState> pipeline,
const ds4_gpu_q4_gather_slots6_args *args,
__unsafe_unretained id<MTLBuffer> src_groups[6],
const NSUInteger src_group_offsets[6],
id<MTLBuffer> ids,
NSUInteger ids_off,
id<MTLBuffer> dst,
NSUInteger dst_off) {
if (!cb || !pipeline || !args || !src_groups || !src_group_offsets || !ids || !dst ||
args->expert_bytes == 0 || (args->expert_bytes & 15u) != 0 ||
args->group_size == 0 || args->n_slots == 0 || args->n_slots > 6) {
return 0;
}
for (uint32_t i = 0; i < 6; i++) {
if (!src_groups[i]) return 0;
}
const uint64_t chunks = args->expert_bytes >> 4;
if (chunks == 0 || chunks > NSUIntegerMax) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:args length:sizeof(*args) atIndex:0];
for (uint32_t i = 0; i < 6; i++) {
[enc setBuffer:src_groups[i] offset:src_group_offsets[i] atIndex:1 + i];
}
[enc setBuffer:ids offset:ids_off atIndex:7];
[enc setBuffer:dst offset:dst_off atIndex:8];
const NSUInteger threads = 256u;
[enc dispatchThreadgroups:MTLSizeMake(((NSUInteger)chunks + threads - 1u) / threads,
(NSUInteger)args->n_slots,
1)
threadsPerThreadgroup:MTLSizeMake(threads, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_mul_mv_slots6_pair_swiglu(
id<MTLCommandBuffer> cb,
id<MTLComputePipelineState> pipeline,
const ds4_gpu_mul_mv_id_args *args,
const ds4_gpu_dsv4_moe_swiglu_weight_args *act,
__unsafe_unretained id<MTLBuffer> src0_a[6],
const NSUInteger src0_a_off[6],
__unsafe_unretained id<MTLBuffer> src0_b[6],
const NSUInteger src0_b_off[6],
id<MTLBuffer> src1,
NSUInteger src1_off,
id<MTLBuffer> dst_a,
NSUInteger dst_a_off,
id<MTLBuffer> dst_b,
NSUInteger dst_b_off,
id<MTLBuffer> dst_mid,
NSUInteger dst_mid_off,
id<MTLBuffer> weights,
NSUInteger weights_off,
NSUInteger threadgroup_bytes,
NSUInteger nsg,
bool rows_per_group_is_nr0) {
if (!cb || !pipeline || !args || !act || !src0_a || !src0_a_off || !src0_b || !src0_b_off ||
!src1 || !dst_a || !dst_b || !dst_mid || !weights ||
args->ne00 <= 0 || args->ne01 <= 0 || args->nei0 != 6 || args->nei1 <= 0) {
return 0;
}
for (uint32_t i = 0; i < 6; i++) {
if (!src0_a[i] || !src0_b[i]) return 0;
}
const NSUInteger nr0 = (NSUInteger)args->nr0;
const NSUInteger rows_per_group = rows_per_group_is_nr0 ? nr0 : nr0 * nsg;
const NSUInteger row_groups = ((NSUInteger)args->ne01 + rows_per_group - 1u) / rows_per_group;
const NSUInteger pairs = (NSUInteger)args->nei0 * (NSUInteger)args->nei1;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:args length:sizeof(*args) atIndex:0];
[enc setBytes:act length:sizeof(*act) atIndex:1];
for (uint32_t i = 0; i < 6; i++) {
[enc setBuffer:src0_a[i] offset:src0_a_off[i] atIndex:2 + i];
}
for (uint32_t i = 0; i < 6; i++) {
[enc setBuffer:src0_b[i] offset:src0_b_off[i] atIndex:8 + i];
}
[enc setBuffer:src1 offset:src1_off atIndex:14];
[enc setBuffer:dst_a offset:dst_a_off atIndex:15];
[enc setBuffer:dst_b offset:dst_b_off atIndex:16];
[enc setBuffer:dst_mid offset:dst_mid_off atIndex:17];
[enc setBuffer:weights offset:weights_off atIndex:18];
if (threadgroup_bytes != 0) {
[enc setThreadgroupMemoryLength:threadgroup_bytes atIndex:0];
}
[enc dispatchThreadgroups:MTLSizeMake(row_groups, 1, pairs)
threadsPerThreadgroup:MTLSizeMake(32, nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_mul_mv_slots6_sum6(
id<MTLCommandBuffer> cb,
id<MTLComputePipelineState> pipeline,
const ds4_gpu_mul_mv_id_args *args,
__unsafe_unretained id<MTLBuffer> src0[6],
const NSUInteger src0_off[6],
id<MTLBuffer> src1,
NSUInteger src1_off,
id<MTLBuffer> dst,
NSUInteger dst_off,
NSUInteger threadgroup_bytes,
NSUInteger nsg) {
if (!cb || !pipeline || !args || !src0 || !src0_off || !src1 || !dst ||
args->ne00 <= 0 || args->ne01 <= 0 || args->nei0 != 6 || args->nei1 <= 0) {
return 0;
}
for (uint32_t i = 0; i < 6; i++) {
if (!src0[i]) return 0;
}
const NSUInteger rows_per_group = (NSUInteger)args->nr0 * nsg;
const NSUInteger row_groups = ((NSUInteger)args->ne01 + rows_per_group - 1u) / rows_per_group;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:args length:sizeof(*args) atIndex:0];
for (uint32_t i = 0; i < 6; i++) {
[enc setBuffer:src0[i] offset:src0_off[i] atIndex:1 + i];
}
[enc setBuffer:src1 offset:src1_off atIndex:7];
[enc setBuffer:dst offset:dst_off atIndex:8];
if (threadgroup_bytes != 0) {
[enc setThreadgroupMemoryLength:threadgroup_bytes atIndex:0];
}
[enc dispatchThreadgroups:MTLSizeMake(row_groups, (NSUInteger)args->nei1, 1)
threadsPerThreadgroup:MTLSizeMake(32, nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_mul_mv_group6_pair_swiglu(
id<MTLCommandBuffer> cb,
id<MTLComputePipelineState> pipeline,
const ds4_gpu_mul_mv_id_args *args,
const ds4_gpu_dsv4_moe_swiglu_weight_args *act,
__unsafe_unretained id<MTLBuffer> src0_a[6],
const NSUInteger src0_a_off[6],
__unsafe_unretained id<MTLBuffer> src0_b[6],
const NSUInteger src0_b_off[6],
id<MTLBuffer> src1,
NSUInteger src1_off,
id<MTLBuffer> dst_a,
NSUInteger dst_a_off,
id<MTLBuffer> dst_b,
NSUInteger dst_b_off,
id<MTLBuffer> dst_mid,
NSUInteger dst_mid_off,
id<MTLBuffer> ids,
NSUInteger ids_off,
id<MTLBuffer> weights,
NSUInteger weights_off,
NSUInteger threadgroup_bytes,
NSUInteger nsg,
bool rows_per_group_is_nr0) {
if (!cb || !pipeline || !args || !act || !src0_a || !src0_a_off || !src0_b || !src0_b_off ||
!src1 || !dst_a || !dst_b || !dst_mid || !ids || !weights ||
args->ne00 <= 0 || args->ne01 <= 0 || args->nei0 != 6 || args->nei1 <= 0 ||
args->ne02 != 384) {
return 0;
}
for (uint32_t i = 0; i < 6; i++) {
if (!src0_a[i] || !src0_b[i]) return 0;
}
const NSUInteger nr0 = (NSUInteger)args->nr0;
const NSUInteger rows_per_group = rows_per_group_is_nr0 ? nr0 : nr0 * nsg;
const NSUInteger row_groups = ((NSUInteger)args->ne01 + rows_per_group - 1u) / rows_per_group;
const NSUInteger pairs = (NSUInteger)args->nei0 * (NSUInteger)args->nei1;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:args length:sizeof(*args) atIndex:0];
[enc setBytes:act length:sizeof(*act) atIndex:1];
for (uint32_t i = 0; i < 6; i++) {
[enc setBuffer:src0_a[i] offset:src0_a_off[i] atIndex:2 + i];
}
for (uint32_t i = 0; i < 6; i++) {
[enc setBuffer:src0_b[i] offset:src0_b_off[i] atIndex:8 + i];
}
[enc setBuffer:src1 offset:src1_off atIndex:14];
[enc setBuffer:dst_a offset:dst_a_off atIndex:15];
[enc setBuffer:dst_b offset:dst_b_off atIndex:16];
[enc setBuffer:dst_mid offset:dst_mid_off atIndex:17];
[enc setBuffer:ids offset:ids_off atIndex:18];
[enc setBuffer:weights offset:weights_off atIndex:19];
if (threadgroup_bytes != 0) {
[enc setThreadgroupMemoryLength:threadgroup_bytes atIndex:0];
}
[enc dispatchThreadgroups:MTLSizeMake(row_groups, 1, pairs)
threadsPerThreadgroup:MTLSizeMake(32, nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_mul_mv_group6_sum6(
id<MTLCommandBuffer> cb,
id<MTLComputePipelineState> pipeline,
const ds4_gpu_mul_mv_id_args *args,
__unsafe_unretained id<MTLBuffer> src0[6],
const NSUInteger src0_off[6],
id<MTLBuffer> src1,
NSUInteger src1_off,
id<MTLBuffer> dst,
NSUInteger dst_off,
id<MTLBuffer> ids,
NSUInteger ids_off,
NSUInteger threadgroup_bytes,
NSUInteger nsg) {
if (!cb || !pipeline || !args || !src0 || !src0_off || !src1 || !dst || !ids ||
args->ne00 <= 0 || args->ne01 <= 0 || args->nei0 != 6 || args->nei1 <= 0 ||
args->ne02 != 384) {
return 0;
}
for (uint32_t i = 0; i < 6; i++) {
if (!src0[i]) return 0;
}
const NSUInteger rows_per_group = (NSUInteger)args->nr0 * nsg;
const NSUInteger row_groups = ((NSUInteger)args->ne01 + rows_per_group - 1u) / rows_per_group;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:args length:sizeof(*args) atIndex:0];
for (uint32_t i = 0; i < 6; i++) {
[enc setBuffer:src0[i] offset:src0_off[i] atIndex:1 + i];
}
[enc setBuffer:src1 offset:src1_off atIndex:7];
[enc setBuffer:dst offset:dst_off atIndex:8];
[enc setBuffer:ids offset:ids_off atIndex:9];
if (threadgroup_bytes != 0) {
[enc setThreadgroupMemoryLength:threadgroup_bytes atIndex:0];
}
[enc dispatchThreadgroups:MTLSizeMake(row_groups, (NSUInteger)args->nei1, 1)
threadsPerThreadgroup:MTLSizeMake(32, nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_mul_mv_addr_iq2_pair_swiglu(
id<MTLCommandBuffer> cb,
id<MTLComputePipelineState> pipeline,
const ds4_gpu_mul_mv_id_args *args,
const ds4_gpu_dsv4_moe_swiglu_weight_args *act,
ds4_gpu_stream_expert_cache_entry * const *entries,
uint32_t n_entries,
id<MTLBuffer> gate_addrs,
id<MTLBuffer> up_addrs,
id<MTLBuffer> src1,
NSUInteger src1_off,
id<MTLBuffer> dst_a,
NSUInteger dst_a_off,
id<MTLBuffer> dst_b,
NSUInteger dst_b_off,
id<MTLBuffer> dst_mid,
NSUInteger dst_mid_off,
id<MTLBuffer> ids,
NSUInteger ids_off,
id<MTLBuffer> weights,
NSUInteger weights_off,
NSUInteger threadgroup_bytes,
NSUInteger nsg,
bool rows_per_group_is_nr0,
id<MTLBuffer> overflow_gate,
id<MTLBuffer> overflow_up) {
if (!cb || !pipeline || !args || !act || !entries ||
(n_entries == 0 && !overflow_gate) ||
!gate_addrs || !up_addrs ||
!src1 || !dst_a || !dst_b || !dst_mid || !ids || !weights ||
args->ne00 <= 0 || args->ne01 <= 0 ||
args->nei0 <= 0 || args->nei0 > DS4_METAL_MAX_ROUTED_EXPERT_USED ||
args->nei1 <= 0 ||
args->ne02 <= 0 || args->ne02 > 384) {
return 0;
}
for (uint32_t i = 0; i < n_entries; i++) {
if (!entries[i] || !entries[i]->gate_buffer || !entries[i]->up_buffer) return 0;
}
if (!ds4_gpu_stream_expert_cache_mark_entries_inflight(entries,
n_entries,
0)) {
return 0;
}
const NSUInteger nr0 = (NSUInteger)args->nr0;
const NSUInteger rows_per_group = rows_per_group_is_nr0 ? nr0 : nr0 * nsg;
const NSUInteger row_groups = ((NSUInteger)args->ne01 + rows_per_group - 1u) / rows_per_group;
const NSUInteger pairs = (NSUInteger)args->nei0 * (NSUInteger)args->nei1;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:args length:sizeof(*args) atIndex:0];
[enc setBytes:act length:sizeof(*act) atIndex:1];
[enc setBuffer:gate_addrs offset:0 atIndex:2];
[enc setBuffer:up_addrs offset:0 atIndex:3];
[enc setBuffer:src1 offset:src1_off atIndex:4];
[enc setBuffer:dst_a offset:dst_a_off atIndex:5];
[enc setBuffer:dst_b offset:dst_b_off atIndex:6];
[enc setBuffer:dst_mid offset:dst_mid_off atIndex:7];
[enc setBuffer:ids offset:ids_off atIndex:8];
[enc setBuffer:weights offset:weights_off atIndex:9];
for (uint32_t i = 0; i < n_entries; i++) {
[enc useResource:entries[i]->gate_buffer usage:MTLResourceUsageRead];
[enc useResource:entries[i]->up_buffer usage:MTLResourceUsageRead];
}
/* Overflow experts are addressed straight into the mapped model views
* when a layer's unique selected set exceeds the cache budget. */
if (overflow_gate) [enc useResource:overflow_gate usage:MTLResourceUsageRead];
if (overflow_up) [enc useResource:overflow_up usage:MTLResourceUsageRead];
if (threadgroup_bytes != 0) {
[enc setThreadgroupMemoryLength:threadgroup_bytes atIndex:0];
}
[enc dispatchThreadgroups:MTLSizeMake(row_groups, 1, pairs)
threadsPerThreadgroup:MTLSizeMake(32, nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_mul_mv_addr_iq2(
id<MTLCommandBuffer> cb,
id<MTLComputePipelineState> pipeline,
const ds4_gpu_mul_mv_id_args *args,
ds4_gpu_stream_expert_cache_entry * const *entries,
uint32_t n_entries,
id<MTLBuffer> addrs,
id<MTLBuffer> src1,
NSUInteger src1_off,
id<MTLBuffer> dst,
NSUInteger dst_off,
id<MTLBuffer> ids,
NSUInteger ids_off,
NSUInteger threadgroup_bytes,
NSUInteger nsg,
bool rows_per_group_is_nr0,
uint32_t resource_kind,
id<MTLBuffer> overflow_resource) {
if (!cb || !pipeline || !args || !entries ||
(n_entries == 0 && !overflow_resource) ||
n_entries > DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT || resource_kind > 2 ||
!addrs || !src1 || !dst || !ids ||
args->ne00 <= 0 || args->ne01 <= 0 ||
args->nei0 <= 0 || args->nei0 > DS4_METAL_MAX_ROUTED_EXPERT_USED ||
args->nei1 <= 0 ||
args->ne02 <= 0 || args->ne02 > 384) {
return 0;
}
for (uint32_t i = 0; i < n_entries; i++) {
if (!entries[i] || !entries[i]->gate_buffer ||
!entries[i]->up_buffer || !entries[i]->down_buffer) return 0;
}
if (n_entries && !ds4_gpu_stream_expert_cache_mark_entries_inflight(entries,
n_entries,
0)) {
return 0;
}
const NSUInteger nr0 = (NSUInteger)args->nr0;
const NSUInteger rows_per_group = rows_per_group_is_nr0 ? nr0 : nr0 * nsg;
const NSUInteger row_groups = ((NSUInteger)args->ne01 + rows_per_group - 1u) / rows_per_group;
const NSUInteger pairs = (NSUInteger)args->nei0 * (NSUInteger)args->nei1;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:args length:sizeof(*args) atIndex:0];
[enc setBuffer:addrs offset:0 atIndex:1];
[enc setBuffer:src1 offset:src1_off atIndex:2];
[enc setBuffer:dst offset:dst_off atIndex:3];
[enc setBuffer:ids offset:ids_off atIndex:4];
for (uint32_t i = 0; i < n_entries; i++) {
id<MTLBuffer> b = resource_kind == 0 ? entries[i]->gate_buffer :
resource_kind == 1 ? entries[i]->up_buffer :
entries[i]->down_buffer;
[enc useResource:b usage:MTLResourceUsageRead];
}
if (overflow_resource) [enc useResource:overflow_resource usage:MTLResourceUsageRead];
if (threadgroup_bytes != 0) {
[enc setThreadgroupMemoryLength:threadgroup_bytes atIndex:0];
}
[enc dispatchThreadgroups:MTLSizeMake(row_groups, 1, pairs)
threadsPerThreadgroup:MTLSizeMake(32, nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_mul_mv_addr_q2_sum6(
id<MTLCommandBuffer> cb,
id<MTLComputePipelineState> pipeline,
const ds4_gpu_mul_mv_id_args *args,
ds4_gpu_stream_expert_cache_entry * const *entries,
uint32_t n_entries,
id<MTLBuffer> addrs,
id<MTLBuffer> src1,
NSUInteger src1_off,
id<MTLBuffer> dst,
NSUInteger dst_off,
id<MTLBuffer> ids,
NSUInteger ids_off,
NSUInteger threadgroup_bytes,
NSUInteger nsg,
id<MTLBuffer> overflow_down) {
if (!cb || !pipeline || !args || !entries ||
(n_entries == 0 && !overflow_down) ||
!addrs || !src1 || !dst || !ids ||
args->ne00 <= 0 || args->ne01 <= 0 || args->nei0 != 6 || args->nei1 <= 0 ||
args->ne02 <= 0 || args->ne02 > 384) {
return 0;
}
for (uint32_t i = 0; i < n_entries; i++) {
if (!entries[i] || !entries[i]->down_buffer) return 0;
}
if (!ds4_gpu_stream_expert_cache_mark_entries_inflight(entries,
n_entries,
0)) {
return 0;
}
const NSUInteger rows_per_group = (NSUInteger)args->nr0 * nsg;
const NSUInteger row_groups = ((NSUInteger)args->ne01 + rows_per_group - 1u) / rows_per_group;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:args length:sizeof(*args) atIndex:0];
[enc setBuffer:addrs offset:0 atIndex:1];
[enc setBuffer:src1 offset:src1_off atIndex:2];
[enc setBuffer:dst offset:dst_off atIndex:3];
[enc setBuffer:ids offset:ids_off atIndex:4];
for (uint32_t i = 0; i < n_entries; i++) {
[enc useResource:entries[i]->down_buffer usage:MTLResourceUsageRead];
}
if (overflow_down) [enc useResource:overflow_down usage:MTLResourceUsageRead];
if (threadgroup_bytes != 0) {
[enc setThreadgroupMemoryLength:threadgroup_bytes atIndex:0];
}
[enc dispatchThreadgroups:MTLSizeMake(row_groups, (NSUInteger)args->nei1, 1)
threadsPerThreadgroup:MTLSizeMake(32, nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_mul_mv_addr_iq2_pair_swiglu_masked(
id<MTLCommandBuffer> cb,
id<MTLComputePipelineState> pipeline,
const ds4_gpu_mul_mv_id_args *args,
const ds4_gpu_dsv4_moe_swiglu_weight_args *act,
const ds4_gpu_stream_expert_split_args *split,
ds4_gpu_stream_expert_cache_entry * const entries[6],
id<MTLBuffer> gate_addrs,
id<MTLBuffer> up_addrs,
id<MTLBuffer> src1,
NSUInteger src1_off,
id<MTLBuffer> dst_a,
NSUInteger dst_a_off,
id<MTLBuffer> dst_b,
NSUInteger dst_b_off,
id<MTLBuffer> dst_mid,
NSUInteger dst_mid_off,
id<MTLBuffer> ids,
NSUInteger ids_off,
id<MTLBuffer> weights,
NSUInteger weights_off,
NSUInteger threadgroup_bytes,
NSUInteger nsg,
bool rows_per_group_is_nr0) {
if (!cb || !pipeline || !args || !act || !split || !entries ||
!gate_addrs || !up_addrs || !src1 || !dst_a || !dst_b || !dst_mid ||
!ids || !weights ||
args->ne00 <= 0 || args->ne01 <= 0 || args->nei0 != 6 || args->nei1 <= 0 ||
args->ne02 <= 0 || args->ne02 > 384) {
return 0;
}
for (uint32_t i = 0; i < 6; i++) {
if ((split->active_mask & (1u << i)) == 0) continue;
if (!entries[i] || !entries[i]->gate_buffer || !entries[i]->up_buffer) return 0;
}
if (!ds4_gpu_stream_expert_cache_mark_entries_inflight(entries,
6,
split->active_mask)) {
return 0;
}
const NSUInteger nr0 = (NSUInteger)args->nr0;
const NSUInteger rows_per_group = rows_per_group_is_nr0 ? nr0 : nr0 * nsg;
const NSUInteger row_groups = ((NSUInteger)args->ne01 + rows_per_group - 1u) / rows_per_group;
const NSUInteger pairs = (NSUInteger)args->nei0 * (NSUInteger)args->nei1;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:args length:sizeof(*args) atIndex:0];
[enc setBytes:act length:sizeof(*act) atIndex:1];
[enc setBytes:split length:sizeof(*split) atIndex:2];
[enc setBuffer:gate_addrs offset:0 atIndex:3];
[enc setBuffer:up_addrs offset:0 atIndex:4];
[enc setBuffer:src1 offset:src1_off atIndex:5];
[enc setBuffer:dst_a offset:dst_a_off atIndex:6];
[enc setBuffer:dst_b offset:dst_b_off atIndex:7];
[enc setBuffer:dst_mid offset:dst_mid_off atIndex:8];
[enc setBuffer:ids offset:ids_off atIndex:9];
[enc setBuffer:weights offset:weights_off atIndex:10];
for (uint32_t i = 0; i < 6; i++) {
if ((split->active_mask & (1u << i)) == 0) continue;
[enc useResource:entries[i]->gate_buffer usage:MTLResourceUsageRead];
[enc useResource:entries[i]->up_buffer usage:MTLResourceUsageRead];
}
if (threadgroup_bytes != 0) {
[enc setThreadgroupMemoryLength:threadgroup_bytes atIndex:0];
}
[enc dispatchThreadgroups:MTLSizeMake(row_groups, 1, pairs)
threadsPerThreadgroup:MTLSizeMake(32, nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_mul_mv_addr_q2_sum6_masked(
id<MTLCommandBuffer> cb,
id<MTLComputePipelineState> pipeline,
const ds4_gpu_mul_mv_id_args *args,
const ds4_gpu_stream_expert_split_args *split,
ds4_gpu_stream_expert_cache_entry * const entries[6],
id<MTLBuffer> addrs,
id<MTLBuffer> src1,
NSUInteger src1_off,
id<MTLBuffer> dst,
NSUInteger dst_off,
id<MTLBuffer> ids,
NSUInteger ids_off,
NSUInteger threadgroup_bytes,
NSUInteger nsg) {
if (!cb || !pipeline || !args || !split || !entries || !addrs ||
!src1 || !dst || !ids ||
args->ne00 <= 0 || args->ne01 <= 0 || args->nei0 != 6 || args->nei1 <= 0 ||
args->ne02 <= 0 || args->ne02 > 384) {
return 0;
}
for (uint32_t i = 0; i < 6; i++) {
if ((split->active_mask & (1u << i)) == 0) continue;
if (!entries[i] || !entries[i]->down_buffer) return 0;
}
if (!ds4_gpu_stream_expert_cache_mark_entries_inflight(entries,
6,
split->active_mask)) {
return 0;
}
const NSUInteger rows_per_group = (NSUInteger)args->nr0 * nsg;
const NSUInteger row_groups = ((NSUInteger)args->ne01 + rows_per_group - 1u) / rows_per_group;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:args length:sizeof(*args) atIndex:0];
[enc setBytes:split length:sizeof(*split) atIndex:1];
[enc setBuffer:addrs offset:0 atIndex:2];
[enc setBuffer:src1 offset:src1_off atIndex:3];
[enc setBuffer:dst offset:dst_off atIndex:4];
[enc setBuffer:ids offset:ids_off atIndex:5];
for (uint32_t i = 0; i < 6; i++) {
if ((split->active_mask & (1u << i)) == 0) continue;
[enc useResource:entries[i]->down_buffer usage:MTLResourceUsageRead];
}
if (threadgroup_bytes != 0) {
[enc setThreadgroupMemoryLength:threadgroup_bytes atIndex:0];
}
[enc dispatchThreadgroups:MTLSizeMake(row_groups, (NSUInteger)args->nei1, 1)
threadsPerThreadgroup:MTLSizeMake(32, nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_mul_mv_group8_pair_swiglu(
id<MTLCommandBuffer> cb,
id<MTLComputePipelineState> pipeline,
const ds4_gpu_mul_mv_id_args *args,
const ds4_gpu_dsv4_moe_swiglu_weight_args *act,
__unsafe_unretained id<MTLBuffer> src0_a[8],
const NSUInteger src0_a_off[8],
__unsafe_unretained id<MTLBuffer> src0_b[8],
const NSUInteger src0_b_off[8],
id<MTLBuffer> src1,
NSUInteger src1_off,
id<MTLBuffer> dst_a,
NSUInteger dst_a_off,
id<MTLBuffer> dst_b,
NSUInteger dst_b_off,
id<MTLBuffer> dst_mid,
NSUInteger dst_mid_off,
id<MTLBuffer> ids,
NSUInteger ids_off,
id<MTLBuffer> weights,
NSUInteger weights_off,
NSUInteger threadgroup_bytes,
NSUInteger nsg,
bool rows_per_group_is_nr0) {
if (!cb || !pipeline || !args || !act || !src0_a || !src0_a_off || !src0_b || !src0_b_off ||
!src1 || !dst_a || !dst_b || !dst_mid || !ids || !weights ||
args->ne00 <= 0 || args->ne01 <= 0 || args->nei0 != 6 || args->nei1 <= 0 ||
args->ne02 != 384) {
return 0;
}
for (uint32_t i = 0; i < 8; i++) {
if (!src0_a[i] || !src0_b[i]) return 0;
}
const NSUInteger nr0 = (NSUInteger)args->nr0;
const NSUInteger rows_per_group = rows_per_group_is_nr0 ? nr0 : nr0 * nsg;
const NSUInteger row_groups = ((NSUInteger)args->ne01 + rows_per_group - 1u) / rows_per_group;
const NSUInteger pairs = (NSUInteger)args->nei0 * (NSUInteger)args->nei1;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:args length:sizeof(*args) atIndex:0];
[enc setBytes:act length:sizeof(*act) atIndex:1];
for (uint32_t i = 0; i < 8; i++) {
[enc setBuffer:src0_a[i] offset:src0_a_off[i] atIndex:2 + i];
}
for (uint32_t i = 0; i < 8; i++) {
[enc setBuffer:src0_b[i] offset:src0_b_off[i] atIndex:10 + i];
}
[enc setBuffer:src1 offset:src1_off atIndex:18];
[enc setBuffer:dst_a offset:dst_a_off atIndex:19];
[enc setBuffer:dst_b offset:dst_b_off atIndex:20];
[enc setBuffer:dst_mid offset:dst_mid_off atIndex:21];
[enc setBuffer:ids offset:ids_off atIndex:22];
[enc setBuffer:weights offset:weights_off atIndex:23];
if (threadgroup_bytes != 0) {
[enc setThreadgroupMemoryLength:threadgroup_bytes atIndex:0];
}
[enc dispatchThreadgroups:MTLSizeMake(row_groups, 1, pairs)
threadsPerThreadgroup:MTLSizeMake(32, nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_mul_mv_group8_sum6(
id<MTLCommandBuffer> cb,
id<MTLComputePipelineState> pipeline,
const ds4_gpu_mul_mv_id_args *args,
__unsafe_unretained id<MTLBuffer> src0[8],
const NSUInteger src0_off[8],
id<MTLBuffer> src1,
NSUInteger src1_off,
id<MTLBuffer> dst,
NSUInteger dst_off,
id<MTLBuffer> ids,
NSUInteger ids_off,
NSUInteger threadgroup_bytes,
NSUInteger nsg) {
if (!cb || !pipeline || !args || !src0 || !src0_off || !src1 || !dst || !ids ||
args->ne00 <= 0 || args->ne01 <= 0 || args->nei0 != 6 || args->nei1 <= 0 ||
args->ne02 != 384) {
return 0;
}
for (uint32_t i = 0; i < 8; i++) {
if (!src0[i]) return 0;
}
const NSUInteger rows_per_group = (NSUInteger)args->nr0 * nsg;
const NSUInteger row_groups = ((NSUInteger)args->ne01 + rows_per_group - 1u) / rows_per_group;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:args length:sizeof(*args) atIndex:0];
for (uint32_t i = 0; i < 8; i++) {
[enc setBuffer:src0[i] offset:src0_off[i] atIndex:1 + i];
}
[enc setBuffer:src1 offset:src1_off atIndex:9];
[enc setBuffer:dst offset:dst_off atIndex:10];
[enc setBuffer:ids offset:ids_off atIndex:11];
if (threadgroup_bytes != 0) {
[enc setThreadgroupMemoryLength:threadgroup_bytes atIndex:0];
}
[enc dispatchThreadgroups:MTLSizeMake(row_groups, (NSUInteger)args->nei1, 1)
threadsPerThreadgroup:MTLSizeMake(32, nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_mul_mv_group24_id(
id<MTLCommandBuffer> cb,
id<MTLComputePipelineState> pipeline,
const ds4_gpu_mul_mv_id_args *args,
__unsafe_unretained id<MTLBuffer> src0[24],
const NSUInteger src0_off[24],
id<MTLBuffer> src1,
NSUInteger src1_off,
id<MTLBuffer> dst,
NSUInteger dst_off,
id<MTLBuffer> ids,
NSUInteger ids_off,
NSUInteger threadgroup_bytes,
NSUInteger nsg,
bool rows_per_group_is_nr0) {
if (!cb || !pipeline || !args || !src0 || !src0_off || !src1 || !dst || !ids ||
args->ne00 <= 0 || args->ne01 <= 0 || args->nei0 != 6 || args->nei1 <= 0 ||
args->ne02 != 384) {
return 0;
}
for (uint32_t i = 0; i < 24; i++) {
if (!src0[i]) return 0;
}
const NSUInteger nr0 = (NSUInteger)args->nr0;
const NSUInteger rows_per_group = rows_per_group_is_nr0 ? nr0 : nr0 * nsg;
const NSUInteger row_groups = ((NSUInteger)args->ne01 + rows_per_group - 1u) / rows_per_group;
const NSUInteger pairs = (NSUInteger)args->nei0 * (NSUInteger)args->nei1;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:args length:sizeof(*args) atIndex:0];
for (uint32_t i = 0; i < 24; i++) {
[enc setBuffer:src0[i] offset:src0_off[i] atIndex:1 + i];
}
[enc setBuffer:src1 offset:src1_off atIndex:25];
[enc setBuffer:dst offset:dst_off atIndex:26];
[enc setBuffer:ids offset:ids_off atIndex:27];
if (threadgroup_bytes != 0) {
[enc setThreadgroupMemoryLength:threadgroup_bytes atIndex:0];
}
[enc dispatchThreadgroups:MTLSizeMake(row_groups, 1, pairs)
threadsPerThreadgroup:MTLSizeMake(32, nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_mul_mv_group24_sum6(
id<MTLCommandBuffer> cb,
id<MTLComputePipelineState> pipeline,
const ds4_gpu_mul_mv_id_args *args,
__unsafe_unretained id<MTLBuffer> src0[24],
const NSUInteger src0_off[24],
id<MTLBuffer> src1,
NSUInteger src1_off,
id<MTLBuffer> dst,
NSUInteger dst_off,
id<MTLBuffer> ids,
NSUInteger ids_off,
NSUInteger threadgroup_bytes,
NSUInteger nsg) {
if (!cb || !pipeline || !args || !src0 || !src0_off || !src1 || !dst || !ids ||
args->ne00 <= 0 || args->ne01 <= 0 || args->nei0 != 6 || args->nei1 <= 0 ||
args->ne02 != 384) {
return 0;
}
for (uint32_t i = 0; i < 24; i++) {
if (!src0[i]) return 0;
}
const NSUInteger rows_per_group = (NSUInteger)args->nr0 * nsg;
const NSUInteger row_groups = ((NSUInteger)args->ne01 + rows_per_group - 1u) / rows_per_group;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:args length:sizeof(*args) atIndex:0];
for (uint32_t i = 0; i < 24; i++) {
[enc setBuffer:src0[i] offset:src0_off[i] atIndex:1 + i];
}
[enc setBuffer:src1 offset:src1_off atIndex:25];
[enc setBuffer:dst offset:dst_off atIndex:26];
[enc setBuffer:ids offset:ids_off atIndex:27];
if (threadgroup_bytes != 0) {
[enc setThreadgroupMemoryLength:threadgroup_bytes atIndex:0];
}
[enc dispatchThreadgroups:MTLSizeMake(row_groups, (NSUInteger)args->nei1, 1)
threadsPerThreadgroup:MTLSizeMake(32, nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_mul_mm_id(
id<MTLCommandBuffer> cb,
id<MTLComputePipelineState> map_pipeline,
id<MTLComputePipelineState> mm_pipeline,
const ds4_gpu_mul_mm_id_map_args *map_args,
const ds4_gpu_mul_mm_id_args *mm_args,
id<MTLBuffer> src0,
NSUInteger src0_off,
id<MTLBuffer> src1,
NSUInteger src1_off,
id<MTLBuffer> dst,
NSUInteger dst_off,
id<MTLBuffer> ids,
NSUInteger ids_off) {
if (!cb || !map_pipeline || !mm_pipeline || !map_args || !mm_args ||
!src0 || !src1 || !dst || !ids ||
mm_args->ne00 <= 0 || mm_args->ne0 <= 0 ||
mm_args->ne20 <= 0 || mm_args->ne21 <= 0 || mm_args->ne02 <= 0) {
return 0;
}
return ds4_gpu_encode_mul_mm_id_map(cb,
map_pipeline,
map_args,
mm_args,
ids,
ids_off) &&
ds4_gpu_encode_mul_mm_id_mapped(cb,
mm_pipeline,
mm_args,
src0,
src0_off,
src1,
src1_off,
dst,
dst_off);
}
static int ds4_gpu_encode_mul_mm_id_map(
id<MTLCommandBuffer> cb,
id<MTLComputePipelineState> map_pipeline,
const ds4_gpu_mul_mm_id_map_args *map_args,
const ds4_gpu_mul_mm_id_args *mm_args,
id<MTLBuffer> ids,
NSUInteger ids_off) {
if (!cb || !map_pipeline || !map_args || !mm_args || !ids ||
mm_args->ne20 <= 0 || mm_args->ne21 <= 0 || mm_args->ne02 <= 0) {
return 0;
}
const NSUInteger tpe_bytes = (NSUInteger)mm_args->ne02 * sizeof(int32_t);
const NSUInteger hids_bytes =
(NSUInteger)mm_args->ne02 * (NSUInteger)mm_args->ne21 * sizeof(int32_t);
if (tpe_bytes > NSUIntegerMax - hids_bytes) return 0;
const NSUInteger work_offset = (tpe_bytes + hids_bytes + 7u) & ~7u;
const uint64_t pair_rows =
(uint64_t)(uint32_t)mm_args->ne20 * (uint32_t)mm_args->ne21;
const uint64_t work_cap =
(pair_rows + 31u * (uint32_t)mm_args->ne02 + 31u) / 32u;
const NSUInteger work_item_bytes = 2u * sizeof(uint32_t);
if (work_cap > (NSUIntegerMax - 8u) / work_item_bytes ||
work_offset > NSUIntegerMax - 8u -
(NSUInteger)work_cap * work_item_bytes) {
return 0;
}
const NSUInteger total_bytes =
work_offset + 8u + (NSUInteger)work_cap * work_item_bytes;
if (!ds4_gpu_ensure_scratch_buffer(&g_moe_id_map_buffer,
&g_moe_id_map_bytes,
total_bytes,
"ds4_moe_id_map")) {
return 0;
}
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:map_pipeline];
[enc setBytes:map_args length:sizeof(*map_args) atIndex:0];
[enc setBuffer:ids offset:ids_off atIndex:1];
[enc setBuffer:g_moe_id_map_buffer offset:0 atIndex:2];
[enc setBuffer:g_moe_id_map_buffer offset:tpe_bytes atIndex:3];
[enc setBuffer:g_moe_id_map_buffer offset:work_offset atIndex:4];
[enc setThreadgroupMemoryLength:(NSUInteger)mm_args->ne02 * (NSUInteger)mm_args->ne20 * sizeof(uint16_t) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(1, 1, 1)
threadsPerThreadgroup:MTLSizeMake((NSUInteger)mm_args->ne02, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_moe_packed_rhs(
id<MTLCommandBuffer> cb,
const ds4_gpu_mul_mm_id_args *args,
id<MTLBuffer> src, NSUInteger offset, bool f16, NSUInteger limit) {
if (!cb || !args || !src || !g_moe_id_map_buffer ||
args->ne00 <= 0 || args->ne20 <= 0 || args->ne21 <= 0 || args->ne02 <= 0)
return 0;
const uint64_t rows = (uint64_t)args->ne20*args->ne21;
const uint64_t tiles = (rows + 31u*(uint64_t)args->ne02 + 31u)/32u;
const uint64_t row_bytes = (uint64_t)args->ne00*sizeof(uint16_t);
const NSUInteger tile_n = 32u;
const uint64_t header = ((uint64_t)args->ne02 + 31u) / 32u * 32u * sizeof(uint32_t);
if (rows > (NSUIntegerMax - header)/row_bytes) return 0;
const NSUInteger bytes = (NSUInteger)(header + rows*row_bytes);
if (bytes > limit ||
!ds4_gpu_ensure_scratch_buffer(&g_moe_packed_rhs_buffer,
&g_moe_packed_rhs_bytes, bytes, "ds4_moe_packed_rhs")) return 0;
const NSUInteger counts_bytes = (NSUInteger)args->ne02*sizeof(uint32_t);
const NSUInteger ids_bytes = (NSUInteger)args->ne02*args->ne21*sizeof(int32_t);
const NSUInteger work_offset = (counts_bytes + ids_bytes + 7u) & ~7u;
id<MTLComputePipelineState> pipeline = ds4_gpu_get_pipeline(
f16 ? "kernel_moe_pack_rhs_f16" : "kernel_moe_pack_rhs_f32");
id<MTLComputePipelineState> offsets = ds4_gpu_get_pipeline("kernel_moe_packed_offsets");
if (!pipeline || !offsets) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
const uint32_t experts = (uint32_t)args->ne02;
[enc setComputePipelineState:offsets];
[enc setBytes:&experts length:sizeof(experts) atIndex:0];
[enc setBuffer:g_moe_id_map_buffer offset:0 atIndex:1];
[enc setBuffer:g_moe_packed_rhs_buffer offset:0 atIndex:2];
[enc dispatchThreadgroups:MTLSizeMake(1, 1, 1)
threadsPerThreadgroup:MTLSizeMake(32, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:args length:sizeof(*args) atIndex:0];
[enc setBuffer:src offset:offset atIndex:1];
[enc setBuffer:g_moe_id_map_buffer offset:0 atIndex:2];
[enc setBuffer:g_moe_id_map_buffer offset:counts_bytes atIndex:3];
[enc setBuffer:g_moe_id_map_buffer offset:work_offset atIndex:4];
[enc setBuffer:g_moe_packed_rhs_buffer offset:0 atIndex:5];
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)tiles*tile_n, 1, 1)
threadsPerThreadgroup:MTLSizeMake(128, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_mul_mm_id_mapped_tile_resources(
id<MTLCommandBuffer> cb,
id<MTLComputePipelineState> mm_pipeline,
const ds4_gpu_mul_mm_id_args *mm_args,
id<MTLBuffer> src0,
NSUInteger src0_off,
id<MTLBuffer> src1,
NSUInteger src1_off,
id<MTLBuffer> dst,
NSUInteger dst_off,
NSUInteger threadgroup_bytes,
ds4_gpu_stream_expert_cache_entry * const *resources,
uint32_t resource_count,
uint32_t resource_kind,
id<MTLBuffer> overflow_resource,
uint32_t tile_rows) {
if (!cb || !mm_pipeline || !mm_args || !src0 || !src1 || !dst ||
!g_moe_id_map_buffer ||
mm_args->ne00 <= 0 || mm_args->ne0 <= 0 ||
mm_args->ne20 <= 0 || mm_args->ne21 <= 0 || mm_args->ne02 <= 0 ||
(tile_rows != 32u && tile_rows != 64u)) {
return 0;
}
/* The simdgroup and TensorOps kernels share the 32-token work map. */
const bool use_resource_hints =
getenv("DS4_METAL_MOE_MM_ID_USE_RESOURCES") != NULL &&
getenv("DS4_METAL_DISABLE_MOE_MM_ID_USE_RESOURCES") == NULL;
const NSUInteger tpe_bytes = (NSUInteger)mm_args->ne02 * sizeof(int32_t);
const NSUInteger hids_bytes = (NSUInteger)mm_args->ne02 * (NSUInteger)mm_args->ne21 * sizeof(int32_t);
if (tpe_bytes > NSUIntegerMax - hids_bytes) {
return 0;
}
const NSUInteger work_offset = (tpe_bytes + hids_bytes + 7u) & ~7u;
const uint64_t pair_rows =
(uint64_t)(uint32_t)mm_args->ne20 * (uint32_t)mm_args->ne21;
const uint64_t work_cap =
(pair_rows + 31u * (uint32_t)mm_args->ne02 + 31u) / 32u;
const NSUInteger work_item_bytes = 2u * sizeof(uint32_t);
if (work_cap > NSUIntegerMax ||
work_offset > NSUIntegerMax - 8u ||
(NSUInteger)work_cap >
(NSUIntegerMax - work_offset - 8u) / work_item_bytes ||
g_moe_id_map_bytes <
work_offset + 8u + (NSUInteger)work_cap * work_item_bytes) {
return 0;
}
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:mm_pipeline];
[enc setBytes:mm_args length:sizeof(*mm_args) atIndex:0];
[enc setBuffer:src0 offset:src0_off atIndex:1];
[enc setBuffer:src1 offset:src1_off atIndex:2];
[enc setBuffer:g_moe_id_map_buffer offset:0 atIndex:3];
[enc setBuffer:g_moe_id_map_buffer offset:tpe_bytes atIndex:4];
[enc setBuffer:dst offset:dst_off atIndex:5];
[enc setBuffer:g_moe_id_map_buffer offset:work_offset atIndex:6];
for (uint32_t i = 0; resources && i < resource_count; i++) {
ds4_gpu_stream_expert_cache_entry *entry = resources[i];
id<MTLBuffer> b = resource_kind == 0 ? entry->gate_buffer :
resource_kind == 1 ? entry->up_buffer : entry->down_buffer;
[enc useResource:b usage:MTLResourceUsageRead];
}
if (overflow_resource) [enc useResource:overflow_resource usage:MTLResourceUsageRead];
if (use_resource_hints) {
[enc useResource:src0 usage:MTLResourceUsageRead];
}
[enc setThreadgroupMemoryLength:threadgroup_bytes atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)work_cap,
((NSUInteger)mm_args->ne0 + tile_rows - 1u) / tile_rows,
1)
threadsPerThreadgroup:MTLSizeMake(128, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_mul_mm_id_mapped_tile(
id<MTLCommandBuffer> cb, id<MTLComputePipelineState> pipeline,
const ds4_gpu_mul_mm_id_args *args,
id<MTLBuffer> src0, NSUInteger src0_off,
id<MTLBuffer> src1, NSUInteger src1_off,
id<MTLBuffer> dst, NSUInteger dst_off, NSUInteger threadgroup_bytes) {
return ds4_gpu_encode_mul_mm_id_mapped_tile_resources(
cb, pipeline, args, src0, src0_off, src1, src1_off, dst, dst_off,
threadgroup_bytes, NULL, 0, 0, nil, 64);
}
static int ds4_gpu_encode_mul_mm_id_addr_mapped_tile(
id<MTLCommandBuffer> cb,
id<MTLComputePipelineState> mm_pipeline,
const ds4_gpu_mul_mm_id_args *mm_args,
id<MTLBuffer> src0_addrs,
id<MTLBuffer> src1,
NSUInteger src1_off,
id<MTLBuffer> dst,
NSUInteger dst_off,
NSUInteger threadgroup_bytes,
ds4_gpu_stream_expert_cache_entry * const *resources,
uint32_t resource_count,
uint32_t resource_kind,
id<MTLBuffer> overflow_resource) {
if (!cb || !mm_pipeline || !mm_args || !src0_addrs || !src1 || !dst ||
!g_moe_id_map_buffer ||
mm_args->ne00 <= 0 || mm_args->ne0 <= 0 ||
mm_args->ne20 <= 0 || mm_args->ne21 <= 0 || mm_args->ne02 <= 0) {
return 0;
}
const NSUInteger tile_n = 32u;
const NSUInteger tpe_bytes = (NSUInteger)mm_args->ne02 * sizeof(int32_t);
const NSUInteger hids_bytes =
(NSUInteger)mm_args->ne02 * (NSUInteger)mm_args->ne21 * sizeof(int32_t);
if (tpe_bytes > NSUIntegerMax - hids_bytes ||
g_moe_id_map_bytes < tpe_bytes + hids_bytes) {
return 0;
}
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:mm_pipeline];
[enc setBytes:mm_args length:sizeof(*mm_args) atIndex:0];
[enc setBuffer:src0_addrs offset:0 atIndex:1];
[enc setBuffer:src1 offset:src1_off atIndex:2];
[enc setBuffer:g_moe_id_map_buffer offset:0 atIndex:3];
[enc setBuffer:g_moe_id_map_buffer offset:tpe_bytes atIndex:4];
[enc setBuffer:dst offset:dst_off atIndex:5];
[enc useResource:src0_addrs usage:MTLResourceUsageRead];
for (uint32_t i = 0; resources && i < resource_count; i++) {
ds4_gpu_stream_expert_cache_entry *entry = resources[i];
if (!entry) continue;
id<MTLBuffer> b =
resource_kind == 0 ? entry->gate_buffer :
resource_kind == 1 ? entry->up_buffer :
entry->down_buffer;
if (b) [enc useResource:b usage:MTLResourceUsageRead];
}
if (overflow_resource) {
[enc useResource:overflow_resource usage:MTLResourceUsageRead];
}
[enc setThreadgroupMemoryLength:threadgroup_bytes atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(((NSUInteger)mm_args->ne21 + tile_n - 1u) / tile_n,
((NSUInteger)mm_args->ne0 + 63u) / 64u,
(NSUInteger)mm_args->ne02)
threadsPerThreadgroup:MTLSizeMake(128, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_mul_mm_id_iq2_pair_swiglu_f16(
id<MTLCommandBuffer> cb,
id<MTLComputePipelineState> pipeline,
bool compact_tile,
const ds4_gpu_mul_mm_id_args *mm_args,
const ds4_gpu_dsv4_moe_swiglu_weight_args *act_args,
id<MTLBuffer> gate_src0,
NSUInteger gate_src0_off,
id<MTLBuffer> up_src0,
NSUInteger up_src0_off,
id<MTLBuffer> src1,
NSUInteger src1_off,
id<MTLBuffer> mid,
NSUInteger mid_off,
id<MTLBuffer> weights,
NSUInteger weights_off) {
if (!cb || !pipeline || !mm_args || !act_args ||
!gate_src0 || !up_src0 || !src1 || !mid || !weights ||
!g_moe_id_map_buffer ||
mm_args->ne00 <= 0 || mm_args->ne0 <= 0 ||
mm_args->ne20 <= 0 || mm_args->ne21 <= 0 || mm_args->ne02 <= 0) {
return 0;
}
const NSUInteger tpe_bytes = (NSUInteger)mm_args->ne02 * sizeof(int32_t);
const NSUInteger hids_bytes = (NSUInteger)mm_args->ne02 * (NSUInteger)mm_args->ne21 * sizeof(int32_t);
if (tpe_bytes > NSUIntegerMax - hids_bytes) {
return 0;
}
const NSUInteger work_offset = (tpe_bytes + hids_bytes + 7u) & ~7u;
const uint64_t pair_rows =
(uint64_t)(uint32_t)mm_args->ne20 * (uint32_t)mm_args->ne21;
const uint64_t work_cap =
(pair_rows + 31u * (uint32_t)mm_args->ne02 + 31u) / 32u;
const NSUInteger work_item_bytes = 2u * sizeof(uint32_t);
if (work_cap > NSUIntegerMax ||
work_offset > NSUIntegerMax - 8u ||
(NSUInteger)work_cap >
(NSUIntegerMax - work_offset - 8u) / work_item_bytes ||
g_moe_id_map_bytes <
work_offset + 8u + (NSUInteger)work_cap * work_item_bytes) {
return 0;
}
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:mm_args length:sizeof(*mm_args) atIndex:0];
[enc setBytes:act_args length:sizeof(*act_args) atIndex:1];
[enc setBuffer:gate_src0 offset:gate_src0_off atIndex:2];
[enc setBuffer:up_src0 offset:up_src0_off atIndex:3];
[enc setBuffer:src1 offset:src1_off atIndex:4];
[enc setBuffer:g_moe_id_map_buffer offset:0 atIndex:5];
[enc setBuffer:g_moe_id_map_buffer offset:tpe_bytes atIndex:6];
[enc setBuffer:mid offset:mid_off atIndex:7];
[enc setBuffer:weights offset:weights_off atIndex:8];
[enc setBuffer:g_moe_id_map_buffer offset:work_offset atIndex:9];
const NSUInteger tile_m = compact_tile ? 32u : 64u;
[enc setThreadgroupMemoryLength:compact_tile ? 8192u : 16384u atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)work_cap,
((NSUInteger)mm_args->ne0 + tile_m - 1u) / tile_m,
1)
threadsPerThreadgroup:MTLSizeMake(compact_tile ? 64u : 128u, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_mul_mm_id_mapped(
id<MTLCommandBuffer> cb,
id<MTLComputePipelineState> mm_pipeline,
const ds4_gpu_mul_mm_id_args *mm_args,
id<MTLBuffer> src0,
NSUInteger src0_off,
id<MTLBuffer> src1,
NSUInteger src1_off,
id<MTLBuffer> dst,
NSUInteger dst_off) {
return ds4_gpu_encode_mul_mm_id_mapped_tile(cb,
mm_pipeline,
mm_args,
src0,
src0_off,
src1,
src1_off,
dst,
dst_off,
8192u);
}
static int ds4_gpu_encode_attn_out_low_mpp(
id<MTLCommandBuffer> cb,
id<MTLComputePipelineState> pipeline,
const ds4_gpu_mul_mm_id_args *mm_args,
id<MTLBuffer> src0,
NSUInteger src0_off,
id<MTLBuffer> src1,
NSUInteger src1_off,
id<MTLBuffer> dst,
NSUInteger dst_off) {
if (!cb || !pipeline || !mm_args || !src0 || !src1 || !dst ||
mm_args->ne00 <= 0 || mm_args->ne0 <= 0 ||
mm_args->ne02 <= 0 || mm_args->ne1 <= 0 || mm_args->ne21 <= 0) {
return 0;
}
const uint32_t tile_n = DS4_METAL_ATTN_OUT_MPP_TILE_N;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:mm_args length:sizeof(*mm_args) atIndex:0];
[enc setBuffer:src0 offset:src0_off atIndex:1];
[enc setBuffer:src1 offset:src1_off atIndex:2];
[enc setBuffer:dst offset:dst_off atIndex:3];
[enc setThreadgroupMemoryLength:8192u atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(((NSUInteger)mm_args->ne21 + (NSUInteger)tile_n - 1u) / (NSUInteger)tile_n,
((NSUInteger)mm_args->ne0 + 63u) / 64u,
(NSUInteger)mm_args->ne02)
threadsPerThreadgroup:MTLSizeMake(128, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_swiglu_flat(
id<MTLCommandBuffer> cb,
id<MTLBuffer> gate,
NSUInteger gate_off,
id<MTLBuffer> up,
NSUInteger up_off,
id<MTLBuffer> out,
NSUInteger out_off,
uint32_t n) {
if (!cb || !gate || !up || !out || n == 0) return 0;
ds4_gpu_glu_args args = {
.ne00 = (int32_t)n,
.nb01 = (uint64_t)n * sizeof(float),
.ne10 = (int32_t)n,
.nb11 = (uint64_t)n * sizeof(float),
.ne0 = (int32_t)n,
.nb1 = (uint64_t)n * sizeof(float),
.i00 = 0,
.i10 = 0,
.alpha = 1.0f,
.limit = 0.0f,
};
NSUInteger nth = g_swiglu_flat_pipeline.maxTotalThreadsPerThreadgroup;
if (nth > 256u) nth = 256u;
if (nth > (NSUInteger)n) nth = (NSUInteger)n;
if (nth == 0u) nth = 1u;
const NSUInteger groups = ((NSUInteger)n + nth - 1u) / nth;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:g_swiglu_flat_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:gate offset:gate_off atIndex:1];
[enc setBuffer:up offset:up_off atIndex:2];
[enc setBuffer:out offset:out_off atIndex:3];
[enc dispatchThreadgroups:MTLSizeMake(groups, 1, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_moe_swiglu_weight(
id<MTLCommandBuffer> cb,
id<MTLBuffer> gate,
NSUInteger gate_off,
id<MTLBuffer> up,
NSUInteger up_off,
id<MTLBuffer> mid,
NSUInteger mid_off,
id<MTLBuffer> weights,
NSUInteger weights_off,
uint32_t width,
uint32_t rows,
float clamp_value,
bool mid_f16) {
if (!cb || !gate || !up || !mid || !weights || width == 0 || rows == 0) return 0;
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_pipeline(mid_f16 ? "kernel_dsv4_moe_swiglu_weight_f16" :
"kernel_dsv4_moe_swiglu_weight");
if (!pipeline) return 0;
ds4_gpu_dsv4_moe_swiglu_weight_args args = {
.width = width,
.rows = rows,
.gate_row_stride = (uint64_t)width * sizeof(float),
.up_row_stride = (uint64_t)width * sizeof(float),
.mid_row_stride = (uint64_t)width * (mid_f16 ? sizeof(uint16_t) : sizeof(float)),
.weight_stride = sizeof(float),
.write_clamped = getenv("DS4_METAL_MOE_WRITE_CLAMPED_ACT") != NULL ? 1u : 0u,
.clamp_value = clamp_value,
};
NSUInteger nth = pipeline.maxTotalThreadsPerThreadgroup;
if (nth > 256u) nth = 256u;
if (nth > width) nth = width;
if (nth == 0) nth = 1u;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:gate offset:gate_off atIndex:1];
[enc setBuffer:up offset:up_off atIndex:2];
[enc setBuffer:mid offset:mid_off atIndex:3];
[enc setBuffer:weights offset:weights_off atIndex:4];
[enc dispatchThreadgroups:MTLSizeMake(rows, 1, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_moe_sum6(
id<MTLCommandBuffer> cb,
id<MTLBuffer> experts,
NSUInteger experts_off,
id<MTLBuffer> out,
NSUInteger out_off,
uint32_t out_dim,
uint32_t n_tokens) {
if (!cb || !experts || !out || out_dim == 0 || n_tokens == 0) return 0;
if (!g_moe_sum6_pipeline) return 0;
const uint64_t out_row_bytes = (uint64_t)out_dim * sizeof(float);
ds4_gpu_dsv4_moe_sum6_args args = {
.width = out_dim,
.tokens = n_tokens,
.src_token_stride = 6u * out_row_bytes,
.dst_token_stride = out_row_bytes,
};
NSUInteger nth = g_moe_sum6_pipeline.maxTotalThreadsPerThreadgroup;
if (nth > 256u) nth = 256u;
if (nth > out_dim) nth = out_dim;
if (nth == 0) nth = 1u;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:g_moe_sum6_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:experts offset:experts_off atIndex:1];
[enc setBuffer:out offset:out_off atIndex:2];
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)n_tokens, 1, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_moe_sum8(
id<MTLCommandBuffer> cb,
id<MTLBuffer> experts,
NSUInteger experts_off,
id<MTLBuffer> out,
NSUInteger out_off,
uint32_t out_dim,
uint32_t n_tokens) {
if (!cb || !experts || !out || out_dim == 0 || n_tokens == 0) return 0;
if (!g_moe_sum8_pipeline) return 0;
const uint64_t out_row_bytes = (uint64_t)out_dim * sizeof(float);
ds4_gpu_dsv4_moe_sum6_args args = {
.width = out_dim,
.tokens = n_tokens,
.src_token_stride = 8u * out_row_bytes,
.dst_token_stride = out_row_bytes,
};
NSUInteger nth = g_moe_sum8_pipeline.maxTotalThreadsPerThreadgroup;
if (nth > 256u) nth = 256u;
if (nth > out_dim) nth = out_dim;
if (nth == 0) nth = 1u;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:g_moe_sum8_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:experts offset:experts_off atIndex:1];
[enc setBuffer:out offset:out_off atIndex:2];
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)n_tokens, 1, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static ds4_gpu_bin_args ds4_gpu_make_moe_add_args(
uint32_t out_dim,
uint32_t n_tokens,
uint64_t src0_token_stride,
uint64_t src1_token_stride,
uint64_t dst_token_stride) {
return (ds4_gpu_bin_args) {
.ne00 = (int32_t)out_dim,
.ne01 = (int32_t)n_tokens,
.ne02 = 1,
.ne03 = 1,
.nb00 = sizeof(float),
.nb01 = src0_token_stride,
.nb02 = (uint64_t)n_tokens * src0_token_stride,
.nb03 = (uint64_t)n_tokens * src0_token_stride,
.ne10 = (int32_t)out_dim,
.ne11 = (int32_t)n_tokens,
.ne12 = 1,
.ne13 = 1,
.nb10 = sizeof(float),
.nb11 = src1_token_stride,
.nb12 = (uint64_t)n_tokens * src1_token_stride,
.nb13 = (uint64_t)n_tokens * src1_token_stride,
.ne0 = (int32_t)out_dim,
.ne1 = (int32_t)n_tokens,
.ne2 = 1,
.ne3 = 1,
.nb0 = sizeof(float),
.nb1 = dst_token_stride,
.nb2 = (uint64_t)n_tokens * dst_token_stride,
.nb3 = (uint64_t)n_tokens * dst_token_stride,
.offs = 0,
.o1 = { 0 },
};
}
static int ds4_gpu_encode_moe_sum_experts(
id<MTLCommandBuffer> cb,
id<MTLBuffer> experts,
NSUInteger experts_off,
id<MTLBuffer> out,
NSUInteger out_off,
uint32_t out_dim,
uint32_t n_expert,
uint32_t n_tokens) {
if (!cb || !experts || !out || out_dim == 0 || n_expert < 2 || n_tokens == 0) return 0;
const uint64_t out_row_bytes = (uint64_t)out_dim * sizeof(float);
const uint64_t expert_token_stride = (uint64_t)n_expert * out_row_bytes;
if (n_expert == 6 &&
ds4_gpu_encode_moe_sum6(cb,
experts,
experts_off,
out,
out_off,
out_dim,
n_tokens)) {
return 1;
}
if (n_expert == 8 &&
ds4_gpu_encode_moe_sum8(cb,
experts,
experts_off,
out,
out_off,
out_dim,
n_tokens)) {
return 1;
}
ds4_gpu_bin_args first =
ds4_gpu_make_moe_add_args(out_dim, n_tokens, expert_token_stride, expert_token_stride, out_row_bytes);
if (!ds4_gpu_encode_bin_f32_rows(cb,
g_add_pipeline,
&first,
experts,
experts_off,
experts,
experts_off + (NSUInteger)out_row_bytes,
out,
out_off)) {
return 0;
}
ds4_gpu_bin_args accum =
ds4_gpu_make_moe_add_args(out_dim, n_tokens, out_row_bytes, expert_token_stride, out_row_bytes);
for (uint32_t slot = 2; slot < n_expert; slot++) {
if (!ds4_gpu_encode_bin_f32_rows(cb,
g_add_pipeline,
&accum,
out,
out_off,
experts,
experts_off + (NSUInteger)((uint64_t)slot * out_row_bytes),
out,
out_off)) {
return 0;
}
}
return 1;
}
static int ds4_gpu_encode_get_rows_i32_token_rows(
id<MTLCommandBuffer> cb,
id<MTLBuffer> table,
NSUInteger table_off,
id<MTLBuffer> tokens,
NSUInteger tokens_off,
const int32_t *token_inline,
id<MTLBuffer> selected,
NSUInteger selected_off,
uint32_t hash_rows,
uint32_t n_cols,
uint32_t n_tokens) {
if (!cb || !table || !selected || hash_rows == 0 || n_cols == 0 || n_tokens == 0) return 0;
if (!tokens && !token_inline) return 0;
const uint64_t table_row_bytes = (uint64_t)n_cols * sizeof(int32_t);
const uint64_t token_bytes = (uint64_t)n_tokens * sizeof(int32_t);
ds4_gpu_get_rows_args args = {
.ne00t = (int64_t)n_cols,
.ne00 = (int64_t)n_cols,
.nb01 = table_row_bytes,
.nb02 = (uint64_t)hash_rows * table_row_bytes,
.nb03 = (uint64_t)hash_rows * table_row_bytes,
.ne10 = (int32_t)n_tokens,
.nb10 = sizeof(int32_t),
.nb11 = token_bytes,
.nb12 = token_bytes,
.nb1 = table_row_bytes,
.nb2 = (uint64_t)n_tokens * table_row_bytes,
.nb3 = (uint64_t)n_tokens * table_row_bytes,
};
NSUInteger nth = (NSUInteger)n_cols;
const NSUInteger max_threads = g_get_rows_i32_pipeline.maxTotalThreadsPerThreadgroup;
if (nth > max_threads) nth = max_threads;
if (nth == 0) nth = 1u;
const NSUInteger nw0 = ((NSUInteger)n_cols + nth - 1u) / nth;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:g_get_rows_i32_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:table offset:table_off atIndex:1];
if (tokens) {
[enc setBuffer:tokens offset:tokens_off atIndex:2];
} else {
[enc setBytes:token_inline length:sizeof(*token_inline) atIndex:2];
}
[enc setBuffer:selected offset:selected_off atIndex:3];
[enc dispatchThreadgroups:MTLSizeMake(nw0 * n_tokens, 1, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_get_rows_f32_router_weights(
id<MTLCommandBuffer> cb,
id<MTLBuffer> probs,
NSUInteger probs_off,
id<MTLBuffer> selected,
NSUInteger selected_off,
id<MTLBuffer> weights,
NSUInteger weights_off,
uint32_t n_expert,
uint32_t n_expert_used,
uint32_t n_tokens) {
if (!cb || !probs || !selected || !weights || n_expert == 0 || n_expert_used == 0 || n_tokens == 0) return 0;
const uint64_t probs_token_bytes = (uint64_t)n_expert * sizeof(float);
const uint64_t selected_row_bytes = (uint64_t)n_expert_used * sizeof(int32_t);
const uint64_t weights_row_bytes = (uint64_t)n_expert_used * sizeof(float);
ds4_gpu_get_rows_args args = {
.ne00t = 1,
.ne00 = 1,
.nb01 = sizeof(float),
.nb02 = probs_token_bytes,
.nb03 = (uint64_t)n_tokens * probs_token_bytes,
.ne10 = (int64_t)n_expert_used,
.nb10 = sizeof(int32_t),
.nb11 = selected_row_bytes,
.nb12 = (uint64_t)n_tokens * selected_row_bytes,
.nb1 = sizeof(float),
.nb2 = weights_row_bytes,
.nb3 = (uint64_t)n_tokens * weights_row_bytes,
};
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:g_get_rows_f32_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:probs offset:probs_off atIndex:1];
[enc setBuffer:selected offset:selected_off atIndex:2];
[enc setBuffer:weights offset:weights_off atIndex:3];
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)n_expert_used, n_tokens, 1)
threadsPerThreadgroup:MTLSizeMake(1, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_sum_rows_f32(
id<MTLCommandBuffer> cb,
id<MTLBuffer> src,
NSUInteger src_off,
id<MTLBuffer> dst,
NSUInteger dst_off,
uint32_t width,
uint32_t rows) {
if (!cb || !src || !dst || width == 0 || rows == 0) return 0;
const uint64_t src_row_bytes = (uint64_t)width * sizeof(float);
ds4_gpu_kargs_sum_rows args = {
.ne00 = (int64_t)width,
.ne01 = (int64_t)rows,
.ne02 = 1,
.ne03 = 1,
.nb00 = sizeof(float),
.nb01 = src_row_bytes,
.nb02 = (uint64_t)rows * src_row_bytes,
.nb03 = (uint64_t)rows * src_row_bytes,
.ne0 = 1,
.ne1 = (int64_t)rows,
.ne2 = 1,
.ne3 = 1,
.nb0 = sizeof(float),
.nb1 = sizeof(float),
.nb2 = (uint64_t)rows * sizeof(float),
.nb3 = (uint64_t)rows * sizeof(float),
};
NSUInteger nth = 32u;
const NSUInteger max_threads = g_sum_rows_f32_f32_pipeline.maxTotalThreadsPerThreadgroup;
while (nth < (NSUInteger)args.ne00 && nth < max_threads) nth *= 2u;
if (nth > max_threads) nth = max_threads;
if (nth > (NSUInteger)args.ne00) nth = (NSUInteger)args.ne00;
if (nth == 0) nth = 1u;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:g_sum_rows_f32_f32_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:src offset:src_off atIndex:1];
[enc setBuffer:dst offset:dst_off atIndex:2];
[enc setThreadgroupMemoryLength:32u * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(rows, 1, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
static int ds4_gpu_encode_router_select(
id<MTLCommandBuffer> cb,
ds4_gpu_tensor *selected,
ds4_gpu_tensor *weights,
ds4_gpu_tensor *probs,
id<MTLBuffer> logitsbuf,
NSUInteger logits_off,
id<MTLBuffer> biasbuf,
NSUInteger bias_off,
id<MTLBuffer> hashbuf,
NSUInteger hash_off,
id<MTLBuffer> tokensbuf,
NSUInteger tokens_off,
id<MTLBuffer> visual_biasbuf,
NSUInteger visual_bias_off,
const int32_t *single_token,
uint32_t hash_rows,
uint32_t vocab_size,
uint32_t n_tokens,
uint32_t n_expert,
uint32_t n_expert_used,
float expert_weight_scale,
bool has_bias,
bool hash_mode,
bool mixed_visual) {
id<MTLBuffer> selectedbuf = ds4_gpu_tensor_buffer(selected);
id<MTLBuffer> weightsbuf = ds4_gpu_tensor_buffer(weights);
id<MTLBuffer> probsbuf = ds4_gpu_tensor_buffer(probs);
const NSUInteger selected_off = ds4_gpu_tensor_offset(selected);
const NSUInteger weights_off = ds4_gpu_tensor_offset(weights);
const NSUInteger probs_off = ds4_gpu_tensor_offset(probs);
if (!cb || !selectedbuf || !weightsbuf || !probsbuf || !logitsbuf ||
(mixed_visual && (!visual_biasbuf || !tokensbuf || single_token)) ||
n_tokens == 0 || n_expert == 0 || n_expert_used == 0) return 0;
const NSUInteger probs_bytes = (NSUInteger)n_tokens * (NSUInteger)n_expert * sizeof(float);
const bool flash_router_fast_path =
n_expert == 256u &&
n_expert_used == 6u &&
fabsf(expert_weight_scale - 1.5f) <= 1.0e-6f;
int ok = 0;
if (flash_router_fast_path && !mixed_visual &&
!g_quality_mode && n_tokens == 1 &&
getenv("DS4_METAL_DISABLE_ROUTER_SELECT_FUSION") == NULL) {
const bool pre_m5_device =
ds4_gpu_device_is_pre_m5_apple_silicon();
const bool use_simd_finalize =
!hash_mode &&
g_dsv4_router_finalize_one_simd_pipeline != nil &&
g_dsv4_router_finalize_one_simd_pipeline.threadExecutionWidth == 32u &&
g_dsv4_router_finalize_one_simd_pipeline.maxTotalThreadsPerThreadgroup >= 256u &&
(pre_m5_device ||
ds4_gpu_device_name_contains("M5")) &&
(!pre_m5_device ||
getenv("DS4_METAL_DISABLE_PRE_M5_ROUTER_SIMD_FINALIZE") == NULL);
const bool use_simd_weights_fusion =
use_simd_finalize &&
g_dsv4_router_finalize_weights_one_simd_pipeline != nil &&
g_dsv4_router_finalize_weights_one_simd_pipeline.threadExecutionWidth == 32u &&
g_dsv4_router_finalize_weights_one_simd_pipeline.maxTotalThreadsPerThreadgroup >= 256u &&
(pre_m5_device ||
ds4_gpu_device_name_contains("M5")) &&
(!pre_m5_device ||
getenv("DS4_METAL_DISABLE_PRE_M5_ROUTER_SIMD_WEIGHTS_FUSION") == NULL);
const bool use_pre_m5_transform_finalize_fusion_default =
!g_ssd_streaming_mode &&
(pre_m5_device || ds4_gpu_device_name_contains("M5")) &&
getenv("DS4_METAL_DISABLE_PRE_M5_ROUTER_TRANSFORM_FINALIZE_FUSION") == NULL;
const bool use_transform_finalize_fusion =
use_simd_weights_fusion &&
!hash_mode &&
use_pre_m5_transform_finalize_fusion_default &&
(pre_m5_device ||
ds4_gpu_device_name_contains("M5")) &&
g_dsv4_router_transform_finalize_weights_one_simd_pipeline != nil &&
g_dsv4_router_transform_finalize_weights_one_simd_pipeline.threadExecutionWidth == 32u &&
g_dsv4_router_transform_finalize_weights_one_simd_pipeline.maxTotalThreadsPerThreadgroup >= 256u;
id<MTLComputePipelineState> softplus_sqrt_pipeline =
use_transform_finalize_fusion
? nil
: ds4_gpu_hot_pipeline(g_dsv4_softplus_sqrt_pipeline,
"kernel_dsv4_softplus_sqrt_f32_4");
id<MTLComputePipelineState> router_finalize_pipeline =
ds4_gpu_hot_pipeline(
use_transform_finalize_fusion
? g_dsv4_router_transform_finalize_weights_one_simd_pipeline
: use_simd_weights_fusion
? g_dsv4_router_finalize_weights_one_simd_pipeline
: use_simd_finalize
? g_dsv4_router_finalize_one_simd_pipeline
: g_dsv4_router_finalize_one_pipeline,
use_transform_finalize_fusion
? "kernel_dsv4_router_transform_finalize_weights_one_simd"
: use_simd_weights_fusion
? "kernel_dsv4_router_finalize_weights_one_simd"
: use_simd_finalize
? "kernel_dsv4_router_finalize_one_simd"
: "kernel_dsv4_router_finalize_one");
id<MTLComputePipelineState> router_weights_pipeline = use_simd_weights_fusion
? nil
: ds4_gpu_hot_pipeline(g_dsv4_router_weights_one_pipeline,
"kernel_dsv4_router_weights_one");
if ((!use_transform_finalize_fusion && !softplus_sqrt_pipeline) ||
!router_finalize_pipeline ||
(!use_simd_weights_fusion && !router_weights_pipeline)) return 0;
if (!use_transform_finalize_fusion) {
ok = ds4_gpu_encode_unary_f32_rows(cb,
softplus_sqrt_pipeline,
logitsbuf,
logits_off,
probsbuf,
probs_off,
n_expert,
1,
1,
0.0f,
0.0f);
if (!ok) return 0;
}
const bool use_token_buffer = single_token == NULL;
ds4_gpu_dsv4_router_select_one_args args = {
.has_bias = has_bias ? 1u : 0u,
.hash_mode = hash_mode ? 1u : 0u,
.use_token_buffer = use_token_buffer ? 1u : 0u,
.token = single_token ? (uint32_t)*single_token : 0u,
.hash_rows = hash_rows,
};
const float zero_f32 = 0.0f;
const int32_t zero_i32 = 0;
if ((has_bias && !biasbuf) ||
(hash_mode && !hashbuf) ||
(use_token_buffer && !tokensbuf)) {
return 0;
}
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:router_finalize_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
if (use_transform_finalize_fusion) {
[enc setBuffer:logitsbuf offset:logits_off atIndex:1];
[enc setBuffer:probsbuf offset:probs_off atIndex:2];
if (has_bias) {
[enc setBuffer:biasbuf offset:bias_off atIndex:3];
} else {
[enc setBytes:&zero_f32 length:sizeof(zero_f32) atIndex:3];
}
[enc setBytes:&zero_i32 length:sizeof(zero_i32) atIndex:4];
if (use_token_buffer) {
[enc setBuffer:tokensbuf offset:tokens_off atIndex:5];
} else {
[enc setBytes:&zero_i32 length:sizeof(zero_i32) atIndex:5];
}
[enc setBuffer:selectedbuf offset:selected_off atIndex:6];
[enc setBuffer:weightsbuf offset:weights_off atIndex:7];
} else {
[enc setBuffer:probsbuf offset:probs_off atIndex:1];
if (has_bias) {
[enc setBuffer:biasbuf offset:bias_off atIndex:2];
} else {
[enc setBytes:&zero_f32 length:sizeof(zero_f32) atIndex:2];
}
if (hash_mode) {
[enc setBuffer:hashbuf offset:hash_off atIndex:3];
} else {
[enc setBytes:&zero_i32 length:sizeof(zero_i32) atIndex:3];
}
if (use_token_buffer) {
[enc setBuffer:tokensbuf offset:tokens_off atIndex:4];
} else {
[enc setBytes:&zero_i32 length:sizeof(zero_i32) atIndex:4];
}
[enc setBuffer:selectedbuf offset:selected_off atIndex:5];
if (use_simd_weights_fusion) {
[enc setBuffer:weightsbuf offset:weights_off atIndex:6];
}
}
const NSUInteger router_finalize_scratch_bytes = use_simd_finalize
? 2u * (256u * sizeof(float) + 256u * sizeof(int32_t))
: 256u * sizeof(float) + 256u * sizeof(int32_t);
[enc setThreadgroupMemoryLength:router_finalize_scratch_bytes atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(1, 1, 1)
threadsPerThreadgroup:MTLSizeMake(256, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (use_simd_weights_fusion) return 1;
enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:router_weights_pipeline];
[enc setBuffer:probsbuf offset:probs_off atIndex:0];
[enc setBuffer:selectedbuf offset:selected_off atIndex:1];
[enc setBuffer:weightsbuf offset:weights_off atIndex:2];
[enc dispatchThreads:MTLSizeMake(6, 1, 1)
threadsPerThreadgroup:MTLSizeMake(6, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
if (flash_router_fast_path && !mixed_visual && !g_quality_mode && n_tokens == 1) {
id<MTLComputePipelineState> softplus_sqrt_pipeline =
ds4_gpu_hot_pipeline(g_dsv4_softplus_sqrt_pipeline,
"kernel_dsv4_softplus_sqrt_f32_4");
ok = softplus_sqrt_pipeline &&
ds4_gpu_encode_unary_f32_rows(cb,
softplus_sqrt_pipeline,
logitsbuf,
logits_off,
probsbuf,
probs_off,
n_expert,
1,
1,
0.0f,
0.0f);
} else {
ok = ds4_gpu_encode_unary_f32_rows(cb,
g_unary_softplus_pipeline,
logitsbuf,
logits_off,
probsbuf,
probs_off,
n_expert,
n_tokens,
1,
0.0f,
0.0f) &&
ds4_gpu_encode_unary_f32_rows(cb,
g_unary_sqrt_pipeline,
probsbuf,
probs_off,
probsbuf,
probs_off,
n_expert,
n_tokens,
1,
0.0f,
0.0f);
}
if (!ok) return 0;
if (mixed_visual) {
if (!g_dsv4_router_select_visual_batch_pipeline ||
n_expert != 256u || n_expert_used != 6u) return 0;
ds4_gpu_dsv4_router_select_visual_args args = {
.hash_rows = hash_rows,
.vocab_size = vocab_size,
.n_tokens = n_tokens,
.has_bias = has_bias ? 1u : 0u,
.hash_mode = hash_mode ? 1u : 0u,
};
const float zero_f32 = 0.0f;
const int32_t zero_i32 = 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:g_dsv4_router_select_visual_batch_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:probsbuf offset:probs_off atIndex:1];
if (has_bias) [enc setBuffer:biasbuf offset:bias_off atIndex:2];
else [enc setBytes:&zero_f32 length:sizeof(zero_f32) atIndex:2];
[enc setBuffer:visual_biasbuf offset:visual_bias_off atIndex:3];
if (hash_mode) [enc setBuffer:hashbuf offset:hash_off atIndex:4];
else [enc setBytes:&zero_i32 length:sizeof(zero_i32) atIndex:4];
[enc setBuffer:tokensbuf offset:tokens_off atIndex:5];
[enc setBuffer:selectedbuf offset:selected_off atIndex:6];
[enc setThreadgroupMemoryLength:
256u * (sizeof(float) + sizeof(int32_t)) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(n_tokens, 1, 1)
threadsPerThreadgroup:MTLSizeMake(256, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
ok = 1;
} else if (hash_mode) {
ok = ds4_gpu_encode_get_rows_i32_token_rows(cb,
hashbuf,
hash_off,
tokensbuf,
tokens_off,
single_token,
selectedbuf,
selected_off,
hash_rows,
n_expert_used,
n_tokens);
} else {
ds4_gpu_tensor *score_tensor = probs;
DS4MetalTensor *selection_view = nil;
if (has_bias) {
if (!biasbuf ||
!ds4_gpu_ensure_scratch_buffer(&g_router_selection_buffer,
&g_router_selection_bytes,
probs_bytes,
"ds4_router_selection")) {
return 0;
}
ds4_gpu_bin_args add_args = ds4_gpu_make_bin_rows_args(n_expert, n_tokens, n_expert);
ok = ds4_gpu_encode_bin_f32_rows(cb,
g_add_pipeline,
&add_args,
probsbuf,
probs_off,
biasbuf,
bias_off,
g_router_selection_buffer,
0);
if (!ok) return 0;
selection_view = [DS4MetalTensor new];
selection_view.buffer = g_router_selection_buffer;
selection_view.offset = 0;
selection_view.bytes = probs_bytes;
selection_view.owner = 0;
score_tensor = (__bridge ds4_gpu_tensor *)selection_view;
}
ok = ds4_gpu_indexer_topk_tensor(selected, score_tensor, n_expert, n_tokens, n_expert_used) != 0;
}
if (!ok) return 0;
const bool use_batch_weights_fusion =
flash_router_fast_path && !g_quality_mode && n_tokens > 1u &&
g_dsv4_router_weights_batch_pipeline != nil &&
ds4_gpu_device_name_contains("M3") &&
getenv("DS4_METAL_DISABLE_ROUTER_WEIGHTS_BATCH_FUSION") == NULL &&
getenv("DS4_METAL_DISABLE_ROUTER_SELECT_FUSION") == NULL;
if (use_batch_weights_fusion) {
const float scale = expert_weight_scale;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:g_dsv4_router_weights_batch_pipeline];
[enc setBytes:&scale length:sizeof(scale) atIndex:0];
[enc setBuffer:probsbuf offset:probs_off atIndex:1];
[enc setBuffer:selectedbuf offset:selected_off atIndex:2];
[enc setBuffer:weightsbuf offset:weights_off atIndex:3];
[enc setThreadgroupMemoryLength:40u * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(n_tokens, 1, 1)
threadsPerThreadgroup:MTLSizeMake(6, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
if (flash_router_fast_path && !g_quality_mode && n_tokens == 1) {
id<MTLComputePipelineState> router_weights_pipeline =
ds4_gpu_hot_pipeline(g_dsv4_router_weights_one_pipeline,
"kernel_dsv4_router_weights_one");
if (!router_weights_pipeline) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:router_weights_pipeline];
[enc setBuffer:probsbuf offset:probs_off atIndex:0];
[enc setBuffer:selectedbuf offset:selected_off atIndex:1];
[enc setBuffer:weightsbuf offset:weights_off atIndex:2];
[enc dispatchThreads:MTLSizeMake(6, 1, 1)
threadsPerThreadgroup:MTLSizeMake(6, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
const NSUInteger sum_bytes = (NSUInteger)n_tokens * sizeof(float);
if (!ds4_gpu_ensure_scratch_buffer(&g_router_weight_sum_buffer,
&g_router_weight_sum_bytes,
sum_bytes,
"ds4_router_weight_sum")) {
return 0;
}
ok = ds4_gpu_encode_get_rows_f32_router_weights(cb,
probsbuf,
probs_off,
selectedbuf,
selected_off,
weightsbuf,
weights_off,
n_expert,
n_expert_used,
n_tokens) &&
ds4_gpu_encode_sum_rows_f32(cb,
weightsbuf,
weights_off,
g_router_weight_sum_buffer,
0,
n_expert_used,
n_tokens) &&
ds4_gpu_encode_unary_f32_rows(cb,
g_unary_clamp_pipeline,
g_router_weight_sum_buffer,
0,
g_router_weight_sum_buffer,
0,
1,
n_tokens,
0,
6.103515625e-5f,
ds4_gpu_positive_infinity());
if (!ok) return 0;
ds4_gpu_bin_args div_args = ds4_gpu_make_bin_rowwise_scalar_args(n_expert_used, n_tokens);
const float scale = expert_weight_scale;
ds4_gpu_bin_args scale_args = ds4_gpu_make_bin_rows_args(n_expert_used, n_tokens, 1);
ok = ds4_gpu_encode_bin_f32_rows(cb,
g_bin_div_row_pipeline,
&div_args,
weightsbuf,
weights_off,
g_router_weight_sum_buffer,
0,
weightsbuf,
weights_off);
if (!ok) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:g_bin_mul_scalar_pipeline];
[enc setBytes:&scale_args length:sizeof(scale_args) atIndex:0];
[enc setBuffer:weightsbuf offset:weights_off atIndex:1];
[enc setBytes:&scale length:sizeof(scale) atIndex:2];
[enc setBuffer:weightsbuf offset:weights_off atIndex:3];
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)scale_args.ne1,
(NSUInteger)scale_args.ne2,
(NSUInteger)scale_args.ne3)
threadsPerThreadgroup:MTLSizeMake(ds4_gpu_bin_threads(n_expert_used, g_bin_mul_scalar_pipeline), 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return 1;
}
int ds4_gpu_glm_kv_lora_rms_norm_tensor(
ds4_gpu_tensor *out,
const ds4_gpu_tensor *kv_raw,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint32_t n_tokens,
uint32_t kv_raw_dim,
uint32_t kv_lora_dim,
float eps) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!out || !kv_raw || !model_map ||
n_tokens == 0 || kv_raw_dim == 0 || kv_lora_dim == 0 ||
kv_lora_dim > kv_raw_dim || (kv_lora_dim & 3u) != 0 ||
!isfinite(eps) || eps < 0.0f) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> rawbuf = ds4_gpu_tensor_buffer(kv_raw);
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out);
const uint64_t raw_bytes = (uint64_t)n_tokens * kv_raw_dim * sizeof(float);
const uint64_t out_bytes = (uint64_t)n_tokens * kv_lora_dim * sizeof(float);
const uint64_t weight_bytes = (uint64_t)kv_lora_dim * sizeof(float);
if (!rawbuf || !outbuf ||
ds4_gpu_tensor_bytes(kv_raw) < raw_bytes ||
ds4_gpu_tensor_bytes(out) < out_bytes) {
fprintf(stderr, "ds4: Metal GLM KV RMS norm received undersized buffers\n");
return 0;
}
if (weight_offset > model_size || weight_bytes > model_size - weight_offset) {
fprintf(stderr, "ds4: Metal GLM KV RMS norm weight range is outside the mapped model\n");
return 0;
}
const bool exact_decode_weight_view =
n_tokens == 1u &&
weight_bytes <= (1ull << 20) &&
getenv("DS4_METAL_DISABLE_DECODE_NORM_EXACT_VIEWS") == NULL;
uint64_t weight_inner = 0;
id<MTLBuffer> weightbuf = exact_decode_weight_view ?
ds4_gpu_wrap_model_exact_range(model_map,
model_size,
weight_offset,
weight_bytes,
&weight_inner) :
ds4_gpu_wrap_model_range(model_map,
model_size,
weight_offset,
weight_bytes,
&weight_inner);
if (!weightbuf) return 0;
id<MTLComputePipelineState> pipeline =
ds4_gpu_hot_pipeline(g_glm_kv_lora_rms_norm_pipeline,
"kernel_glm_kv_lora_rms_norm");
if (!pipeline) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
ds4_gpu_glm_kv_lora_rms_norm_args args = {
.n_tokens = n_tokens,
.kv_raw_dim = kv_raw_dim,
.kv_lora_dim = kv_lora_dim,
.eps = eps,
};
const NSUInteger nth = ds4_gpu_rms_norm_threads(kv_lora_dim);
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:rawbuf offset:ds4_gpu_tensor_offset(kv_raw) atIndex:1];
[enc setBuffer:weightbuf offset:(NSUInteger)weight_inner atIndex:2];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(out) atIndex:3];
[enc setThreadgroupMemoryLength:nth * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)n_tokens, 1, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "GLM KV RMS norm")) return 0;
}
return 1;
}
int ds4_gpu_glm_k_b_project_typed_tensor(
ds4_gpu_tensor *out,
const ds4_gpu_tensor *kv_norm,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint32_t weight_type,
uint32_t n_tokens,
uint32_t kv_lora_dim,
uint32_t qk_nope,
uint32_t n_head) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!out || !kv_norm || !model_map ||
n_tokens == 0 || kv_lora_dim == 0 ||
qk_nope == 0 || n_head == 0) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out);
id<MTLBuffer> kvbuf = ds4_gpu_tensor_buffer(kv_norm);
uint64_t row_bytes = 0;
if (!ds4_gpu_quant_row_bytes(weight_type, qk_nope, &row_bytes)) {
fprintf(stderr, "ds4: Metal GLM k_b projection received unsupported weight type\n");
return 0;
}
const uint64_t weight_rows = (uint64_t)n_head * kv_lora_dim;
const uint64_t weight_bytes = weight_rows * row_bytes;
const uint64_t kv_bytes = (uint64_t)n_tokens * kv_lora_dim * sizeof(float);
const uint64_t out_bytes = (uint64_t)n_tokens * n_head * qk_nope * sizeof(float);
if (!outbuf || !kvbuf ||
ds4_gpu_tensor_bytes(kv_norm) < kv_bytes ||
ds4_gpu_tensor_bytes(out) < out_bytes) {
fprintf(stderr, "ds4: Metal GLM k_b projection received undersized buffers\n");
return 0;
}
if (weight_offset > model_size || weight_bytes > model_size - weight_offset) {
fprintf(stderr, "ds4: Metal GLM k_b projection range is outside the mapped model\n");
return 0;
}
const NSUInteger q_blocks = ((NSUInteger)qk_nope + 31u) / 32u;
if (q_blocks > 8u) {
fprintf(stderr, "ds4: Metal GLM k_b projection q width is too large for the tiled kernel\n");
return 0;
}
uint64_t weight_inner = 0;
id<MTLBuffer> weightbuf =
ds4_gpu_wrap_model_range(model_map,
model_size,
weight_offset,
weight_bytes,
&weight_inner);
if (!weightbuf) return 0;
id<MTLComputePipelineState> pipeline =
ds4_gpu_hot_pipeline(g_glm_k_b_project_pipeline,
"kernel_glm_k_b_project_q8_0");
if (!pipeline) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
ds4_gpu_glm_k_b_project_args args = {
.n_tokens = n_tokens,
.kv_lora_dim = kv_lora_dim,
.qk_nope = qk_nope,
.n_head = n_head,
.row_bytes = (uint32_t)row_bytes,
.weight_type = weight_type,
.pad1 = 0,
.pad2 = 0,
};
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:weightbuf offset:(NSUInteger)weight_inner atIndex:1];
[enc setBuffer:kvbuf offset:ds4_gpu_tensor_offset(kv_norm) atIndex:2];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(out) atIndex:3];
[enc setThreadgroupMemoryLength:(NSUInteger)kv_lora_dim * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)n_tokens, (NSUInteger)n_head, 1)
threadsPerThreadgroup:MTLSizeMake(32, q_blocks, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "GLM k_b projection")) return 0;
}
return 1;
}
int ds4_gpu_glm_k_b_project_tensor(
ds4_gpu_tensor *out,
const ds4_gpu_tensor *kv_norm,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint32_t n_tokens,
uint32_t kv_lora_dim,
uint32_t qk_nope,
uint32_t n_head) {
return ds4_gpu_glm_k_b_project_typed_tensor(out,
kv_norm,
model_map,
model_size,
weight_offset,
DS4_METAL_TENSOR_Q8_0,
n_tokens,
kv_lora_dim,
qk_nope,
n_head);
}
int ds4_gpu_glm_store_compact_kv_tensor(
ds4_gpu_tensor *kv_lora_cache,
ds4_gpu_tensor *k_rope_cache,
const ds4_gpu_tensor *kv_norm,
const ds4_gpu_tensor *kv_raw,
uint32_t pos0,
uint32_t n_tokens,
uint32_t cache_cap,
uint32_t kv_raw_dim,
uint32_t kv_lora_dim,
uint32_t qk_rope,
bool cache_f16) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!kv_lora_cache || !k_rope_cache || !kv_norm || !kv_raw ||
n_tokens == 0 || cache_cap == 0 ||
kv_raw_dim == 0 || kv_lora_dim == 0 ||
kv_lora_dim > kv_raw_dim ||
qk_rope > kv_raw_dim - kv_lora_dim ||
pos0 > cache_cap || n_tokens > cache_cap - pos0) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> kvcachebuf = ds4_gpu_tensor_buffer(kv_lora_cache);
id<MTLBuffer> ropecachebuf = ds4_gpu_tensor_buffer(k_rope_cache);
id<MTLBuffer> kvnormbuf = ds4_gpu_tensor_buffer(kv_norm);
id<MTLBuffer> kvrawbuf = ds4_gpu_tensor_buffer(kv_raw);
const uint64_t cache_elem_bytes = cache_f16 ? sizeof(uint16_t) : sizeof(float);
const uint64_t kv_cache_bytes =
(uint64_t)cache_cap * kv_lora_dim * cache_elem_bytes;
const uint64_t rope_cache_bytes =
(uint64_t)cache_cap * qk_rope * cache_elem_bytes;
const uint64_t kv_norm_bytes =
(uint64_t)n_tokens * kv_lora_dim * sizeof(float);
const uint64_t kv_raw_bytes =
(uint64_t)n_tokens * kv_raw_dim * sizeof(float);
if (!kvcachebuf || !ropecachebuf || !kvnormbuf || !kvrawbuf ||
ds4_gpu_tensor_bytes(kv_lora_cache) < kv_cache_bytes ||
ds4_gpu_tensor_bytes(k_rope_cache) < rope_cache_bytes ||
ds4_gpu_tensor_bytes(kv_norm) < kv_norm_bytes ||
ds4_gpu_tensor_bytes(kv_raw) < kv_raw_bytes) {
fprintf(stderr, "ds4: Metal GLM compact KV store received undersized buffers\n");
return 0;
}
id<MTLComputePipelineState> pipeline =
ds4_gpu_hot_pipeline(g_glm_store_compact_kv_pipeline,
"kernel_glm_store_compact_kv");
if (!pipeline) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
ds4_gpu_glm_store_compact_kv_args args = {
.pos0 = pos0,
.n_tokens = n_tokens,
.cache_cap = cache_cap,
.kv_raw_dim = kv_raw_dim,
.kv_lora_dim = kv_lora_dim,
.qk_rope = qk_rope,
.cache_f16 = cache_f16 ? 1u : 0u,
.pad1 = 0,
};
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:kvnormbuf offset:ds4_gpu_tensor_offset(kv_norm) atIndex:1];
[enc setBuffer:kvrawbuf offset:ds4_gpu_tensor_offset(kv_raw) atIndex:2];
[enc setBuffer:kvcachebuf offset:ds4_gpu_tensor_offset(kv_lora_cache) atIndex:3];
[enc setBuffer:ropecachebuf offset:ds4_gpu_tensor_offset(k_rope_cache) atIndex:4];
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)n_tokens, 2, 1)
threadsPerThreadgroup:MTLSizeMake(256, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "GLM compact KV store")) return 0;
}
return 1;
}
int ds4_gpu_glm_qkv_norm_store_compact_kv_tensor(
ds4_gpu_tensor *q_out,
const ds4_gpu_tensor *q,
const void *model_map,
uint64_t model_size,
uint64_t q_weight_offset,
uint32_t q_n,
ds4_gpu_tensor *kv_lora_cache,
ds4_gpu_tensor *k_rope_cache,
const ds4_gpu_tensor *kv_raw,
uint64_t kv_weight_offset,
uint32_t pos0,
uint32_t n_tokens,
uint32_t cache_cap,
uint32_t kv_raw_dim,
uint32_t kv_lora_dim,
uint32_t qk_rope,
bool cache_f16,
float eps) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!q_out || !q || !kv_lora_cache || !k_rope_cache || !kv_raw ||
!model_map || n_tokens == 0 || cache_cap == 0 ||
q_n == 0 || kv_raw_dim == 0 || kv_lora_dim == 0 ||
(q_n & 3u) != 0 || (kv_lora_dim & 3u) != 0 ||
kv_lora_dim > kv_raw_dim ||
qk_rope > kv_raw_dim - kv_lora_dim ||
pos0 > cache_cap || n_tokens > cache_cap - pos0) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> qbuf = ds4_gpu_tensor_buffer(q);
id<MTLBuffer> qoutbuf = ds4_gpu_tensor_buffer(q_out);
id<MTLBuffer> kvcachebuf = ds4_gpu_tensor_buffer(kv_lora_cache);
id<MTLBuffer> ropecachebuf = ds4_gpu_tensor_buffer(k_rope_cache);
id<MTLBuffer> kvrawbuf = ds4_gpu_tensor_buffer(kv_raw);
const uint64_t cache_elem_bytes = cache_f16 ? sizeof(uint16_t) : sizeof(float);
const uint64_t q_row_bytes = (uint64_t)q_n * sizeof(float);
const uint64_t kv_weight_bytes = (uint64_t)kv_lora_dim * sizeof(float);
const uint64_t kv_cache_bytes =
(uint64_t)cache_cap * kv_lora_dim * cache_elem_bytes;
const uint64_t rope_cache_bytes =
(uint64_t)cache_cap * qk_rope * cache_elem_bytes;
const uint64_t kv_raw_bytes =
(uint64_t)n_tokens * kv_raw_dim * sizeof(float);
if (!qbuf || !qoutbuf || !kvcachebuf || !ropecachebuf || !kvrawbuf ||
ds4_gpu_tensor_bytes(q) < q_row_bytes * n_tokens ||
ds4_gpu_tensor_bytes(q_out) < q_row_bytes * n_tokens ||
ds4_gpu_tensor_bytes(kv_lora_cache) < kv_cache_bytes ||
ds4_gpu_tensor_bytes(k_rope_cache) < rope_cache_bytes ||
ds4_gpu_tensor_bytes(kv_raw) < kv_raw_bytes) {
fprintf(stderr, "ds4: Metal GLM fused q/kv norm compact store received undersized buffers\n");
return 0;
}
if (q_weight_offset > model_size || q_row_bytes > model_size - q_weight_offset ||
kv_weight_offset > model_size || kv_weight_bytes > model_size - kv_weight_offset) {
fprintf(stderr, "ds4: Metal GLM fused q/kv norm compact store weight range is outside the mapped model\n");
return 0;
}
uint64_t q_weight_inner = 0;
id<MTLBuffer> q_weightbuf = ds4_gpu_wrap_model_range(model_map, model_size,
q_weight_offset, q_row_bytes,
&q_weight_inner);
if (!q_weightbuf) return 0;
uint64_t kv_weight_inner = 0;
id<MTLBuffer> kv_weightbuf = ds4_gpu_wrap_model_range(model_map, model_size,
kv_weight_offset, kv_weight_bytes,
&kv_weight_inner);
if (!kv_weightbuf) return 0;
id<MTLComputePipelineState> pipeline =
ds4_gpu_hot_pipeline(g_glm_qkv_norm_store_compact_kv_pipeline,
"kernel_glm_qkv_norm_store_compact_kv");
if (!pipeline) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
ds4_gpu_glm_qkv_norm_store_compact_kv_args args = {
.pos0 = pos0,
.n_tokens = n_tokens,
.cache_cap = cache_cap,
.q_n = q_n,
.q_n4 = q_n / 4u,
.kv_raw_dim = kv_raw_dim,
.kv_lora_dim = kv_lora_dim,
.kv_lora_n4 = kv_lora_dim / 4u,
.qk_rope = qk_rope,
.cache_f16 = cache_f16 ? 1u : 0u,
.eps = eps,
.pad0 = 0,
};
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:qbuf offset:ds4_gpu_tensor_offset(q) atIndex:1];
[enc setBuffer:q_weightbuf offset:(NSUInteger)q_weight_inner atIndex:2];
[enc setBuffer:qoutbuf offset:ds4_gpu_tensor_offset(q_out) atIndex:3];
[enc setBuffer:kvrawbuf offset:ds4_gpu_tensor_offset(kv_raw) atIndex:4];
[enc setBuffer:kv_weightbuf offset:(NSUInteger)kv_weight_inner atIndex:5];
[enc setBuffer:kvcachebuf offset:ds4_gpu_tensor_offset(kv_lora_cache) atIndex:6];
[enc setBuffer:ropecachebuf offset:ds4_gpu_tensor_offset(k_rope_cache) atIndex:7];
[enc setThreadgroupMemoryLength:32u * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)n_tokens, 3, 1)
threadsPerThreadgroup:MTLSizeMake(ds4_gpu_rms_norm_threads(q_n), 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "GLM fused q/kv norm compact store")) return 0;
}
return 1;
}
int ds4_gpu_glm_store_indexer_k_tensor(
ds4_gpu_tensor *indexer_key_cache,
const ds4_gpu_tensor *raw_k,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint64_t bias_offset,
uint32_t pos0,
uint32_t n_tokens,
uint32_t cache_cap,
uint32_t head_dim,
uint32_t rot_dim,
uint32_t n_ctx_orig,
float eps,
float freq_base,
float freq_scale,
float ext_factor,
float attn_factor,
float beta_fast,
float beta_slow,
bool cache_f16) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!indexer_key_cache || !raw_k || !model_map ||
n_tokens == 0 || cache_cap == 0 ||
head_dim == 0 || rot_dim == 0 ||
rot_dim > head_dim || (rot_dim & 1u) != 0 ||
pos0 > cache_cap || n_tokens > cache_cap - pos0) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> cachebuf = ds4_gpu_tensor_buffer(indexer_key_cache);
id<MTLBuffer> rawbuf = ds4_gpu_tensor_buffer(raw_k);
const uint64_t cache_elem_bytes = cache_f16 ? sizeof(uint16_t) : sizeof(float);
const uint64_t cache_bytes =
(uint64_t)cache_cap * head_dim * cache_elem_bytes;
const uint64_t raw_bytes =
(uint64_t)n_tokens * head_dim * sizeof(float);
const uint64_t norm_bytes = (uint64_t)head_dim * sizeof(float);
if (!cachebuf || !rawbuf ||
ds4_gpu_tensor_bytes(indexer_key_cache) < cache_bytes ||
ds4_gpu_tensor_bytes(raw_k) < raw_bytes) {
fprintf(stderr, "ds4: Metal GLM indexer K store received undersized buffers\n");
return 0;
}
if (weight_offset > model_size || norm_bytes > model_size - weight_offset ||
bias_offset > model_size || norm_bytes > model_size - bias_offset) {
fprintf(stderr, "ds4: Metal GLM indexer K norm range is outside the mapped model\n");
return 0;
}
uint64_t weight_inner = 0;
uint64_t bias_inner = 0;
id<MTLBuffer> weightbuf =
ds4_gpu_wrap_model_range(model_map, model_size,
weight_offset, norm_bytes,
&weight_inner);
id<MTLBuffer> biasbuf =
ds4_gpu_wrap_model_range(model_map, model_size,
bias_offset, norm_bytes,
&bias_inner);
if (!weightbuf || !biasbuf) return 0;
id<MTLComputePipelineState> pipeline =
ds4_gpu_hot_pipeline(g_glm_store_indexer_k_pipeline,
"kernel_glm_store_indexer_k");
if (!pipeline) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
ds4_gpu_glm_store_indexer_k_args args = {
.pos0 = pos0,
.n_tokens = n_tokens,
.cache_cap = cache_cap,
.head_dim = head_dim,
.rot_dim = rot_dim,
.n_ctx_orig = n_ctx_orig,
.cache_f16 = cache_f16 ? 1u : 0u,
.pad0 = 0,
.eps = eps,
.freq_base = freq_base,
.freq_scale = freq_scale,
.ext_factor = ext_factor,
.attn_factor = attn_factor,
.beta_fast = beta_fast,
.beta_slow = beta_slow,
.pad1 = 0.0f,
};
const NSUInteger nth = ds4_gpu_rms_norm_threads(head_dim);
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:rawbuf offset:ds4_gpu_tensor_offset(raw_k) atIndex:1];
[enc setBuffer:weightbuf offset:(NSUInteger)weight_inner atIndex:2];
[enc setBuffer:biasbuf offset:(NSUInteger)bias_inner atIndex:3];
[enc setBuffer:cachebuf offset:ds4_gpu_tensor_offset(indexer_key_cache) atIndex:4];
[enc setThreadgroupMemoryLength:nth * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)n_tokens, 1, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "GLM indexer K store")) return 0;
}
return 1;
}
int ds4_gpu_glm53_indexer_pool_update_tensor(
ds4_gpu_tensor *pool_cache,
ds4_gpu_tensor *tail_k,
ds4_gpu_tensor *tail_gate,
const ds4_gpu_tensor *raw_k,
const ds4_gpu_tensor *gate,
const void *model_map,
uint64_t model_size,
uint64_t norm_weight_offset,
uint64_t norm_bias_offset,
uint64_t ape_offset,
uint32_t pos0,
uint32_t n_tokens,
uint32_t cache_cap,
uint32_t head_dim,
uint32_t pool_size,
float eps,
bool cache_f16) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!pool_cache || !tail_k || !tail_gate || !raw_k || !gate || !model_map ||
n_tokens == 0 || cache_cap == 0 || head_dim == 0 || pool_size != 4u ||
head_dim > 1024u || !isfinite(eps) || eps <= 0.0f ||
pos0 > cache_cap || n_tokens > cache_cap - pos0) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> poolbuf = ds4_gpu_tensor_buffer(pool_cache);
id<MTLBuffer> tailkbuf = ds4_gpu_tensor_buffer(tail_k);
id<MTLBuffer> tailgatebuf = ds4_gpu_tensor_buffer(tail_gate);
id<MTLBuffer> rawbuf = ds4_gpu_tensor_buffer(raw_k);
id<MTLBuffer> gatebuf = ds4_gpu_tensor_buffer(gate);
const uint64_t pool_cap =
((uint64_t)cache_cap + pool_size - 1u) / pool_size;
const uint64_t cache_elem_bytes = cache_f16 ? sizeof(uint16_t) : sizeof(float);
const uint64_t pool_bytes = pool_cap * head_dim * cache_elem_bytes;
const uint64_t tail_bytes = (uint64_t)pool_size * head_dim * sizeof(float);
const uint64_t input_bytes = (uint64_t)n_tokens * head_dim * sizeof(float);
const uint64_t norm_bytes = (uint64_t)head_dim * sizeof(float);
const uint64_t ape_bytes =
(uint64_t)pool_size * head_dim * sizeof(uint16_t);
if (!poolbuf || !tailkbuf || !tailgatebuf || !rawbuf || !gatebuf ||
ds4_gpu_tensor_bytes(pool_cache) < pool_bytes ||
ds4_gpu_tensor_bytes(tail_k) < tail_bytes ||
ds4_gpu_tensor_bytes(tail_gate) < tail_bytes ||
ds4_gpu_tensor_bytes(raw_k) < input_bytes ||
ds4_gpu_tensor_bytes(gate) < input_bytes) {
fprintf(stderr, "ds4: Metal GLM-5.3 indexer pool received undersized buffers\n");
return 0;
}
if (norm_weight_offset > model_size ||
norm_bytes > model_size - norm_weight_offset ||
norm_bias_offset > model_size ||
norm_bytes > model_size - norm_bias_offset ||
ape_offset > model_size || ape_bytes > model_size - ape_offset) {
fprintf(stderr, "ds4: Metal GLM-5.3 indexer pool model range is invalid\n");
return 0;
}
uint64_t norm_weight_inner = 0;
uint64_t norm_bias_inner = 0;
uint64_t ape_inner = 0;
id<MTLBuffer> norm_weightbuf = ds4_gpu_wrap_model_range(
model_map, model_size, norm_weight_offset, norm_bytes, &norm_weight_inner);
id<MTLBuffer> norm_biasbuf = ds4_gpu_wrap_model_range(
model_map, model_size, norm_bias_offset, norm_bytes, &norm_bias_inner);
id<MTLBuffer> apebuf = ds4_gpu_wrap_model_range(
model_map, model_size, ape_offset, ape_bytes, &ape_inner);
if (!norm_weightbuf || !norm_biasbuf || !apebuf) return 0;
id<MTLComputePipelineState> pipeline = ds4_gpu_hot_pipeline(
g_glm53_indexer_pool_update_pipeline,
"kernel_glm53_indexer_pool_update");
if (!pipeline) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBuffer:norm_weightbuf offset:(NSUInteger)norm_weight_inner atIndex:3];
[enc setBuffer:norm_biasbuf offset:(NSUInteger)norm_bias_inner atIndex:4];
[enc setBuffer:apebuf offset:(NSUInteger)ape_inner atIndex:5];
[enc setBuffer:poolbuf offset:ds4_gpu_tensor_offset(pool_cache) atIndex:6];
[enc setBuffer:tailkbuf offset:ds4_gpu_tensor_offset(tail_k) atIndex:7];
[enc setBuffer:tailgatebuf offset:ds4_gpu_tensor_offset(tail_gate) atIndex:8];
[enc setThreadgroupMemoryLength:
((NSUInteger)pool_size * head_dim + 2u * pool_size) * sizeof(float)
atIndex:0];
uint32_t done = 0;
const uint32_t leading = pos0 % pool_size;
if (leading != 0u) {
uint32_t chunk = pool_size - leading;
if (chunk > n_tokens) chunk = n_tokens;
ds4_gpu_glm53_indexer_pool_update_args args = {
.pos0 = pos0,
.n_tokens = chunk,
.cache_cap = cache_cap,
.head_dim = head_dim,
.pool_size = pool_size,
.cache_f16 = cache_f16 ? 1u : 0u,
.eps = eps,
.pad0 = 0,
};
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:rawbuf offset:ds4_gpu_tensor_offset(raw_k) atIndex:1];
[enc setBuffer:gatebuf offset:ds4_gpu_tensor_offset(gate) atIndex:2];
[enc dispatchThreadgroups:MTLSizeMake(1, 1, 1)
threadsPerThreadgroup:MTLSizeMake(head_dim, 1, 1)];
done = chunk;
}
if (done < n_tokens) {
const uint32_t chunk = n_tokens - done;
ds4_gpu_glm53_indexer_pool_update_args args = {
.pos0 = pos0 + done,
.n_tokens = chunk,
.cache_cap = cache_cap,
.head_dim = head_dim,
.pool_size = pool_size,
.cache_f16 = cache_f16 ? 1u : 0u,
.eps = eps,
.pad0 = 0,
};
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:rawbuf
offset:ds4_gpu_tensor_offset(raw_k) +
(NSUInteger)((uint64_t)done * head_dim * sizeof(float))
atIndex:1];
[enc setBuffer:gatebuf
offset:ds4_gpu_tensor_offset(gate) +
(NSUInteger)((uint64_t)done * head_dim * sizeof(float))
atIndex:2];
const uint32_t groups = (chunk + pool_size - 1u) / pool_size;
[enc dispatchThreadgroups:MTLSizeMake(groups, 1, 1)
threadsPerThreadgroup:MTLSizeMake(head_dim, 1, 1)];
}
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "GLM-5.3 indexer pool update")) {
return 0;
}
}
return 1;
}
int ds4_gpu_glm53_expand_pool_selection_tensor(
ds4_gpu_tensor *raw_selected,
const ds4_gpu_tensor *pool_selected,
uint32_t n_tokens,
uint32_t pos0,
uint32_t selected_pools,
uint32_t index_topk,
uint32_t pool_size,
uint32_t output_width) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!raw_selected || !pool_selected || n_tokens == 0 ||
selected_pools == 0 || index_topk == 0 || pool_size == 0 ||
selected_pools > index_topk / pool_size ||
output_width < index_topk || output_width < index_topk + pool_size - 1u) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> rawbuf = ds4_gpu_tensor_buffer(raw_selected);
id<MTLBuffer> poolbuf = ds4_gpu_tensor_buffer(pool_selected);
const uint64_t raw_bytes =
(uint64_t)n_tokens * output_width * sizeof(uint32_t);
const uint64_t pool_bytes =
(uint64_t)n_tokens * selected_pools * sizeof(uint32_t);
if (!rawbuf || !poolbuf ||
ds4_gpu_tensor_bytes(raw_selected) < raw_bytes ||
ds4_gpu_tensor_bytes(pool_selected) < pool_bytes) {
fprintf(stderr, "ds4: Metal GLM-5.3 pool expansion received undersized buffers\n");
return 0;
}
id<MTLComputePipelineState> pipeline = ds4_gpu_hot_pipeline(
g_glm53_expand_pool_selection_pipeline,
"kernel_glm53_expand_pool_selection");
if (!pipeline) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
ds4_gpu_glm53_expand_pool_selection_args args = {
.n_tokens = n_tokens,
.pos0 = pos0,
.selected_pools = selected_pools,
.index_topk = index_topk,
.pool_size = pool_size,
.output_width = output_width,
};
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:poolbuf offset:ds4_gpu_tensor_offset(pool_selected) atIndex:1];
[enc setBuffer:rawbuf offset:ds4_gpu_tensor_offset(raw_selected) atIndex:2];
const NSUInteger total = (NSUInteger)n_tokens * output_width;
const NSUInteger nth = MIN((NSUInteger)256, pipeline.maxTotalThreadsPerThreadgroup);
[enc dispatchThreads:MTLSizeMake(total, 1, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "GLM-5.3 pool selection expansion")) {
return 0;
}
}
return 1;
}
int ds4_gpu_glm_build_kv_cache_tensor(
ds4_gpu_tensor *key_cache,
ds4_gpu_tensor *value_cache,
const ds4_gpu_tensor *kv_raw,
const ds4_gpu_tensor *k_nope,
const ds4_gpu_tensor *value,
uint32_t pos0,
uint32_t n_tokens,
uint32_t cache_cap,
uint32_t n_head,
uint32_t kv_raw_dim,
uint32_t kv_lora_dim,
uint32_t qk_nope,
uint32_t qk_rope,
uint32_t value_dim,
uint32_t n_ctx_orig,
float freq_base,
float freq_scale,
float ext_factor,
float attn_factor,
float beta_fast,
float beta_slow,
bool cache_f16) {
if (!g_initialized && !ds4_gpu_init()) return 0;
const uint32_t qk_dim = qk_nope + qk_rope;
if (!key_cache || !value_cache || !kv_raw || !k_nope || !value ||
n_tokens == 0 || cache_cap == 0 || n_head == 0 ||
kv_raw_dim == 0 || kv_lora_dim == 0 ||
qk_nope == 0 || value_dim == 0 ||
kv_lora_dim + qk_rope > kv_raw_dim ||
qk_dim < qk_nope || (qk_rope & 1u) != 0 ||
pos0 > cache_cap || n_tokens > cache_cap - pos0 ||
(qk_rope != 0 && (!isfinite(freq_base) || freq_base <= 0.0f)) ||
(qk_rope != 0 && (!isfinite(freq_scale) || freq_scale <= 0.0f)) ||
!isfinite(ext_factor) || !isfinite(attn_factor) ||
!isfinite(beta_fast) || !isfinite(beta_slow)) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> keybuf = ds4_gpu_tensor_buffer(key_cache);
id<MTLBuffer> valbuf = ds4_gpu_tensor_buffer(value_cache);
id<MTLBuffer> rawbuf = ds4_gpu_tensor_buffer(kv_raw);
id<MTLBuffer> knbuf = ds4_gpu_tensor_buffer(k_nope);
id<MTLBuffer> vbuf = ds4_gpu_tensor_buffer(value);
const uint64_t cache_elem_bytes = cache_f16 ? sizeof(uint16_t) : sizeof(float);
const uint64_t key_bytes = (uint64_t)cache_cap * n_head * qk_dim * cache_elem_bytes;
const uint64_t cache_value_bytes = (uint64_t)cache_cap * n_head * value_dim * cache_elem_bytes;
const uint64_t raw_bytes = (uint64_t)n_tokens * kv_raw_dim * sizeof(float);
const uint64_t kn_bytes = (uint64_t)n_tokens * n_head * qk_nope * sizeof(float);
const uint64_t value_bytes = (uint64_t)n_tokens * n_head * value_dim * sizeof(float);
if (!keybuf || !valbuf || !rawbuf || !knbuf || !vbuf ||
ds4_gpu_tensor_bytes(key_cache) < key_bytes ||
ds4_gpu_tensor_bytes(value_cache) < cache_value_bytes ||
ds4_gpu_tensor_bytes(kv_raw) < raw_bytes ||
ds4_gpu_tensor_bytes(k_nope) < kn_bytes ||
ds4_gpu_tensor_bytes(value) < value_bytes) {
fprintf(stderr, "ds4: Metal GLM KV cache builder received undersized buffers\n");
return 0;
}
const bool decode_group4 =
n_tokens == 1u &&
n_head >= 4u &&
ds4_gpu_env_bool("DS4_METAL_DISABLE_GLM_DECODE_KV_GROUP4") <= 0;
id<MTLComputePipelineState> pipeline =
decode_group4 ?
ds4_gpu_hot_pipeline(g_glm_build_kv_cache_decode_group4_pipeline,
"kernel_glm_build_kv_cache_decode_group4") :
ds4_gpu_hot_pipeline(g_glm_build_kv_cache_pipeline,
"kernel_glm_build_kv_cache");
if (!pipeline) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
ds4_gpu_glm_build_kv_cache_args args = {
.pos0 = pos0,
.n_tokens = n_tokens,
.cache_cap = cache_cap,
.n_head = n_head,
.kv_raw_dim = kv_raw_dim,
.kv_lora_dim = kv_lora_dim,
.qk_nope = qk_nope,
.qk_rope = qk_rope,
.value_dim = value_dim,
.n_ctx_orig = n_ctx_orig,
.cache_f16 = cache_f16 ? 1u : 0u,
.pad0 = 0u,
.freq_base = freq_base,
.freq_scale = freq_scale,
.ext_factor = ext_factor,
.attn_factor = attn_factor,
.beta_fast = beta_fast,
.beta_slow = beta_slow,
};
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:rawbuf offset:ds4_gpu_tensor_offset(kv_raw) atIndex:1];
[enc setBuffer:knbuf offset:ds4_gpu_tensor_offset(k_nope) atIndex:2];
[enc setBuffer:vbuf offset:ds4_gpu_tensor_offset(value) atIndex:3];
[enc setBuffer:keybuf offset:ds4_gpu_tensor_offset(key_cache) atIndex:4];
[enc setBuffer:valbuf offset:ds4_gpu_tensor_offset(value_cache) atIndex:5];
const NSUInteger group_y = decode_group4 ?
((NSUInteger)n_head + 3u) / 4u :
(NSUInteger)n_head;
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)n_tokens, group_y, 1)
threadsPerThreadgroup:MTLSizeMake(256, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "GLM KV cache build")) return 0;
}
return 1;
}
int ds4_gpu_glm_build_kv_cache_flash_tensor(
ds4_gpu_tensor *key_cache,
ds4_gpu_tensor *value_cache,
const ds4_gpu_tensor *kv_raw,
const ds4_gpu_tensor *k_nope,
const ds4_gpu_tensor *value,
uint32_t pos0,
uint32_t n_tokens,
uint32_t cache_cap,
uint32_t n_head,
uint32_t kv_raw_dim,
uint32_t kv_lora_dim,
uint32_t qk_nope,
uint32_t qk_rope,
uint32_t value_dim,
uint32_t n_ctx_orig,
float freq_base,
float freq_scale,
float ext_factor,
float attn_factor,
float beta_fast,
float beta_slow,
bool cache_f16) {
if (!g_initialized && !ds4_gpu_init()) return 0;
const uint32_t qk_dim = qk_nope + qk_rope;
if (!key_cache || !value_cache || !kv_raw || !k_nope || !value ||
pos0 != 0 || n_tokens == 0 || cache_cap == 0 || n_head == 0 ||
kv_raw_dim == 0 || kv_lora_dim == 0 ||
qk_nope == 0 || value_dim == 0 ||
kv_lora_dim + qk_rope > kv_raw_dim ||
qk_dim < qk_nope || (qk_rope & 1u) != 0 ||
n_tokens > cache_cap ||
(qk_rope != 0 && (!isfinite(freq_base) || freq_base <= 0.0f)) ||
(qk_rope != 0 && (!isfinite(freq_scale) || freq_scale <= 0.0f)) ||
!isfinite(ext_factor) || !isfinite(attn_factor) ||
!isfinite(beta_fast) || !isfinite(beta_slow)) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> keybuf = ds4_gpu_tensor_buffer(key_cache);
id<MTLBuffer> valbuf = ds4_gpu_tensor_buffer(value_cache);
id<MTLBuffer> rawbuf = ds4_gpu_tensor_buffer(kv_raw);
id<MTLBuffer> knbuf = ds4_gpu_tensor_buffer(k_nope);
id<MTLBuffer> vbuf = ds4_gpu_tensor_buffer(value);
const uint64_t cache_elem_bytes = cache_f16 ? sizeof(uint16_t) : sizeof(float);
const uint64_t key_bytes = (uint64_t)cache_cap * n_head * qk_dim * cache_elem_bytes;
const uint64_t cache_value_bytes = (uint64_t)cache_cap * n_head * value_dim * cache_elem_bytes;
const uint64_t raw_bytes = (uint64_t)n_tokens * kv_raw_dim * sizeof(float);
const uint64_t kn_bytes = (uint64_t)n_tokens * n_head * qk_nope * sizeof(float);
const uint64_t value_bytes = (uint64_t)n_tokens * n_head * value_dim * sizeof(float);
if (!keybuf || !valbuf || !rawbuf || !knbuf || !vbuf ||
ds4_gpu_tensor_bytes(key_cache) < key_bytes ||
ds4_gpu_tensor_bytes(value_cache) < cache_value_bytes ||
ds4_gpu_tensor_bytes(kv_raw) < raw_bytes ||
ds4_gpu_tensor_bytes(k_nope) < kn_bytes ||
ds4_gpu_tensor_bytes(value) < value_bytes) {
fprintf(stderr, "ds4: Metal GLM staged KV cache builder received undersized buffers\n");
return 0;
}
const NSUInteger q_row_bytes_f16 = (NSUInteger)qk_dim * sizeof(uint16_t);
const NSUInteger v_row_bytes_f16 = (NSUInteger)value_dim * sizeof(uint16_t);
const NSUInteger key_f16_offset = 0;
const NSUInteger key_f16_bytes =
(NSUInteger)n_tokens * (NSUInteger)n_head * q_row_bytes_f16;
const NSUInteger value_f16_offset = key_f16_bytes;
const NSUInteger value_f16_bytes =
(NSUInteger)n_tokens * (NSUInteger)n_head * v_row_bytes_f16;
const NSUInteger kv_f16_bytes = key_f16_bytes + value_f16_bytes;
if (!ds4_gpu_ensure_scratch_buffer(&g_flash_attn_kv_buffer,
&g_flash_attn_kv_bytes,
kv_f16_bytes,
"ds4_glm_flash_attn_kv_f16")) {
return 0;
}
id<MTLComputePipelineState> pipeline =
ds4_gpu_hot_pipeline(g_glm_build_kv_cache_flash_pipeline,
"kernel_glm_build_kv_cache_flash");
if (!pipeline) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
ds4_gpu_glm_build_kv_cache_args args = {
.pos0 = pos0,
.n_tokens = n_tokens,
.cache_cap = cache_cap,
.n_head = n_head,
.kv_raw_dim = kv_raw_dim,
.kv_lora_dim = kv_lora_dim,
.qk_nope = qk_nope,
.qk_rope = qk_rope,
.value_dim = value_dim,
.n_ctx_orig = n_ctx_orig,
.cache_f16 = cache_f16 ? 1u : 0u,
.pad0 = 0u,
.freq_base = freq_base,
.freq_scale = freq_scale,
.ext_factor = ext_factor,
.attn_factor = attn_factor,
.beta_fast = beta_fast,
.beta_slow = beta_slow,
};
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:rawbuf offset:ds4_gpu_tensor_offset(kv_raw) atIndex:1];
[enc setBuffer:knbuf offset:ds4_gpu_tensor_offset(k_nope) atIndex:2];
[enc setBuffer:vbuf offset:ds4_gpu_tensor_offset(value) atIndex:3];
[enc setBuffer:keybuf offset:ds4_gpu_tensor_offset(key_cache) atIndex:4];
[enc setBuffer:valbuf offset:ds4_gpu_tensor_offset(value_cache) atIndex:5];
[enc setBuffer:g_flash_attn_kv_buffer offset:key_f16_offset atIndex:6];
[enc setBuffer:g_flash_attn_kv_buffer offset:value_f16_offset atIndex:7];
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)n_tokens, (NSUInteger)n_head, 1)
threadsPerThreadgroup:MTLSizeMake(256, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "GLM staged KV cache build")) return 0;
}
return 1;
}
static int ds4_gpu_glm_attention_flash_tensor_impl(
ds4_gpu_tensor *heads,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *key_cache,
const ds4_gpu_tensor *value_cache,
uint32_t pos0,
uint32_t n_tokens,
uint32_t cache_len,
uint32_t cache_cap,
uint32_t n_head,
uint32_t qk_dim,
uint32_t value_dim,
bool cache_f16,
int kv_pre_staged) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!heads || !q || !key_cache || !value_cache ||
n_tokens == 0 || cache_len == 0 || cache_cap == 0 ||
n_head == 0 || qk_dim != 256u || value_dim != 256u ||
cache_len > cache_cap ||
pos0 > cache_len || n_tokens > cache_len - pos0 ||
cache_len > ds4_gpu_glm_flash_attention_max_cache_len()) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> headsbuf = ds4_gpu_tensor_buffer(heads);
id<MTLBuffer> qbuf = ds4_gpu_tensor_buffer(q);
id<MTLBuffer> keybuf = ds4_gpu_tensor_buffer(key_cache);
id<MTLBuffer> valbuf = ds4_gpu_tensor_buffer(value_cache);
const uint64_t heads_bytes = (uint64_t)n_tokens * n_head * value_dim * sizeof(float);
const uint64_t q_bytes = (uint64_t)n_tokens * n_head * qk_dim * sizeof(float);
const uint64_t cache_elem_bytes = cache_f16 ? sizeof(uint16_t) : sizeof(float);
const uint64_t key_bytes = (uint64_t)cache_cap * n_head * qk_dim * cache_elem_bytes;
const uint64_t value_bytes = (uint64_t)cache_cap * n_head * value_dim * cache_elem_bytes;
if (!headsbuf || !qbuf || !keybuf || !valbuf ||
ds4_gpu_tensor_bytes(heads) < heads_bytes ||
ds4_gpu_tensor_bytes(q) < q_bytes ||
ds4_gpu_tensor_bytes(key_cache) < key_bytes ||
ds4_gpu_tensor_bytes(value_cache) < value_bytes) {
fprintf(stderr, "ds4: Metal GLM FlashAttention received undersized buffers\n");
return 0;
}
const uint64_t key_elems = (uint64_t)cache_len * n_head * qk_dim;
const uint64_t value_elems = (uint64_t)cache_len * n_head * value_dim;
if (key_elems > UINT32_MAX || value_elems > UINT32_MAX) {
return 0;
}
const uint32_t nqptg = 8;
const uint32_t ncpsg = 64;
const uint32_t nsg = 4;
const bool has_kvpad = (cache_len % ncpsg) != 0;
const bool bc_mask = (n_tokens % nqptg) != 0;
const NSUInteger q_row_bytes = (NSUInteger)qk_dim * sizeof(float);
const NSUInteger q_row_bytes_f16 = (NSUInteger)qk_dim * sizeof(uint16_t);
const NSUInteger v_row_bytes = (NSUInteger)value_dim * sizeof(float);
const NSUInteger v_row_bytes_f16 = (NSUInteger)value_dim * sizeof(uint16_t);
const NSUInteger mask_bytes = (NSUInteger)n_tokens * (NSUInteger)cache_len * sizeof(uint16_t);
const NSUInteger key_f16_offset = 0;
const NSUInteger key_f16_bytes =
(NSUInteger)cache_len * (NSUInteger)n_head * q_row_bytes_f16;
const NSUInteger value_f16_offset = key_f16_bytes;
const NSUInteger value_f16_bytes =
(NSUInteger)cache_len * (NSUInteger)n_head * v_row_bytes_f16;
const NSUInteger kv_f16_bytes = key_f16_bytes + value_f16_bytes;
const NSUInteger pad_bytes = has_kvpad
? (NSUInteger)ncpsg * ((NSUInteger)n_head * (q_row_bytes_f16 + v_row_bytes_f16) +
(NSUInteger)n_tokens * sizeof(uint16_t))
: 1u;
const NSUInteger nblk0 = ((NSUInteger)cache_len + ncpsg - 1u) / ncpsg;
const NSUInteger nblk1 = ((NSUInteger)n_tokens + nqptg - 1u) / nqptg;
const NSUInteger blk_bytes = ds4_gpu_align_up_ns(nblk0 * nblk1, 32u);
id<MTLBuffer> mask_buffer =
ds4_gpu_glm_prefill_mask_buffer(pos0, n_tokens, cache_len, mask_bytes);
if (!mask_buffer) return 0;
if (kv_pre_staged) {
if (!g_flash_attn_kv_buffer || g_flash_attn_kv_bytes < kv_f16_bytes) {
fprintf(stderr, "ds4: GLM staged FlashAttention KV scratch is missing\n");
return 0;
}
} else if (!ds4_gpu_ensure_scratch_buffer(&g_flash_attn_kv_buffer,
&g_flash_attn_kv_bytes,
kv_f16_bytes,
"ds4_glm_flash_attn_kv_f16")) {
return 0;
}
if (!ds4_gpu_ensure_scratch_buffer(&g_flash_attn_pad_buffer,
&g_flash_attn_pad_bytes,
pad_bytes,
"ds4_glm_flash_attn_pad") ||
!ds4_gpu_ensure_scratch_buffer(&g_flash_attn_blk_buffer,
&g_flash_attn_blk_bytes,
blk_bytes,
"ds4_glm_flash_attn_blk")) {
return 0;
}
id<MTLComputePipelineState> pad_pipeline = nil;
if (has_kvpad) {
pad_pipeline = ds4_gpu_get_flash_attn_pad_pipeline(true, (int32_t)ncpsg);
if (!pad_pipeline) return 0;
}
id<MTLComputePipelineState> blk_pipeline =
ds4_gpu_get_flash_attn_blk_pipeline((int32_t)nqptg, (int32_t)ncpsg);
id<MTLComputePipelineState> attn_pipeline =
ds4_gpu_get_flash_attn_pipeline("kernel_flash_attn_ext_f16_dk256_dv256",
true, false, false, false, has_kvpad, bc_mask,
(int32_t)qk_dim,
(int32_t)value_dim,
(int32_t)nsg);
if (!blk_pipeline || !attn_pipeline) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
if (!kv_pre_staged) {
const bool copied = cache_f16 ?
(ds4_gpu_encode_cpy_f16_f16_3d(cb,
keybuf,
ds4_gpu_tensor_offset(key_cache),
g_flash_attn_kv_buffer,
key_f16_offset,
qk_dim,
cache_len,
n_head,
(uint64_t)n_head * q_row_bytes_f16,
q_row_bytes_f16,
q_row_bytes_f16,
(uint64_t)cache_len * q_row_bytes_f16) &&
ds4_gpu_encode_cpy_f16_f16_3d(cb,
valbuf,
ds4_gpu_tensor_offset(value_cache),
g_flash_attn_kv_buffer,
value_f16_offset,
value_dim,
cache_len,
n_head,
(uint64_t)n_head * v_row_bytes_f16,
v_row_bytes_f16,
v_row_bytes_f16,
(uint64_t)cache_len * v_row_bytes_f16)) :
(ds4_gpu_encode_cpy_f32_f16_3d(cb,
keybuf,
ds4_gpu_tensor_offset(key_cache),
g_flash_attn_kv_buffer,
key_f16_offset,
qk_dim,
cache_len,
n_head,
(uint64_t)n_head * q_row_bytes,
q_row_bytes,
q_row_bytes_f16,
(uint64_t)cache_len * q_row_bytes_f16) &&
ds4_gpu_encode_cpy_f32_f16_3d(cb,
valbuf,
ds4_gpu_tensor_offset(value_cache),
g_flash_attn_kv_buffer,
value_f16_offset,
value_dim,
cache_len,
n_head,
(uint64_t)n_head * v_row_bytes,
v_row_bytes,
v_row_bytes_f16,
(uint64_t)cache_len * v_row_bytes_f16));
if (!copied) {
return 0;
}
}
if (has_kvpad) {
ds4_gpu_flash_attn_pad_args pad_args = {
.ne11 = (int32_t)cache_len,
.ne_12_2 = (int32_t)n_head,
.ne_12_3 = 1,
.nb11 = q_row_bytes_f16,
.nb12 = (uint64_t)cache_len * q_row_bytes_f16,
.nb13 = (uint64_t)cache_len * (uint64_t)n_head * q_row_bytes_f16,
.nb21 = v_row_bytes_f16,
.nb22 = (uint64_t)cache_len * v_row_bytes_f16,
.nb23 = (uint64_t)cache_len * (uint64_t)n_head * v_row_bytes_f16,
.ne31 = (int32_t)n_tokens,
.ne32 = 1,
.ne33 = 1,
.nb31 = (uint64_t)cache_len * sizeof(uint16_t),
.nb32 = mask_bytes,
.nb33 = mask_bytes,
};
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pad_pipeline];
[enc setBytes:&pad_args length:sizeof(pad_args) atIndex:0];
[enc setBuffer:g_flash_attn_kv_buffer offset:key_f16_offset atIndex:1];
[enc setBuffer:g_flash_attn_kv_buffer offset:value_f16_offset atIndex:2];
[enc setBuffer:mask_buffer offset:0 atIndex:3];
[enc setBuffer:g_flash_attn_pad_buffer offset:0 atIndex:4];
[enc dispatchThreadgroups:MTLSizeMake(ncpsg, n_head, 1)
threadsPerThreadgroup:MTLSizeMake(32, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
}
ds4_gpu_flash_attn_blk_args blk_args = {
.ne01 = (int32_t)n_tokens,
.ne30 = (int32_t)cache_len,
.ne31 = (int32_t)n_tokens,
.ne32 = 1,
.ne33 = 1,
.nb31 = (uint64_t)cache_len * sizeof(uint16_t),
.nb32 = mask_bytes,
.nb33 = mask_bytes,
};
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:blk_pipeline];
[enc setBytes:&blk_args length:sizeof(blk_args) atIndex:0];
[enc setBuffer:mask_buffer offset:0 atIndex:1];
[enc setBuffer:g_flash_attn_blk_buffer offset:0 atIndex:2];
[enc dispatchThreadgroups:MTLSizeMake(nblk0, nblk1, 1)
threadsPerThreadgroup:MTLSizeMake(32, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
ds4_gpu_flash_attn_vec_args args = {
.ne01 = (int32_t)n_tokens,
.ne02 = (int32_t)n_head,
.ne03 = 1,
.nb01 = (uint64_t)n_head * q_row_bytes,
.nb02 = q_row_bytes,
.nb03 = (uint64_t)n_tokens * n_head * q_row_bytes,
.ne11 = (int32_t)cache_len,
.ne_12_2 = (int32_t)n_head,
.ne_12_3 = 1,
.ns10 = (int32_t)qk_dim,
.nb11 = q_row_bytes_f16,
.nb12 = (uint64_t)cache_len * q_row_bytes_f16,
.nb13 = (uint64_t)cache_len * (uint64_t)n_head * q_row_bytes_f16,
.ns20 = (int32_t)value_dim,
.nb21 = v_row_bytes_f16,
.nb22 = (uint64_t)cache_len * v_row_bytes_f16,
.nb23 = (uint64_t)cache_len * (uint64_t)n_head * v_row_bytes_f16,
.ne31 = (int32_t)n_tokens,
.ne32 = 1,
.ne33 = 1,
.nb31 = (uint64_t)cache_len * sizeof(uint16_t),
.nb32 = mask_bytes,
.nb33 = mask_bytes,
.ne1 = (int32_t)n_head,
.ne2 = (int32_t)n_tokens,
.ne3 = 1,
.scale = 1.0f / sqrtf((float)qk_dim),
.max_bias = 0.0f,
.m0 = 0.0f,
.m1 = 0.0f,
.n_head_log2 = 0,
.logit_softcap = 0.0f,
};
const NSUInteger padded_v = ds4_gpu_align_up_ns(value_dim, 64u);
const NSUInteger shared_elems = (NSUInteger)nqptg *
((NSUInteger)qk_dim + 2u * padded_v + 2u * (2u * (NSUInteger)ncpsg));
const NSUInteger shared_bytes = ds4_gpu_align_up_ns(shared_elems * (sizeof(float) / 2u), 16u);
enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:attn_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:qbuf offset:ds4_gpu_tensor_offset(q) atIndex:1];
[enc setBuffer:g_flash_attn_kv_buffer offset:key_f16_offset atIndex:2];
[enc setBuffer:g_flash_attn_kv_buffer offset:value_f16_offset atIndex:3];
[enc setBuffer:mask_buffer offset:0 atIndex:4];
[enc setBuffer:qbuf offset:ds4_gpu_tensor_offset(q) atIndex:5];
[enc setBuffer:g_flash_attn_pad_buffer offset:0 atIndex:6];
[enc setBuffer:g_flash_attn_blk_buffer offset:0 atIndex:7];
[enc setBuffer:headsbuf offset:ds4_gpu_tensor_offset(heads) atIndex:8];
[enc setThreadgroupMemoryLength:shared_bytes atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(nblk1, n_head, 1)
threadsPerThreadgroup:MTLSizeMake(32, nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "GLM FlashAttention")) return 0;
}
return 1;
}
int ds4_gpu_glm_attention_flash_tensor(
ds4_gpu_tensor *heads,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *key_cache,
const ds4_gpu_tensor *value_cache,
uint32_t pos0,
uint32_t n_tokens,
uint32_t cache_len,
uint32_t cache_cap,
uint32_t n_head,
uint32_t qk_dim,
uint32_t value_dim,
bool cache_f16) {
return ds4_gpu_glm_attention_flash_tensor_impl(heads,
q,
key_cache,
value_cache,
pos0,
n_tokens,
cache_len,
cache_cap,
n_head,
qk_dim,
value_dim,
cache_f16,
0);
}
int ds4_gpu_glm_attention_flash_staged_tensor(
ds4_gpu_tensor *heads,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *key_cache,
const ds4_gpu_tensor *value_cache,
uint32_t pos0,
uint32_t n_tokens,
uint32_t cache_len,
uint32_t cache_cap,
uint32_t n_head,
uint32_t qk_dim,
uint32_t value_dim,
bool cache_f16) {
if (pos0 != 0 || n_tokens != cache_len) return 0;
return ds4_gpu_glm_attention_flash_tensor_impl(heads,
q,
key_cache,
value_cache,
pos0,
n_tokens,
cache_len,
cache_cap,
n_head,
qk_dim,
value_dim,
cache_f16,
1);
}
int ds4_gpu_glm_attention_full_tensor(
ds4_gpu_tensor *heads,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *key_cache,
const ds4_gpu_tensor *value_cache,
uint32_t pos0,
uint32_t n_tokens,
uint32_t cache_len,
uint32_t cache_cap,
uint32_t n_head,
uint32_t qk_dim,
uint32_t value_dim,
bool cache_f16) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!heads || !q || !key_cache || !value_cache ||
n_tokens == 0 || cache_len == 0 || cache_cap == 0 ||
n_head == 0 || qk_dim == 0 || value_dim == 0 ||
(qk_dim & 3u) != 0 ||
cache_len > cache_cap ||
pos0 > cache_len || n_tokens > cache_len - pos0 ||
cache_len > ds4_gpu_glm_full_attention_max_cache_len()) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> headsbuf = ds4_gpu_tensor_buffer(heads);
id<MTLBuffer> qbuf = ds4_gpu_tensor_buffer(q);
id<MTLBuffer> keybuf = ds4_gpu_tensor_buffer(key_cache);
id<MTLBuffer> valbuf = ds4_gpu_tensor_buffer(value_cache);
const uint64_t heads_bytes = (uint64_t)n_tokens * n_head * value_dim * sizeof(float);
const uint64_t q_bytes = (uint64_t)n_tokens * n_head * qk_dim * sizeof(float);
const uint64_t cache_elem_bytes = cache_f16 ? sizeof(uint16_t) : sizeof(float);
const uint64_t key_bytes = (uint64_t)cache_cap * n_head * qk_dim * cache_elem_bytes;
const uint64_t value_bytes = (uint64_t)cache_cap * n_head * value_dim * cache_elem_bytes;
if (!headsbuf || !qbuf || !keybuf || !valbuf ||
ds4_gpu_tensor_bytes(heads) < heads_bytes ||
ds4_gpu_tensor_bytes(q) < q_bytes ||
ds4_gpu_tensor_bytes(key_cache) < key_bytes ||
ds4_gpu_tensor_bytes(value_cache) < value_bytes) {
fprintf(stderr, "ds4: Metal GLM attention received undersized buffers\n");
return 0;
}
id<MTLComputePipelineState> pipeline =
ds4_gpu_hot_pipeline(g_glm_attention_full_pipeline,
"kernel_glm_attention_full");
if (!pipeline) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
const uint32_t full_attn_mode = 2u;
ds4_gpu_glm_attention_full_args args = {
.pos0 = pos0,
.n_tokens = n_tokens,
.cache_len = cache_len,
.cache_cap = cache_cap,
.n_head = n_head,
.qk_dim = qk_dim,
.value_dim = value_dim,
.pad0 = full_attn_mode,
.cache_f16 = cache_f16 ? 1u : 0u,
.pad1 = 0u,
.pad2 = 0u,
.scale = 1.0f / sqrtf((float)qk_dim),
};
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:qbuf offset:ds4_gpu_tensor_offset(q) atIndex:1];
[enc setBuffer:keybuf offset:ds4_gpu_tensor_offset(key_cache) atIndex:2];
[enc setBuffer:valbuf offset:ds4_gpu_tensor_offset(value_cache) atIndex:3];
[enc setBuffer:headsbuf offset:ds4_gpu_tensor_offset(heads) atIndex:4];
[enc setThreadgroupMemoryLength:ds4_gpu_align_up_ns(
(256u + (NSUInteger)cache_len) * sizeof(float), 16u) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)n_tokens, (NSUInteger)n_head, 1)
threadsPerThreadgroup:MTLSizeMake(256, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "GLM full attention")) return 0;
}
return 1;
}
int ds4_gpu_glm_fill_selected_range_tensor(
ds4_gpu_tensor *selected,
uint32_t n_selected) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!selected || n_selected == 0) return 0;
@autoreleasepool {
id<MTLBuffer> selectedbuf = ds4_gpu_tensor_buffer(selected);
const uint64_t selected_bytes = (uint64_t)n_selected * sizeof(uint32_t);
if (!selectedbuf || ds4_gpu_tensor_bytes(selected) < selected_bytes) {
fprintf(stderr, "ds4: Metal GLM selected range received undersized buffer\n");
return 0;
}
id<MTLComputePipelineState> pipeline =
ds4_gpu_hot_pipeline(g_glm_fill_selected_range_pipeline,
"kernel_glm_fill_selected_range");
if (!pipeline) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
ds4_gpu_glm_fill_selected_range_args args = {
.n_selected = n_selected,
};
const NSUInteger nth = 256u;
const NSUInteger n_groups = ((NSUInteger)n_selected + nth - 1u) / nth;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:selectedbuf offset:ds4_gpu_tensor_offset(selected) atIndex:1];
[enc dispatchThreadgroups:MTLSizeMake(n_groups, 1, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "GLM selected range")) return 0;
}
return 1;
}
int ds4_gpu_glm_fill_selected_range_batch_tensor(
ds4_gpu_tensor *selected,
uint32_t n_tokens,
uint32_t pos0,
uint32_t n_selected,
uint32_t pad_row) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!selected || n_tokens == 0 || n_selected == 0) return 0;
@autoreleasepool {
id<MTLBuffer> selectedbuf = ds4_gpu_tensor_buffer(selected);
const uint64_t total = (uint64_t)n_tokens * n_selected;
if (n_tokens != 0 && total / n_tokens != n_selected) return 0;
if (total > UINT64_MAX / sizeof(uint32_t)) return 0;
const uint64_t selected_bytes = total * sizeof(uint32_t);
if (!selectedbuf || ds4_gpu_tensor_bytes(selected) < selected_bytes) {
fprintf(stderr, "ds4: Metal GLM selected range batch received undersized buffer\n");
return 0;
}
id<MTLComputePipelineState> pipeline =
ds4_gpu_hot_pipeline(g_glm_fill_selected_range_batch_pipeline,
"kernel_glm_fill_selected_range_batch");
if (!pipeline) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
ds4_gpu_glm_fill_selected_range_batch_args args = {
.n_tokens = n_tokens,
.pos0 = pos0,
.n_selected = n_selected,
.pad_row = pad_row,
};
const NSUInteger nth = 256u;
const NSUInteger n_groups = ((NSUInteger)total + nth - 1u) / nth;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:selectedbuf offset:ds4_gpu_tensor_offset(selected) atIndex:1];
[enc dispatchThreadgroups:MTLSizeMake(n_groups, 1, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "GLM selected range batch")) return 0;
}
return 1;
}
static int ds4_gpu_glm_rope_tail_offset_tensor(
ds4_gpu_tensor *x,
uint32_t n_tokens,
uint32_t n_head,
uint32_t head_dim,
uint32_t rot_dim,
uint32_t rot_offset,
uint32_t pos0,
uint32_t n_ctx_orig,
float freq_base,
float freq_scale,
float ext_factor,
float attn_factor,
float beta_fast,
float beta_slow,
const char *label) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!x || n_tokens == 0 || n_head == 0 || head_dim == 0 ||
rot_dim == 0 || rot_offset > head_dim || rot_dim > head_dim - rot_offset ||
(rot_dim & 1u) != 0 ||
pos0 > UINT32_MAX - n_tokens ||
!isfinite(freq_base) || freq_base <= 0.0f ||
!isfinite(freq_scale) || freq_scale <= 0.0f ||
!isfinite(ext_factor) || !isfinite(attn_factor) ||
!isfinite(beta_fast) || !isfinite(beta_slow)) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> xbuf = ds4_gpu_tensor_buffer(x);
const uint64_t bytes = (uint64_t)n_tokens * n_head * head_dim * sizeof(float);
if (!xbuf || ds4_gpu_tensor_bytes(x) < bytes) {
fprintf(stderr, "ds4: Metal %s received undersized buffer\n",
label ? label : "GLM RoPE");
return 0;
}
id<MTLComputePipelineState> pipeline =
ds4_gpu_hot_pipeline(g_glm_indexer_rope_tail_pipeline,
"kernel_glm_indexer_rope_tail_f32");
if (!pipeline) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
ds4_gpu_glm_rope_tail_args args = {
.n_tokens = n_tokens,
.n_head = n_head,
.head_dim = head_dim,
.rot_dim = rot_dim,
.rot_offset = rot_offset,
.pos0 = pos0,
.n_ctx_orig = n_ctx_orig,
.freq_base = freq_base,
.freq_scale = freq_scale,
.ext_factor = ext_factor,
.attn_factor = attn_factor,
.beta_fast = beta_fast,
.beta_slow = beta_slow,
};
const NSUInteger nth = ds4_gpu_rms_norm_threads(rot_dim);
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:1];
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)n_head, (NSUInteger)n_tokens, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, label ? label : "GLM RoPE")) return 0;
}
return 1;
}
int ds4_gpu_glm_rope_tail_tensor(
ds4_gpu_tensor *x,
uint32_t n_tokens,
uint32_t n_head,
uint32_t head_dim,
uint32_t rot_dim,
uint32_t pos0,
uint32_t n_ctx_orig,
float freq_base,
float freq_scale,
float ext_factor,
float attn_factor,
float beta_fast,
float beta_slow) {
if (rot_dim > head_dim) return 0;
return ds4_gpu_glm_rope_tail_offset_tensor(x,
n_tokens,
n_head,
head_dim,
rot_dim,
head_dim - rot_dim,
pos0,
n_ctx_orig,
freq_base,
freq_scale,
ext_factor,
attn_factor,
beta_fast,
beta_slow,
"GLM RoPE");
}
int ds4_gpu_glm_indexer_rope_tail_tensor(
ds4_gpu_tensor *x,
uint32_t n_tokens,
uint32_t n_head,
uint32_t head_dim,
uint32_t rot_dim,
uint32_t pos0,
uint32_t n_ctx_orig,
float freq_base,
float freq_scale,
float ext_factor,
float attn_factor,
float beta_fast,
float beta_slow) {
return ds4_gpu_glm_rope_tail_offset_tensor(x,
n_tokens,
n_head,
head_dim,
rot_dim,
0,
pos0,
n_ctx_orig,
freq_base,
freq_scale,
ext_factor,
attn_factor,
beta_fast,
beta_slow,
"GLM indexer RoPE");
}
static int ds4_gpu_v41_index_score_wide_off(void) {
static int off = -1;
if (off < 0) off = getenv("DS4_METAL_DISABLE_V41_INDEX_SCORE_WIDE") != NULL;
return off;
}
static int ds4_gpu_glm_indexer_score_one_impl(
ds4_gpu_tensor *scores,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *weights,
const ds4_gpu_tensor *indexer_key_cache,
uint32_t n_rows,
uint32_t n_head,
uint32_t head_dim,
float scale,
bool cache_f16,
bool fp4_inputs) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!scores || !q || !weights || !indexer_key_cache ||
n_rows == 0 || n_head == 0 || head_dim == 0 ||
!isfinite(scale) || scale <= 0.0f) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> scoresbuf = ds4_gpu_tensor_buffer(scores);
id<MTLBuffer> qbuf = ds4_gpu_tensor_buffer(q);
id<MTLBuffer> weightsbuf = ds4_gpu_tensor_buffer(weights);
id<MTLBuffer> cachebuf = ds4_gpu_tensor_buffer(indexer_key_cache);
const uint64_t cache_elem_bytes = cache_f16 ? sizeof(uint16_t) : sizeof(float);
const uint64_t score_bytes = (uint64_t)n_rows * sizeof(float);
const uint64_t q_bytes = (uint64_t)n_head * head_dim * sizeof(float);
const uint64_t weights_bytes = (uint64_t)n_head * sizeof(float);
const uint64_t cache_bytes = (uint64_t)n_rows * head_dim * cache_elem_bytes;
if (!scoresbuf || !qbuf || !weightsbuf || !cachebuf ||
ds4_gpu_tensor_bytes(scores) < score_bytes ||
ds4_gpu_tensor_bytes(q) < q_bytes ||
ds4_gpu_tensor_bytes(weights) < weights_bytes ||
ds4_gpu_tensor_bytes(indexer_key_cache) < cache_bytes) {
fprintf(stderr, "ds4: Metal GLM indexer score received undersized buffers\n");
return 0;
}
ds4_gpu_glm_indexer_score_one_args args = {
.n_rows = n_rows,
.n_head = n_head,
.head_dim = head_dim,
.cache_f16 = cache_f16 ? 1u : 0u,
.scale = scale,
};
if (n_head == 32u && head_dim == 128u) {
/* long rows stream one key per thread when FP4 query and keys make
* its summation order exact; the rest keep the per-key kernel */
const bool wide = (g_test_flags & DS4_GPU_TEST_INDEX_SCORE_WIDE) != 0u ||
((g_test_flags & DS4_GPU_TEST_INDEX_SCORE_DIRECT) == 0u && fp4_inputs &&
n_rows >= 8192u && !ds4_gpu_v41_index_score_wide_off());
id<MTLComputePipelineState> direct_pipeline = wide ?
ds4_gpu_hot_pipeline(g_glm_indexer_score_one_wide_pipeline,
"kernel_glm_indexer_score_one_wide") :
ds4_gpu_hot_pipeline(g_glm_indexer_score_one_direct_pipeline,
"kernel_glm_indexer_score_one_direct");
if (!direct_pipeline) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:direct_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:qbuf offset:ds4_gpu_tensor_offset(q) atIndex:1];
[enc setBuffer:weightsbuf offset:ds4_gpu_tensor_offset(weights) atIndex:2];
[enc setBuffer:cachebuf offset:ds4_gpu_tensor_offset(indexer_key_cache) atIndex:3];
[enc setBuffer:scoresbuf offset:ds4_gpu_tensor_offset(scores) atIndex:4];
if (wide) {
[enc setThreadgroupMemoryLength:(4096u + 32u) * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)(n_rows + 255u) / 256u, 1, 1)
threadsPerThreadgroup:MTLSizeMake(256, 1, 1)];
} else {
[enc setThreadgroupMemoryLength:(128u + 4u) * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)n_rows, 1, 1)
threadsPerThreadgroup:MTLSizeMake(32, 4, 1)];
}
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "GLM indexer direct score")) return 0;
return 1;
}
id<MTLComputePipelineState> pipeline =
ds4_gpu_hot_pipeline(g_glm_indexer_score_one_pipeline,
"kernel_glm_indexer_score_one");
if (!pipeline) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
const NSUInteger nth = ds4_gpu_rms_norm_threads(head_dim);
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:qbuf offset:ds4_gpu_tensor_offset(q) atIndex:1];
[enc setBuffer:weightsbuf offset:ds4_gpu_tensor_offset(weights) atIndex:2];
[enc setBuffer:cachebuf offset:ds4_gpu_tensor_offset(indexer_key_cache) atIndex:3];
[enc setBuffer:scoresbuf offset:ds4_gpu_tensor_offset(scores) atIndex:4];
[enc setThreadgroupMemoryLength:nth * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)n_rows, 1, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "GLM indexer score")) return 0;
}
return 1;
}
int ds4_gpu_glm_indexer_score_one_tensor(
ds4_gpu_tensor *scores,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *weights,
const ds4_gpu_tensor *indexer_key_cache,
uint32_t n_rows,
uint32_t n_head,
uint32_t head_dim,
float scale,
bool cache_f16) {
return ds4_gpu_glm_indexer_score_one_impl(scores, q, weights, indexer_key_cache,
n_rows, n_head, head_dim, scale, cache_f16, false);
}
int ds4_gpu_dsv41_indexer_score_one_tensor(
ds4_gpu_tensor *scores,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *weights,
const ds4_gpu_tensor *indexer_key_cache,
uint32_t n_rows,
uint32_t n_head,
uint32_t head_dim,
float scale,
bool cache_f16) {
return ds4_gpu_glm_indexer_score_one_impl(scores, q, weights, indexer_key_cache,
n_rows, n_head, head_dim, scale, cache_f16, true);
}
static int ds4_gpu_glm_indexer_scores_batch_grouped_tensor(
ds4_gpu_tensor *scores,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *weights,
const ds4_gpu_tensor *indexer_key_cache,
uint32_t n_rows,
uint32_t n_tokens,
uint32_t pos0,
uint32_t row_group_size,
uint32_t n_head,
uint32_t head_dim,
float scale,
bool cache_f16,
bool tiled_f32) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!scores || !q || !weights || !indexer_key_cache ||
n_rows == 0 || n_tokens == 0 || row_group_size == 0 ||
n_head == 0 || head_dim != 128 ||
pos0 > UINT32_MAX - n_tokens ||
(uint64_t)(pos0 + n_tokens) / row_group_size > n_rows ||
!isfinite(scale) || scale <= 0.0f) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> scoresbuf = ds4_gpu_tensor_buffer(scores);
id<MTLBuffer> qbuf = ds4_gpu_tensor_buffer(q);
id<MTLBuffer> weightsbuf = ds4_gpu_tensor_buffer(weights);
id<MTLBuffer> cachebuf = ds4_gpu_tensor_buffer(indexer_key_cache);
const uint64_t cache_elem_bytes = cache_f16 ? sizeof(uint16_t) : sizeof(float);
const uint64_t score_bytes = (uint64_t)n_rows * n_tokens * sizeof(float);
const uint64_t q_bytes = (uint64_t)n_tokens * n_head * head_dim * sizeof(float);
const uint64_t weights_bytes = (uint64_t)n_tokens * n_head * sizeof(float);
const uint64_t cache_bytes = (uint64_t)n_rows * head_dim * cache_elem_bytes;
if (!scoresbuf || !qbuf || !weightsbuf || !cachebuf ||
ds4_gpu_tensor_bytes(scores) < score_bytes ||
ds4_gpu_tensor_bytes(q) < q_bytes ||
ds4_gpu_tensor_bytes(weights) < weights_bytes ||
ds4_gpu_tensor_bytes(indexer_key_cache) < cache_bytes) {
fprintf(stderr, "ds4: Metal GLM indexer batch scores received undersized buffers\n");
return 0;
}
const bool force_scalar = g_quality_mode;
const bool use_tiled_f32 = tiled_f32;
/* a verify block's few rows take the tiled kernel too: the scalar one
* reduces every head once per key and row */
static int verify_tiled = -1;
if (verify_tiled < 0) verify_tiled = getenv("DS4_METAL_DISABLE_V41_VERIFY_TILED_INDEX") == NULL;
const bool use_tiled = !force_scalar && n_head == 32u && head_dim == 128u &&
(n_tokens >= 8u || (n_tokens >= 2u && g_v41_verify_rows && verify_tiled));
id<MTLComputePipelineState> pipeline =
use_tiled
? ds4_gpu_hot_pipeline(use_tiled_f32 ? g_glm_indexer_scores_tiled_f32_pipeline
: g_glm_indexer_scores_tiled_pipeline,
use_tiled_f32 ? "kernel_glm_indexer_scores_tiled_f32"
: "kernel_glm_indexer_scores_tiled")
: ds4_gpu_hot_pipeline(g_glm_indexer_scores_batch_pipeline,
"kernel_glm_indexer_scores_batch");
if (!pipeline) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
ds4_gpu_glm_indexer_scores_batch_args args = {
.n_rows = n_rows,
.n_tokens = n_tokens,
.n_head = n_head,
.head_dim = head_dim,
.pos0 = pos0,
.cache_f16 = cache_f16 ? 1u : 0u,
.row_group_size = row_group_size,
.pad0 = 0,
.q_token_stride = (uint64_t)n_head * head_dim * sizeof(float),
.q_head_stride = (uint64_t)head_dim * sizeof(float),
.weights_token_stride = (uint64_t)n_head * sizeof(float),
.score_token_stride = (uint64_t)n_rows * sizeof(float),
.scale = scale,
};
const NSUInteger nth = ds4_gpu_rms_norm_threads(head_dim);
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:qbuf offset:ds4_gpu_tensor_offset(q) atIndex:1];
[enc setBuffer:weightsbuf offset:ds4_gpu_tensor_offset(weights) atIndex:2];
[enc setBuffer:cachebuf offset:ds4_gpu_tensor_offset(indexer_key_cache) atIndex:3];
[enc setBuffer:scoresbuf offset:ds4_gpu_tensor_offset(scores) atIndex:4];
if (use_tiled) {
const NSUInteger q_shared = 8u * 128u;
const NSUInteger k_shared = 32u * 128u;
const NSUInteger dot_shared = 8u * 32u;
if (use_tiled_f32) {
[enc setThreadgroupMemoryLength:(q_shared + k_shared + dot_shared) *
sizeof(float) atIndex:0];
} else {
[enc setThreadgroupMemoryLength:(q_shared + k_shared) * sizeof(uint16_t) +
dot_shared * sizeof(float) atIndex:0];
}
[enc dispatchThreadgroups:MTLSizeMake(((NSUInteger)n_rows + 31u) / 32u,
((NSUInteger)n_tokens + 7u) / 8u,
1)
threadsPerThreadgroup:MTLSizeMake(32, 4, 1)];
} else {
[enc setThreadgroupMemoryLength:nth * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)n_rows,
(NSUInteger)n_tokens,
1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
}
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "GLM indexer batch scores")) return 0;
}
return 1;
}
int ds4_gpu_glm_indexer_scores_batch_tensor(
ds4_gpu_tensor *scores,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *weights,
const ds4_gpu_tensor *indexer_key_cache,
uint32_t n_rows,
uint32_t n_tokens,
uint32_t pos0,
uint32_t n_head,
uint32_t head_dim,
float scale,
bool cache_f16) {
return ds4_gpu_glm_indexer_scores_batch_grouped_tensor(
scores, q, weights, indexer_key_cache, n_rows, n_tokens, pos0, 1u,
n_head, head_dim, scale, cache_f16, false);
}
int ds4_gpu_glm53_indexer_scores_batch_tensor(
ds4_gpu_tensor *scores,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *weights,
const ds4_gpu_tensor *indexer_key_cache,
uint32_t n_rows,
uint32_t n_tokens,
uint32_t pos0,
uint32_t pool_size,
uint32_t n_head,
uint32_t head_dim,
float scale,
bool cache_f16) {
if (pool_size != 4u) return 0;
return ds4_gpu_glm_indexer_scores_batch_grouped_tensor(
scores, q, weights, indexer_key_cache, n_rows, n_tokens, pos0,
pool_size, n_head, head_dim, scale, cache_f16, false);
}
int ds4_gpu_dsv41_tensor_ops_available(void) {
return ds4_gpu_mpp_available() && !g_quality_mode;
}
static uint64_t ds4_gpu_dsv41_indexer_flags_bytes(uint32_t source_rows, uint32_t rows) {
return (((uint64_t)rows + ((uint64_t)source_rows + 63u) / 64u) * 4u + 255u) & ~UINT64_C(255);
}
uint64_t ds4_gpu_dsv41_indexer_packed_bytes(uint32_t source_rows, uint32_t rows) {
return ds4_gpu_dsv41_indexer_flags_bytes(source_rows, rows) +
(uint64_t)rows * 32u * 128u * 2u + (((uint64_t)source_rows + 63u) / 64u) * 64u * 128u * 2u;
}
int ds4_gpu_dsv41_indexer_pack(ds4_gpu_tensor *packed,
const ds4_gpu_tensor *q, const ds4_gpu_tensor *keys,
uint32_t source_rows, uint32_t rows) {
if (!source_rows || source_rows > INT32_MAX || !rows || rows > INT32_MAX ||
!ds4_gpu_dsv41_tensor_ops_available()) return 0;
const ds4_gpu_tensor *tensors[] = {q, keys, packed};
const uint64_t bytes[] = {(uint64_t)rows * 32u * 128u * 4u,
(uint64_t)source_rows * 128u * 4u, ds4_gpu_dsv41_indexer_packed_bytes(source_rows, rows)};
for (uint32_t i = 0; i < 3; i++)
if (!tensors[i] || !ds4_gpu_tensor_buffer(tensors[i]) || ds4_gpu_tensor_bytes(tensors[i]) < bytes[i])
return 0;
@autoreleasepool {
id<MTLComputePipelineState> pipeline = ds4_gpu_get_pipeline("kernel_dsv41_indexer_pack");
if (!pipeline) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
id<MTLComputeCommandEncoder> enc = cb ? ds4_gpu_compute_encoder(cb) : nil;
if (!enc) return 0;
const uint32_t args[] = {source_rows, rows, 0, 0};
const uint64_t flags_bytes = ds4_gpu_dsv41_indexer_flags_bytes(source_rows, rows);
const uint64_t offset = ds4_gpu_tensor_offset(packed);
[enc setComputePipelineState:pipeline];
[enc setBytes:args length:sizeof(args) atIndex:0];
[enc setBuffer:ds4_gpu_tensor_buffer(q) offset:ds4_gpu_tensor_offset(q) atIndex:1];
[enc setBuffer:ds4_gpu_tensor_buffer(keys) offset:ds4_gpu_tensor_offset(keys) atIndex:2];
[enc setBuffer:ds4_gpu_tensor_buffer(packed) offset:offset atIndex:3];
[enc setBuffer:ds4_gpu_tensor_buffer(packed) offset:offset + flags_bytes atIndex:4];
[enc setBuffer:ds4_gpu_tensor_buffer(packed) offset:offset + flags_bytes + (uint64_t)rows * 8192u atIndex:5];
[enc setThreadgroupMemoryLength:16 atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)rows + ((NSUInteger)source_rows + 63u) / 64u, 1, 1)
threadsPerThreadgroup:MTLSizeMake(128, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_finish_command_buffer(cb, owned, "V4.1 index input packing");
}
}
int ds4_gpu_dsv41_indexer_scores_packed(ds4_gpu_tensor *scores,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *weights,
const ds4_gpu_tensor *keys,
const ds4_gpu_tensor *packed,
uint32_t source_rows, uint32_t rows,
uint32_t start, uint32_t ratio,
uint32_t packed_rows, uint32_t offset) {
if ((ratio != 1u && ratio != 2u) || !source_rows || source_rows > INT32_MAX || !rows ||
!packed_rows || packed_rows > INT32_MAX ||
rows > UINT32_MAX - start || (start + rows) / ratio > source_rows ||
offset > packed_rows || rows > packed_rows - offset ||
!ds4_gpu_dsv41_tensor_ops_available()) return 0;
const ds4_gpu_tensor *tensors[] = {q, weights, keys, scores, packed};
const uint64_t bytes[] = {(uint64_t)rows * 32u * 128u * 4u, (uint64_t)rows * 32u * 4u,
(uint64_t)source_rows * 128u * 4u, (uint64_t)rows * source_rows * 4u,
ds4_gpu_dsv41_indexer_packed_bytes(source_rows, packed_rows)};
for (uint32_t i = 0; i < 5; i++)
if (!tensors[i] || !ds4_gpu_tensor_buffer(tensors[i]) || ds4_gpu_tensor_bytes(tensors[i]) < bytes[i])
return 0;
@autoreleasepool {
id<MTLComputePipelineState> pipeline = ds4_gpu_get_pipeline("kernel_dsv41_indexer_scores_packed");
if (!pipeline) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
id<MTLComputeCommandEncoder> enc = cb ? ds4_gpu_compute_encoder(cb) : nil;
if (!enc) return 0;
const uint32_t args[] = {source_rows, rows, start, ratio}, range[] = {packed_rows, offset};
const uint64_t flags_bytes = ds4_gpu_dsv41_indexer_flags_bytes(source_rows, packed_rows);
const uint64_t base = ds4_gpu_tensor_offset(packed);
[enc setComputePipelineState:pipeline];
[enc setBytes:args length:sizeof(args) atIndex:0];
[enc setBytes:range length:sizeof(range) atIndex:1];
for (NSUInteger i = 0; i < 5; i++)
[enc setBuffer:ds4_gpu_tensor_buffer(tensors[i]) offset:ds4_gpu_tensor_offset(tensors[i]) atIndex:i + 2];
[enc setBuffer:ds4_gpu_tensor_buffer(packed) offset:base + flags_bytes atIndex:7];
[enc setBuffer:ds4_gpu_tensor_buffer(packed) offset:base + flags_bytes + (uint64_t)packed_rows * 8192u atIndex:8];
[enc setThreadgroupMemoryLength:32u * 64u * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(((NSUInteger)source_rows + 63u) / 64u, rows, 1)
threadsPerThreadgroup:MTLSizeMake(128, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_finish_command_buffer(cb, owned, "V4.1 packed index scores");
}
}
int ds4_gpu_dsv41_indexer_scores_batch(ds4_gpu_tensor *scores,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *weights,
const ds4_gpu_tensor *keys,
uint32_t source_rows, uint32_t rows,
uint32_t start, uint32_t ratio) {
if (ratio != 1u && ratio != 2u) return 0;
return ds4_gpu_glm_indexer_scores_batch_grouped_tensor(scores, q, weights,
keys, source_rows, rows, start, ratio, 32, 128, 1.0f / 64.0f, false, true);
}
int ds4_gpu_glm_qk_lowrank_typed_tensor(
ds4_gpu_tensor *qk_low,
const ds4_gpu_tensor *q,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint32_t weight_type,
uint32_t n_head,
uint32_t kv_lora_dim,
uint32_t qk_nope,
uint32_t qk_dim) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!qk_low || !q || !model_map ||
n_head == 0 || kv_lora_dim == 0 ||
qk_nope == 0 || qk_nope > qk_dim) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(qk_low);
id<MTLBuffer> qbuf = ds4_gpu_tensor_buffer(q);
uint64_t row_bytes = 0;
if (!ds4_gpu_quant_row_bytes(weight_type, qk_nope, &row_bytes)) {
fprintf(stderr, "ds4: Metal GLM qk lowrank received unsupported weight type\n");
return 0;
}
const uint64_t weight_rows = (uint64_t)n_head * kv_lora_dim;
const uint64_t weight_bytes = weight_rows * row_bytes;
const uint64_t out_bytes = (uint64_t)n_head * kv_lora_dim * sizeof(float);
const uint64_t q_bytes = (uint64_t)n_head * qk_dim * sizeof(float);
if (!outbuf || !qbuf ||
ds4_gpu_tensor_bytes(qk_low) < out_bytes ||
ds4_gpu_tensor_bytes(q) < q_bytes) {
fprintf(stderr, "ds4: Metal GLM qk lowrank received undersized buffers\n");
return 0;
}
if (weight_offset > model_size || weight_bytes > model_size - weight_offset) {
fprintf(stderr, "ds4: Metal GLM qk lowrank range is outside the mapped model\n");
return 0;
}
uint64_t weight_inner = 0;
id<MTLBuffer> weightbuf =
ds4_gpu_wrap_model_range(model_map,
model_size,
weight_offset,
weight_bytes,
&weight_inner);
if (!weightbuf) return 0;
const int use_glm52 =
n_head == 64u &&
kv_lora_dim == 512u &&
qk_nope == 192u &&
qk_dim == 256u &&
row_bytes == 204u &&
weight_type == DS4_METAL_TENSOR_Q8_0;
/* Coalesced simdgroup variant: lanes split the 192-dot so weight
* reads coalesce, and 2048 threadgroups replace 64. The thread-
* per-row kernels measured ~7.5x off the weight-bandwidth floor
* (7.2ms of the decode token by skip-ablation). Covers the GLM 5.2
* shape for both Q8_0 k_b and the DenseQ4 GGUF's Q4_0 k_b (the Q8
* fast path above never engaged there). */
const int use_glm52_sg =
(n_head == 32u || n_head == 64u) &&
kv_lora_dim == 512u &&
qk_nope == 192u &&
qk_dim == 256u &&
((weight_type == DS4_METAL_TENSOR_Q8_0 && row_bytes == 204u) ||
(weight_type == DS4_METAL_TENSOR_Q4_0 && row_bytes == 108u)) &&
getenv("DS4_METAL_DISABLE_GLM_QKLOW_SG") == NULL &&
ds4_gpu_hot_pipeline(g_glm_qk_lowrank_glm52_sg_pipeline,
"kernel_glm_qk_lowrank_q8_0_glm52_sg") != nil;
if (getenv("DS4_METAL_GLM_QKLOW_DEBUG")) {
static int printed = 0;
if (!printed) {
printed = 1;
fprintf(stderr, "ds4: qk_lowrank decode path: use_glm52=%d sg=%d n_head=%u kv=%u nope=%u dim=%u rb=%llu type=%u\n",
use_glm52, use_glm52_sg, n_head, kv_lora_dim, qk_nope, qk_dim,
(unsigned long long)row_bytes, weight_type);
}
}
id<MTLComputePipelineState> pipeline =
use_glm52_sg ?
ds4_gpu_hot_pipeline(g_glm_qk_lowrank_glm52_sg_pipeline,
"kernel_glm_qk_lowrank_q8_0_glm52_sg") :
use_glm52 ?
ds4_gpu_hot_pipeline(g_glm_qk_lowrank_glm52_pipeline,
"kernel_glm_qk_lowrank_q8_0_glm52") :
ds4_gpu_hot_pipeline(g_glm_qk_lowrank_pipeline,
"kernel_glm_qk_lowrank_q8_0");
if (!pipeline) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
ds4_gpu_glm_qk_lowrank_args args = {
.n_head = n_head,
.kv_lora_dim = kv_lora_dim,
.qk_nope = qk_nope,
.qk_dim = qk_dim,
.row_bytes = (uint32_t)row_bytes,
.weight_type = weight_type,
.pad1 = 0,
.pad2 = 0,
};
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:weightbuf offset:(NSUInteger)weight_inner atIndex:1];
[enc setBuffer:qbuf offset:ds4_gpu_tensor_offset(q) atIndex:2];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(qk_low) atIndex:3];
if (use_glm52_sg) {
/* 8 simdgroups x 2 rows per threadgroup: (64, 512/16) grid. */
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)n_head,
(NSUInteger)(kv_lora_dim / 16u),
1)
threadsPerThreadgroup:MTLSizeMake(32, 8, 1)];
} else {
if (use_glm52) {
[enc setThreadgroupMemoryLength:192u * sizeof(float) atIndex:0];
}
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)n_head, 1, 1)
threadsPerThreadgroup:MTLSizeMake(256, 1, 1)];
}
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "GLM qk lowrank")) return 0;
}
return 1;
}
int ds4_gpu_glm_qk_lowrank_q8_0_tensor(
ds4_gpu_tensor *qk_low,
const ds4_gpu_tensor *q,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint32_t n_head,
uint32_t kv_lora_dim,
uint32_t qk_nope,
uint32_t qk_dim) {
return ds4_gpu_glm_qk_lowrank_typed_tensor(qk_low,
q,
model_map,
model_size,
weight_offset,
DS4_METAL_TENSOR_Q8_0,
n_head,
kv_lora_dim,
qk_nope,
qk_dim);
}
int ds4_gpu_glm_qk_lowrank_typed_batch_tensor(
ds4_gpu_tensor *qk_low,
const ds4_gpu_tensor *q,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint32_t weight_type,
uint32_t n_tokens,
uint32_t n_head,
uint32_t kv_lora_dim,
uint32_t qk_nope,
uint32_t qk_dim) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!qk_low || !q || !model_map ||
n_tokens == 0 || n_head == 0 || kv_lora_dim == 0 ||
qk_nope == 0 || qk_nope > qk_dim) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(qk_low);
id<MTLBuffer> qbuf = ds4_gpu_tensor_buffer(q);
uint64_t row_bytes = 0;
if (!ds4_gpu_quant_row_bytes(weight_type, qk_nope, &row_bytes)) {
fprintf(stderr, "ds4: Metal GLM batch qk lowrank received unsupported weight type\n");
return 0;
}
const uint64_t weight_rows = (uint64_t)n_head * kv_lora_dim;
const uint64_t weight_bytes = weight_rows * row_bytes;
const uint64_t out_bytes =
(uint64_t)n_tokens * n_head * kv_lora_dim * sizeof(float);
const uint64_t q_bytes =
(uint64_t)n_tokens * n_head * qk_dim * sizeof(float);
if (!outbuf || !qbuf ||
ds4_gpu_tensor_bytes(qk_low) < out_bytes ||
ds4_gpu_tensor_bytes(q) < q_bytes) {
fprintf(stderr, "ds4: Metal GLM batch qk lowrank received undersized buffers\n");
return 0;
}
if (weight_offset > model_size || weight_bytes > model_size - weight_offset) {
fprintf(stderr, "ds4: Metal GLM batch qk lowrank range is outside the mapped model\n");
return 0;
}
uint64_t weight_inner = 0;
id<MTLBuffer> weightbuf =
ds4_gpu_wrap_model_range(model_map,
model_size,
weight_offset,
weight_bytes,
&weight_inner);
if (!weightbuf) return 0;
const int use_glm52_t4 =
n_tokens >= 4u &&
n_head == 64u &&
kv_lora_dim == 512u &&
qk_nope == 192u &&
qk_dim == 256u &&
row_bytes == 204u &&
weight_type == DS4_METAL_TENSOR_Q8_0;
id<MTLComputePipelineState> pipeline =
use_glm52_t4 ?
ds4_gpu_hot_pipeline(g_glm_qk_lowrank_batch_glm52_t4_pipeline,
"kernel_glm_qk_lowrank_q8_0_batch_glm52_t4") :
ds4_gpu_hot_pipeline(g_glm_qk_lowrank_batch_pipeline,
"kernel_glm_qk_lowrank_q8_0_batch");
if (!pipeline) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
uint32_t head_base = 0;
uint32_t head_count = n_head;
ds4_gpu_tp_attn_head_range(n_head, 8u, &head_base, &head_count);
ds4_gpu_glm_qk_lowrank_batch_args args = {
.n_tokens = n_tokens,
.n_head = n_head,
.kv_lora_dim = kv_lora_dim,
.qk_nope = qk_nope,
.qk_dim = qk_dim,
.row_bytes = (uint32_t)row_bytes,
.weight_type = weight_type,
.head_base = head_base,
};
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:weightbuf offset:(NSUInteger)weight_inner atIndex:1];
[enc setBuffer:qbuf offset:ds4_gpu_tensor_offset(q) atIndex:2];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(qk_low) atIndex:3];
if (use_glm52_t4) {
[enc setThreadgroupMemoryLength:4u * 192u * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)head_count,
((NSUInteger)n_tokens + 3u) / 4u,
1)
threadsPerThreadgroup:MTLSizeMake(256, 1, 1)];
} else {
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)head_count,
(NSUInteger)n_tokens,
1)
threadsPerThreadgroup:MTLSizeMake(256, 1, 1)];
}
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "GLM batch qk lowrank")) return 0;
}
return 1;
}
int ds4_gpu_glm_qk_lowrank_q8_0_batch_tensor(
ds4_gpu_tensor *qk_low,
const ds4_gpu_tensor *q,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint32_t n_tokens,
uint32_t n_head,
uint32_t kv_lora_dim,
uint32_t qk_nope,
uint32_t qk_dim) {
return ds4_gpu_glm_qk_lowrank_typed_batch_tensor(qk_low,
q,
model_map,
model_size,
weight_offset,
DS4_METAL_TENSOR_Q8_0,
n_tokens,
n_head,
kv_lora_dim,
qk_nope,
qk_dim);
}
int ds4_gpu_glm_value_project_typed_batch_heads_tensor(
ds4_gpu_tensor *heads,
const ds4_gpu_tensor *lora,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint32_t weight_type,
uint32_t n_tokens,
uint32_t n_head,
uint32_t kv_lora_dim,
uint32_t value_dim) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!heads || !lora || !model_map ||
n_tokens == 0 || n_head == 0 ||
kv_lora_dim == 0 || value_dim == 0) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> headsbuf = ds4_gpu_tensor_buffer(heads);
id<MTLBuffer> lorabuf = ds4_gpu_tensor_buffer(lora);
uint64_t row_bytes = 0;
if (!ds4_gpu_quant_row_bytes(weight_type, kv_lora_dim, &row_bytes)) {
fprintf(stderr, "ds4: Metal GLM batch value project received unsupported weight type\n");
return 0;
}
const uint64_t weight_rows = (uint64_t)n_head * value_dim;
const uint64_t weight_bytes = weight_rows * row_bytes;
const uint64_t heads_bytes =
(uint64_t)n_tokens * n_head * value_dim * sizeof(float);
const uint64_t lora_bytes =
(uint64_t)n_tokens * n_head * kv_lora_dim * sizeof(float);
if (!headsbuf || !lorabuf ||
ds4_gpu_tensor_bytes(heads) < heads_bytes ||
ds4_gpu_tensor_bytes(lora) < lora_bytes) {
fprintf(stderr, "ds4: Metal GLM batch value project received undersized buffers\n");
return 0;
}
if (weight_offset > model_size || weight_bytes > model_size - weight_offset) {
fprintf(stderr, "ds4: Metal GLM batch value project range is outside the mapped model\n");
return 0;
}
uint64_t weight_inner = 0;
id<MTLBuffer> weightbuf =
ds4_gpu_wrap_model_range(model_map,
model_size,
weight_offset,
weight_bytes,
&weight_inner);
if (!weightbuf) return 0;
const int use_mma =
n_head == 64u &&
kv_lora_dim == 512u &&
value_dim == 256u &&
row_bytes == 544u &&
weight_type == DS4_METAL_TENSOR_Q8_0 &&
n_tokens >= 32u;
id<MTLComputePipelineState> pipeline =
use_mma ?
ds4_gpu_hot_pipeline(g_glm_value_project_q8_0_batch_heads_mma_pipeline,
"kernel_glm_value_project_q8_0_batch_heads_mma") :
ds4_gpu_hot_pipeline(g_glm_value_project_q8_0_batch_heads_pipeline,
"kernel_glm_value_project_q8_0_batch_heads");
if (!pipeline) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
uint32_t head_base = 0;
uint32_t head_count = n_head;
ds4_gpu_tp_attn_head_range(n_head, 8u, &head_base, &head_count);
ds4_gpu_glm_qk_lowrank_batch_args args = {
.n_tokens = n_tokens,
.n_head = n_head,
.kv_lora_dim = kv_lora_dim,
.qk_nope = 0,
.qk_dim = value_dim,
.row_bytes = (uint32_t)row_bytes,
.weight_type = weight_type,
.head_base = head_base,
};
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:weightbuf offset:(NSUInteger)weight_inner atIndex:1];
[enc setBuffer:lorabuf offset:ds4_gpu_tensor_offset(lora) atIndex:2];
[enc setBuffer:headsbuf offset:ds4_gpu_tensor_offset(heads) atIndex:3];
if (use_mma) {
[enc setThreadgroupMemoryLength:16u * 1024u atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(((NSUInteger)n_tokens + 31u) / 32u,
((NSUInteger)value_dim + 63u) / 64u,
(NSUInteger)head_count)
threadsPerThreadgroup:MTLSizeMake(128, 1, 1)];
} else {
[enc setThreadgroupMemoryLength:(NSUInteger)kv_lora_dim * sizeof(float)
atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)head_count,
(NSUInteger)n_tokens,
1)
threadsPerThreadgroup:MTLSizeMake(256, 1, 1)];
}
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "GLM batch value project")) return 0;
}
return 1;
}
int ds4_gpu_glm_value_project_q8_0_batch_heads_tensor(
ds4_gpu_tensor *heads,
const ds4_gpu_tensor *lora,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint32_t n_tokens,
uint32_t n_head,
uint32_t kv_lora_dim,
uint32_t value_dim) {
return ds4_gpu_glm_value_project_typed_batch_heads_tensor(heads,
lora,
model_map,
model_size,
weight_offset,
DS4_METAL_TENSOR_Q8_0,
n_tokens,
n_head,
kv_lora_dim,
value_dim);
}
int ds4_gpu_glm_attention_indexed_decode_typed_tensor(
ds4_gpu_tensor *heads,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *qk_low,
const ds4_gpu_tensor *kv_lora_cache,
const ds4_gpu_tensor *k_rope_cache,
const void *model_map,
uint64_t model_size,
uint64_t value_weight_offset,
uint32_t value_weight_type,
const ds4_gpu_tensor *selected,
uint32_t n_selected,
uint32_t cache_cap,
bool cache_f16,
uint32_t n_head,
uint32_t kv_lora_dim,
uint32_t qk_nope,
uint32_t qk_rope,
uint32_t value_dim,
uint32_t n_ctx_orig,
float freq_base,
float freq_scale,
float ext_factor,
float attn_factor,
float beta_fast,
float beta_slow) {
if (!g_initialized && !ds4_gpu_init()) return 0;
const uint32_t qk_dim = qk_nope + qk_rope;
if (!heads || !q || !qk_low || !kv_lora_cache || !k_rope_cache ||
!model_map || !selected ||
n_selected == 0 || cache_cap == 0 || n_selected > cache_cap ||
n_head == 0 || kv_lora_dim == 0 ||
qk_nope == 0 || (qk_rope & 1u) != 0 ||
value_dim == 0 || qk_dim < qk_nope ||
(qk_rope != 0 && (!isfinite(freq_base) || freq_base <= 0.0f)) ||
(qk_rope != 0 && (!isfinite(freq_scale) || freq_scale <= 0.0f)) ||
!isfinite(ext_factor) || !isfinite(attn_factor) ||
!isfinite(beta_fast) || !isfinite(beta_slow)) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> headsbuf = ds4_gpu_tensor_buffer(heads);
id<MTLBuffer> qbuf = ds4_gpu_tensor_buffer(q);
id<MTLBuffer> lowbuf = ds4_gpu_tensor_buffer(qk_low);
id<MTLBuffer> kvcachebuf = ds4_gpu_tensor_buffer(kv_lora_cache);
id<MTLBuffer> ropecachebuf = ds4_gpu_tensor_buffer(k_rope_cache);
id<MTLBuffer> selectedbuf = ds4_gpu_tensor_buffer(selected);
const uint64_t cache_elem_bytes = cache_f16 ? sizeof(uint16_t) : sizeof(float);
uint64_t value_row_bytes = 0;
if (!ds4_gpu_quant_row_bytes(value_weight_type, kv_lora_dim, &value_row_bytes)) {
fprintf(stderr, "ds4: Metal GLM indexed attention received unsupported value type\n");
return 0;
}
const uint64_t value_weight_rows = (uint64_t)n_head * value_dim;
const uint64_t value_weight_bytes = value_weight_rows * value_row_bytes;
const uint64_t heads_bytes = (uint64_t)n_head * value_dim * sizeof(float);
const uint64_t q_bytes = (uint64_t)n_head * qk_dim * sizeof(float);
const uint64_t low_bytes = (uint64_t)n_head * kv_lora_dim * sizeof(float);
const uint64_t kv_cache_bytes = (uint64_t)cache_cap * kv_lora_dim * cache_elem_bytes;
const uint64_t rope_cache_bytes = (uint64_t)cache_cap * qk_rope * cache_elem_bytes;
const uint64_t selected_bytes = (uint64_t)n_selected * sizeof(uint32_t);
if (!headsbuf || !qbuf || !lowbuf || !kvcachebuf || !ropecachebuf || !selectedbuf ||
ds4_gpu_tensor_bytes(heads) < heads_bytes ||
ds4_gpu_tensor_bytes(q) < q_bytes ||
ds4_gpu_tensor_bytes(qk_low) < low_bytes ||
ds4_gpu_tensor_bytes(kv_lora_cache) < kv_cache_bytes ||
ds4_gpu_tensor_bytes(k_rope_cache) < rope_cache_bytes ||
ds4_gpu_tensor_bytes(selected) < selected_bytes) {
fprintf(stderr, "ds4: Metal GLM indexed attention received undersized buffers\n");
return 0;
}
if (value_weight_offset > model_size ||
value_weight_bytes > model_size - value_weight_offset) {
fprintf(stderr, "ds4: Metal GLM indexed attention value range is outside the mapped model\n");
return 0;
}
uint64_t value_inner = 0;
id<MTLBuffer> valuebuf =
ds4_gpu_wrap_model_range(model_map,
model_size,
value_weight_offset,
value_weight_bytes,
&value_inner);
if (!valuebuf) return 0;
id<MTLComputePipelineState> pipeline =
ds4_gpu_hot_pipeline(g_glm_attention_indexed_decode_pipeline,
"kernel_glm_attention_indexed_decode");
if (!pipeline) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
ds4_gpu_glm_attention_indexed_decode_args args = {
.n_selected = n_selected,
.cache_cap = cache_cap,
.cache_f16 = cache_f16 ? 1u : 0u,
.n_head = n_head,
.kv_lora_dim = kv_lora_dim,
.qk_nope = qk_nope,
.qk_rope = qk_rope,
.value_dim = value_dim,
.n_ctx_orig = n_ctx_orig,
.value_row_bytes = (uint32_t)value_row_bytes,
.scale = 1.0f / sqrtf((float)qk_dim),
.freq_base = freq_base,
.freq_scale = freq_scale,
.ext_factor = ext_factor,
.attn_factor = attn_factor,
.beta_fast = beta_fast,
.beta_slow = beta_slow,
.value_type = value_weight_type,
};
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:qbuf offset:ds4_gpu_tensor_offset(q) atIndex:1];
[enc setBuffer:lowbuf offset:ds4_gpu_tensor_offset(qk_low) atIndex:2];
[enc setBuffer:kvcachebuf offset:ds4_gpu_tensor_offset(kv_lora_cache) atIndex:3];
[enc setBuffer:ropecachebuf offset:ds4_gpu_tensor_offset(k_rope_cache) atIndex:4];
[enc setBuffer:valuebuf offset:(NSUInteger)value_inner atIndex:5];
[enc setBuffer:selectedbuf offset:ds4_gpu_tensor_offset(selected) atIndex:6];
[enc setBuffer:headsbuf offset:ds4_gpu_tensor_offset(heads) atIndex:7];
const NSUInteger scratch_floats =
256u + (NSUInteger)n_selected + (NSUInteger)kv_lora_dim;
[enc setThreadgroupMemoryLength:ds4_gpu_align_up_ns(
scratch_floats * sizeof(float), 16u) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)n_head, 1, 1)
threadsPerThreadgroup:MTLSizeMake(256, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "GLM indexed attention decode")) return 0;
}
return 1;
}
int ds4_gpu_glm_attention_indexed_decode_tensor(
ds4_gpu_tensor *heads,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *qk_low,
const ds4_gpu_tensor *kv_lora_cache,
const ds4_gpu_tensor *k_rope_cache,
const void *model_map,
uint64_t model_size,
uint64_t value_weight_offset,
const ds4_gpu_tensor *selected,
uint32_t n_selected,
uint32_t cache_cap,
bool cache_f16,
uint32_t n_head,
uint32_t kv_lora_dim,
uint32_t qk_nope,
uint32_t qk_rope,
uint32_t value_dim,
uint32_t n_ctx_orig,
float freq_base,
float freq_scale,
float ext_factor,
float attn_factor,
float beta_fast,
float beta_slow) {
return ds4_gpu_glm_attention_indexed_decode_typed_tensor(heads,
q,
qk_low,
kv_lora_cache,
k_rope_cache,
model_map,
model_size,
value_weight_offset,
DS4_METAL_TENSOR_Q8_0,
selected,
n_selected,
cache_cap,
cache_f16,
n_head,
kv_lora_dim,
qk_nope,
qk_rope,
value_dim,
n_ctx_orig,
freq_base,
freq_scale,
ext_factor,
attn_factor,
beta_fast,
beta_slow);
}
int ds4_gpu_glm_attention_indexed_decode_split_group8_typed_tensor(
ds4_gpu_tensor *heads,
ds4_gpu_tensor *partial_lora,
ds4_gpu_tensor *partial_ms,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *qk_low,
const ds4_gpu_tensor *kv_lora_cache,
const ds4_gpu_tensor *k_rope_cache,
const void *model_map,
uint64_t model_size,
uint64_t value_weight_offset,
uint32_t value_weight_type,
const ds4_gpu_tensor *selected,
uint32_t n_selected,
bool selected_rows_valid,
uint32_t cache_cap,
bool cache_f16,
uint32_t n_head,
uint32_t kv_lora_dim,
uint32_t qk_nope,
uint32_t qk_rope,
uint32_t value_dim,
uint32_t n_ctx_orig,
uint32_t block_rows,
uint32_t n_blocks,
float freq_base,
float freq_scale,
float ext_factor,
float attn_factor,
float beta_fast,
float beta_slow) {
if (!g_initialized && !ds4_gpu_init()) return 0;
const uint32_t qk_dim = qk_nope + qk_rope;
const uint32_t needed_blocks =
block_rows != 0u ? (n_selected + block_rows - 1u) / block_rows : 0u;
if (!heads || !partial_lora || !partial_ms || !q || !qk_low ||
!kv_lora_cache || !k_rope_cache || !model_map || !selected ||
n_selected == 0 || cache_cap == 0 || n_selected > cache_cap ||
n_head == 0 || (n_head % 8u) != 0 ||
kv_lora_dim != 512u ||
qk_nope == 0 || qk_rope != 64u ||
value_dim == 0 || qk_dim < qk_nope ||
block_rows == 0u || needed_blocks == 0u ||
n_blocks < needed_blocks || n_blocks > 64u ||
!cache_f16 ||
(qk_rope != 0 && (!isfinite(freq_base) || freq_base <= 0.0f)) ||
(qk_rope != 0 && (!isfinite(freq_scale) || freq_scale <= 0.0f)) ||
!isfinite(ext_factor) || !isfinite(attn_factor) ||
!isfinite(beta_fast) || !isfinite(beta_slow)) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> headsbuf = ds4_gpu_tensor_buffer(heads);
id<MTLBuffer> partial_lorabuf = ds4_gpu_tensor_buffer(partial_lora);
id<MTLBuffer> partial_msbuf = ds4_gpu_tensor_buffer(partial_ms);
id<MTLBuffer> qbuf = ds4_gpu_tensor_buffer(q);
id<MTLBuffer> lowbuf = ds4_gpu_tensor_buffer(qk_low);
id<MTLBuffer> kvcachebuf = ds4_gpu_tensor_buffer(kv_lora_cache);
id<MTLBuffer> ropecachebuf = ds4_gpu_tensor_buffer(k_rope_cache);
id<MTLBuffer> selectedbuf = ds4_gpu_tensor_buffer(selected);
const uint64_t cache_elem_bytes = sizeof(uint16_t);
uint64_t value_row_bytes = 0;
if (!ds4_gpu_quant_row_bytes(value_weight_type, kv_lora_dim, &value_row_bytes)) {
fprintf(stderr, "ds4: Metal GLM split grouped indexed attention received unsupported value type\n");
return 0;
}
const uint64_t value_weight_rows = (uint64_t)n_head * value_dim;
const uint64_t value_weight_bytes = value_weight_rows * value_row_bytes;
const uint64_t heads_bytes = (uint64_t)n_head * value_dim * sizeof(float);
const uint64_t partial_lora_bytes =
(uint64_t)n_blocks * n_head * kv_lora_dim * sizeof(float);
const uint64_t partial_ms_bytes =
(uint64_t)n_blocks * n_head * 2u * sizeof(float);
const uint64_t q_bytes = (uint64_t)n_head * qk_dim * sizeof(float);
const uint64_t low_bytes = (uint64_t)n_head * kv_lora_dim * sizeof(float);
const uint64_t kv_cache_bytes = (uint64_t)cache_cap * kv_lora_dim * cache_elem_bytes;
const uint64_t rope_cache_bytes = (uint64_t)cache_cap * qk_rope * cache_elem_bytes;
const uint64_t selected_bytes = (uint64_t)n_selected * sizeof(uint32_t);
if (!headsbuf || !partial_lorabuf || !partial_msbuf ||
!qbuf || !lowbuf || !kvcachebuf || !ropecachebuf || !selectedbuf ||
ds4_gpu_tensor_bytes(heads) < heads_bytes ||
ds4_gpu_tensor_bytes(partial_lora) < partial_lora_bytes ||
ds4_gpu_tensor_bytes(partial_ms) < partial_ms_bytes ||
ds4_gpu_tensor_bytes(q) < q_bytes ||
ds4_gpu_tensor_bytes(qk_low) < low_bytes ||
ds4_gpu_tensor_bytes(kv_lora_cache) < kv_cache_bytes ||
ds4_gpu_tensor_bytes(k_rope_cache) < rope_cache_bytes ||
ds4_gpu_tensor_bytes(selected) < selected_bytes) {
fprintf(stderr, "ds4: Metal GLM split grouped indexed attention received undersized buffers\n");
return 0;
}
if (value_weight_offset > model_size ||
value_weight_bytes > model_size - value_weight_offset) {
fprintf(stderr, "ds4: Metal GLM split grouped indexed attention value range is outside the mapped model\n");
return 0;
}
uint64_t value_inner = 0;
id<MTLBuffer> valuebuf =
ds4_gpu_wrap_model_range(model_map,
model_size,
value_weight_offset,
value_weight_bytes,
&value_inner);
if (!valuebuf) return 0;
const bool use_valid_fullheads =
selected_rows_valid && (n_head % 8u) == 0u;
id<MTLComputePipelineState> partial_pipeline =
ds4_gpu_hot_pipeline(use_valid_fullheads ?
g_glm_attention_indexed_decode_split_group8_partial_valid_fullheads_pipeline :
g_glm_attention_indexed_decode_split_group8_partial_pipeline,
use_valid_fullheads ?
"kernel_glm_attention_indexed_decode_split_group8_partial_valid_fullheads" :
"kernel_glm_attention_indexed_decode_split_group8_partial");
const bool use_reduce16 =
n_blocks == 16u && block_rows == 128u &&
n_selected == 2048u && value_dim == 256u;
id<MTLComputePipelineState> reduce_pipeline =
ds4_gpu_hot_pipeline(use_reduce16 ?
g_glm_attention_indexed_decode_split_group8_reduce16_pipeline :
g_glm_attention_indexed_decode_split_group8_reduce_pipeline,
use_reduce16 ?
"kernel_glm_attention_indexed_decode_split_group8_reduce16" :
"kernel_glm_attention_indexed_decode_split_group8_reduce");
if (!partial_pipeline || !reduce_pipeline) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
ds4_gpu_glm_attention_indexed_decode_split_args args = {
.n_selected = n_selected,
.cache_cap = cache_cap,
.cache_f16 = 1u,
.n_head = n_head,
.kv_lora_dim = kv_lora_dim,
.qk_nope = qk_nope,
.qk_rope = qk_rope,
.value_dim = value_dim,
.n_ctx_orig = n_ctx_orig,
.value_row_bytes = (uint32_t)value_row_bytes,
.block_rows = block_rows,
.n_blocks = n_blocks,
.scale = 1.0f / sqrtf((float)qk_dim),
.freq_base = freq_base,
.freq_scale = freq_scale,
.ext_factor = ext_factor,
.attn_factor = attn_factor,
.beta_fast = beta_fast,
.beta_slow = beta_slow,
.value_type = value_weight_type,
};
const NSUInteger stage_rows = 16u;
const NSUInteger kv_vecs = (NSUInteger)kv_lora_dim / 4u;
const NSUInteger rope_vecs = (NSUInteger)qk_rope / 4u;
const NSUInteger partial_scratch_bytes =
stage_rows * kv_vecs * sizeof(uint16_t) * 4u +
stage_rows * rope_vecs * sizeof(float) * 4u;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:partial_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:qbuf offset:ds4_gpu_tensor_offset(q) atIndex:1];
[enc setBuffer:lowbuf offset:ds4_gpu_tensor_offset(qk_low) atIndex:2];
[enc setBuffer:kvcachebuf offset:ds4_gpu_tensor_offset(kv_lora_cache) atIndex:3];
[enc setBuffer:ropecachebuf offset:ds4_gpu_tensor_offset(k_rope_cache) atIndex:4];
[enc setBuffer:selectedbuf offset:ds4_gpu_tensor_offset(selected) atIndex:5];
[enc setBuffer:partial_lorabuf offset:ds4_gpu_tensor_offset(partial_lora) atIndex:6];
[enc setBuffer:partial_msbuf offset:ds4_gpu_tensor_offset(partial_ms) atIndex:7];
[enc setThreadgroupMemoryLength:partial_scratch_bytes atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)n_head / 8u, (NSUInteger)n_blocks, 1)
threadsPerThreadgroup:MTLSizeMake(32, 8, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
const NSUInteger reduce_scratch_floats = 256u + 64u + (NSUInteger)kv_lora_dim;
enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:reduce_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:partial_lorabuf offset:ds4_gpu_tensor_offset(partial_lora) atIndex:1];
[enc setBuffer:partial_msbuf offset:ds4_gpu_tensor_offset(partial_ms) atIndex:2];
[enc setBuffer:valuebuf offset:(NSUInteger)value_inner atIndex:3];
[enc setBuffer:headsbuf offset:ds4_gpu_tensor_offset(heads) atIndex:4];
[enc setThreadgroupMemoryLength:reduce_scratch_floats * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)n_head, 1, 1)
threadsPerThreadgroup:MTLSizeMake(256, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "GLM split grouped indexed attention decode")) return 0;
}
return 1;
}
int ds4_gpu_glm_attention_indexed_decode_split_group8_tensor(
ds4_gpu_tensor *heads,
ds4_gpu_tensor *partial_lora,
ds4_gpu_tensor *partial_ms,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *qk_low,
const ds4_gpu_tensor *kv_lora_cache,
const ds4_gpu_tensor *k_rope_cache,
const void *model_map,
uint64_t model_size,
uint64_t value_weight_offset,
const ds4_gpu_tensor *selected,
uint32_t n_selected,
bool selected_rows_valid,
uint32_t cache_cap,
bool cache_f16,
uint32_t n_head,
uint32_t kv_lora_dim,
uint32_t qk_nope,
uint32_t qk_rope,
uint32_t value_dim,
uint32_t n_ctx_orig,
uint32_t block_rows,
uint32_t n_blocks,
float freq_base,
float freq_scale,
float ext_factor,
float attn_factor,
float beta_fast,
float beta_slow) {
return ds4_gpu_glm_attention_indexed_decode_split_group8_typed_tensor(heads,
partial_lora,
partial_ms,
q,
qk_low,
kv_lora_cache,
k_rope_cache,
model_map,
model_size,
value_weight_offset,
DS4_METAL_TENSOR_Q8_0,
selected,
n_selected,
selected_rows_valid,
cache_cap,
cache_f16,
n_head,
kv_lora_dim,
qk_nope,
qk_rope,
value_dim,
n_ctx_orig,
block_rows,
n_blocks,
freq_base,
freq_scale,
ext_factor,
attn_factor,
beta_fast,
beta_slow);
}
int ds4_gpu_glm_attention_indexed_batch_typed_tensor(
ds4_gpu_tensor *heads,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *qk_low,
const ds4_gpu_tensor *kv_lora_cache,
const ds4_gpu_tensor *k_rope_cache,
const void *model_map,
uint64_t model_size,
uint64_t value_weight_offset,
uint32_t value_weight_type,
const ds4_gpu_tensor *selected,
uint32_t n_tokens,
uint32_t n_selected,
uint32_t cache_cap,
bool cache_f16,
uint32_t n_head,
uint32_t kv_lora_dim,
uint32_t qk_nope,
uint32_t qk_rope,
uint32_t value_dim,
uint32_t n_ctx_orig,
float freq_base,
float freq_scale,
float ext_factor,
float attn_factor,
float beta_fast,
float beta_slow) {
if (!g_initialized && !ds4_gpu_init()) return 0;
const uint32_t qk_dim = qk_nope + qk_rope;
if (!heads || !q || !qk_low || !kv_lora_cache || !k_rope_cache ||
!model_map || !selected ||
n_tokens == 0 || n_selected == 0 || cache_cap == 0 || n_selected > cache_cap ||
n_head == 0 || kv_lora_dim == 0 ||
qk_nope == 0 || (qk_rope & 1u) != 0 ||
value_dim == 0 || qk_dim < qk_nope ||
(qk_rope != 0 && (!isfinite(freq_base) || freq_base <= 0.0f)) ||
(qk_rope != 0 && (!isfinite(freq_scale) || freq_scale <= 0.0f)) ||
!isfinite(ext_factor) || !isfinite(attn_factor) ||
!isfinite(beta_fast) || !isfinite(beta_slow)) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> headsbuf = ds4_gpu_tensor_buffer(heads);
id<MTLBuffer> qbuf = ds4_gpu_tensor_buffer(q);
id<MTLBuffer> lowbuf = ds4_gpu_tensor_buffer(qk_low);
id<MTLBuffer> kvcachebuf = ds4_gpu_tensor_buffer(kv_lora_cache);
id<MTLBuffer> ropecachebuf = ds4_gpu_tensor_buffer(k_rope_cache);
id<MTLBuffer> selectedbuf = ds4_gpu_tensor_buffer(selected);
const uint64_t cache_elem_bytes = cache_f16 ? sizeof(uint16_t) : sizeof(float);
uint64_t value_row_bytes = 0;
if (!ds4_gpu_quant_row_bytes(value_weight_type, kv_lora_dim, &value_row_bytes)) {
fprintf(stderr, "ds4: Metal GLM indexed batch attention received unsupported value type\n");
return 0;
}
const uint64_t value_weight_rows = (uint64_t)n_head * value_dim;
const uint64_t value_weight_bytes = value_weight_rows * value_row_bytes;
const uint64_t heads_bytes =
(uint64_t)n_tokens * n_head * value_dim * sizeof(float);
const uint64_t q_bytes =
(uint64_t)n_tokens * n_head * qk_dim * sizeof(float);
const uint64_t low_bytes =
(uint64_t)n_tokens * n_head * kv_lora_dim * sizeof(float);
const uint64_t kv_cache_bytes = (uint64_t)cache_cap * kv_lora_dim * cache_elem_bytes;
const uint64_t rope_cache_bytes = (uint64_t)cache_cap * qk_rope * cache_elem_bytes;
const uint64_t selected_bytes =
(uint64_t)n_tokens * n_selected * sizeof(uint32_t);
if (!headsbuf || !qbuf || !lowbuf || !kvcachebuf || !ropecachebuf || !selectedbuf ||
ds4_gpu_tensor_bytes(heads) < heads_bytes ||
ds4_gpu_tensor_bytes(q) < q_bytes ||
ds4_gpu_tensor_bytes(qk_low) < low_bytes ||
ds4_gpu_tensor_bytes(kv_lora_cache) < kv_cache_bytes ||
ds4_gpu_tensor_bytes(k_rope_cache) < rope_cache_bytes ||
ds4_gpu_tensor_bytes(selected) < selected_bytes) {
fprintf(stderr, "ds4: Metal GLM indexed batch attention received undersized buffers\n");
return 0;
}
if (value_weight_offset > model_size ||
value_weight_bytes > model_size - value_weight_offset) {
fprintf(stderr, "ds4: Metal GLM indexed batch attention value range is outside the mapped model\n");
return 0;
}
uint64_t value_inner = 0;
id<MTLBuffer> valuebuf =
ds4_gpu_wrap_model_range(model_map, model_size,
value_weight_offset,
value_weight_bytes,
&value_inner);
if (!valuebuf) return 0;
const uint64_t grouped_attn = (n_tokens >= 128u) ? 8u : 2u;
const NSUInteger q2_bit_words = ((NSUInteger)cache_cap + 31u) / 32u;
const NSUInteger q2_scratch_bytes =
q2_bit_words * sizeof(uint32_t) +
4u * ((NSUInteger)kv_lora_dim + (NSUInteger)qk_rope) * sizeof(uint16_t) +
8u * (NSUInteger)kv_lora_dim * sizeof(float);
const NSUInteger max_tg_mem = [g_device maxThreadgroupMemoryLength];
const bool q2_fits = max_tg_mem == 0 || q2_scratch_bytes <= max_tg_mem;
const bool use_q2_group4 = grouped_attn == 4u && n_tokens >= 2u && q2_fits;
const bool use_group8 =
grouped_attn == 8u ||
(grouped_attn == 4u && !use_q2_group4);
const bool use_group2 = grouped_attn == 1u || grouped_attn == 2u;
id<MTLComputePipelineState> pipeline = use_q2_group4 ?
ds4_gpu_hot_pipeline(g_glm_attention_indexed_batch_q2_group4_pipeline,
"kernel_glm_attention_indexed_batch_q2_group4") :
(use_group8 ?
ds4_gpu_hot_pipeline(g_glm_attention_indexed_batch_group8_pipeline,
"kernel_glm_attention_indexed_batch_group8") :
(use_group2 ?
ds4_gpu_hot_pipeline(g_glm_attention_indexed_batch_group2_pipeline,
"kernel_glm_attention_indexed_batch_group2") :
ds4_gpu_hot_pipeline(g_glm_attention_indexed_batch_pipeline,
"kernel_glm_attention_indexed_batch")));
if (!pipeline) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
ds4_gpu_glm_attention_indexed_batch_args args = {
.n_tokens = n_tokens,
.n_selected = n_selected,
.cache_cap = cache_cap,
.cache_f16 = cache_f16 ? 1u : 0u,
.n_head = n_head,
.kv_lora_dim = kv_lora_dim,
.qk_nope = qk_nope,
.qk_rope = qk_rope,
.value_dim = value_dim,
.n_ctx_orig = n_ctx_orig,
.value_row_bytes = (uint32_t)value_row_bytes,
.value_type = value_weight_type,
.scale = 1.0f / sqrtf((float)qk_dim),
.freq_base = freq_base,
.freq_scale = freq_scale,
.ext_factor = ext_factor,
.attn_factor = attn_factor,
.beta_fast = beta_fast,
.beta_slow = beta_slow,
.head_base = 0,
};
const NSUInteger scratch_bytes = use_q2_group4 ?
q2_scratch_bytes :
(use_group8 ?
(8u * ((NSUInteger)kv_lora_dim + (NSUInteger)qk_rope) * sizeof(uint16_t) +
8u * (NSUInteger)kv_lora_dim * sizeof(float)) :
((use_group2 ?
(512u + 2u * (NSUInteger)n_selected + 2u * (NSUInteger)kv_lora_dim) :
(256u + (NSUInteger)n_selected + (NSUInteger)kv_lora_dim)) * sizeof(float)));
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:qbuf offset:ds4_gpu_tensor_offset(q) atIndex:1];
[enc setBuffer:lowbuf offset:ds4_gpu_tensor_offset(qk_low) atIndex:2];
[enc setBuffer:kvcachebuf offset:ds4_gpu_tensor_offset(kv_lora_cache) atIndex:3];
[enc setBuffer:ropecachebuf offset:ds4_gpu_tensor_offset(k_rope_cache) atIndex:4];
[enc setBuffer:valuebuf offset:(NSUInteger)value_inner atIndex:5];
[enc setBuffer:selectedbuf offset:ds4_gpu_tensor_offset(selected) atIndex:6];
[enc setBuffer:headsbuf offset:ds4_gpu_tensor_offset(heads) atIndex:7];
[enc setThreadgroupMemoryLength:scratch_bytes atIndex:0];
if (use_q2_group4) {
[enc dispatchThreadgroups:MTLSizeMake(((NSUInteger)n_head + 3u) / 4u,
((NSUInteger)n_tokens + 1u) / 2u,
1)
threadsPerThreadgroup:MTLSizeMake(32, 4, 1)];
} else if (use_group8) {
[enc dispatchThreadgroups:MTLSizeMake(((NSUInteger)n_head + 7u) / 8u,
(NSUInteger)n_tokens,
1)
threadsPerThreadgroup:MTLSizeMake(32, 8, 1)];
} else if (use_group2) {
[enc dispatchThreadgroups:MTLSizeMake(((NSUInteger)n_head + 1u) / 2u,
(NSUInteger)n_tokens,
1)
threadsPerThreadgroup:MTLSizeMake(256, 1, 1)];
} else {
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)n_head, (NSUInteger)n_tokens, 1)
threadsPerThreadgroup:MTLSizeMake(256, 1, 1)];
}
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "GLM indexed batch attention")) return 0;
}
return 1;
}
int ds4_gpu_glm_attention_indexed_batch_tensor(
ds4_gpu_tensor *heads,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *qk_low,
const ds4_gpu_tensor *kv_lora_cache,
const ds4_gpu_tensor *k_rope_cache,
const void *model_map,
uint64_t model_size,
uint64_t value_weight_offset,
const ds4_gpu_tensor *selected,
uint32_t n_tokens,
uint32_t n_selected,
uint32_t cache_cap,
bool cache_f16,
uint32_t n_head,
uint32_t kv_lora_dim,
uint32_t qk_nope,
uint32_t qk_rope,
uint32_t value_dim,
uint32_t n_ctx_orig,
float freq_base,
float freq_scale,
float ext_factor,
float attn_factor,
float beta_fast,
float beta_slow) {
return ds4_gpu_glm_attention_indexed_batch_typed_tensor(heads,
q,
qk_low,
kv_lora_cache,
k_rope_cache,
model_map,
model_size,
value_weight_offset,
DS4_METAL_TENSOR_Q8_0,
selected,
n_tokens,
n_selected,
cache_cap,
cache_f16,
n_head,
kv_lora_dim,
qk_nope,
qk_rope,
value_dim,
n_ctx_orig,
freq_base,
freq_scale,
ext_factor,
attn_factor,
beta_fast,
beta_slow);
}
int ds4_gpu_sort_i32_rows_asc_tensor(
ds4_gpu_tensor *dst,
const ds4_gpu_tensor *src,
uint32_t row_width,
uint32_t n_rows) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!dst || !src || row_width == 0 || n_rows == 0 ||
(row_width & (row_width - 1u)) != 0) {
return 0;
}
@autoreleasepool {
id<MTLComputePipelineState> pipeline =
ds4_gpu_hot_pipeline(g_dsv4_sort_i32_rows_asc_pipeline,
"kernel_dsv4_sort_i32_rows_asc");
if (!pipeline) return 0;
const uint64_t bytes = (uint64_t)row_width * n_rows * sizeof(int32_t);
id<MTLBuffer> srcbuf = ds4_gpu_tensor_buffer(src);
id<MTLBuffer> dstbuf = ds4_gpu_tensor_buffer(dst);
if (!srcbuf || !dstbuf ||
ds4_gpu_tensor_bytes(src) < bytes ||
ds4_gpu_tensor_bytes(dst) < bytes) {
fprintf(stderr, "ds4: Metal row sort received undersized buffers\n");
return 0;
}
NSUInteger threads = (NSUInteger)row_width;
const NSUInteger max_threads = pipeline.maxTotalThreadsPerThreadgroup;
if (max_threads != 0 && threads > max_threads) threads = max_threads;
if (threads == 0) return 0;
const NSUInteger scratch_bytes = (NSUInteger)row_width * sizeof(int32_t);
const NSUInteger max_tg_mem = [g_device maxThreadgroupMemoryLength];
if (max_tg_mem != 0 && scratch_bytes > max_tg_mem) {
fprintf(stderr, "ds4: Metal row sort scratch exceeds threadgroup memory limit\n");
return 0;
}
ds4_gpu_dsv4_topk_mask_args args = {
.ne00 = (int64_t)row_width,
.ne01 = (int64_t)n_rows,
.nb00 = sizeof(int32_t),
.nb01 = (uint64_t)row_width * sizeof(int32_t),
.ne0 = (int64_t)row_width,
.ne1 = (int64_t)n_rows,
.nb0 = sizeof(int32_t),
.nb1 = (uint64_t)row_width * sizeof(int32_t),
};
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:srcbuf offset:ds4_gpu_tensor_offset(src) atIndex:1];
[enc setBuffer:dstbuf offset:ds4_gpu_tensor_offset(dst) atIndex:2];
[enc setThreadgroupMemoryLength:scratch_bytes atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)n_rows, 1, 1)
threadsPerThreadgroup:MTLSizeMake(threads, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "sort i32 rows asc")) return 0;
}
return 1;
}
static int ds4_gpu_glm_attention_indexed_batch_lora_layout_tensor(
ds4_gpu_tensor *lora_out,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *qk_low,
const ds4_gpu_tensor *kv_lora_cache,
const ds4_gpu_tensor *k_rope_cache,
const ds4_gpu_tensor *selected,
uint32_t n_tokens,
uint32_t n_selected,
uint32_t cache_cap,
bool cache_f16,
uint32_t n_head,
uint32_t kv_lora_dim,
uint32_t qk_nope,
uint32_t qk_rope,
uint32_t n_ctx_orig,
float freq_base,
float freq_scale,
float ext_factor,
float attn_factor,
float beta_fast,
float beta_slow,
bool selected_rows_valid) {
if (!g_initialized && !ds4_gpu_init()) return 0;
const uint32_t qk_dim = qk_nope + qk_rope;
if (!lora_out || !q || !qk_low || !kv_lora_cache || !k_rope_cache || !selected ||
n_tokens == 0 || n_selected == 0 || cache_cap == 0 || n_selected > cache_cap ||
n_head == 0 || kv_lora_dim == 0 ||
qk_nope == 0 || (qk_rope & 1u) != 0 ||
qk_dim < qk_nope ||
(qk_rope != 0 && (!isfinite(freq_base) || freq_base <= 0.0f)) ||
(qk_rope != 0 && (!isfinite(freq_scale) || freq_scale <= 0.0f)) ||
!isfinite(ext_factor) || !isfinite(attn_factor) ||
!isfinite(beta_fast) || !isfinite(beta_slow)) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> lorabuf = ds4_gpu_tensor_buffer(lora_out);
id<MTLBuffer> qbuf = ds4_gpu_tensor_buffer(q);
id<MTLBuffer> lowbuf = ds4_gpu_tensor_buffer(qk_low);
id<MTLBuffer> kvcachebuf = ds4_gpu_tensor_buffer(kv_lora_cache);
id<MTLBuffer> ropecachebuf = ds4_gpu_tensor_buffer(k_rope_cache);
id<MTLBuffer> selectedbuf = ds4_gpu_tensor_buffer(selected);
const uint64_t cache_elem_bytes = cache_f16 ? sizeof(uint16_t) : sizeof(float);
const uint64_t lora_bytes =
(uint64_t)n_tokens * n_head * kv_lora_dim * sizeof(float);
const uint64_t q_bytes =
(uint64_t)n_tokens * n_head * qk_dim * sizeof(float);
const uint64_t low_bytes =
(uint64_t)n_tokens * n_head * kv_lora_dim * sizeof(float);
const uint64_t kv_cache_bytes =
(uint64_t)cache_cap * kv_lora_dim * cache_elem_bytes;
const uint64_t rope_cache_bytes =
(uint64_t)cache_cap * qk_rope * cache_elem_bytes;
const uint64_t selected_bytes =
(uint64_t)n_tokens * n_selected * sizeof(uint32_t);
if (!lorabuf || !qbuf || !lowbuf || !kvcachebuf || !ropecachebuf || !selectedbuf ||
ds4_gpu_tensor_bytes(lora_out) < lora_bytes ||
ds4_gpu_tensor_bytes(q) < q_bytes ||
ds4_gpu_tensor_bytes(qk_low) < low_bytes ||
ds4_gpu_tensor_bytes(kv_lora_cache) < kv_cache_bytes ||
ds4_gpu_tensor_bytes(k_rope_cache) < rope_cache_bytes ||
ds4_gpu_tensor_bytes(selected) < selected_bytes) {
fprintf(stderr, "ds4: Metal GLM indexed batch attention-lora received undersized buffers\n");
return 0;
}
const bool use_vec_lora =
cache_f16 && kv_lora_dim == 512u &&
(qk_rope == 0u || qk_rope == 64u);
const bool full_head_groups = (n_head % 8u) == 0u;
id<MTLComputePipelineState> pipeline = nil;
if (use_vec_lora && selected_rows_valid && full_head_groups) {
pipeline = ds4_gpu_hot_pipeline(
g_glm_attention_indexed_batch_lora_group8_vec_valid_fullheads_pipeline,
"kernel_glm_attention_indexed_batch_lora_group8_vec_valid_fullheads");
} else if (use_vec_lora && selected_rows_valid) {
pipeline = ds4_gpu_hot_pipeline(
g_glm_attention_indexed_batch_lora_group8_vec_valid_pipeline,
"kernel_glm_attention_indexed_batch_lora_group8_vec_valid");
} else if (use_vec_lora) {
pipeline = ds4_gpu_hot_pipeline(
g_glm_attention_indexed_batch_lora_group8_vec_pipeline,
"kernel_glm_attention_indexed_batch_lora_group8_vec");
} else {
pipeline = ds4_gpu_hot_pipeline(g_glm_attention_indexed_batch_group8_pipeline,
"kernel_glm_attention_indexed_batch_group8");
}
if (!pipeline) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
ds4_gpu_glm_attention_indexed_batch_args args = {
.n_tokens = n_tokens,
.n_selected = n_selected,
.cache_cap = cache_cap,
.cache_f16 = cache_f16 ? 1u : 0u,
.n_head = n_head,
.kv_lora_dim = kv_lora_dim,
.qk_nope = qk_nope,
.qk_rope = qk_rope,
.value_dim = kv_lora_dim,
.n_ctx_orig = n_ctx_orig,
.value_row_bytes = 0,
.value_type = 1u,
.scale = 1.0f / sqrtf((float)qk_dim),
.freq_base = freq_base,
.freq_scale = freq_scale,
.ext_factor = ext_factor,
.attn_factor = attn_factor,
.beta_fast = beta_fast,
.beta_slow = beta_slow,
.head_base = 0,
};
uint32_t head_count = n_head;
ds4_gpu_tp_attn_head_range(n_head, 8u, &args.head_base, &head_count);
const NSUInteger scratch_bytes = use_vec_lora ?
(16u * ((NSUInteger)kv_lora_dim / 4u) * sizeof(uint16_t) * 4u +
16u * ((NSUInteger)qk_rope / 4u) * sizeof(float) * 4u) :
(8u * ((NSUInteger)kv_lora_dim + (NSUInteger)qk_rope) * sizeof(uint16_t) +
8u * (NSUInteger)kv_lora_dim * sizeof(float));
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:qbuf offset:ds4_gpu_tensor_offset(q) atIndex:1];
[enc setBuffer:lowbuf offset:ds4_gpu_tensor_offset(qk_low) atIndex:2];
[enc setBuffer:kvcachebuf offset:ds4_gpu_tensor_offset(kv_lora_cache) atIndex:3];
[enc setBuffer:ropecachebuf offset:ds4_gpu_tensor_offset(k_rope_cache) atIndex:4];
if (use_vec_lora) {
[enc setBuffer:selectedbuf offset:ds4_gpu_tensor_offset(selected) atIndex:5];
[enc setBuffer:lorabuf offset:ds4_gpu_tensor_offset(lora_out) atIndex:6];
} else {
[enc setBuffer:lorabuf offset:ds4_gpu_tensor_offset(lora_out) atIndex:5];
[enc setBuffer:selectedbuf offset:ds4_gpu_tensor_offset(selected) atIndex:6];
[enc setBuffer:lorabuf offset:ds4_gpu_tensor_offset(lora_out) atIndex:7];
}
[enc setThreadgroupMemoryLength:scratch_bytes atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(((NSUInteger)head_count + 7u) / 8u,
(NSUInteger)n_tokens,
1)
threadsPerThreadgroup:MTLSizeMake(32, 8, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "GLM indexed batch attention-lora")) return 0;
}
return 1;
}
int ds4_gpu_glm_attention_indexed_batch_lora_causal_tensor(
ds4_gpu_tensor *lora_out,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *qk_low,
const ds4_gpu_tensor *kv_lora_cache,
const ds4_gpu_tensor *k_rope_cache,
uint32_t n_tokens,
uint32_t pos0,
uint32_t n_selected,
uint32_t cache_cap,
bool cache_f16,
uint32_t n_head,
uint32_t kv_lora_dim,
uint32_t qk_nope,
uint32_t qk_rope,
uint32_t n_ctx_orig,
float freq_base,
float freq_scale,
float ext_factor,
float attn_factor,
float beta_fast,
float beta_slow) {
if (!g_initialized && !ds4_gpu_init()) return 0;
const uint32_t qk_dim = qk_nope + qk_rope;
if (!lora_out || !q || !qk_low || !kv_lora_cache || !k_rope_cache ||
n_tokens == 0 || n_selected == 0 || cache_cap == 0 || n_selected > cache_cap ||
pos0 > n_selected || n_tokens > n_selected - pos0 ||
n_head == 0 || kv_lora_dim != 512u ||
qk_nope == 0 || (qk_rope != 0u && qk_rope != 64u) ||
qk_dim < qk_nope ||
(qk_rope != 0u && (!isfinite(freq_base) || freq_base <= 0.0f)) ||
(qk_rope != 0u && (!isfinite(freq_scale) || freq_scale <= 0.0f)) ||
!isfinite(ext_factor) || !isfinite(attn_factor) ||
!isfinite(beta_fast) || !isfinite(beta_slow)) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> lorabuf = ds4_gpu_tensor_buffer(lora_out);
id<MTLBuffer> qbuf = ds4_gpu_tensor_buffer(q);
id<MTLBuffer> lowbuf = ds4_gpu_tensor_buffer(qk_low);
id<MTLBuffer> kvcachebuf = ds4_gpu_tensor_buffer(kv_lora_cache);
id<MTLBuffer> ropecachebuf = ds4_gpu_tensor_buffer(k_rope_cache);
const uint64_t cache_elem_bytes =
cache_f16 ? sizeof(uint16_t) : sizeof(float);
const uint64_t lora_bytes =
(uint64_t)n_tokens * n_head * kv_lora_dim * sizeof(float);
const uint64_t q_bytes =
(uint64_t)n_tokens * n_head * qk_dim * sizeof(float);
const uint64_t low_bytes =
(uint64_t)n_tokens * n_head * kv_lora_dim * sizeof(float);
const uint64_t kv_cache_bytes =
(uint64_t)cache_cap * kv_lora_dim * cache_elem_bytes;
const uint64_t rope_cache_bytes =
(uint64_t)cache_cap * qk_rope * cache_elem_bytes;
if (!lorabuf || !qbuf || !lowbuf || !kvcachebuf || !ropecachebuf ||
ds4_gpu_tensor_bytes(lora_out) < lora_bytes ||
ds4_gpu_tensor_bytes(q) < q_bytes ||
ds4_gpu_tensor_bytes(qk_low) < low_bytes ||
ds4_gpu_tensor_bytes(kv_lora_cache) < kv_cache_bytes ||
ds4_gpu_tensor_bytes(k_rope_cache) < rope_cache_bytes) {
fprintf(stderr, "ds4: Metal GLM causal batch attention-lora received undersized buffers\n");
return 0;
}
const bool full_head_groups = (n_head % 8u) == 0u;
id<MTLComputePipelineState> pipeline = full_head_groups ?
ds4_gpu_hot_pipeline(
g_glm_attention_indexed_batch_lora_group8_vec_causal_fullheads_pipeline,
"kernel_glm_attention_indexed_batch_lora_group8_vec_causal_fullheads") :
ds4_gpu_hot_pipeline(
g_glm_attention_indexed_batch_lora_group8_vec_causal_pipeline,
"kernel_glm_attention_indexed_batch_lora_group8_vec_causal");
if (!pipeline) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
ds4_gpu_glm_attention_indexed_batch_args args = {
.n_tokens = n_tokens,
.n_selected = n_selected,
.cache_cap = cache_cap,
.cache_f16 = cache_f16 ? 1u : 0u,
.n_head = n_head,
.kv_lora_dim = kv_lora_dim,
.qk_nope = qk_nope,
.qk_rope = qk_rope,
.value_dim = kv_lora_dim,
.n_ctx_orig = n_ctx_orig,
.value_row_bytes = 0,
.value_type = 1u,
.pos0 = pos0,
.scale = 1.0f / sqrtf((float)qk_dim),
.freq_base = freq_base,
.freq_scale = freq_scale,
.ext_factor = ext_factor,
.attn_factor = attn_factor,
.beta_fast = beta_fast,
.beta_slow = beta_slow,
.head_base = 0,
};
uint32_t head_count = n_head;
ds4_gpu_tp_attn_head_range(n_head, 8u, &args.head_base, &head_count);
const NSUInteger scratch_bytes =
16u * ((NSUInteger)kv_lora_dim / 4u) * sizeof(uint16_t) * 4u +
16u * ((NSUInteger)qk_rope / 4u) * sizeof(float) * 4u;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:qbuf offset:ds4_gpu_tensor_offset(q) atIndex:1];
[enc setBuffer:lowbuf offset:ds4_gpu_tensor_offset(qk_low) atIndex:2];
[enc setBuffer:kvcachebuf offset:ds4_gpu_tensor_offset(kv_lora_cache) atIndex:3];
[enc setBuffer:ropecachebuf offset:ds4_gpu_tensor_offset(k_rope_cache) atIndex:4];
[enc setBuffer:lorabuf offset:ds4_gpu_tensor_offset(lora_out) atIndex:5];
[enc setThreadgroupMemoryLength:scratch_bytes atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(((NSUInteger)head_count + 7u) / 8u,
(NSUInteger)n_tokens,
1)
threadsPerThreadgroup:MTLSizeMake(32, 8, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "GLM causal indexed batch attention-lora")) return 0;
}
return 1;
}
int ds4_gpu_glm_attention_indexed_batch_lora_tensor(
ds4_gpu_tensor *lora_out,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *qk_low,
const ds4_gpu_tensor *kv_lora_cache,
const ds4_gpu_tensor *k_rope_cache,
const ds4_gpu_tensor *selected,
uint32_t n_tokens,
uint32_t n_selected,
uint32_t cache_cap,
bool cache_f16,
uint32_t n_head,
uint32_t kv_lora_dim,
uint32_t qk_nope,
uint32_t qk_rope,
uint32_t n_ctx_orig,
float freq_base,
float freq_scale,
float ext_factor,
float attn_factor,
float beta_fast,
float beta_slow) {
return ds4_gpu_glm_attention_indexed_batch_lora_layout_tensor(lora_out,
q,
qk_low,
kv_lora_cache,
k_rope_cache,
selected,
n_tokens,
n_selected,
cache_cap,
cache_f16,
n_head,
kv_lora_dim,
qk_nope,
qk_rope,
n_ctx_orig,
freq_base,
freq_scale,
ext_factor,
attn_factor,
beta_fast,
beta_slow,
false);
}
int ds4_gpu_glm_attention_indexed_batch_lora_valid_tensor(
ds4_gpu_tensor *lora_out,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *qk_low,
const ds4_gpu_tensor *kv_lora_cache,
const ds4_gpu_tensor *k_rope_cache,
const ds4_gpu_tensor *selected,
uint32_t n_tokens,
uint32_t n_selected,
uint32_t cache_cap,
bool cache_f16,
uint32_t n_head,
uint32_t kv_lora_dim,
uint32_t qk_nope,
uint32_t qk_rope,
uint32_t n_ctx_orig,
float freq_base,
float freq_scale,
float ext_factor,
float attn_factor,
float beta_fast,
float beta_slow) {
return ds4_gpu_glm_attention_indexed_batch_lora_layout_tensor(lora_out,
q,
qk_low,
kv_lora_cache,
k_rope_cache,
selected,
n_tokens,
n_selected,
cache_cap,
cache_f16,
n_head,
kv_lora_dim,
qk_nope,
qk_rope,
n_ctx_orig,
freq_base,
freq_scale,
ext_factor,
attn_factor,
beta_fast,
beta_slow,
true);
}
int ds4_gpu_glm_router_select_tensor(
ds4_gpu_tensor *selected,
ds4_gpu_tensor *weights,
ds4_gpu_tensor *probs,
const void *model_map,
uint64_t model_size,
uint64_t bias_offset,
const ds4_gpu_tensor *logits,
uint32_t n_expert,
uint32_t n_expert_used,
float expert_weight_scale) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!selected || !weights || !probs || !logits || !model_map ||
n_expert == 0 || n_expert > 512u ||
n_expert_used == 0 || n_expert_used > n_expert) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> logitsbuf = ds4_gpu_tensor_buffer(logits);
id<MTLBuffer> selectedbuf = ds4_gpu_tensor_buffer(selected);
id<MTLBuffer> weightsbuf = ds4_gpu_tensor_buffer(weights);
id<MTLBuffer> probsbuf = ds4_gpu_tensor_buffer(probs);
if (!logitsbuf || !selectedbuf || !weightsbuf || !probsbuf ||
ds4_gpu_tensor_bytes(logits) < (uint64_t)n_expert * sizeof(float) ||
ds4_gpu_tensor_bytes(selected) < (uint64_t)n_expert_used * sizeof(int32_t) ||
ds4_gpu_tensor_bytes(weights) < (uint64_t)n_expert_used * sizeof(float) ||
ds4_gpu_tensor_bytes(probs) < (uint64_t)n_expert * sizeof(float)) {
fprintf(stderr, "ds4: Metal GLM router received undersized buffers\n");
return 0;
}
const uint64_t bias_bytes = (uint64_t)n_expert * sizeof(float);
if (bias_offset > model_size || bias_bytes > model_size - bias_offset) {
fprintf(stderr, "ds4: Metal GLM router bias range is outside the mapped model\n");
return 0;
}
const bool exact_bias_view =
bias_bytes <= (1ull << 20) &&
getenv("DS4_METAL_DISABLE_DECODE_ROUTER_BIAS_EXACT_VIEWS") == NULL;
uint64_t bias_inner = 0;
id<MTLBuffer> biasbuf = exact_bias_view ?
ds4_gpu_wrap_model_exact_range(model_map,
model_size,
bias_offset,
bias_bytes,
&bias_inner) :
ds4_gpu_wrap_model_range(model_map,
model_size,
bias_offset,
bias_bytes,
&bias_inner);
if (!biasbuf) return 0;
id<MTLComputePipelineState> pipeline =
ds4_gpu_hot_pipeline(g_glm_router_select_one_pipeline,
"kernel_glm_router_select_one");
if (!pipeline) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
ds4_gpu_glm_router_select_one_args args = {
.n_expert = n_expert,
.n_expert_used = n_expert_used,
.expert_weight_scale = expert_weight_scale,
.pad0 = 0,
};
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:logitsbuf offset:ds4_gpu_tensor_offset(logits) atIndex:1];
[enc setBuffer:biasbuf offset:(NSUInteger)bias_inner atIndex:2];
[enc setBuffer:selectedbuf offset:ds4_gpu_tensor_offset(selected) atIndex:3];
[enc setBuffer:weightsbuf offset:ds4_gpu_tensor_offset(weights) atIndex:4];
[enc setBuffer:probsbuf offset:ds4_gpu_tensor_offset(probs) atIndex:5];
const NSUInteger router_threads = n_expert > 256u ? 512u : 256u;
[enc setThreadgroupMemoryLength:router_threads *
(sizeof(float) + sizeof(int32_t)) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(1, 1, 1)
threadsPerThreadgroup:MTLSizeMake(router_threads, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "GLM router select")) return 0;
}
return 1;
}
int ds4_gpu_glm_router_select_batch_tensor(
ds4_gpu_tensor *selected,
ds4_gpu_tensor *weights,
ds4_gpu_tensor *probs,
const void *model_map,
uint64_t model_size,
uint64_t bias_offset,
const ds4_gpu_tensor *logits,
uint32_t n_expert,
uint32_t n_expert_used,
float expert_weight_scale,
uint32_t n_tokens) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!selected || !weights || !probs || !logits || !model_map ||
n_tokens == 0 ||
n_expert == 0 || n_expert > 512u ||
n_expert_used == 0 || n_expert_used > n_expert) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> logitsbuf = ds4_gpu_tensor_buffer(logits);
id<MTLBuffer> selectedbuf = ds4_gpu_tensor_buffer(selected);
id<MTLBuffer> weightsbuf = ds4_gpu_tensor_buffer(weights);
id<MTLBuffer> probsbuf = ds4_gpu_tensor_buffer(probs);
const uint64_t logits_bytes = (uint64_t)n_tokens * n_expert * sizeof(float);
const uint64_t selected_bytes = (uint64_t)n_tokens * n_expert_used * sizeof(int32_t);
const uint64_t weights_bytes = (uint64_t)n_tokens * n_expert_used * sizeof(float);
const uint64_t probs_bytes = (uint64_t)n_tokens * n_expert * sizeof(float);
if (!logitsbuf || !selectedbuf || !weightsbuf || !probsbuf ||
ds4_gpu_tensor_bytes(logits) < logits_bytes ||
ds4_gpu_tensor_bytes(selected) < selected_bytes ||
ds4_gpu_tensor_bytes(weights) < weights_bytes ||
ds4_gpu_tensor_bytes(probs) < probs_bytes) {
fprintf(stderr, "ds4: Metal GLM batch router received undersized buffers\n");
return 0;
}
const uint64_t bias_bytes = (uint64_t)n_expert * sizeof(float);
if (bias_offset > model_size || bias_bytes > model_size - bias_offset) {
fprintf(stderr, "ds4: Metal GLM batch router bias range is outside the mapped model\n");
return 0;
}
uint64_t bias_inner = 0;
id<MTLBuffer> biasbuf = ds4_gpu_wrap_model_range(model_map, model_size,
bias_offset, bias_bytes,
&bias_inner);
if (!biasbuf) return 0;
id<MTLComputePipelineState> pipeline =
ds4_gpu_hot_pipeline(g_glm_router_select_one_pipeline,
"kernel_glm_router_select_one");
if (!pipeline) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
ds4_gpu_glm_router_select_one_args args = {
.n_expert = n_expert,
.n_expert_used = n_expert_used,
.expert_weight_scale = expert_weight_scale,
.pad0 = 0,
};
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:logitsbuf offset:ds4_gpu_tensor_offset(logits) atIndex:1];
[enc setBuffer:biasbuf offset:(NSUInteger)bias_inner atIndex:2];
[enc setBuffer:selectedbuf offset:ds4_gpu_tensor_offset(selected) atIndex:3];
[enc setBuffer:weightsbuf offset:ds4_gpu_tensor_offset(weights) atIndex:4];
[enc setBuffer:probsbuf offset:ds4_gpu_tensor_offset(probs) atIndex:5];
const NSUInteger router_threads = n_expert > 256u ? 512u : 256u;
[enc setThreadgroupMemoryLength:router_threads *
(sizeof(float) + sizeof(int32_t)) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)n_tokens, 1, 1)
threadsPerThreadgroup:MTLSizeMake(router_threads, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "GLM batch router select")) return 0;
}
return 1;
}
static bool ds4_gpu_glm_gate_pair_type_supported(
uint32_t gate_type,
uint32_t up_type) {
return gate_type == up_type &&
(gate_type == DS4_METAL_TENSOR_Q2_K ||
gate_type == DS4_METAL_TENSOR_Q4_K ||
gate_type == DS4_METAL_TENSOR_Q5_K);
}
static bool ds4_gpu_glm_down_type_supported(uint32_t down_type) {
return down_type == DS4_METAL_TENSOR_Q2_K ||
down_type == DS4_METAL_TENSOR_Q4_K ||
down_type == DS4_METAL_TENSOR_Q5_K ||
down_type == DS4_METAL_TENSOR_Q6_K;
}
int ds4_gpu_glm_routed_moe_one_tensor(
ds4_gpu_tensor *out,
ds4_gpu_tensor *mid,
const void *model_map,
uint64_t model_size,
uint64_t gate_offset,
uint64_t up_offset,
uint64_t down_offset,
uint32_t gate_type,
uint32_t up_type,
uint32_t down_type,
uint64_t gate_expert_bytes,
uint64_t gate_row_bytes,
uint64_t up_expert_bytes,
uint64_t up_row_bytes,
uint64_t down_expert_bytes,
uint64_t down_row_bytes,
uint32_t expert_in_dim,
uint32_t expert_mid_dim,
uint32_t out_dim,
const ds4_gpu_tensor *selected,
const ds4_gpu_tensor *weights,
uint32_t n_total_expert,
uint32_t n_expert,
float swiglu_clamp,
uint32_t layer_index,
const ds4_gpu_tensor *x,
bool force_resident) {
if (!g_initialized && !ds4_gpu_init()) return 0;
/* TP sharding: only the owned contiguous expert range is mapped,
* so bind from the owned base, validate only its bytes, and tell the
* kernels the first expert id present at that base. */
uint32_t first_expert = 0;
uint32_t n_bind_expert = 0;
ds4_gpu_tp_expert_range(n_total_expert, &first_expert, &n_bind_expert);
/* Streaming address tables point at individually cached experts by their
* global id. Keep the source offsets global; ownership is enforced by the
* kernels before they dereference an address-table slot. */
const bool streaming_tp =
g_ssd_streaming_mode && g_tp_split_world == 2;
if (streaming_tp) {
first_expert = 0;
n_bind_expert = n_total_expert;
}
const int32_t tp_expert_base_host = (int32_t)first_expert;
gate_offset += (uint64_t)first_expert * gate_expert_bytes;
up_offset += (uint64_t)first_expert * up_expert_bytes;
down_offset += (uint64_t)first_expert * down_expert_bytes;
if (!out || !mid || !model_map || !selected || !weights || !x ||
n_total_expert == 0 || n_expert == 0 || n_expert > 256u ||
n_expert > n_total_expert ||
expert_in_dim == 0 || expert_mid_dim == 0 || out_dim == 0 ||
gate_expert_bytes == 0 || gate_row_bytes == 0 ||
up_expert_bytes == 0 || up_row_bytes == 0 ||
down_expert_bytes == 0 || down_row_bytes == 0 ||
(expert_in_dim % 256u) != 0 ||
(expert_mid_dim % 256u) != 0 ||
!ds4_gpu_glm_gate_pair_type_supported(gate_type, up_type) ||
!ds4_gpu_glm_down_type_supported(down_type)) {
return 0;
}
if ((uint64_t)n_total_expert > UINT64_MAX / gate_expert_bytes ||
(uint64_t)n_total_expert > UINT64_MAX / up_expert_bytes ||
(uint64_t)n_total_expert > UINT64_MAX / down_expert_bytes ||
(uint64_t)expert_mid_dim > UINT64_MAX / gate_row_bytes ||
(uint64_t)expert_mid_dim > UINT64_MAX / up_row_bytes ||
(uint64_t)out_dim > UINT64_MAX / down_row_bytes) {
fprintf(stderr, "ds4: Metal GLM routed MoE tensor byte size overflow\n");
return 0;
}
const uint64_t gate_tensor_bytes = (uint64_t)n_bind_expert * gate_expert_bytes;
const uint64_t up_tensor_bytes = (uint64_t)n_bind_expert * up_expert_bytes;
const uint64_t down_tensor_bytes = (uint64_t)n_bind_expert * down_expert_bytes;
if (gate_expert_bytes != (uint64_t)expert_mid_dim * gate_row_bytes ||
up_expert_bytes != (uint64_t)expert_mid_dim * up_row_bytes ||
down_expert_bytes != (uint64_t)out_dim * down_row_bytes) {
fprintf(stderr, "ds4: Metal GLM routed MoE received inconsistent expert strides\n");
return 0;
}
ds4_gpu_stream_expert_cache_note_token(layer_index);
if (gate_offset > model_size || gate_tensor_bytes > model_size - gate_offset ||
up_offset > model_size || up_tensor_bytes > model_size - up_offset ||
down_offset > model_size || down_tensor_bytes > model_size - down_offset) {
fprintf(stderr, "ds4: Metal GLM routed MoE tensor range is outside the mapped model\n");
return 0;
}
@autoreleasepool {
id<MTLBuffer> xbuf = ds4_gpu_tensor_buffer(x);
id<MTLBuffer> midbuf = ds4_gpu_tensor_buffer(mid);
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out);
id<MTLBuffer> selectedbuf = ds4_gpu_tensor_buffer(selected);
id<MTLBuffer> weightsbuf = ds4_gpu_tensor_buffer(weights);
const uint64_t x_bytes = (uint64_t)expert_in_dim * sizeof(float);
const uint64_t mid_bytes = (uint64_t)n_expert * expert_mid_dim * sizeof(float);
const uint64_t out_bytes = (uint64_t)out_dim * sizeof(float);
if (!xbuf || !midbuf || !outbuf || !selectedbuf || !weightsbuf ||
ds4_gpu_tensor_bytes(x) < x_bytes ||
ds4_gpu_tensor_bytes(mid) < mid_bytes ||
ds4_gpu_tensor_bytes(out) < out_bytes ||
ds4_gpu_tensor_bytes(selected) < (uint64_t)n_expert * sizeof(int32_t) ||
ds4_gpu_tensor_bytes(weights) < (uint64_t)n_expert * sizeof(float)) {
fprintf(stderr, "ds4: Metal GLM routed MoE received undersized activation buffers\n");
return 0;
}
const BOOL gate_pair_q2 = gate_type == DS4_METAL_TENSOR_Q2_K;
const BOOL gate_pair_q5 = gate_type == DS4_METAL_TENSOR_Q5_K;
const BOOL down_scalar_q2 = down_type == DS4_METAL_TENSOR_Q2_K;
const BOOL down_scalar_q4 = down_type == DS4_METAL_TENSOR_Q4_K;
const BOOL down_simd_q4 = down_scalar_q4;
const BOOL down_simd_q5 = down_type == DS4_METAL_TENSOR_Q5_K;
const BOOL down_simd_q6 = down_type == DS4_METAL_TENSOR_Q6_K;
const BOOL down_simd = down_simd_q4 || down_simd_q5 || down_simd_q6;
const BOOL stream_addr_q2 =
gate_pair_q2 && down_scalar_q2 &&
g_glm_q2_k_addr_pair_swiglu2_f32_pipeline != nil &&
g_glm_q2_k_addr_down_f32_pipeline != nil;
const BOOL stream_addr_q4 =
!gate_pair_q2 && !gate_pair_q5 && down_scalar_q4 &&
g_glm_q4_k_addr_pair_swiglu_f32_pipeline != nil &&
g_glm_q4_k_addr_down_f32_pipeline != nil;
BOOL use_stream_expert_addr_table =
g_ssd_streaming_mode &&
!force_resident &&
getenv("DS4_METAL_GLM_DISABLE_STREAMING_EXPERT_CACHE") == NULL &&
(stream_addr_q2 || stream_addr_q4) &&
layer_index < DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER &&
n_total_expert <= DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT &&
n_expert <= 8u &&
ds4_gpu_stream_expert_cache_configured_budget() >= n_expert &&
ds4_gpu_stream_expert_cache_note_expert_size(gate_expert_bytes,
down_expert_bytes) &&
ds4_gpu_stream_expert_cache_effective_cap(layer_index,
n_total_expert,
n_expert) != 0;
int32_t stream_selected_ids[8] = {0, 0, 0, 0, 0, 0, 0, 0};
ds4_gpu_stream_expert_cache_entry *stream_entries[8] = {
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
};
uint64_t stream_gate_abs_offsets[8] = {0, 0, 0, 0, 0, 0, 0, 0};
uint64_t stream_up_abs_offsets[8] = {0, 0, 0, 0, 0, 0, 0, 0};
uint64_t stream_down_abs_offsets[8] = {0, 0, 0, 0, 0, 0, 0, 0};
uint32_t stream_missing_mask = 0;
uint32_t stream_entry_count = 0;
uint32_t stream_resident_mask = 0;
BOOL use_stream_split_deferred = false;
id<MTLBuffer> stream_gate_addr_buf = nil;
id<MTLBuffer> stream_up_addr_buf = nil;
id<MTLBuffer> stream_down_addr_buf = nil;
const int glm_stream_timing =
ds4_gpu_stream_expert_timing_summary_enabled();
if (use_stream_expert_addr_table) {
const int had_batch = g_batch_cb != nil;
int stream_ok = 1;
const int have_prefetched_selected =
ds4_gpu_glm_stream_selected_prefetch_take(model_map,
model_size,
layer_index,
n_total_expert,
n_expert,
gate_offset,
up_offset,
down_offset,
gate_expert_bytes,
down_expert_bytes,
stream_selected_ids);
if (have_prefetched_selected) {
if (had_batch && g_batch_has_work &&
g_stream_expert_pending_load.active &&
ds4_gpu_flush_commands() == 0) {
stream_ok = 0;
}
} else {
if (had_batch && ds4_gpu_end_commands() == 0) {
stream_ok = 0;
}
if (stream_ok &&
ds4_gpu_tensor_read(selected,
0,
stream_selected_ids,
(uint64_t)n_expert * sizeof(stream_selected_ids[0])) == 0) {
stream_ok = 0;
}
}
for (uint32_t i = 0; stream_ok && i < n_expert; i++) {
if (stream_selected_ids[i] < 0 ||
(uint32_t)stream_selected_ids[i] >= n_total_expert) {
fprintf(stderr,
"ds4: Metal GLM routed MoE selected expert id %d is outside 0..%u\n",
stream_selected_ids[i],
n_total_expert);
stream_ok = 0;
}
}
if (stream_ok) {
ds4_gpu_stream_expert_cache_note_selected_hotness(layer_index,
stream_selected_ids,
n_expert);
if (!ds4_gpu_moe_selected_hotlist_record(layer_index,
stream_selected_ids,
n_expert,
n_total_expert)) {
stream_ok = 0;
}
}
if (stream_ok) {
g_glm_stream_expert_addr_table_building++;
for (uint32_t i = 0; stream_ok && i < n_expert; i++) {
const uint64_t expert_id = (uint64_t)(uint32_t)stream_selected_ids[i];
if (expert_id > UINT64_MAX / gate_expert_bytes ||
expert_id > UINT64_MAX / down_expert_bytes) {
fprintf(stderr, "ds4: Metal GLM routed MoE selected expert offset overflow\n");
stream_ok = 0;
break;
}
const uint64_t gate_rel = expert_id * gate_expert_bytes;
const uint64_t down_rel = expert_id * down_expert_bytes;
if (gate_rel > UINT64_MAX - gate_offset ||
gate_rel > UINT64_MAX - up_offset ||
down_rel > UINT64_MAX - down_offset) {
fprintf(stderr, "ds4: Metal GLM routed MoE selected expert offset overflow\n");
stream_ok = 0;
break;
}
stream_gate_abs_offsets[i] = gate_offset + gate_rel;
stream_up_abs_offsets[i] = up_offset + gate_rel;
stream_down_abs_offsets[i] = down_offset + down_rel;
stream_entries[i] =
ds4_gpu_stream_expert_cache_peek(model_map,
model_size,
layer_index,
(uint32_t)stream_selected_ids[i],
n_total_expert,
n_expert,
stream_gate_abs_offsets[i],
stream_up_abs_offsets[i],
stream_down_abs_offsets[i],
gate_expert_bytes,
down_expert_bytes);
if (!stream_entries[i]) {
stream_missing_mask |= 1u << i;
} else {
stream_resident_mask |= 1u << i;
}
}
if (stream_ok && glm_stream_timing) {
ds4_gpu_stream_expert_timing_note_cache_class(
stream_resident_mask,
stream_missing_mask);
}
use_stream_split_deferred =
stream_ok &&
getenv("DS4_METAL_DISABLE_GLM_STREAMING_EXPERT_SPLIT") == NULL &&
stream_missing_mask != 0 &&
stream_resident_mask != 0 &&
ds4_gpu_stream_expert_split_worthwhile(stream_resident_mask,
stream_missing_mask) &&
ds4_gpu_stream_expert_split_ready() &&
((gate_pair_q2 &&
g_glm_q2_k_addr_pair_swiglu2_masked_f32_pipeline != nil) ||
(!gate_pair_q2 && !gate_pair_q5 &&
g_glm_q4_k_addr_pair_swiglu_masked_f32_pipeline != nil));
if (use_stream_split_deferred) {
const ds4_gpu_stream_expert_table table = {
.model_map = model_map,
.model_size = model_size,
.layer = layer_index,
.n_total_expert = n_total_expert,
.gate_offset = gate_offset,
.up_offset = up_offset,
.down_offset = down_offset,
.gate_expert_bytes = gate_expert_bytes,
.down_expert_bytes = down_expert_bytes,
};
if (!ds4_gpu_stream_expert_cache_begin_selected_load(
&table,
stream_selected_ids,
n_expert)) {
stream_ok = 0;
}
}
if (stream_ok && stream_missing_mask != 0 &&
!use_stream_split_deferred &&
!ds4_gpu_stream_expert_cache_load_selected_missing(
model_map,
model_size,
layer_index,
stream_selected_ids,
n_total_expert,
n_expert,
stream_gate_abs_offsets,
stream_up_abs_offsets,
stream_down_abs_offsets,
gate_expert_bytes,
down_expert_bytes,
stream_missing_mask,
stream_entries)) {
fprintf(stderr,
"ds4: Metal GLM streaming expert cache failed to load "
"layer=%u missing=0x%x budget=%u\n",
layer_index,
stream_missing_mask,
ds4_gpu_stream_expert_cache_configured_budget());
stream_ok = 0;
}
for (uint32_t i = 0; stream_ok && i < n_expert; i++) {
ds4_gpu_stream_expert_cache_entry *entry = stream_entries[i];
if (!entry) {
if (use_stream_split_deferred &&
(stream_missing_mask & (1u << i)) != 0) {
continue;
}
stream_ok = 0;
break;
}
if (!ds4_gpu_stream_expert_cache_set_addr_slot(
layer_index,
(uint32_t)stream_selected_ids[i],
entry->gate_buffer,
entry->gate_inner,
entry->up_buffer,
entry->up_inner,
entry->down_buffer,
entry->down_inner)) {
stream_ok = 0;
break;
}
stream_entry_count++;
}
if (stream_ok &&
!ds4_gpu_stream_expert_cache_addr_buffers(layer_index,
&stream_gate_addr_buf,
&stream_up_addr_buf,
&stream_down_addr_buf)) {
stream_ok = 0;
}
g_glm_stream_expert_addr_table_building--;
}
if (!have_prefetched_selected && had_batch &&
ds4_gpu_begin_commands() == 0) {
stream_ok = 0;
}
if (!stream_ok) return 0;
ds4_gpu_stream_expert_cache_prune_layer(layer_index,
n_total_expert,
n_expert,
stream_selected_ids,
n_expert);
ds4_gpu_stream_expert_cache_prune_global(layer_index,
stream_selected_ids,
n_expert);
}
uint64_t gate_inner = 0;
uint64_t up_inner = 0;
uint64_t down_inner = 0;
id<MTLBuffer> gatebuf = nil;
id<MTLBuffer> upbuf = nil;
id<MTLBuffer> downbuf = nil;
if (!use_stream_expert_addr_table) {
gatebuf = ds4_gpu_wrap_model_range(model_map, model_size,
gate_offset, gate_tensor_bytes,
&gate_inner);
upbuf = ds4_gpu_wrap_model_range(model_map, model_size,
up_offset, up_tensor_bytes,
&up_inner);
downbuf = ds4_gpu_wrap_model_range(model_map, model_size,
down_offset, down_tensor_bytes,
&down_inner);
if (!gatebuf || !upbuf || !downbuf) return 0;
}
id<MTLComputePipelineState> pair_pipeline =
(use_stream_split_deferred ?
(gate_pair_q2 ?
ds4_gpu_hot_pipeline(g_glm_q2_k_addr_pair_swiglu2_masked_f32_pipeline,
"kernel_glm_q2_K_addr_pair_swiglu2_f32_masked") :
ds4_gpu_hot_pipeline(g_glm_q4_k_addr_pair_swiglu_masked_f32_pipeline,
"kernel_glm_q4_K_addr_pair_swiglu_f32_masked")) :
use_stream_expert_addr_table ?
(gate_pair_q2 ?
ds4_gpu_hot_pipeline(g_glm_q2_k_addr_pair_swiglu2_f32_pipeline,
"kernel_glm_q2_K_addr_pair_swiglu2_f32") :
ds4_gpu_hot_pipeline(g_glm_q4_k_addr_pair_swiglu_f32_pipeline,
"kernel_glm_q4_K_addr_pair_swiglu_f32")) :
gate_pair_q2 ?
ds4_gpu_hot_pipeline(g_glm_q2_k_pair_swiglu_f32_pipeline,
"kernel_glm_q2_K_pair_swiglu_f32") :
gate_pair_q5 ?
ds4_gpu_hot_pipeline(g_glm_q5_k_pair_swiglu_f32_pipeline,
"kernel_glm_q5_K_pair_swiglu_f32") :
ds4_gpu_hot_pipeline(g_glm_q4_k_pair_swiglu2_f32_pipeline,
"kernel_glm_q4_K_pair_swiglu2_f32"));
id<MTLComputePipelineState> down_pipeline =
(use_stream_expert_addr_table ?
(down_scalar_q2 ?
ds4_gpu_hot_pipeline(g_glm_q2_k_addr_down_f32_pipeline,
"kernel_glm_q2_K_addr_down_f32") :
ds4_gpu_hot_pipeline(g_glm_q4_k_addr_down_f32_pipeline,
"kernel_glm_q4_K_addr_down_f32")) :
down_scalar_q2 ?
ds4_gpu_hot_pipeline(g_glm_q2_k_down_f32_pipeline,
"kernel_glm_q2_K_down_f32") :
down_scalar_q4 ?
ds4_gpu_hot_pipeline(g_glm_q4_k_down_f32_pipeline,
"kernel_glm_q4_K_down_f32") :
down_simd_q5 ?
ds4_gpu_hot_pipeline(g_glm_q5_k_down_f32_pipeline,
"kernel_glm_q5_K_down_f32") :
ds4_gpu_hot_pipeline(g_glm_q6_k_down_f32_pipeline,
"kernel_glm_q6_K_down_f32"));
if (!pair_pipeline || !down_pipeline) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
const bool glm_moe_stage_profile =
g_batch_cb != nil &&
ds4_gpu_stage_profile_enabled_for_layer(
"DS4_METAL_GLM_MOE_ONE_STAGE_PROFILE",
"DS4_METAL_GLM_MOE_ONE_STAGE_PROFILE_LAYER",
layer_index);
const char *glm_moe_stage_filter =
getenv("DS4_METAL_GLM_MOE_STAGE_PROFILE_FILTER");
const char *glm_pair_path =
use_stream_expert_addr_table ?
(gate_pair_q2 ? "q2_stream_addr_swiglu" :
"q4_stream_addr_swiglu") :
gate_pair_q2 ? "q2_scalar_swiglu" :
gate_pair_q5 ? "q5_pair_simd_swiglu" : "q4_pair2_simd_swiglu";
const char *glm_down_path =
use_stream_expert_addr_table ?
(down_scalar_q2 ? "q2_stream_addr_down" :
"q4_stream_addr_down_simd") :
down_scalar_q2 ? "q2_down_simd" :
down_scalar_q4 ? "q4_down_simd" :
down_simd_q5 ? "q5_down_simd" : "q6_down_simd";
double glm_moe_stage_t0 = 0.0;
if (glm_moe_stage_profile) {
if (ds4_gpu_end_commands() == 0 || ds4_gpu_begin_commands() == 0) {
return 0;
}
cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
glm_moe_stage_t0 = ds4_gpu_now_ms();
}
int ok = 1;
#define DS4_METAL_PROFILE_GLM_MOE_ONE_STAGE(name) do { \
if (ok && glm_moe_stage_profile) { \
if (ds4_gpu_end_commands() == 0) { \
ok = 0; \
} else { \
const char *stage_name = (name); \
const double now_ms = ds4_gpu_now_ms(); \
const int print_stage = \
!glm_moe_stage_filter || !glm_moe_stage_filter[0] || \
strstr(stage_name, glm_moe_stage_filter) != NULL; \
if (print_stage) { \
fprintf(stderr, \
"ds4: Metal GLM routed MoE one stage layer=%u tokens=1 experts=%u " \
"gate=%s down=%s pair=%s down_path=%s %s=%.3f ms\n", \
layer_index, n_expert, \
ds4_gpu_metal_tensor_type_name(gate_type), \
ds4_gpu_metal_tensor_type_name(down_type), \
glm_pair_path, glm_down_path, \
stage_name, now_ms - glm_moe_stage_t0); \
} \
glm_moe_stage_t0 = now_ms; \
if (ds4_gpu_begin_commands() == 0) { \
ok = 0; \
} else { \
cb = ds4_gpu_command_buffer(&owned); \
if (!cb) ok = 0; \
} \
} \
} \
} while (0)
ds4_gpu_glm_routed_moe_args args = {
.tp_rank = g_tp_split_rank,
.tp_world = g_tp_split_world,
.tp_expert_base = tp_expert_base_host,
.in_dim = expert_in_dim,
.mid_dim = expert_mid_dim,
.out_dim = out_dim,
.n_total_expert = n_total_expert,
.n_expert_used = n_expert,
.n_tokens = 1,
.mid_token_stride = n_expert * expert_mid_dim,
.down_type = down_type,
.swiglu_clamp = swiglu_clamp,
.gate_expert_bytes = gate_expert_bytes,
.gate_row_bytes = gate_row_bytes,
.up_expert_bytes = up_expert_bytes,
.up_row_bytes = up_row_bytes,
.down_expert_bytes = down_expert_bytes,
.down_row_bytes = down_row_bytes,
};
const NSUInteger pair_x_groups =
gate_pair_q2 ? (use_stream_expert_addr_table ?
(NSUInteger)((expert_mid_dim + 1u) / 2u) :
(NSUInteger)((expert_mid_dim + 7u) / 8u)) :
gate_pair_q5 ? (NSUInteger)((expert_mid_dim + 7u) / 8u) :
use_stream_expert_addr_table ? (NSUInteger)((expert_mid_dim + 3u) / 4u) :
(NSUInteger)((expert_mid_dim + 1u) / 2u);
const NSUInteger pair_threadgroup_bytes = 0u;
const NSUInteger pair_threads = 64u;
const NSUInteger down_x_groups =
down_scalar_q2 ? (NSUInteger)((out_dim + 7u) / 8u) :
down_simd_q4 ? (NSUInteger)((out_dim + 3u) / 4u) :
down_simd_q5 ? (NSUInteger)((out_dim + 3u) / 4u) :
down_simd_q6 ? (NSUInteger)((out_dim + 3u) / 4u) :
(NSUInteger)out_dim;
const NSUInteger down_threadgroup_bytes =
(down_scalar_q2 || down_simd) ? 0u : 256u * sizeof(float);
const NSUInteger down_threads =
(down_scalar_q2 || down_simd) ? 64u : 256u;
if (use_stream_expert_addr_table &&
!ds4_gpu_stream_expert_cache_mark_entries_inflight(
stream_entries,
use_stream_split_deferred ? n_expert : stream_entry_count,
use_stream_split_deferred ? stream_resident_mask : 0)) {
return 0;
}
const int glm_stream_split_timing =
use_stream_split_deferred && glm_stream_timing;
double glm_stream_split_t0 =
glm_stream_split_timing ? ds4_gpu_now_ms() : 0.0;
double glm_stream_split_resident_ms = 0.0;
double glm_stream_split_missing_load_ms = 0.0;
double glm_stream_split_missing_wait_ms = 0.0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pair_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
if (use_stream_split_deferred) {
[enc setBytes:&stream_resident_mask length:sizeof(stream_resident_mask) atIndex:1];
[enc setBuffer:stream_gate_addr_buf offset:0u atIndex:2];
[enc setBuffer:stream_up_addr_buf offset:0u atIndex:3];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:4];
[enc setBuffer:selectedbuf offset:ds4_gpu_tensor_offset(selected) atIndex:5];
[enc setBuffer:weightsbuf offset:ds4_gpu_tensor_offset(weights) atIndex:6];
[enc setBuffer:midbuf offset:ds4_gpu_tensor_offset(mid) atIndex:7];
} else {
[enc setBuffer:use_stream_expert_addr_table ? stream_gate_addr_buf : gatebuf
offset:use_stream_expert_addr_table ? 0u : (NSUInteger)gate_inner
atIndex:1];
[enc setBuffer:use_stream_expert_addr_table ? stream_up_addr_buf : upbuf
offset:use_stream_expert_addr_table ? 0u : (NSUInteger)up_inner
atIndex:2];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:3];
[enc setBuffer:selectedbuf offset:ds4_gpu_tensor_offset(selected) atIndex:4];
[enc setBuffer:weightsbuf offset:ds4_gpu_tensor_offset(weights) atIndex:5];
[enc setBuffer:midbuf offset:ds4_gpu_tensor_offset(mid) atIndex:6];
}
if (use_stream_expert_addr_table) {
const uint32_t use_count =
use_stream_split_deferred ? n_expert : stream_entry_count;
const uint32_t use_mask =
use_stream_split_deferred ? stream_resident_mask : 0;
for (uint32_t i = 0; i < use_count; i++) {
if (use_mask != 0 && (use_mask & (1u << i)) == 0) continue;
[enc useResource:stream_entries[i]->gate_buffer usage:MTLResourceUsageRead];
[enc useResource:stream_entries[i]->up_buffer usage:MTLResourceUsageRead];
}
}
if (pair_threadgroup_bytes != 0u) {
[enc setThreadgroupMemoryLength:pair_threadgroup_bytes atIndex:0];
}
[enc dispatchThreadgroups:MTLSizeMake(pair_x_groups,
(NSUInteger)n_expert,
1)
threadsPerThreadgroup:MTLSizeMake(pair_threads, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
DS4_METAL_PROFILE_GLM_MOE_ONE_STAGE("pair");
if (ok && use_stream_split_deferred) {
id<MTLCommandBuffer> resident_cb = nil;
const int resident_owned = owned;
if (owned) {
resident_cb = cb;
[resident_cb commit];
cb = nil;
} else {
ok = ds4_gpu_flush_commands();
}
if (glm_stream_split_timing) {
const double now_ms = ds4_gpu_now_ms();
glm_stream_split_resident_ms = now_ms - glm_stream_split_t0;
glm_stream_split_t0 = now_ms;
}
if (ok) {
ok = ds4_gpu_stream_expert_pending_load_finish(stream_entries);
}
if (glm_stream_split_timing) {
const double now_ms = ds4_gpu_now_ms();
glm_stream_split_missing_load_ms = now_ms - glm_stream_split_t0;
glm_stream_split_t0 = now_ms;
}
if (ok) {
ds4_gpu_stream_expert_cache_prune_layer(layer_index,
n_total_expert,
n_expert,
stream_selected_ids,
n_expert);
ds4_gpu_stream_expert_cache_prune_global(layer_index,
stream_selected_ids,
n_expert);
ok = ds4_gpu_stream_expert_cache_addr_buffers(layer_index,
&stream_gate_addr_buf,
&stream_up_addr_buf,
&stream_down_addr_buf);
}
if (ok) {
if (resident_owned) {
ok = ds4_gpu_wait_command_buffer(
resident_cb,
"GLM streaming split resident pair");
ds4_gpu_stream_expert_cache_note_owned_completed();
} else {
ok = ds4_gpu_wait_pending_command_buffers(
"GLM streaming split resident pair");
}
}
if (glm_stream_split_timing) {
const double now_ms = ds4_gpu_now_ms();
glm_stream_split_missing_wait_ms = now_ms - glm_stream_split_t0;
glm_stream_split_t0 = now_ms;
ds4_gpu_stream_expert_timing_note_split(
stream_resident_mask,
stream_missing_mask,
glm_stream_split_resident_ms,
glm_stream_split_missing_load_ms +
glm_stream_split_missing_wait_ms);
ds4_gpu_stream_expert_timing_note_split_missing_detail(
glm_stream_split_missing_load_ms,
0.0,
0.0,
0.0,
glm_stream_split_missing_wait_ms);
}
if (ok &&
!ds4_gpu_stream_expert_cache_mark_entries_inflight(stream_entries,
n_expert,
stream_missing_mask)) {
ok = 0;
}
if (ok) {
cb = ds4_gpu_command_buffer(&owned);
if (!cb) ok = 0;
}
if (ok) {
enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pair_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBytes:&stream_missing_mask length:sizeof(stream_missing_mask) atIndex:1];
[enc setBuffer:stream_gate_addr_buf offset:0u atIndex:2];
[enc setBuffer:stream_up_addr_buf offset:0u atIndex:3];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:4];
[enc setBuffer:selectedbuf offset:ds4_gpu_tensor_offset(selected) atIndex:5];
[enc setBuffer:weightsbuf offset:ds4_gpu_tensor_offset(weights) atIndex:6];
[enc setBuffer:midbuf offset:ds4_gpu_tensor_offset(mid) atIndex:7];
for (uint32_t i = 0; i < n_expert; i++) {
if ((stream_missing_mask & (1u << i)) == 0) continue;
[enc useResource:stream_entries[i]->gate_buffer usage:MTLResourceUsageRead];
[enc useResource:stream_entries[i]->up_buffer usage:MTLResourceUsageRead];
}
if (pair_threadgroup_bytes != 0u) {
[enc setThreadgroupMemoryLength:pair_threadgroup_bytes atIndex:0];
}
[enc dispatchThreadgroups:MTLSizeMake(pair_x_groups,
(NSUInteger)n_expert,
1)
threadsPerThreadgroup:MTLSizeMake(pair_threads, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
}
}
if (!ok) return 0;
enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:down_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:use_stream_expert_addr_table ? stream_down_addr_buf : downbuf
offset:use_stream_expert_addr_table ? 0u : (NSUInteger)down_inner
atIndex:1];
[enc setBuffer:selectedbuf offset:ds4_gpu_tensor_offset(selected) atIndex:2];
[enc setBuffer:midbuf offset:ds4_gpu_tensor_offset(mid) atIndex:3];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(out) atIndex:4];
if (use_stream_expert_addr_table) {
const uint32_t use_count =
use_stream_split_deferred ? n_expert : stream_entry_count;
for (uint32_t i = 0; i < use_count; i++) {
[enc useResource:stream_entries[i]->down_buffer usage:MTLResourceUsageRead];
}
}
if (down_threadgroup_bytes != 0u) {
[enc setThreadgroupMemoryLength:down_threadgroup_bytes atIndex:0];
}
[enc dispatchThreadgroups:MTLSizeMake(down_x_groups, 1, 1)
threadsPerThreadgroup:MTLSizeMake(down_threads, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
DS4_METAL_PROFILE_GLM_MOE_ONE_STAGE("down");
if (!ok) return 0;
if (!ds4_gpu_finish_command_buffer(cb, owned, "GLM routed MoE")) return 0;
#undef DS4_METAL_PROFILE_GLM_MOE_ONE_STAGE
}
return 1;
}
static bool ds4_gpu_glm_grouped_moe_fast_default(void) {
return !g_quality_mode;
}
static bool ds4_gpu_glm_routed_moe_batch_grouped_available(
uint32_t gate_type,
uint32_t up_type,
uint32_t down_type,
uint32_t n_expert,
uint32_t n_tokens) {
if (n_tokens < 32u ||
!ds4_gpu_glm_gate_pair_type_supported(gate_type, up_type) ||
!ds4_gpu_glm_down_type_supported(down_type) ||
ds4_gpu_mul_mm_id_map0_name(n_expert) == NULL) {
return false;
}
const bool fast_default = ds4_gpu_glm_grouped_moe_fast_default();
if (!fast_default) {
return false;
}
if (n_tokens < 96u) return false;
return ds4_gpu_get_pipeline(ds4_gpu_mul_mm_id_map0_name(n_expert)) != nil &&
ds4_gpu_routed_mm_pipeline(gate_type) != nil &&
ds4_gpu_routed_mm_pipeline(up_type) != nil &&
ds4_gpu_routed_mm_f16_rhs_pipeline(down_type) != nil;
}
static bool ds4_gpu_glm_grouped_moe_layer_enabled(uint32_t layer_index) {
(void)layer_index;
return true;
}
static int ds4_gpu_glm_routed_moe_batch_grouped_tensor(
ds4_gpu_tensor *out,
ds4_gpu_tensor *mid,
const void *model_map,
uint64_t model_size,
uint64_t gate_offset,
uint64_t up_offset,
uint64_t down_offset,
uint32_t gate_type,
uint32_t up_type,
uint32_t down_type,
uint64_t gate_expert_bytes,
uint64_t gate_row_bytes,
uint64_t up_expert_bytes,
uint64_t up_row_bytes,
uint64_t down_expert_bytes,
uint64_t down_row_bytes,
uint32_t expert_in_dim,
uint32_t expert_mid_dim,
uint32_t out_dim,
const ds4_gpu_tensor *selected,
const ds4_gpu_tensor *weights,
uint32_t n_total_expert,
uint32_t n_expert,
float swiglu_clamp,
uint32_t layer_index,
const ds4_gpu_tensor *x,
uint32_t n_tokens) {
if (!ds4_gpu_glm_routed_moe_batch_grouped_available(gate_type,
up_type,
down_type,
n_expert,
n_tokens)) {
return 0;
}
if (n_expert > UINT32_MAX / n_tokens ||
(uint64_t)n_total_expert > UINT64_MAX / gate_expert_bytes ||
(uint64_t)n_total_expert > UINT64_MAX / up_expert_bytes ||
(uint64_t)n_total_expert > UINT64_MAX / down_expert_bytes) {
return 0;
}
const uint32_t pair_rows = n_tokens * n_expert;
if ((uint64_t)pair_rows > UINT64_MAX / expert_mid_dim ||
(uint64_t)pair_rows > UINT64_MAX / out_dim ||
(uint64_t)n_tokens > UINT64_MAX / expert_in_dim ||
(uint64_t)n_tokens > UINT64_MAX / out_dim) {
return 0;
}
const bool mid_f16 = true;
const NSUInteger mm_id_threadgroup_bytes = 8192u;
const uint64_t compact_mid_values = (uint64_t)pair_rows * expert_mid_dim;
const uint64_t down_values = (uint64_t)pair_rows * out_dim;
const uint64_t x_values = (uint64_t)n_tokens * expert_in_dim;
const uint64_t out_values = (uint64_t)n_tokens * out_dim;
if (compact_mid_values > UINT64_MAX / sizeof(float) ||
compact_mid_values > UINT64_MAX / (mid_f16 ? sizeof(uint16_t) : sizeof(float)) ||
down_values > UINT64_MAX / sizeof(float) ||
x_values > UINT64_MAX / sizeof(float) ||
out_values > UINT64_MAX / sizeof(float)) {
return 0;
}
const uint64_t gate_scratch_bytes = compact_mid_values * sizeof(float);
const uint64_t mid_bytes = compact_mid_values * (mid_f16 ? sizeof(uint16_t) : sizeof(float));
const uint64_t down_scratch_bytes = down_values * sizeof(float);
const uint64_t x_bytes = x_values * sizeof(float);
const uint64_t out_bytes = out_values * sizeof(float);
const uint64_t selected_values = (uint64_t)n_tokens * n_expert;
const uint64_t selected_bytes = selected_values * sizeof(int32_t);
const uint64_t weights_bytes = selected_values * sizeof(float);
if (gate_scratch_bytes > UINT64_MAX - gate_scratch_bytes ||
gate_scratch_bytes > NSUIntegerMax ||
gate_scratch_bytes * 2ull > NSUIntegerMax ||
down_scratch_bytes > NSUIntegerMax) {
return 0;
}
uint32_t first_expert = 0;
uint32_t n_bind_expert = 0;
ds4_gpu_tp_expert_range(n_total_expert, &first_expert, &n_bind_expert);
gate_offset += (uint64_t)first_expert * gate_expert_bytes;
up_offset += (uint64_t)first_expert * up_expert_bytes;
down_offset += (uint64_t)first_expert * down_expert_bytes;
const uint64_t gate_tensor_bytes = (uint64_t)n_bind_expert * gate_expert_bytes;
const uint64_t up_tensor_bytes = (uint64_t)n_bind_expert * up_expert_bytes;
const uint64_t down_tensor_bytes = (uint64_t)n_bind_expert * down_expert_bytes;
@autoreleasepool {
id<MTLBuffer> xbuf = ds4_gpu_tensor_buffer(x);
id<MTLBuffer> midbuf = ds4_gpu_tensor_buffer(mid);
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out);
id<MTLBuffer> selectedbuf = ds4_gpu_tensor_buffer(selected);
id<MTLBuffer> weightsbuf = ds4_gpu_tensor_buffer(weights);
if (!xbuf || !midbuf || !outbuf || !selectedbuf || !weightsbuf ||
ds4_gpu_tensor_bytes(x) < x_bytes ||
ds4_gpu_tensor_bytes(mid) < mid_bytes ||
ds4_gpu_tensor_bytes(out) < out_bytes ||
ds4_gpu_tensor_bytes(selected) < selected_bytes ||
ds4_gpu_tensor_bytes(weights) < weights_bytes) {
fprintf(stderr, "ds4: Metal GLM grouped routed MoE received undersized activation buffers\n");
return 0;
}
if (!ds4_gpu_ensure_scratch_buffer(&g_moe_gate_scratch_buffer,
&g_moe_gate_scratch_bytes,
(NSUInteger)(gate_scratch_bytes * 2ull),
"ds4_glm_moe_gate_up_scratch")) {
return 0;
}
if (n_expert > 1 &&
!ds4_gpu_ensure_scratch_buffer(&g_moe_down_scratch_buffer,
&g_moe_down_scratch_bytes,
(NSUInteger)down_scratch_bytes,
"ds4_glm_moe_down_scratch")) {
return 0;
}
uint64_t gate_inner = 0;
uint64_t up_inner = 0;
uint64_t down_inner = 0;
id<MTLBuffer> gatebuf = ds4_gpu_wrap_model_range(model_map, model_size,
gate_offset, gate_tensor_bytes,
&gate_inner);
id<MTLBuffer> upbuf = ds4_gpu_wrap_model_range(model_map, model_size,
up_offset, up_tensor_bytes,
&up_inner);
id<MTLBuffer> downbuf = ds4_gpu_wrap_model_range(model_map, model_size,
down_offset, down_tensor_bytes,
&down_inner);
if (!gatebuf || !upbuf || !downbuf) return 0;
id<MTLComputePipelineState> map_pipeline =
ds4_gpu_get_pipeline(ds4_gpu_mul_mm_id_map0_name(n_expert));
id<MTLComputePipelineState> gate_pipeline = ds4_gpu_routed_mm_pipeline(gate_type);
id<MTLComputePipelineState> up_pipeline = ds4_gpu_routed_mm_pipeline(up_type);
id<MTLComputePipelineState> down_pipeline =
ds4_gpu_routed_mm_f16_rhs_pipeline(down_type);
if (!map_pipeline || !gate_pipeline || !up_pipeline || !down_pipeline) {
return 0;
}
ds4_gpu_mul_mm_id_map_args map_args =
ds4_gpu_make_mul_mm_id_map_args(expert_in_dim, n_total_expert, 1, n_expert, n_tokens);
ds4_gpu_mul_mm_id_args gate_args =
ds4_gpu_make_mul_mm_id_args(expert_in_dim, expert_mid_dim, n_total_expert,
gate_row_bytes, gate_expert_bytes,
1, n_expert, n_tokens);
ds4_gpu_mul_mm_id_args up_args =
ds4_gpu_make_mul_mm_id_args(expert_in_dim, expert_mid_dim, n_total_expert,
up_row_bytes, up_expert_bytes,
1, n_expert, n_tokens);
ds4_gpu_mul_mm_id_args down_args =
ds4_gpu_make_mul_mm_id_args_src1_size(expert_mid_dim, out_dim, n_total_expert,
down_row_bytes, down_expert_bytes,
n_expert, n_expert, n_tokens,
mid_f16 ? sizeof(uint16_t) : sizeof(float));
gate_args.tp_rank = g_tp_split_rank;
gate_args.tp_world = g_tp_split_world;
gate_args.tp_expert_base = (int32_t)first_expert;
up_args.tp_rank = g_tp_split_rank;
up_args.tp_world = g_tp_split_world;
up_args.tp_expert_base = (int32_t)first_expert;
down_args.tp_rank = g_tp_split_rank;
down_args.tp_world = g_tp_split_world;
down_args.tp_expert_base = (int32_t)first_expert;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
const bool glm_moe_stage_profile = false;
const char *glm_moe_stage_filter = NULL;
double glm_moe_stage_t0 = 0.0;
if (glm_moe_stage_profile) {
if (ds4_gpu_end_commands() == 0 || ds4_gpu_begin_commands() == 0) {
return 0;
}
cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
glm_moe_stage_t0 = ds4_gpu_now_ms();
}
int ok = 1;
#define DS4_METAL_PROFILE_GLM_GROUPED_MOE_STAGE(name) do { \
if (ok && glm_moe_stage_profile) { \
if (ds4_gpu_end_commands() == 0) { \
ok = 0; \
} else { \
const char *stage_name = (name); \
const double now_ms = ds4_gpu_now_ms(); \
const int print_stage = \
!glm_moe_stage_filter || !glm_moe_stage_filter[0] || \
strstr(stage_name, glm_moe_stage_filter) != NULL; \
if (print_stage) { \
fprintf(stderr, \
"ds4: Metal GLM grouped routed MoE stage layer=%u tokens=%u pairs=%u experts=%u " \
"gate=%s down=%s mid=%s %s=%.3f ms\n", \
layer_index, n_tokens, pair_rows, n_expert, \
ds4_gpu_metal_tensor_type_name(gate_type), \
ds4_gpu_metal_tensor_type_name(down_type), \
mid_f16 ? "f16" : "f32", \
stage_name, now_ms - glm_moe_stage_t0); \
} \
glm_moe_stage_t0 = now_ms; \
if (ds4_gpu_begin_commands() == 0) { \
ok = 0; \
} else { \
cb = ds4_gpu_command_buffer(&owned); \
if (!cb) ok = 0; \
} \
} \
} \
} while (0)
ok = ds4_gpu_encode_mul_mm_id_map(cb,
map_pipeline,
&map_args,
&gate_args,
selectedbuf,
ds4_gpu_tensor_offset(selected));
DS4_METAL_PROFILE_GLM_GROUPED_MOE_STAGE("map");
if (ok) {
ok = ds4_gpu_encode_mul_mm_id_mapped_tile(cb,
gate_pipeline,
&gate_args,
gatebuf,
(NSUInteger)gate_inner,
xbuf,
ds4_gpu_tensor_offset(x),
g_moe_gate_scratch_buffer,
0,
mm_id_threadgroup_bytes);
}
DS4_METAL_PROFILE_GLM_GROUPED_MOE_STAGE("gate");
if (ok) {
ok = ds4_gpu_encode_mul_mm_id_mapped_tile(cb,
up_pipeline,
&up_args,
upbuf,
(NSUInteger)up_inner,
xbuf,
ds4_gpu_tensor_offset(x),
g_moe_gate_scratch_buffer,
(NSUInteger)gate_scratch_bytes,
mm_id_threadgroup_bytes);
}
DS4_METAL_PROFILE_GLM_GROUPED_MOE_STAGE("up");
if (ok) {
ok = ds4_gpu_encode_moe_swiglu_weight(cb,
g_moe_gate_scratch_buffer,
0,
g_moe_gate_scratch_buffer,
(NSUInteger)gate_scratch_bytes,
midbuf,
ds4_gpu_tensor_offset(mid),
weightsbuf,
ds4_gpu_tensor_offset(weights),
expert_mid_dim,
pair_rows,
swiglu_clamp,
mid_f16);
}
DS4_METAL_PROFILE_GLM_GROUPED_MOE_STAGE("activation_weight");
id<MTLBuffer> down_dst = n_expert == 1 ? outbuf : g_moe_down_scratch_buffer;
NSUInteger down_dst_off = n_expert == 1 ? ds4_gpu_tensor_offset(out) : 0;
if (ok) {
ok = ds4_gpu_encode_mul_mm_id_mapped_tile(cb,
down_pipeline,
&down_args,
downbuf,
(NSUInteger)down_inner,
midbuf,
ds4_gpu_tensor_offset(mid),
down_dst,
down_dst_off,
mm_id_threadgroup_bytes);
}
DS4_METAL_PROFILE_GLM_GROUPED_MOE_STAGE("down");
if (ok && n_expert > 1) {
ok = ds4_gpu_encode_moe_sum_experts(cb,
down_dst,
down_dst_off,
outbuf,
ds4_gpu_tensor_offset(out),
out_dim,
n_expert,
n_tokens);
}
DS4_METAL_PROFILE_GLM_GROUPED_MOE_STAGE("sum");
if (!ok) return 0;
if (!ds4_gpu_finish_command_buffer(cb, owned, "GLM grouped routed batch MoE")) {
return 0;
}
#undef DS4_METAL_PROFILE_GLM_GROUPED_MOE_STAGE
}
return 1;
}
static int ds4_gpu_glm_routed_moe_batch_grouped_addr_tensor(
ds4_gpu_tensor *out,
ds4_gpu_tensor *mid,
id<MTLBuffer> gate_addrs,
id<MTLBuffer> up_addrs,
id<MTLBuffer> down_addrs,
id<MTLBuffer> overflow_gate,
id<MTLBuffer> overflow_up,
id<MTLBuffer> overflow_down,
uint32_t gate_type,
uint32_t up_type,
uint32_t down_type,
uint64_t gate_expert_bytes,
uint64_t gate_row_bytes,
uint64_t up_expert_bytes,
uint64_t up_row_bytes,
uint64_t down_expert_bytes,
uint64_t down_row_bytes,
uint32_t expert_in_dim,
uint32_t expert_mid_dim,
uint32_t out_dim,
const ds4_gpu_tensor *selected,
const ds4_gpu_tensor *weights,
uint32_t n_total_expert,
uint32_t n_expert,
float swiglu_clamp,
uint32_t layer_index,
const ds4_gpu_tensor *x,
uint32_t n_tokens,
ds4_gpu_stream_expert_cache_entry * const *resources,
uint32_t resource_count) {
if (n_expert > UINT32_MAX / n_tokens ||
!gate_addrs || !up_addrs || !down_addrs ||
!ds4_gpu_glm_routed_moe_batch_grouped_available(gate_type,
up_type,
down_type,
n_expert,
n_tokens)) {
return 0;
}
const uint32_t pair_rows = n_tokens * n_expert;
if ((uint64_t)pair_rows > UINT64_MAX / expert_mid_dim ||
(uint64_t)pair_rows > UINT64_MAX / out_dim ||
(uint64_t)n_tokens > UINT64_MAX / expert_in_dim ||
(uint64_t)n_tokens > UINT64_MAX / out_dim) {
return 0;
}
const bool mid_f16 = true;
const NSUInteger mm_id_threadgroup_bytes = 8192u;
const uint64_t compact_mid_values = (uint64_t)pair_rows * expert_mid_dim;
const uint64_t down_values = (uint64_t)pair_rows * out_dim;
const uint64_t x_values = (uint64_t)n_tokens * expert_in_dim;
const uint64_t out_values = (uint64_t)n_tokens * out_dim;
if (compact_mid_values > UINT64_MAX / sizeof(float) ||
compact_mid_values > UINT64_MAX / sizeof(uint16_t) ||
down_values > UINT64_MAX / sizeof(float) ||
x_values > UINT64_MAX / sizeof(float) ||
out_values > UINT64_MAX / sizeof(float)) {
return 0;
}
const uint64_t gate_scratch_bytes = compact_mid_values * sizeof(float);
const uint64_t mid_bytes = compact_mid_values * sizeof(uint16_t);
const uint64_t down_scratch_bytes = down_values * sizeof(float);
const uint64_t x_bytes = x_values * sizeof(float);
const uint64_t out_bytes = out_values * sizeof(float);
const uint64_t selected_values = (uint64_t)n_tokens * n_expert;
const uint64_t selected_bytes = selected_values * sizeof(int32_t);
const uint64_t weights_bytes = selected_values * sizeof(float);
if (gate_scratch_bytes > UINT64_MAX - gate_scratch_bytes ||
gate_scratch_bytes > NSUIntegerMax ||
gate_scratch_bytes * 2ull > NSUIntegerMax ||
down_scratch_bytes > NSUIntegerMax) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> xbuf = ds4_gpu_tensor_buffer(x);
id<MTLBuffer> midbuf = ds4_gpu_tensor_buffer(mid);
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out);
id<MTLBuffer> selectedbuf = ds4_gpu_tensor_buffer(selected);
id<MTLBuffer> weightsbuf = ds4_gpu_tensor_buffer(weights);
if (!xbuf || !midbuf || !outbuf || !selectedbuf || !weightsbuf ||
ds4_gpu_tensor_bytes(x) < x_bytes ||
ds4_gpu_tensor_bytes(mid) < mid_bytes ||
ds4_gpu_tensor_bytes(out) < out_bytes ||
ds4_gpu_tensor_bytes(selected) < selected_bytes ||
ds4_gpu_tensor_bytes(weights) < weights_bytes) {
fprintf(stderr, "ds4: Metal GLM grouped-address routed MoE received undersized activation buffers\n");
return 0;
}
if (!ds4_gpu_ensure_scratch_buffer(&g_moe_gate_scratch_buffer,
&g_moe_gate_scratch_bytes,
(NSUInteger)(gate_scratch_bytes * 2ull),
"ds4_glm_moe_gate_up_scratch")) {
return 0;
}
if (n_expert > 1 &&
!ds4_gpu_ensure_scratch_buffer(&g_moe_down_scratch_buffer,
&g_moe_down_scratch_bytes,
(NSUInteger)down_scratch_bytes,
"ds4_glm_moe_down_scratch")) {
return 0;
}
id<MTLComputePipelineState> map_pipeline =
ds4_gpu_get_pipeline(ds4_gpu_mul_mm_id_map0_name(n_expert));
id<MTLComputePipelineState> gate_pipeline =
ds4_gpu_routed_mm_addr_pipeline(gate_type);
id<MTLComputePipelineState> up_pipeline =
ds4_gpu_routed_mm_addr_pipeline(up_type);
id<MTLComputePipelineState> down_pipeline =
ds4_gpu_routed_mm_addr_f16_rhs_pipeline(down_type);
if (!map_pipeline || !gate_pipeline || !up_pipeline || !down_pipeline) {
return 0;
}
ds4_gpu_mul_mm_id_map_args map_args =
ds4_gpu_make_mul_mm_id_map_args(expert_in_dim, n_total_expert, 1, n_expert, n_tokens);
ds4_gpu_mul_mm_id_args gate_args =
ds4_gpu_make_mul_mm_id_args(expert_in_dim, expert_mid_dim, n_total_expert,
gate_row_bytes, gate_expert_bytes,
1, n_expert, n_tokens);
ds4_gpu_mul_mm_id_args up_args =
ds4_gpu_make_mul_mm_id_args(expert_in_dim, expert_mid_dim, n_total_expert,
up_row_bytes, up_expert_bytes,
1, n_expert, n_tokens);
ds4_gpu_mul_mm_id_args down_args =
ds4_gpu_make_mul_mm_id_args_src1_size(expert_mid_dim, out_dim, n_total_expert,
down_row_bytes, down_expert_bytes,
n_expert, n_expert, n_tokens,
sizeof(uint16_t));
if (!ds4_gpu_stream_expert_cache_mark_entries_inflight(resources,
resource_count,
0)) {
return 0;
}
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
const bool glm_moe_stage_profile = false;
const char *glm_moe_stage_filter = NULL;
double glm_moe_stage_t0 = 0.0;
if (glm_moe_stage_profile) {
if (ds4_gpu_end_commands() == 0 || ds4_gpu_begin_commands() == 0) {
return 0;
}
cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
glm_moe_stage_t0 = ds4_gpu_now_ms();
}
int ok = 1;
#define DS4_METAL_PROFILE_GLM_GROUPED_ADDR_MOE_STAGE(name) do { \
if (ok && glm_moe_stage_profile) { \
if (ds4_gpu_end_commands() == 0) { \
ok = 0; \
} else { \
const char *stage_name = (name); \
const double now_ms = ds4_gpu_now_ms(); \
const int print_stage = \
!glm_moe_stage_filter || !glm_moe_stage_filter[0] || \
strstr(stage_name, glm_moe_stage_filter) != NULL; \
if (print_stage) { \
fprintf(stderr, \
"ds4: Metal GLM grouped-address routed MoE stage layer=%u tokens=%u pairs=%u experts=%u " \
"gate=%s down=%s mid=f16 %s=%.3f ms\n", \
layer_index, n_tokens, pair_rows, n_expert, \
ds4_gpu_metal_tensor_type_name(gate_type), \
ds4_gpu_metal_tensor_type_name(down_type), \
stage_name, now_ms - glm_moe_stage_t0); \
} \
glm_moe_stage_t0 = now_ms; \
if (ds4_gpu_begin_commands() == 0) { \
ok = 0; \
} else { \
cb = ds4_gpu_command_buffer(&owned); \
if (!cb) ok = 0; \
} \
} \
} \
} while (0)
ok = ds4_gpu_encode_mul_mm_id_map(cb,
map_pipeline,
&map_args,
&gate_args,
selectedbuf,
ds4_gpu_tensor_offset(selected));
DS4_METAL_PROFILE_GLM_GROUPED_ADDR_MOE_STAGE("map");
if (ok) {
ok = ds4_gpu_encode_mul_mm_id_addr_mapped_tile(cb,
gate_pipeline,
&gate_args,
gate_addrs,
xbuf,
ds4_gpu_tensor_offset(x),
g_moe_gate_scratch_buffer,
0,
mm_id_threadgroup_bytes,
resources,
resource_count,
0,
overflow_gate);
}
DS4_METAL_PROFILE_GLM_GROUPED_ADDR_MOE_STAGE("gate");
if (ok) {
ok = ds4_gpu_encode_mul_mm_id_addr_mapped_tile(cb,
up_pipeline,
&up_args,
up_addrs,
xbuf,
ds4_gpu_tensor_offset(x),
g_moe_gate_scratch_buffer,
(NSUInteger)gate_scratch_bytes,
mm_id_threadgroup_bytes,
resources,
resource_count,
1,
overflow_up);
}
DS4_METAL_PROFILE_GLM_GROUPED_ADDR_MOE_STAGE("up");
if (ok) {
ok = ds4_gpu_encode_moe_swiglu_weight(cb,
g_moe_gate_scratch_buffer,
0,
g_moe_gate_scratch_buffer,
(NSUInteger)gate_scratch_bytes,
midbuf,
ds4_gpu_tensor_offset(mid),
weightsbuf,
ds4_gpu_tensor_offset(weights),
expert_mid_dim,
pair_rows,
swiglu_clamp,
mid_f16);
}
DS4_METAL_PROFILE_GLM_GROUPED_ADDR_MOE_STAGE("activation_weight");
id<MTLBuffer> down_dst = n_expert == 1 ? outbuf : g_moe_down_scratch_buffer;
NSUInteger down_dst_off = n_expert == 1 ? ds4_gpu_tensor_offset(out) : 0;
if (ok) {
ok = ds4_gpu_encode_mul_mm_id_addr_mapped_tile(cb,
down_pipeline,
&down_args,
down_addrs,
midbuf,
ds4_gpu_tensor_offset(mid),
down_dst,
down_dst_off,
mm_id_threadgroup_bytes,
resources,
resource_count,
2,
overflow_down);
}
DS4_METAL_PROFILE_GLM_GROUPED_ADDR_MOE_STAGE("down");
if (ok && n_expert > 1) {
ok = ds4_gpu_encode_moe_sum_experts(cb,
down_dst,
down_dst_off,
outbuf,
ds4_gpu_tensor_offset(out),
out_dim,
n_expert,
n_tokens);
}
DS4_METAL_PROFILE_GLM_GROUPED_ADDR_MOE_STAGE("sum");
if (!ok) return 0;
if (!ds4_gpu_finish_command_buffer(cb, owned,
"GLM grouped-address routed batch MoE")) {
return 0;
}
#undef DS4_METAL_PROFILE_GLM_GROUPED_ADDR_MOE_STAGE
}
return 1;
}
static int ds4_gpu_glm_routed_moe_batch_tensor_impl(
ds4_gpu_tensor *out,
ds4_gpu_tensor *mid,
const void *model_map,
uint64_t model_size,
uint64_t gate_offset,
uint64_t up_offset,
uint64_t down_offset,
uint32_t gate_type,
uint32_t up_type,
uint32_t down_type,
uint64_t gate_expert_bytes,
uint64_t gate_row_bytes,
uint64_t up_expert_bytes,
uint64_t up_row_bytes,
uint64_t down_expert_bytes,
uint64_t down_row_bytes,
uint32_t expert_in_dim,
uint32_t expert_mid_dim,
uint32_t out_dim,
const ds4_gpu_tensor *selected,
const ds4_gpu_tensor *weights,
uint32_t n_total_expert,
uint32_t n_expert,
float swiglu_clamp,
uint32_t layer_index,
const ds4_gpu_tensor *x,
uint32_t n_tokens,
uint32_t mid_token_stride,
bool allow_grouped,
bool force_scalar_q4_pair) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!out || !mid || !model_map || !selected || !weights || !x ||
n_tokens == 0 ||
n_total_expert == 0 || n_expert == 0 || n_expert > 256u ||
n_expert > n_total_expert ||
expert_in_dim == 0 || expert_mid_dim == 0 || out_dim == 0 ||
gate_expert_bytes == 0 || gate_row_bytes == 0 ||
up_expert_bytes == 0 || up_row_bytes == 0 ||
down_expert_bytes == 0 || down_row_bytes == 0 ||
(expert_in_dim % 256u) != 0 ||
(expert_mid_dim % 256u) != 0 ||
!ds4_gpu_glm_gate_pair_type_supported(gate_type, up_type) ||
!ds4_gpu_glm_down_type_supported(down_type)) {
return 0;
}
const uint64_t per_token_mid = (uint64_t)n_expert * expert_mid_dim;
if ((uint64_t)mid_token_stride < per_token_mid ||
(uint64_t)n_total_expert > UINT64_MAX / gate_expert_bytes ||
(uint64_t)n_total_expert > UINT64_MAX / up_expert_bytes ||
(uint64_t)n_total_expert > UINT64_MAX / down_expert_bytes ||
(uint64_t)expert_mid_dim > UINT64_MAX / gate_row_bytes ||
(uint64_t)expert_mid_dim > UINT64_MAX / up_row_bytes ||
(uint64_t)out_dim > UINT64_MAX / down_row_bytes ||
(uint64_t)n_tokens > UINT64_MAX / expert_in_dim ||
(uint64_t)n_tokens > UINT64_MAX / out_dim) {
fprintf(stderr, "ds4: Metal GLM routed batch MoE tensor byte size overflow\n");
return 0;
}
const uint64_t full_gate_tensor_bytes =
(uint64_t)n_total_expert * gate_expert_bytes;
const uint64_t full_up_tensor_bytes =
(uint64_t)n_total_expert * up_expert_bytes;
const uint64_t full_down_tensor_bytes =
(uint64_t)n_total_expert * down_expert_bytes;
if (gate_expert_bytes != (uint64_t)expert_mid_dim * gate_row_bytes ||
up_expert_bytes != (uint64_t)expert_mid_dim * up_row_bytes ||
down_expert_bytes != (uint64_t)out_dim * down_row_bytes) {
fprintf(stderr, "ds4: Metal GLM routed batch MoE received inconsistent expert strides\n");
return 0;
}
if (gate_offset > model_size || full_gate_tensor_bytes > model_size - gate_offset ||
up_offset > model_size || full_up_tensor_bytes > model_size - up_offset ||
down_offset > model_size || full_down_tensor_bytes > model_size - down_offset) {
fprintf(stderr, "ds4: Metal GLM routed batch MoE tensor range is outside the mapped model\n");
return 0;
}
if (allow_grouped &&
(!g_ssd_streaming_mode ||
ds4_gpu_glm_streaming_prefill_full_layer_active()) &&
ds4_gpu_glm_grouped_moe_layer_enabled(layer_index) &&
ds4_gpu_glm_routed_moe_batch_grouped_available(gate_type,
up_type,
down_type,
n_expert,
n_tokens)) {
return ds4_gpu_glm_routed_moe_batch_grouped_tensor(out,
mid,
model_map,
model_size,
gate_offset,
up_offset,
down_offset,
gate_type,
up_type,
down_type,
gate_expert_bytes,
gate_row_bytes,
up_expert_bytes,
up_row_bytes,
down_expert_bytes,
down_row_bytes,
expert_in_dim,
expert_mid_dim,
out_dim,
selected,
weights,
n_total_expert,
n_expert,
swiglu_clamp,
layer_index,
x,
n_tokens);
}
uint32_t first_expert = 0;
uint32_t n_bind_expert = 0;
ds4_gpu_tp_expert_range(n_total_expert, &first_expert, &n_bind_expert);
/* Streaming address tables are indexed by global expert id. As in the
* one-token path, keep model offsets global and let the kernels enforce
* rank ownership before dereferencing the selected address. */
const bool streaming_tp =
g_ssd_streaming_mode && g_tp_split_world == 2;
if (streaming_tp) {
first_expert = 0;
n_bind_expert = n_total_expert;
}
gate_offset += (uint64_t)first_expert * gate_expert_bytes;
up_offset += (uint64_t)first_expert * up_expert_bytes;
down_offset += (uint64_t)first_expert * down_expert_bytes;
const uint64_t gate_tensor_bytes = (uint64_t)n_bind_expert * gate_expert_bytes;
const uint64_t up_tensor_bytes = (uint64_t)n_bind_expert * up_expert_bytes;
const uint64_t down_tensor_bytes = (uint64_t)n_bind_expert * down_expert_bytes;
@autoreleasepool {
id<MTLBuffer> xbuf = ds4_gpu_tensor_buffer(x);
id<MTLBuffer> midbuf = ds4_gpu_tensor_buffer(mid);
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out);
id<MTLBuffer> selectedbuf = ds4_gpu_tensor_buffer(selected);
id<MTLBuffer> weightsbuf = ds4_gpu_tensor_buffer(weights);
const uint64_t x_values = (uint64_t)n_tokens * expert_in_dim;
const uint64_t out_values = (uint64_t)n_tokens * out_dim;
const uint64_t mid_values =
(uint64_t)(n_tokens - 1u) * mid_token_stride + per_token_mid;
const uint64_t selected_values = (uint64_t)n_tokens * n_expert;
if (x_values > UINT64_MAX / sizeof(float) ||
out_values > UINT64_MAX / sizeof(float) ||
mid_values > UINT64_MAX / sizeof(float) ||
selected_values > UINT64_MAX / sizeof(int32_t)) {
fprintf(stderr, "ds4: Metal GLM routed batch MoE activation byte size overflow\n");
return 0;
}
const uint64_t x_bytes = x_values * sizeof(float);
const uint64_t mid_bytes = mid_values * sizeof(float);
const uint64_t out_bytes = out_values * sizeof(float);
const uint64_t selected_bytes = selected_values * sizeof(int32_t);
const uint64_t weights_bytes = selected_values * sizeof(float);
if (!xbuf || !midbuf || !outbuf || !selectedbuf || !weightsbuf ||
ds4_gpu_tensor_bytes(x) < x_bytes ||
ds4_gpu_tensor_bytes(mid) < mid_bytes ||
ds4_gpu_tensor_bytes(out) < out_bytes ||
ds4_gpu_tensor_bytes(selected) < selected_bytes ||
ds4_gpu_tensor_bytes(weights) < weights_bytes) {
fprintf(stderr, "ds4: Metal GLM routed batch MoE received undersized activation buffers\n");
return 0;
}
uint64_t gate_inner = 0;
uint64_t up_inner = 0;
uint64_t down_inner = 0;
id<MTLBuffer> gatebuf = nil;
id<MTLBuffer> upbuf = nil;
id<MTLBuffer> downbuf = nil;
id<MTLBuffer> stream_gate_addr_buf = nil;
id<MTLBuffer> stream_up_addr_buf = nil;
id<MTLBuffer> stream_down_addr_buf = nil;
id<MTLBuffer> stream_overflow_gate = nil;
id<MTLBuffer> stream_overflow_up = nil;
id<MTLBuffer> stream_overflow_down = nil;
ds4_gpu_stream_expert_cache_entry
*stream_resources[DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT] = { NULL };
uint32_t stream_resource_count = 0;
uint32_t stream_unique = 0;
const BOOL gate_pair_q2 = gate_type == DS4_METAL_TENSOR_Q2_K;
const BOOL gate_pair_q5 = gate_type == DS4_METAL_TENSOR_Q5_K;
const BOOL down_scalar_q2 = down_type == DS4_METAL_TENSOR_Q2_K;
const BOOL down_scalar_q4 = down_type == DS4_METAL_TENSOR_Q4_K;
const BOOL down_simd_q4 = down_scalar_q4;
const BOOL down_simd_q5 = down_type == DS4_METAL_TENSOR_Q5_K;
const BOOL down_simd_q6 = down_type == DS4_METAL_TENSOR_Q6_K;
const BOOL down_simd = down_simd_q4 || down_simd_q5 || down_simd_q6;
const BOOL stream_addr_q2 =
gate_pair_q2 && down_scalar_q2 &&
g_glm_q2_k_addr_pair_swiglu2_f32_pipeline != nil &&
g_glm_q2_k_addr_down_f32_pipeline != nil;
const BOOL stream_addr_q4 =
!gate_pair_q2 && !gate_pair_q5 && down_scalar_q4 &&
g_glm_q4_k_addr_pair_swiglu_f32_pipeline != nil &&
g_glm_q4_k_addr_down_f32_pipeline != nil;
BOOL use_stream_expert_addr_table =
g_ssd_streaming_mode &&
!ds4_gpu_glm_streaming_prefill_full_layer_active() &&
n_tokens > 1 &&
(stream_addr_q2 || stream_addr_q4) &&
layer_index < DS4_METAL_STREAM_EXPERT_CACHE_MAX_LAYER &&
n_total_expert <= DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT &&
n_expert <= DS4_METAL_STREAM_EXPERT_CACHE_MAX_SELECTED &&
ds4_gpu_stream_expert_cache_note_expert_size(gate_expert_bytes,
down_expert_bytes) &&
ds4_gpu_stream_expert_cache_effective_cap(layer_index,
n_total_expert,
n_expert) != 0;
const BOOL use_stream_grouped_addr_table =
use_stream_expert_addr_table &&
allow_grouped &&
getenv("DS4_METAL_GLM_DISABLE_STREAMING_GROUPED_ADDR_PREFILL") == NULL &&
ds4_gpu_glm_routed_moe_batch_grouped_available(gate_type,
up_type,
down_type,
n_expert,
n_tokens);
const BOOL enable_q4_pair4 = true;
const BOOL q4_scalar_pair = false;
const BOOL q4_pair2 =
!use_stream_expert_addr_table &&
!gate_pair_q5 && !q4_scalar_pair &&
(force_scalar_q4_pair || !enable_q4_pair4);
id<MTLComputePipelineState> pair_pipeline =
use_stream_expert_addr_table ?
(gate_pair_q2 ?
ds4_gpu_hot_pipeline(g_glm_q2_k_addr_pair_swiglu2_f32_pipeline,
"kernel_glm_q2_K_addr_pair_swiglu2_f32") :
ds4_gpu_hot_pipeline(g_glm_q4_k_addr_pair_swiglu_f32_pipeline,
"kernel_glm_q4_K_addr_pair_swiglu_f32")) :
gate_pair_q2 ?
ds4_gpu_hot_pipeline(g_glm_q2_k_pair_swiglu_f32_pipeline,
"kernel_glm_q2_K_pair_swiglu_f32") :
gate_pair_q5 ?
ds4_gpu_hot_pipeline(g_glm_q5_k_pair_swiglu_f32_pipeline,
"kernel_glm_q5_K_pair_swiglu_f32") :
(q4_scalar_pair ?
ds4_gpu_hot_pipeline(g_glm_q4_k_pair_swiglu_f32_pipeline,
"kernel_glm_q4_K_pair_swiglu_f32") :
q4_pair2 ?
ds4_gpu_hot_pipeline(g_glm_q4_k_pair_swiglu2_f32_pipeline,
"kernel_glm_q4_K_pair_swiglu2_f32") :
ds4_gpu_hot_pipeline(g_glm_q4_k_pair_swiglu4_f32_pipeline,
"kernel_glm_q4_K_pair_swiglu4_f32"));
id<MTLComputePipelineState> down_pipeline =
use_stream_expert_addr_table ?
(down_scalar_q2 ?
ds4_gpu_hot_pipeline(g_glm_q2_k_addr_down_f32_pipeline,
"kernel_glm_q2_K_addr_down_f32") :
ds4_gpu_hot_pipeline(g_glm_q4_k_addr_down_f32_pipeline,
"kernel_glm_q4_K_addr_down_f32")) :
down_scalar_q2 ?
ds4_gpu_hot_pipeline(g_glm_q2_k_down_f32_pipeline,
"kernel_glm_q2_K_down_f32") :
down_scalar_q4 ?
ds4_gpu_hot_pipeline(g_glm_q4_k_down_f32_pipeline,
"kernel_glm_q4_K_down_f32") :
down_simd_q5 ?
ds4_gpu_hot_pipeline(g_glm_q5_k_down_f32_pipeline,
"kernel_glm_q5_K_down_f32") :
ds4_gpu_hot_pipeline(g_glm_q6_k_down_f32_pipeline,
"kernel_glm_q6_K_down_f32");
if (!pair_pipeline || !down_pipeline) return 0;
if (use_stream_expert_addr_table) {
const int had_batch = g_batch_cb != nil;
if (had_batch && ds4_gpu_end_commands() == 0) {
return 0;
}
if (!ds4_gpu_stream_expert_cache_prepare_selected_batch(
model_map,
model_size,
layer_index,
selected,
n_tokens,
n_total_expert,
n_expert,
gate_offset,
up_offset,
down_offset,
gate_expert_bytes,
down_expert_bytes,
&stream_gate_addr_buf,
&stream_up_addr_buf,
&stream_down_addr_buf,
stream_resources,
&stream_resource_count,
&stream_unique,
&stream_overflow_gate,
&stream_overflow_up,
&stream_overflow_down)) {
return 0;
}
if (stream_unique == 0) {
ds4_gpu_stream_expert_cache_clear_layer(layer_index);
return 0;
}
if (had_batch && ds4_gpu_begin_commands() == 0) {
ds4_gpu_stream_expert_cache_clear_layer(layer_index);
return 0;
}
if (use_stream_grouped_addr_table) {
return ds4_gpu_glm_routed_moe_batch_grouped_addr_tensor(
out,
mid,
stream_gate_addr_buf,
stream_up_addr_buf,
stream_down_addr_buf,
stream_overflow_gate,
stream_overflow_up,
stream_overflow_down,
gate_type,
up_type,
down_type,
gate_expert_bytes,
gate_row_bytes,
up_expert_bytes,
up_row_bytes,
down_expert_bytes,
down_row_bytes,
expert_in_dim,
expert_mid_dim,
out_dim,
selected,
weights,
n_total_expert,
n_expert,
swiglu_clamp,
layer_index,
x,
n_tokens,
stream_resources,
stream_resource_count);
}
} else {
gatebuf = ds4_gpu_wrap_model_range(model_map, model_size,
gate_offset, gate_tensor_bytes,
&gate_inner);
upbuf = ds4_gpu_wrap_model_range(model_map, model_size,
up_offset, up_tensor_bytes,
&up_inner);
downbuf = ds4_gpu_wrap_model_range(model_map, model_size,
down_offset, down_tensor_bytes,
&down_inner);
if (!gatebuf || !upbuf || !downbuf) return 0;
}
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
const bool glm_moe_stage_profile = false;
const char *glm_moe_stage_filter = NULL;
const char *glm_pair_path = use_stream_expert_addr_table ?
(gate_pair_q2 ? "q2_stream_addr_swiglu" :
"q4_stream_addr_swiglu") :
gate_pair_q2 ? "q2_scalar_swiglu" :
gate_pair_q5 ? "q5_pair_simd_swiglu" :
(q4_scalar_pair ? "q4_scalar_swiglu" :
(q4_pair2 ? "q4_pair2_simd_swiglu" :
"q4_pair4_simd_swiglu"));
const char *glm_down_path =
use_stream_expert_addr_table ?
(down_scalar_q2 ? "q2_stream_addr_down" : "q4_stream_addr_down_simd") :
down_scalar_q2 ? "q2_down_scalar" :
down_scalar_q4 ? "q4_down_simd" :
down_simd_q5 ? "q5_down_simd" : "q6_down_simd";
double glm_moe_stage_t0 = 0.0;
if (glm_moe_stage_profile) {
if (ds4_gpu_end_commands() == 0 || ds4_gpu_begin_commands() == 0) {
return 0;
}
cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
glm_moe_stage_t0 = ds4_gpu_now_ms();
}
int ok = 1;
#define DS4_METAL_PROFILE_GLM_MOE_BATCH_STAGE(name) do { \
if (ok && glm_moe_stage_profile) { \
if (ds4_gpu_end_commands() == 0) { \
ok = 0; \
} else { \
const char *stage_name = (name); \
const double now_ms = ds4_gpu_now_ms(); \
const int print_stage = \
!glm_moe_stage_filter || !glm_moe_stage_filter[0] || \
strstr(stage_name, glm_moe_stage_filter) != NULL; \
if (print_stage) { \
fprintf(stderr, \
"ds4: Metal GLM routed MoE batch stage layer=%u tokens=%u experts=%u " \
"gate=%s down=%s pair=%s down_path=%s %s=%.3f ms\n", \
layer_index, n_tokens, n_expert, \
ds4_gpu_metal_tensor_type_name(gate_type), \
ds4_gpu_metal_tensor_type_name(down_type), \
glm_pair_path, glm_down_path, \
stage_name, now_ms - glm_moe_stage_t0); \
} \
glm_moe_stage_t0 = now_ms; \
if (ds4_gpu_begin_commands() == 0) { \
ok = 0; \
} else { \
cb = ds4_gpu_command_buffer(&owned); \
if (!cb) ok = 0; \
} \
} \
} \
} while (0)
ds4_gpu_glm_routed_moe_args args = {
.tp_rank = g_tp_split_rank,
.tp_world = g_tp_split_world,
.tp_expert_base = (int32_t)first_expert,
.in_dim = expert_in_dim,
.mid_dim = expert_mid_dim,
.out_dim = out_dim,
.n_total_expert = n_total_expert,
.n_expert_used = n_expert,
.n_tokens = n_tokens,
.mid_token_stride = mid_token_stride,
.down_type = down_type,
.swiglu_clamp = swiglu_clamp,
.gate_expert_bytes = gate_expert_bytes,
.gate_row_bytes = gate_row_bytes,
.up_expert_bytes = up_expert_bytes,
.up_row_bytes = up_row_bytes,
.down_expert_bytes = down_expert_bytes,
.down_row_bytes = down_row_bytes,
};
const NSUInteger pair_x_groups =
gate_pair_q2 ? (use_stream_expert_addr_table ?
(NSUInteger)((expert_mid_dim + 1u) / 2u) :
(NSUInteger)((expert_mid_dim + 7u) / 8u)) :
gate_pair_q5 ? (NSUInteger)((expert_mid_dim + 7u) / 8u) :
use_stream_expert_addr_table ? (NSUInteger)((expert_mid_dim + 3u) / 4u) :
q4_scalar_pair ? (NSUInteger)expert_mid_dim :
q4_pair2 ? (NSUInteger)((expert_mid_dim + 1u) / 2u) :
(NSUInteger)((expert_mid_dim + 7u) / 8u);
const NSUInteger pair_threadgroup_bytes =
q4_scalar_pair ? 512u * sizeof(float) : 0u;
const NSUInteger pair_threads =
q4_scalar_pair ? 256u : 64u;
const NSUInteger down_x_groups =
down_scalar_q2 ? (NSUInteger)((out_dim + 7u) / 8u) :
down_simd_q4 ? (NSUInteger)((out_dim + 3u) / 4u) :
down_simd_q5 ? (NSUInteger)((out_dim + 3u) / 4u) :
down_simd_q6 ? (NSUInteger)((out_dim + 3u) / 4u) :
(NSUInteger)out_dim;
const NSUInteger down_threadgroup_bytes =
down_simd ? 0u : 256u * sizeof(float);
const NSUInteger down_threads =
down_simd ? 64u : 256u;
if (use_stream_expert_addr_table &&
!ds4_gpu_stream_expert_cache_mark_entries_inflight(stream_resources,
stream_resource_count,
0)) {
return 0;
}
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pair_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:use_stream_expert_addr_table ? stream_gate_addr_buf : gatebuf
offset:use_stream_expert_addr_table ? 0u : (NSUInteger)gate_inner
atIndex:1];
[enc setBuffer:use_stream_expert_addr_table ? stream_up_addr_buf : upbuf
offset:use_stream_expert_addr_table ? 0u : (NSUInteger)up_inner
atIndex:2];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:3];
[enc setBuffer:selectedbuf offset:ds4_gpu_tensor_offset(selected) atIndex:4];
[enc setBuffer:weightsbuf offset:ds4_gpu_tensor_offset(weights) atIndex:5];
[enc setBuffer:midbuf offset:ds4_gpu_tensor_offset(mid) atIndex:6];
if (use_stream_expert_addr_table) {
for (uint32_t i = 0; i < stream_resource_count; i++) {
[enc useResource:stream_resources[i]->gate_buffer usage:MTLResourceUsageRead];
[enc useResource:stream_resources[i]->up_buffer usage:MTLResourceUsageRead];
}
if (stream_overflow_gate) [enc useResource:stream_overflow_gate usage:MTLResourceUsageRead];
if (stream_overflow_up) [enc useResource:stream_overflow_up usage:MTLResourceUsageRead];
}
if (pair_threadgroup_bytes != 0u) {
[enc setThreadgroupMemoryLength:pair_threadgroup_bytes atIndex:0];
}
[enc dispatchThreadgroups:MTLSizeMake(pair_x_groups,
(NSUInteger)n_expert,
(NSUInteger)n_tokens)
threadsPerThreadgroup:MTLSizeMake(pair_threads, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
DS4_METAL_PROFILE_GLM_MOE_BATCH_STAGE("pair");
enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:down_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:use_stream_expert_addr_table ? stream_down_addr_buf : downbuf
offset:use_stream_expert_addr_table ? 0u : (NSUInteger)down_inner
atIndex:1];
[enc setBuffer:selectedbuf offset:ds4_gpu_tensor_offset(selected) atIndex:2];
[enc setBuffer:midbuf offset:ds4_gpu_tensor_offset(mid) atIndex:3];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(out) atIndex:4];
if (use_stream_expert_addr_table) {
for (uint32_t i = 0; i < stream_resource_count; i++) {
[enc useResource:stream_resources[i]->down_buffer usage:MTLResourceUsageRead];
}
if (stream_overflow_down) [enc useResource:stream_overflow_down usage:MTLResourceUsageRead];
}
if (down_threadgroup_bytes != 0u) {
[enc setThreadgroupMemoryLength:down_threadgroup_bytes atIndex:0];
}
[enc dispatchThreadgroups:MTLSizeMake(down_x_groups,
(NSUInteger)n_tokens,
1)
threadsPerThreadgroup:MTLSizeMake(down_threads, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
DS4_METAL_PROFILE_GLM_MOE_BATCH_STAGE("down");
if (!ok) return 0;
if (!ds4_gpu_finish_command_buffer(cb, owned, "GLM routed batch MoE")) return 0;
#undef DS4_METAL_PROFILE_GLM_MOE_BATCH_STAGE
}
return 1;
}
int ds4_gpu_glm_routed_moe_batch_tensor(
ds4_gpu_tensor *out,
ds4_gpu_tensor *mid,
const void *model_map,
uint64_t model_size,
uint64_t gate_offset,
uint64_t up_offset,
uint64_t down_offset,
uint32_t gate_type,
uint32_t up_type,
uint32_t down_type,
uint64_t gate_expert_bytes,
uint64_t gate_row_bytes,
uint64_t up_expert_bytes,
uint64_t up_row_bytes,
uint64_t down_expert_bytes,
uint64_t down_row_bytes,
uint32_t expert_in_dim,
uint32_t expert_mid_dim,
uint32_t out_dim,
const ds4_gpu_tensor *selected,
const ds4_gpu_tensor *weights,
uint32_t n_total_expert,
uint32_t n_expert,
float swiglu_clamp,
uint32_t layer_index,
const ds4_gpu_tensor *x,
uint32_t n_tokens,
uint32_t mid_token_stride,
bool force_resident) {
(void)force_resident;
return ds4_gpu_glm_routed_moe_batch_tensor_impl(out,
mid,
model_map,
model_size,
gate_offset,
up_offset,
down_offset,
gate_type,
up_type,
down_type,
gate_expert_bytes,
gate_row_bytes,
up_expert_bytes,
up_row_bytes,
down_expert_bytes,
down_row_bytes,
expert_in_dim,
expert_mid_dim,
out_dim,
selected,
weights,
n_total_expert,
n_expert,
swiglu_clamp,
layer_index,
x,
n_tokens,
mid_token_stride,
true,
false);
}
int ds4_gpu_glm_routed_moe_batch_direct_scalar_q4_tensor(
ds4_gpu_tensor *out,
ds4_gpu_tensor *mid,
const void *model_map,
uint64_t model_size,
uint64_t gate_offset,
uint64_t up_offset,
uint64_t down_offset,
uint32_t gate_type,
uint32_t up_type,
uint32_t down_type,
uint64_t gate_expert_bytes,
uint64_t gate_row_bytes,
uint64_t up_expert_bytes,
uint64_t up_row_bytes,
uint64_t down_expert_bytes,
uint64_t down_row_bytes,
uint32_t expert_in_dim,
uint32_t expert_mid_dim,
uint32_t out_dim,
const ds4_gpu_tensor *selected,
const ds4_gpu_tensor *weights,
uint32_t n_total_expert,
uint32_t n_expert,
float swiglu_clamp,
uint32_t layer_index,
const ds4_gpu_tensor *x,
uint32_t n_tokens,
uint32_t mid_token_stride) {
return ds4_gpu_glm_routed_moe_batch_tensor_impl(out,
mid,
model_map,
model_size,
gate_offset,
up_offset,
down_offset,
gate_type,
up_type,
down_type,
gate_expert_bytes,
gate_row_bytes,
up_expert_bytes,
up_row_bytes,
down_expert_bytes,
down_row_bytes,
expert_in_dim,
expert_mid_dim,
out_dim,
selected,
weights,
n_total_expert,
n_expert,
swiglu_clamp,
layer_index,
x,
n_tokens,
mid_token_stride,
false,
false);
}
int ds4_gpu_router_select_tensor(
ds4_gpu_tensor *selected,
ds4_gpu_tensor *weights,
ds4_gpu_tensor *probs,
const void *model_map,
uint64_t model_size,
uint64_t bias_offset,
uint64_t hash_offset,
uint32_t hash_rows,
uint32_t token,
uint32_t n_expert,
uint32_t n_expert_used,
float expert_weight_scale,
uint32_t n_expert_groups,
uint32_t n_group_used,
bool has_bias,
bool hash_mode,
const ds4_gpu_tensor *logits) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!selected || !weights || !probs || !logits || !model_map ||
n_expert == 0 || n_expert_used == 0) return 0;
if (hash_mode && token >= hash_rows) return 0;
if (n_expert_groups > 1u || n_group_used > 0u) {
fprintf(stderr, "ds4: Metal router group gating is not part of this DeepSeek V4 path\n");
return 0;
}
@autoreleasepool {
id<MTLBuffer> logitsbuf = ds4_gpu_tensor_buffer(logits);
id<MTLBuffer> selectedbuf = ds4_gpu_tensor_buffer(selected);
id<MTLBuffer> weightsbuf = ds4_gpu_tensor_buffer(weights);
id<MTLBuffer> probsbuf = ds4_gpu_tensor_buffer(probs);
if (!logitsbuf || !selectedbuf || !weightsbuf || !probsbuf ||
ds4_gpu_tensor_bytes(logits) < (uint64_t)n_expert * sizeof(float) ||
ds4_gpu_tensor_bytes(selected) < (uint64_t)n_expert_used * sizeof(int) ||
ds4_gpu_tensor_bytes(weights) < (uint64_t)n_expert_used * sizeof(float) ||
ds4_gpu_tensor_bytes(probs) < (uint64_t)n_expert * sizeof(float)) {
fprintf(stderr, "ds4: Metal router select received undersized buffers\n");
return 0;
}
uint64_t bias_inner = 0;
uint64_t hash_inner = 0;
id<MTLBuffer> biasbuf = nil;
id<MTLBuffer> hashbuf = nil;
NSUInteger bias_set_offset = 0;
NSUInteger hash_set_offset = 0;
if (has_bias && !hash_mode) {
const uint64_t bias_bytes = (uint64_t)n_expert * sizeof(float);
biasbuf = ds4_gpu_wrap_model_range(model_map, model_size, bias_offset, bias_bytes, &bias_inner);
if (!biasbuf) return 0;
bias_set_offset = (NSUInteger)bias_inner;
}
if (hash_mode) {
const uint64_t hash_bytes = (uint64_t)hash_rows * n_expert_used * sizeof(int32_t);
hashbuf = ds4_gpu_wrap_model_range(model_map, model_size, hash_offset, hash_bytes, &hash_inner);
if (!hashbuf) return 0;
hash_set_offset = (NSUInteger)hash_inner;
}
const bool had_batch = g_batch_cb != nil;
if (!had_batch && ds4_gpu_begin_commands() == 0) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
const int32_t token_i32 = (int32_t)token;
int ok = cb &&
ds4_gpu_encode_router_select(cb,
selected,
weights,
probs,
logitsbuf,
ds4_gpu_tensor_offset(logits),
biasbuf,
bias_set_offset,
hashbuf,
hash_set_offset,
nil,
0,
nil,
0,
&token_i32,
hash_rows,
0,
1,
n_expert,
n_expert_used,
expert_weight_scale,
has_bias && !hash_mode,
hash_mode,
false);
if (!had_batch) {
ok = ds4_gpu_end_commands() != 0 && ok;
}
if (!ok) return 0;
}
return 1;
}
int ds4_gpu_router_select_batch_tensor(
ds4_gpu_tensor *selected,
ds4_gpu_tensor *weights,
ds4_gpu_tensor *probs,
const void *model_map,
uint64_t model_size,
uint64_t bias_offset,
uint64_t hash_offset,
uint32_t hash_rows,
uint32_t n_expert_groups,
uint32_t n_group_used,
bool has_bias,
bool hash_mode,
const ds4_gpu_tensor *logits,
const ds4_gpu_tensor *tokens,
uint32_t n_expert,
uint32_t n_expert_used,
float expert_weight_scale,
uint32_t n_tokens) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!selected || !weights || !probs || !logits || !tokens || !model_map ||
n_expert == 0 || n_expert_used == 0 || n_tokens == 0) return 0;
if (n_expert_groups > 1u || n_group_used > 0u) {
fprintf(stderr, "ds4: Metal router group gating is not part of this DeepSeek V4 path\n");
return 0;
}
@autoreleasepool {
id<MTLBuffer> logitsbuf = ds4_gpu_tensor_buffer(logits);
id<MTLBuffer> selectedbuf = ds4_gpu_tensor_buffer(selected);
id<MTLBuffer> weightsbuf = ds4_gpu_tensor_buffer(weights);
id<MTLBuffer> probsbuf = ds4_gpu_tensor_buffer(probs);
id<MTLBuffer> tokensbuf = ds4_gpu_tensor_buffer(tokens);
if (!logitsbuf || !selectedbuf || !weightsbuf || !probsbuf || !tokensbuf ||
ds4_gpu_tensor_bytes(logits) < (uint64_t)n_tokens * n_expert * sizeof(float) ||
ds4_gpu_tensor_bytes(selected) < (uint64_t)n_tokens * n_expert_used * sizeof(int) ||
ds4_gpu_tensor_bytes(weights) < (uint64_t)n_tokens * n_expert_used * sizeof(float) ||
ds4_gpu_tensor_bytes(probs) < (uint64_t)n_tokens * n_expert * sizeof(float) ||
ds4_gpu_tensor_bytes(tokens) < (uint64_t)n_tokens * sizeof(int32_t)) {
fprintf(stderr, "ds4: Metal router batch select received undersized buffers\n");
return 0;
}
uint64_t bias_inner = 0;
uint64_t hash_inner = 0;
id<MTLBuffer> biasbuf = nil;
id<MTLBuffer> hashbuf = nil;
NSUInteger bias_set_offset = 0;
NSUInteger hash_set_offset = 0;
if (has_bias && !hash_mode) {
const uint64_t bias_bytes = (uint64_t)n_expert * sizeof(float);
biasbuf = ds4_gpu_wrap_model_range(model_map, model_size, bias_offset, bias_bytes, &bias_inner);
if (!biasbuf) return 0;
bias_set_offset = (NSUInteger)bias_inner;
}
if (hash_mode) {
const uint64_t hash_bytes = (uint64_t)hash_rows * n_expert_used * sizeof(int32_t);
hashbuf = ds4_gpu_wrap_model_range(model_map, model_size, hash_offset, hash_bytes, &hash_inner);
if (!hashbuf) return 0;
hash_set_offset = (NSUInteger)hash_inner;
}
const bool had_batch = g_batch_cb != nil;
if (!had_batch && ds4_gpu_begin_commands() == 0) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
int ok = cb &&
ds4_gpu_encode_router_select(cb,
selected,
weights,
probs,
logitsbuf,
ds4_gpu_tensor_offset(logits),
biasbuf,
bias_set_offset,
hashbuf,
hash_set_offset,
tokensbuf,
ds4_gpu_tensor_offset(tokens),
nil,
0,
NULL,
hash_rows,
0,
n_tokens,
n_expert,
n_expert_used,
expert_weight_scale,
has_bias && !hash_mode,
hash_mode,
false);
if (!had_batch) {
ok = ds4_gpu_end_commands() != 0 && ok;
}
if (!ok) return 0;
}
return 1;
}
int ds4_gpu_router_select_batch_visual_tensor(
ds4_gpu_tensor *selected,
ds4_gpu_tensor *weights,
ds4_gpu_tensor *probs,
const void *model_map,
uint64_t model_size,
uint64_t bias_offset,
uint64_t hash_offset,
uint32_t hash_rows,
bool has_bias,
bool hash_mode,
const void *vision_map,
uint64_t vision_size,
uint64_t visual_bias_offset,
const ds4_gpu_tensor *logits,
const ds4_gpu_tensor *tokens,
uint32_t vocab_size,
uint32_t n_expert,
uint32_t n_expert_used,
float expert_weight_scale,
uint32_t n_tokens) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!selected || !weights || !probs || !logits || !tokens ||
!model_map || !vision_map || vocab_size == 0 || n_tokens == 0 ||
n_expert != 256u || n_expert_used != 6u ||
fabsf(expert_weight_scale - 1.5f) > 1.0e-6f) return 0;
@autoreleasepool {
id<MTLBuffer> logitsbuf = ds4_gpu_tensor_buffer(logits);
id<MTLBuffer> selectedbuf = ds4_gpu_tensor_buffer(selected);
id<MTLBuffer> weightsbuf = ds4_gpu_tensor_buffer(weights);
id<MTLBuffer> probsbuf = ds4_gpu_tensor_buffer(probs);
id<MTLBuffer> tokensbuf = ds4_gpu_tensor_buffer(tokens);
if (!logitsbuf || !selectedbuf || !weightsbuf || !probsbuf || !tokensbuf ||
ds4_gpu_tensor_bytes(logits) < (uint64_t)n_tokens * n_expert * sizeof(float) ||
ds4_gpu_tensor_bytes(selected) < (uint64_t)n_tokens * n_expert_used * sizeof(int) ||
ds4_gpu_tensor_bytes(weights) < (uint64_t)n_tokens * n_expert_used * sizeof(float) ||
ds4_gpu_tensor_bytes(probs) < (uint64_t)n_tokens * n_expert * sizeof(float) ||
ds4_gpu_tensor_bytes(tokens) < (uint64_t)n_tokens * sizeof(int32_t)) {
fprintf(stderr, "ds4: Metal visual router received undersized buffers\n");
return 0;
}
uint64_t bias_inner = 0, hash_inner = 0, visual_inner = 0;
id<MTLBuffer> biasbuf = nil, hashbuf = nil;
NSUInteger bias_set_offset = 0, hash_set_offset = 0;
if (has_bias && !hash_mode) {
biasbuf = ds4_gpu_wrap_model_range(model_map, model_size,
bias_offset,
(uint64_t)n_expert * sizeof(float),
&bias_inner);
if (!biasbuf) return 0;
bias_set_offset = (NSUInteger)bias_inner;
}
if (hash_mode) {
hashbuf = ds4_gpu_wrap_model_range(
model_map, model_size, hash_offset,
(uint64_t)hash_rows * n_expert_used * sizeof(int32_t),
&hash_inner);
if (!hashbuf) return 0;
hash_set_offset = (NSUInteger)hash_inner;
}
id<MTLBuffer> visual_biasbuf = ds4_gpu_wrap_model_range(
vision_map, vision_size, visual_bias_offset,
(uint64_t)n_expert * sizeof(float), &visual_inner);
if (!visual_biasbuf) return 0;
const bool had_batch = g_batch_cb != nil;
if (!had_batch && ds4_gpu_begin_commands() == 0) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
int ok = cb && ds4_gpu_encode_router_select(
cb, selected, weights, probs,
logitsbuf, ds4_gpu_tensor_offset(logits),
biasbuf, bias_set_offset,
hashbuf, hash_set_offset,
tokensbuf, ds4_gpu_tensor_offset(tokens),
visual_biasbuf, (NSUInteger)visual_inner,
NULL, hash_rows, vocab_size, n_tokens,
n_expert, n_expert_used, expert_weight_scale,
has_bias && !hash_mode, hash_mode, true);
if (!had_batch) ok = ds4_gpu_end_commands() != 0 && ok;
if (!ok) return 0;
}
return 1;
}
int ds4_gpu_routed_moe_set_selected_override(const int32_t *selected, uint32_t n_selected) {
if (n_selected > DS4_METAL_MAX_ROUTED_EXPERT_USED ||
(!selected && n_selected != 0)) return 0;
for (uint32_t i = 0; i < n_selected; i++) {
g_routed_moe_selected_override[i] = selected[i];
}
g_routed_moe_selected_override_n = n_selected;
return 1;
}
/* Single-simdgroup MXFP4 decode MoE policy: the nsg=1 pair_swiglu/sum6
* variants are the pre-M5 Apple-Silicon default for single-token decode
* (bit-exact; only the row-to-simdgroup mapping changes). Read on the
* dispatch path so A/B harnesses can toggle it after engine creation. */
static bool ds4_gpu_mxfp4_moe_decode_nsg1_enabled(uint32_t n_tokens) {
if (n_tokens != 1) return false;
return ds4_gpu_device_is_pre_m5_apple_silicon() &&
getenv("DS4_METAL_DISABLE_PRE_M5_MXFP4_MOE_DECODE_NSG1") == NULL;
}
int ds4_gpu_routed_moe_one_tensor(
ds4_gpu_tensor *out,
ds4_gpu_tensor *gate,
ds4_gpu_tensor *up,
ds4_gpu_tensor *mid,
ds4_gpu_tensor *experts,
const void *model_map,
uint64_t model_size,
uint64_t gate_offset,
uint64_t up_offset,
uint64_t down_offset,
uint32_t gate_type,
uint32_t down_type,
uint64_t gate_expert_bytes,
uint64_t gate_row_bytes,
uint64_t down_expert_bytes,
uint64_t down_row_bytes,
uint32_t expert_in_dim,
uint32_t expert_mid_dim,
uint32_t out_dim,
const ds4_gpu_tensor *selected,
const ds4_gpu_tensor *weights,
uint32_t n_total_expert,
uint32_t n_expert,
float clamp,
const ds4_gpu_tensor *x,
const ds4_gpu_tensor *add_in,
uint32_t layer_index,
bool force_resident) {
BOOL parallel_ffn_scope
__attribute__((cleanup(ds4_gpu_parallel_ffn_scope_cleanup))) =
g_parallel_q8_pending;
if (!g_initialized && !ds4_gpu_init()) return 0;
/* TP sharding: only the owned contiguous expert range is mapped,
* so bind from the owned base, validate only its bytes, and tell the
* kernels the first expert id present at that base. */
uint32_t first_expert = 0;
uint32_t n_bind_expert = 0;
ds4_gpu_tp_expert_range(n_total_expert, &first_expert, &n_bind_expert);
const int32_t tp_expert_base_host = (int32_t)first_expert;
gate_offset += (uint64_t)first_expert * gate_expert_bytes;
up_offset += (uint64_t)first_expert * gate_expert_bytes;
down_offset += (uint64_t)first_expert * down_expert_bytes;
if (!out || !gate || !up || !mid || !x || !model_map || !selected || !weights ||
n_total_expert == 0 || n_expert == 0 ||
n_expert > DS4_METAL_MAX_ROUTED_EXPERT_USED ||
gate_expert_bytes == 0 || down_expert_bytes == 0 ||
gate_row_bytes == 0 || down_row_bytes == 0) {
if (getenv("DS4_GLM_TP_DEBUG")) fprintf(stderr, "ds4: routed_moe_one silent return at line %d\n", 32044);
return 0;
}
if ((expert_in_dim % 256u) != 0 || (expert_mid_dim % 256u) != 0) { if (getenv("DS4_GLM_TP_DEBUG")) fprintf(stderr, "ds4: routed_moe_one silent return at line %d\n", 32047); return 0; }
ds4_gpu_stream_expert_cache_note_token(layer_index);
@autoreleasepool {
id<MTLBuffer> xbuf = ds4_gpu_tensor_buffer(x);
id<MTLBuffer> gatebuf = ds4_gpu_tensor_buffer(gate);
id<MTLBuffer> upbuf = ds4_gpu_tensor_buffer(up);
id<MTLBuffer> midbuf = ds4_gpu_tensor_buffer(mid);
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out);
id<MTLBuffer> expertsbuf = ds4_gpu_tensor_buffer(experts);
id<MTLBuffer> selectedbuf = ds4_gpu_tensor_buffer(selected);
id<MTLBuffer> selected_exec_buf = selectedbuf;
NSUInteger selected_exec_off = ds4_gpu_tensor_offset(selected);
id<MTLBuffer> weightsbuf = ds4_gpu_tensor_buffer(weights);
const uint64_t x_bytes = (uint64_t)expert_in_dim * sizeof(float);
const uint64_t mid_bytes = (uint64_t)n_expert * expert_mid_dim * sizeof(float);
const uint64_t out_bytes = (uint64_t)out_dim * sizeof(float);
if (!xbuf || !gatebuf || !upbuf || !midbuf || !outbuf || !selectedbuf || !weightsbuf ||
ds4_gpu_tensor_bytes(x) < x_bytes ||
ds4_gpu_tensor_bytes(gate) < mid_bytes ||
ds4_gpu_tensor_bytes(up) < mid_bytes ||
ds4_gpu_tensor_bytes(mid) < mid_bytes ||
ds4_gpu_tensor_bytes(out) < out_bytes ||
ds4_gpu_tensor_bytes(selected) < (uint64_t)n_expert * sizeof(int) ||
ds4_gpu_tensor_bytes(weights) < (uint64_t)n_expert * sizeof(float)) {
fprintf(stderr, "ds4: Metal routed tensor MoE received undersized activation buffers\n");
return 0;
}
if (n_expert > 1 &&
(!expertsbuf ||
ds4_gpu_tensor_bytes(experts) < (uint64_t)n_expert * out_dim * sizeof(float))) {
fprintf(stderr, "ds4: Metal routed tensor MoE received undersized expert output buffer\n");
return 0;
}
if ((uint64_t)n_total_expert > UINT64_MAX / gate_expert_bytes ||
(uint64_t)n_total_expert > UINT64_MAX / down_expert_bytes) {
fprintf(stderr, "ds4: Metal routed MoE tensor byte size overflow\n");
return 0;
}
const uint64_t gate_tensor_bytes = (uint64_t)n_bind_expert * gate_expert_bytes;
const uint64_t down_tensor_bytes = (uint64_t)n_bind_expert * down_expert_bytes;
uint64_t gate_inner = 0;
uint64_t up_inner = 0;
uint64_t down_inner = 0;
id<MTLBuffer> gate_buf = nil;
id<MTLBuffer> up_buf = nil;
id<MTLBuffer> down_buf = nil;
__unsafe_unretained id<MTLBuffer> gate_slot_bufs[DS4_METAL_MAX_ROUTED_EXPERT_USED] = { nil };
__unsafe_unretained id<MTLBuffer> up_slot_bufs[DS4_METAL_MAX_ROUTED_EXPERT_USED] = { nil };
__unsafe_unretained id<MTLBuffer> down_slot_bufs[DS4_METAL_MAX_ROUTED_EXPERT_USED] = { nil };
ds4_gpu_stream_expert_cache_entry *stream_slot_entries[DS4_METAL_MAX_ROUTED_EXPERT_USED] = { NULL };
NSUInteger gate_slot_offsets[DS4_METAL_MAX_ROUTED_EXPERT_USED] = { 0 };
NSUInteger up_slot_offsets[DS4_METAL_MAX_ROUTED_EXPERT_USED] = { 0 };
NSUInteger down_slot_offsets[DS4_METAL_MAX_ROUTED_EXPERT_USED] = { 0 };
uint64_t stream_gate_abs_offsets[DS4_METAL_MAX_ROUTED_EXPERT_USED] = { 0 };
uint64_t stream_up_abs_offsets[DS4_METAL_MAX_ROUTED_EXPERT_USED] = { 0 };
uint64_t stream_down_abs_offsets[DS4_METAL_MAX_ROUTED_EXPERT_USED] = { 0 };
id<MTLBuffer> stream_gate_addr_buf = nil;
id<MTLBuffer> stream_up_addr_buf = nil;
id<MTLBuffer> stream_down_addr_buf = nil;
bool use_stream_expert_addr_table = false;
bool use_stream_expert_masked_addr_table = false;
bool use_stream_compact_addr_table = false;
bool use_stream_expert_cache = false;
bool use_stream_expert_split_candidate = false;
bool use_stream_expert_split_deferred = false;
bool stream_expert_split_completed = false;
uint32_t stream_expert_resident_mask = 0;
uint32_t stream_expert_missing_mask = 0;
__unsafe_unretained id<MTLBuffer> gate_group6_bufs[6] = { nil, nil, nil, nil, nil, nil };
__unsafe_unretained id<MTLBuffer> up_group6_bufs[6] = { nil, nil, nil, nil, nil, nil };
__unsafe_unretained id<MTLBuffer> down_group6_bufs[6] = { nil, nil, nil, nil, nil, nil };
NSUInteger gate_group6_offsets[6] = { 0, 0, 0, 0, 0, 0 };
NSUInteger up_group6_offsets[6] = { 0, 0, 0, 0, 0, 0 };
NSUInteger down_group6_offsets[6] = { 0, 0, 0, 0, 0, 0 };
__unsafe_unretained id<MTLBuffer> gate_group8_bufs[8] = { nil, nil, nil, nil, nil, nil, nil, nil };
__unsafe_unretained id<MTLBuffer> up_group8_bufs[8] = { nil, nil, nil, nil, nil, nil, nil, nil };
__unsafe_unretained id<MTLBuffer> down_group8_bufs[8] = { nil, nil, nil, nil, nil, nil, nil, nil };
NSUInteger gate_group8_offsets[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
NSUInteger up_group8_offsets[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
NSUInteger down_group8_offsets[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
__unsafe_unretained id<MTLBuffer> gate_group24_bufs[24] = { nil };
__unsafe_unretained id<MTLBuffer> up_group24_bufs[24] = { nil };
__unsafe_unretained id<MTLBuffer> down_group24_bufs[24] = { nil };
NSUInteger gate_group24_offsets[24] = { 0 };
NSUInteger up_group24_offsets[24] = { 0 };
NSUInteger down_group24_offsets[24] = { 0 };
DS4MetalQ4ExpertTable *gate_table = nil;
DS4MetalQ4ExpertTable *up_table = nil;
DS4MetalQ4ExpertTable *down_table = nil;
id q4_table_layer_residency = nil;
int32_t selected_ids[DS4_METAL_MAX_ROUTED_EXPERT_USED] = { 0 };
const uint32_t n_tokens = 1;
const uint32_t pair_rows = n_tokens * n_expert;
const uint64_t down_scratch_bytes = (uint64_t)pair_rows * out_dim * sizeof(float);
if ((n_expert > 1 && !expertsbuf &&
!ds4_gpu_ensure_scratch_buffer(&g_moe_down_scratch_buffer,
&g_moe_down_scratch_bytes,
(NSUInteger)down_scratch_bytes,
"ds4_moe_down_scratch"))) {
if (getenv("DS4_GLM_TP_DEBUG")) fprintf(stderr, "ds4: routed_moe_one silent return at line %d\n", 32148);
return 0;
}
const uint32_t gate_nr0 = ds4_gpu_routed_mv_nr0(gate_type);
const uint32_t down_nr0 = ds4_gpu_routed_mv_nr0(down_type);
id<MTLComputePipelineState> gate_mv_pipeline = ds4_gpu_routed_mv_pipeline(gate_type);
id<MTLComputePipelineState> down_mv_pipeline = ds4_gpu_routed_mv_pipeline(down_type);
if (gate_nr0 == 0 || down_nr0 == 0 || !gate_mv_pipeline || !down_mv_pipeline) {
fprintf(stderr, "ds4: unsupported Metal routed MoE quant types gate=%u down=%u\n",
gate_type, down_type);
if (getenv("DS4_GLM_TP_DEBUG")) fprintf(stderr, "ds4: routed_moe_one silent return at line %d\n", 32158);
return 0;
}
ds4_gpu_mul_mv_id_args gate_args =
ds4_gpu_make_mul_mv_id_args(expert_in_dim, expert_mid_dim, n_total_expert,
gate_row_bytes, gate_expert_bytes,
1, n_expert, n_tokens, gate_nr0);
ds4_gpu_mul_mv_id_args down_args =
ds4_gpu_make_mul_mv_id_args(expert_mid_dim, out_dim, n_total_expert,
down_row_bytes, down_expert_bytes,
n_expert, n_expert, n_tokens, down_nr0);
/* Tensor-parallel expert ownership; non-TP calls keep tp_world at 1. */
gate_args.tp_rank = g_tp_split_rank;
gate_args.tp_world = g_tp_full_expert_bind ? 1u : g_tp_split_world;
gate_args.tp_expert_base = tp_expert_base_host;
down_args.tp_rank = g_tp_split_rank;
down_args.tp_world = g_tp_full_expert_bind ? 1u : g_tp_split_world;
down_args.tp_addend = add_in != NULL;
down_args.tp_expert_base = tp_expert_base_host;
const NSUInteger gate_smem = ds4_gpu_routed_mv_smem(gate_type);
const NSUInteger down_smem = ds4_gpu_routed_mv_smem(down_type);
const NSUInteger gate_nsg = ds4_gpu_routed_mv_nsg(gate_type);
const NSUInteger down_nsg = ds4_gpu_routed_mv_nsg(down_type);
const bool gate_rows_per_group_is_nr0 = ds4_gpu_routed_mv_rows_per_group_is_nr0(gate_type);
const bool down_rows_per_group_is_nr0 = ds4_gpu_routed_mv_rows_per_group_is_nr0(down_type);
int pair_swiglu_nsg = 2;
int down_sum6_nsg = 2;
int ok = 1;
const bool write_clamped_moe =
getenv("DS4_METAL_MOE_WRITE_CLAMPED_ACT") != NULL;
/* The selected nsg=1 encoders dispatch exactly 32 threads. */
const bool use_mxfp4_moe_decode_tg_multiple =
ds4_gpu_device_is_pre_m5_apple_silicon() &&
getenv("DS4_METAL_DISABLE_PRE_M5_MXFP4_MOE_DECODE_TG_MULTIPLE") == NULL &&
gate_type == DS4_METAL_TENSOR_MXFP4 &&
down_type == DS4_METAL_TENSOR_MXFP4 &&
n_tokens == 1 &&
n_expert == 6 &&
g_tp_split_world == 1 &&
add_in == NULL &&
!g_quality_mode &&
!write_clamped_moe &&
(force_resident || !g_ssd_streaming_mode);
const bool use_mxfp4_moe_decode_fixed_route_pair =
use_mxfp4_moe_decode_tg_multiple &&
getenv("DS4_METAL_DISABLE_PRE_M5_MXFP4_MOE_DECODE_FIXED_ROUTE_PAIR") == NULL &&
((expert_in_dim == 4096 && expert_mid_dim == 2048 && n_total_expert == 256 &&
gate_row_bytes == 2176) ||
(expert_in_dim == 5120 && expert_mid_dim == 2304 && n_total_expert == 384 &&
gate_row_bytes == 2720)) &&
gate_args.nei0 == 6 &&
gate_args.nei1 == 1 &&
gate_args.ne11 == 1 &&
gate_args.tp_world == 1 &&
gate_args.tp_expert_base == 0 &&
g_moe_mul_mv_id_mxfp4_pair_swiglu_fixed_route_pipeline_nsg1 != nil;
const bool use_mxfp4_moe_decode_fixed_route_sum6 =
use_mxfp4_moe_decode_fixed_route_pair &&
getenv("DS4_METAL_DISABLE_PRE_M5_MXFP4_MOE_DECODE_FIXED_ROUTE_SUM6") == NULL &&
(out_dim == 4096 || out_dim == 5120) &&
down_row_bytes == (uint64_t)expert_mid_dim / 32u * 17u &&
down_expert_bytes == (uint64_t)out_dim * down_row_bytes &&
down_args.nei0 == 6 &&
down_args.nei1 == 1 &&
down_args.ne11 == 6 &&
down_args.tp_world == 1 &&
down_args.tp_expert_base == 0 &&
!down_args.tp_addend &&
g_moe_mul_mv_id_mxfp4_sum6_fixed_route_pipeline_nsg1 != nil;
const bool use_mxfp4_moe_decode_sum6_full_rows =
use_mxfp4_moe_decode_fixed_route_sum6 &&
getenv("DS4_METAL_DISABLE_PRE_M5_MXFP4_MOE_DECODE_SUM6_FULL_ROWS") == NULL &&
down_args.ne00 == (int32_t)expert_mid_dim &&
down_args.ne01 == (int32_t)out_dim &&
down_args.ne0 == (int32_t)out_dim &&
down_args.nr0 == 2 &&
g_moe_mul_mv_id_mxfp4_sum6_fixed_route_full_rows_pipeline_nsg1 != nil;
/* Compile-time trip counts for the one-token fixed-route decode MoE
* kernels. Every shape the specialized kernels assume is proven above
* (gate ne00 4096 / row bytes 2176; down ne00 2048 / row bytes 1088 /
* nei0 6, or the DeepSeek V4.1 Flash 5120 / 2720; 2304 / 1224 shape),
* so the specialization only removes runtime loop bounds; the
* per-lane K walk and accumulate order stay byte-identical. */
const bool use_mxfp4_moe_decode_static_trip_pair =
use_mxfp4_moe_decode_fixed_route_pair &&
getenv("DS4_METAL_DISABLE_PRE_M5_MXFP4_MOE_DECODE_STATIC_TRIP") == NULL &&
((gate_args.ne00 == 4096 && gate_args.nb01 == 2176) ||
(gate_args.ne00 == 5120 && gate_args.nb01 == 2720)) &&
g_moe_mul_mv_id_mxfp4_pair_swiglu_fixed_route_static_pipeline_nsg1 != nil;
const bool use_mxfp4_moe_decode_static_trip_down =
use_mxfp4_moe_decode_sum6_full_rows &&
use_mxfp4_moe_decode_static_trip_pair &&
(down_args.nb01 == 1088 || down_args.nb01 == 1224) &&
down_args.nei0 == 6 &&
g_moe_mul_mv_id_mxfp4_sum6_fixed_route_full_rows_static_pipeline_nsg1 != nil;
const bool use_m5_tp_mxfp4_static =
ds4_gpu_device_is_m5_apple_silicon() &&
getenv("DS4_METAL_DISABLE_M5_TP_MXFP4_STATIC") == NULL &&
gate_type == DS4_METAL_TENSOR_MXFP4 &&
down_type == DS4_METAL_TENSOR_MXFP4 &&
n_expert == 6 && n_total_expert == 256 &&
expert_in_dim == 4096 && expert_mid_dim == 2048 &&
out_dim == 4096 && gate_row_bytes == 2176 &&
gate_expert_bytes == 4456448 && down_row_bytes == 1088 &&
down_expert_bytes == 4456448 &&
gate_args.nei0 == 6 && gate_args.nei1 == 1 &&
gate_args.ne11 == 1 && down_args.nei0 == 6 &&
down_args.nei1 == 1 && down_args.ne11 == 6 &&
gate_args.tp_world == 2 && down_args.tp_world == 2 &&
((g_tp_split_rank == 0 && first_expert == 0u) ||
(g_tp_split_rank == 1 &&
first_expert + n_bind_expert == n_total_expert)) &&
!g_quality_mode && !write_clamped_moe &&
(force_resident || !g_ssd_streaming_mode) &&
g_moe_mul_mv_id_mxfp4_pair_swiglu_tp_static_pipeline_nsg1 != nil &&
g_moe_mul_mv_id_mxfp4_sum6_tp_full_rows_static_pipeline_nsg1 != nil;
id<MTLComputePipelineState> pair_swiglu_pipeline = nil;
if (gate_type == DS4_METAL_TENSOR_IQ2_XXS) {
pair_swiglu_pipeline = g_moe_mul_mv_id_iq2_xxs_pair_swiglu_pipeline;
} else if (gate_type == DS4_METAL_TENSOR_Q4_K) {
pair_swiglu_pipeline = g_moe_mul_mv_id_q4_k_pair_swiglu_pipeline;
} else if (gate_type == DS4_METAL_TENSOR_MXFP4) {
pair_swiglu_pipeline = g_moe_mul_mv_id_mxfp4_pair_swiglu_pipeline;
if (use_m5_tp_mxfp4_static) {
pair_swiglu_pipeline =
g_moe_mul_mv_id_mxfp4_pair_swiglu_tp_static_pipeline_nsg1;
pair_swiglu_nsg = 1;
} else if (ds4_gpu_mxfp4_moe_decode_nsg1_enabled(n_tokens)) {
pair_swiglu_pipeline =
use_mxfp4_moe_decode_static_trip_pair ?
g_moe_mul_mv_id_mxfp4_pair_swiglu_fixed_route_static_pipeline_nsg1 :
use_mxfp4_moe_decode_fixed_route_pair ?
g_moe_mul_mv_id_mxfp4_pair_swiglu_fixed_route_pipeline_nsg1 :
(use_mxfp4_moe_decode_tg_multiple ?
g_moe_mul_mv_id_mxfp4_pair_swiglu_pipeline_nsg1_tg_multiple :
g_moe_mul_mv_id_mxfp4_pair_swiglu_pipeline_nsg1);
pair_swiglu_nsg = 1;
}
}
const bool use_iq2_pair_pack2 =
gate_type == DS4_METAL_TENSOR_IQ2_XXS && n_tokens == 1 &&
n_expert == 6 && n_total_expert == 256 &&
expert_in_dim == 4096 && expert_mid_dim == 2048 &&
getenv("DS4_METAL_DISABLE_M5_IQ2_PAIR_PACK2") == NULL &&
ds4_gpu_device_is_m5_apple_silicon();
if (use_iq2_pair_pack2 &&
g_moe_mul_mv_id_iq2_xxs_pair_swiglu_pack2_pipeline) {
pair_swiglu_pipeline =
g_moe_mul_mv_id_iq2_xxs_pair_swiglu_pack2_pipeline;
pair_swiglu_nsg = 4;
}
const bool fuse_pair_swiglu =
!g_quality_mode &&
!write_clamped_moe &&
getenv("DS4_METAL_DISABLE_ROUTED_PAIR_SWIGLU_FUSION") == NULL &&
pair_swiglu_pipeline != nil;
id<MTLComputePipelineState> down_sum6_pipeline = nil;
if (down_type == DS4_METAL_TENSOR_Q2_K) {
down_sum6_pipeline = g_moe_mul_mv_id_q2_k_sum6_pipeline;
} else if (down_type == DS4_METAL_TENSOR_Q4_K) {
down_sum6_pipeline = g_moe_mul_mv_id_q4_k_sum6_pipeline;
} else if (down_type == DS4_METAL_TENSOR_MXFP4) {
down_sum6_pipeline = g_moe_mul_mv_id_mxfp4_sum6_pipeline;
if (use_m5_tp_mxfp4_static) {
down_sum6_pipeline =
g_moe_mul_mv_id_mxfp4_sum6_tp_full_rows_static_pipeline_nsg1;
down_sum6_nsg = 1;
} else if (ds4_gpu_mxfp4_moe_decode_nsg1_enabled(n_tokens)) {
down_sum6_pipeline =
use_mxfp4_moe_decode_static_trip_down ?
g_moe_mul_mv_id_mxfp4_sum6_fixed_route_full_rows_static_pipeline_nsg1 :
use_mxfp4_moe_decode_sum6_full_rows ?
g_moe_mul_mv_id_mxfp4_sum6_fixed_route_full_rows_pipeline_nsg1 :
(use_mxfp4_moe_decode_fixed_route_sum6 ?
g_moe_mul_mv_id_mxfp4_sum6_fixed_route_pipeline_nsg1 :
(use_mxfp4_moe_decode_tg_multiple ?
g_moe_mul_mv_id_mxfp4_sum6_pipeline_nsg1_tg_multiple :
g_moe_mul_mv_id_mxfp4_sum6_pipeline_nsg1));
down_sum6_nsg = 1;
}
} else if (down_type == DS4_METAL_TENSOR_IQ2_XXS &&
g_tp_split_world == 2) {
/* IQ2 down-sum exists for the GLM TP resident split only; the
* streaming paths must keep their addr/masked chain (expert
* bytes are not at their model-map offsets when streamed). */
down_sum6_pipeline = g_moe_mul_mv_id_iq2_xxs_sum6_pipeline;
}
const bool direct_down_sum =
!g_quality_mode &&
(n_expert == 6 || (n_expert == 8 && g_tp_split_world == 2) ||
g_tp_full_expert_bind) &&
n_tokens == 1 &&
down_sum6_pipeline != nil;
if (g_parallel_q8_pending) {
/* A concurrent encoder invalidates every implicit dependency in
* this generic function. Admit only resident fused pair-SwiGLU
* producers followed by direct sum6 consumers. Those are the two
* levels explicitly bracketed by the concurrent encoder. */
const bool parallel_iq2_route =
g_parallel_ffn_mode == 2 &&
gate_type == DS4_METAL_TENSOR_IQ2_XXS &&
down_type == DS4_METAL_TENSOR_Q2_K &&
n_tokens == 1 && n_expert == 6 && n_total_expert == 256 &&
expert_in_dim == 4096 && expert_mid_dim == 2048 &&
out_dim == 4096 && gate_row_bytes == 1056 &&
gate_expert_bytes == 2162688 && down_row_bytes == 672 &&
down_expert_bytes == 2752512 &&
first_expert == 0 && n_bind_expert == n_total_expert &&
g_tp_split_rank == 0 && g_tp_split_world == 1 &&
add_in == NULL && (force_resident || !g_ssd_streaming_mode) &&
!write_clamped_moe && fuse_pair_swiglu && direct_down_sum;
const bool parallel_mxfp4_tp_route =
g_parallel_ffn_mode == 2 &&
gate_type == DS4_METAL_TENSOR_MXFP4 &&
down_type == DS4_METAL_TENSOR_MXFP4 &&
n_tokens == 1 && n_expert == 6 &&
((n_total_expert == 256 &&
expert_in_dim == 4096 && expert_mid_dim == 2048 &&
out_dim == 4096 && gate_row_bytes == 2176 &&
gate_expert_bytes == 4456448 && down_row_bytes == 1088 &&
down_expert_bytes == 4456448) ||
(n_total_expert == 384 &&
expert_in_dim == 5120 && expert_mid_dim == 2304 &&
out_dim == 5120 && gate_row_bytes == 2720 &&
gate_expert_bytes == 6266880 && down_row_bytes == 1224 &&
down_expert_bytes == 6266880)) &&
((g_tp_split_world == 1 && first_expert == 0u &&
n_bind_expert == n_total_expert) ||
(g_tp_split_world == 2 &&
((g_tp_split_rank == 0 && first_expert == 0u) ||
(g_tp_split_rank == 1 &&
first_expert + n_bind_expert == n_total_expert)))) &&
add_in == NULL &&
(force_resident || !g_ssd_streaming_mode) &&
!write_clamped_moe && fuse_pair_swiglu && direct_down_sum;
if (!parallel_iq2_route && !parallel_mxfp4_tp_route) {
fprintf(stderr,
"ds4: concurrent FFN requires a supported resident "
"fused pair-SwiGLU + direct sum6 route\n");
return 0;
}
}
/* The expert-ownership split lives only in the fused id pair+sum6
* kernels; every other routed variant would silently compute full
* sums on both ranks and double the combine. Fail fast instead. */
if ((g_tp_split_world > 1 || add_in) && !(fuse_pair_swiglu && direct_down_sum)) {
fprintf(stderr, "ds4: tensor-parallel routed MoE requires the fused pair+sum6 decode path\n");
return 0;
}
const uint64_t q4_selected_min_tensor_bytes = 2ull * 1024ull * 1024ull * 1024ull;
/*
* The grouped Q4 experiment keeps selected IDs on GPU, but it also walks
* every expert window in the layer. On PRO Q4 this measured far slower
* than the active selected-slot path, so keep it opt-in for profiling.
*/
const bool enable_q4_grouped_experts =
!g_ssd_streaming_mode &&
getenv("DS4_METAL_ENABLE_Q4_GROUPED_EXPERTS") != NULL &&
getenv("DS4_METAL_DISABLE_Q4_GROUPED_EXPERTS") == NULL;
const bool use_q4_grouped_experts =
gate_type == DS4_METAL_TENSOR_Q4_K &&
down_type == DS4_METAL_TENSOR_Q4_K &&
n_expert == 6 &&
n_tokens == 1 &&
n_total_expert >= 128 &&
gate_tensor_bytes >= q4_selected_min_tensor_bytes &&
down_tensor_bytes >= q4_selected_min_tensor_bytes &&
fuse_pair_swiglu &&
direct_down_sum &&
g_moe_mul_mv_group_q4_k_pair_swiglu_pipeline != nil &&
g_moe_mul_mv_group_q4_k_sum6_pipeline != nil &&
enable_q4_grouped_experts;
const uint32_t q4_expert_group_size =
use_q4_grouped_experts ? ds4_gpu_q4_expert_group_size(n_total_expert) : 0;
const bool q4_grouped_boundary =
use_q4_grouped_experts &&
g_batch_cb != nil &&
getenv("DS4_METAL_DISABLE_Q4_GROUPED_BOUNDARY") == NULL;
const bool q4_grouped_cache_views =
getenv("DS4_METAL_Q4_GROUPED_CACHE_VIEWS") != NULL;
const uint32_t q4_group6_expert_group_size = 64;
const bool use_q4_group6_experts =
!g_ssd_streaming_mode &&
!use_q4_grouped_experts &&
gate_type == DS4_METAL_TENSOR_Q4_K &&
down_type == DS4_METAL_TENSOR_Q4_K &&
n_expert == 6 &&
n_tokens == 1 &&
n_total_expert == q4_group6_expert_group_size * 6u &&
gate_tensor_bytes >= q4_selected_min_tensor_bytes &&
down_tensor_bytes >= q4_selected_min_tensor_bytes &&
fuse_pair_swiglu &&
direct_down_sum &&
g_moe_mul_mv_group6_q4_k_pair_swiglu_pipeline != nil &&
g_moe_mul_mv_group6_q4_k_sum6_pipeline != nil &&
getenv("DS4_METAL_ENABLE_Q4_GROUP6_EXPERT_TABLE") != NULL &&
getenv("DS4_METAL_DISABLE_Q4_GROUP6_EXPERT_TABLE") == NULL;
const uint32_t q4_group8_expert_group_size = 48;
const bool use_q4_group8_experts =
!g_ssd_streaming_mode &&
!use_q4_grouped_experts &&
!use_q4_group6_experts &&
gate_type == DS4_METAL_TENSOR_Q4_K &&
down_type == DS4_METAL_TENSOR_Q4_K &&
n_expert == 6 &&
n_tokens == 1 &&
n_total_expert == q4_group8_expert_group_size * 8u &&
gate_tensor_bytes >= q4_selected_min_tensor_bytes &&
down_tensor_bytes >= q4_selected_min_tensor_bytes &&
fuse_pair_swiglu &&
direct_down_sum &&
g_moe_mul_mv_group8_q4_k_pair_swiglu_pipeline != nil &&
g_moe_mul_mv_group8_q4_k_sum6_pipeline != nil &&
getenv("DS4_METAL_ENABLE_Q4_GROUP8_EXPERT_TABLE") != NULL &&
getenv("DS4_METAL_DISABLE_Q4_GROUP8_EXPERT_TABLE") == NULL;
const uint32_t q4_group24_expert_group_size = 16;
const bool use_q4_group24_experts =
!g_ssd_streaming_mode &&
!use_q4_grouped_experts &&
!use_q4_group6_experts &&
!use_q4_group8_experts &&
gate_type == DS4_METAL_TENSOR_Q4_K &&
down_type == DS4_METAL_TENSOR_Q4_K &&
n_expert == 6 &&
n_tokens == 1 &&
n_total_expert == q4_group24_expert_group_size * 24u &&
gate_tensor_bytes >= q4_selected_min_tensor_bytes &&
down_tensor_bytes >= q4_selected_min_tensor_bytes &&
direct_down_sum &&
g_moe_mul_mv_group24_q4_k_id_pipeline != nil &&
g_moe_mul_mv_group24_q4_k_sum6_pipeline != nil &&
getenv("DS4_METAL_ENABLE_Q4_GROUP24_EXPERT_TABLE") != NULL &&
getenv("DS4_METAL_DISABLE_Q4_GROUP24_EXPERT_TABLE") == NULL;
const bool q4_group24_exact_views =
use_q4_group24_experts &&
getenv("DS4_METAL_Q4_GROUP24_EXACT_VIEWS") != NULL &&
getenv("DS4_METAL_Q4_GROUP24_BASE_VIEWS") == NULL;
const uint64_t max_buffer_len = g_device ? (uint64_t)[g_device maxBufferLength] : 0;
const bool can_wrap_q4_exact_tensors =
max_buffer_len != 0 &&
gate_tensor_bytes <= max_buffer_len &&
down_tensor_bytes <= max_buffer_len;
/*
* The full-tensor Q4 ID path is the closest arithmetic analogue to IQ2,
* but PRO Q4 routed tensors are multi-GiB. Keep it opt-in: even with
* per-layer command boundaries it is much slower than binding only the
* six active experts on current M3 Ultra Metal.
*/
const bool enable_q4_exact_tensor_id =
!g_ssd_streaming_mode &&
getenv("DS4_METAL_ENABLE_Q4_EXACT_TENSOR_ID") != NULL &&
getenv("DS4_METAL_DISABLE_Q4_EXACT_TENSOR_ID") == NULL;
const bool use_q4_exact_tensor_id =
!use_q4_grouped_experts &&
!use_q4_group6_experts &&
!use_q4_group8_experts &&
!use_q4_group24_experts &&
gate_type == DS4_METAL_TENSOR_Q4_K &&
down_type == DS4_METAL_TENSOR_Q4_K &&
n_expert == 6 &&
n_tokens == 1 &&
n_total_expert == 384 &&
gate_tensor_bytes >= q4_selected_min_tensor_bytes &&
down_tensor_bytes >= q4_selected_min_tensor_bytes &&
can_wrap_q4_exact_tensors &&
fuse_pair_swiglu &&
direct_down_sum &&
enable_q4_exact_tensor_id;
const bool q4_exact_boundary =
use_q4_exact_tensor_id &&
g_batch_cb != nil &&
getenv("DS4_METAL_DISABLE_Q4_EXACT_BOUNDARY") == NULL;
const bool q4_expert_table_auto =
gate_type == DS4_METAL_TENSOR_Q4_K &&
down_type == DS4_METAL_TENSOR_Q4_K &&
ds4_gpu_pro_q4_expert_table_auto_enabled(n_total_expert,
n_expert,
gate_tensor_bytes,
down_tensor_bytes);
const bool q4_expert_address_auto =
gate_type == DS4_METAL_TENSOR_Q4_K &&
down_type == DS4_METAL_TENSOR_Q4_K &&
ds4_gpu_pro_q4_expert_address_auto_enabled(n_total_expert,
n_expert,
gate_tensor_bytes,
down_tensor_bytes);
const bool q4_table_queue_residency =
ds4_gpu_q4_table_queue_residency_enabled(q4_expert_table_auto ||
q4_expert_address_auto);
const bool use_q4_expert_address_table =
!g_ssd_streaming_mode &&
!use_q4_grouped_experts &&
!use_q4_group6_experts &&
!use_q4_group8_experts &&
!use_q4_group24_experts &&
!use_q4_exact_tensor_id &&
gate_type == DS4_METAL_TENSOR_Q4_K &&
down_type == DS4_METAL_TENSOR_Q4_K &&
n_expert == 6 &&
n_tokens == 1 &&
n_total_expert == 384 &&
gate_tensor_bytes >= q4_selected_min_tensor_bytes &&
down_tensor_bytes >= q4_selected_min_tensor_bytes &&
fuse_pair_swiglu &&
direct_down_sum &&
g_moe_mul_mv_addr_q4_k_pair_swiglu_pipeline != nil &&
g_moe_mul_mv_addr_q4_k_sum6_pipeline != nil &&
(getenv("DS4_METAL_ENABLE_Q4_EXPERT_ADDRESS_TABLE") != NULL ||
q4_expert_address_auto) &&
(getenv("DS4_METAL_Q4_ADDR_USE_RESOURCES") != NULL ||
getenv("DS4_METAL_Q4_TABLE_RESIDENCY_SET") != NULL ||
q4_table_queue_residency ||
ds4_gpu_q4_table_model_residency_enabled() ||
getenv("DS4_METAL_USE_QUEUE_RESIDENCY_SET") != NULL) &&
getenv("DS4_METAL_DISABLE_Q4_EXPERT_ADDRESS_TABLE") == NULL;
const bool enable_q4_expert_table =
getenv("DS4_METAL_ENABLE_Q4_EXPERT_TABLE") != NULL ||
q4_expert_table_auto;
const bool use_q4_expert_table =
!g_ssd_streaming_mode &&
!use_q4_grouped_experts &&
!use_q4_group6_experts &&
!use_q4_group8_experts &&
!use_q4_group24_experts &&
!use_q4_exact_tensor_id &&
!use_q4_expert_address_table &&
gate_type == DS4_METAL_TENSOR_Q4_K &&
down_type == DS4_METAL_TENSOR_Q4_K &&
n_expert == 6 &&
n_tokens == 1 &&
n_total_expert == 384 &&
gate_tensor_bytes >= q4_selected_min_tensor_bytes &&
down_tensor_bytes >= q4_selected_min_tensor_bytes &&
fuse_pair_swiglu &&
direct_down_sum &&
g_moe_mul_mv_table_q4_k_pair_swiglu_pipeline != nil &&
g_moe_mul_mv_table_q4_k_sum6_pipeline != nil &&
g_moe_table_q4_pair_gate_encoder != nil &&
g_moe_table_q4_pair_up_encoder != nil &&
g_moe_table_q4_sum_down_encoder != nil &&
enable_q4_expert_table &&
(getenv("DS4_METAL_Q4_TABLE_USE_RESOURCES") != NULL ||
getenv("DS4_METAL_Q4_TABLE_RESIDENCY_SET") != NULL ||
q4_table_queue_residency ||
ds4_gpu_q4_table_model_residency_enabled() ||
getenv("DS4_METAL_USE_QUEUE_RESIDENCY_SET") != NULL) &&
getenv("DS4_METAL_DISABLE_Q4_EXPERT_TABLE") == NULL;
const bool q4_table_boundary =
use_q4_expert_table &&
g_batch_cb != nil &&
getenv("DS4_METAL_Q4_TABLE_RESIDENCY_SET") != NULL &&
!q4_table_queue_residency &&
getenv("DS4_METAL_DISABLE_Q4_TABLE_BOUNDARY") == NULL;
const bool enable_q4_gather_slots =
!g_ssd_streaming_mode &&
getenv("DS4_METAL_ENABLE_Q4_GATHER_SLOTS") != NULL &&
getenv("DS4_METAL_DISABLE_Q4_GATHER_SLOTS") == NULL;
const bool use_q4_gather_slots =
!use_q4_grouped_experts &&
!use_q4_group6_experts &&
!use_q4_group8_experts &&
!use_q4_group24_experts &&
!use_q4_exact_tensor_id &&
!use_q4_expert_address_table &&
!use_q4_expert_table &&
gate_type == DS4_METAL_TENSOR_Q4_K &&
down_type == DS4_METAL_TENSOR_Q4_K &&
n_expert == 6 &&
n_tokens == 1 &&
n_total_expert == q4_group6_expert_group_size * 6u &&
gate_tensor_bytes >= q4_selected_min_tensor_bytes &&
down_tensor_bytes >= q4_selected_min_tensor_bytes &&
fuse_pair_swiglu &&
direct_down_sum &&
g_moe_q4_gather_slots6_pipeline != nil &&
g_moe_mul_mv_slots6_q4_k_pair_swiglu_pipeline != nil &&
g_moe_mul_mv_slots6_q4_k_sum6_pipeline != nil &&
enable_q4_gather_slots;
const bool use_q4_selected_slots =
!force_resident &&
!use_q4_grouped_experts &&
!use_q4_group6_experts &&
!use_q4_group8_experts &&
!use_q4_group24_experts &&
!use_q4_exact_tensor_id &&
!use_q4_expert_address_table &&
!use_q4_expert_table &&
!use_q4_gather_slots &&
gate_type == DS4_METAL_TENSOR_Q4_K &&
down_type == DS4_METAL_TENSOR_Q4_K &&
n_expert == 6 &&
n_tokens == 1 &&
n_total_expert >= 128 &&
(g_ssd_streaming_mode ||
(gate_tensor_bytes >= q4_selected_min_tensor_bytes &&
down_tensor_bytes >= q4_selected_min_tensor_bytes)) &&
fuse_pair_swiglu &&
direct_down_sum &&
ds4_gpu_q4_selected_paths_allowed() &&
g_moe_mul_mv_slots6_q4_k_pair_swiglu_pipeline != nil &&
g_moe_mul_mv_slots6_q4_k_sum6_pipeline != nil &&
getenv("DS4_METAL_DISABLE_Q4_SELECTED_EXPERT_VIEWS") == NULL;
const bool use_iq2_selected_slots =
!force_resident &&
g_ssd_streaming_mode &&
gate_type == DS4_METAL_TENSOR_IQ2_XXS &&
down_type == DS4_METAL_TENSOR_Q2_K &&
n_expert == 6 &&
n_tokens == 1 &&
fuse_pair_swiglu &&
direct_down_sum &&
g_moe_mul_mv_slots6_iq2_xxs_pair_swiglu_pipeline != nil &&
g_moe_mul_mv_slots6_q2_k_sum6_pipeline != nil &&
getenv("DS4_METAL_DISABLE_IQ2_SELECTED_EXPERT_VIEWS") == NULL;
const bool use_mxfp4_selected_slots =
!force_resident &&
g_ssd_streaming_mode &&
gate_type == DS4_METAL_TENSOR_MXFP4 &&
down_type == DS4_METAL_TENSOR_MXFP4 &&
n_expert == 6 &&
n_tokens == 1 &&
n_total_expert >= 128 &&
fuse_pair_swiglu &&
direct_down_sum &&
g_moe_mul_mv_slots6_mxfp4_pair_swiglu_pipeline != nil &&
g_moe_mul_mv_slots6_mxfp4_sum6_pipeline != nil &&
getenv("DS4_METAL_DISABLE_MXFP4_SELECTED_EXPERT_VIEWS") == NULL;
const bool use_iq2_stream_addr_table =
!force_resident &&
g_ssd_streaming_mode &&
gate_type == DS4_METAL_TENSOR_IQ2_XXS &&
down_type == DS4_METAL_TENSOR_IQ2_XXS &&
n_expert <= DS4_METAL_MAX_ROUTED_EXPERT_USED &&
n_tokens == 1 &&
fuse_pair_swiglu &&
g_moe_mul_mv_addr_iq2_xxs_pair_swiglu_pipeline != nil &&
g_moe_mul_mv_addr_iq2_xxs_pipeline != nil &&
getenv("DS4_METAL_DISABLE_IQ2_STREAM_ADDR_TABLE") == NULL;
const bool use_selected_slots =
use_q4_selected_slots || use_iq2_selected_slots ||
use_mxfp4_selected_slots || use_iq2_stream_addr_table;
id<MTLComputePipelineState> slots_pair_swiglu_pipeline =
use_iq2_selected_slots ? g_moe_mul_mv_slots6_iq2_xxs_pair_swiglu_pipeline :
(use_mxfp4_selected_slots ? g_moe_mul_mv_slots6_mxfp4_pair_swiglu_pipeline :
g_moe_mul_mv_slots6_q4_k_pair_swiglu_pipeline);
id<MTLComputePipelineState> slots_sum6_pipeline =
use_iq2_selected_slots ? g_moe_mul_mv_slots6_q2_k_sum6_pipeline :
(use_mxfp4_selected_slots ? g_moe_mul_mv_slots6_mxfp4_sum6_pipeline :
g_moe_mul_mv_slots6_q4_k_sum6_pipeline);
const char *selected_profile_env = getenv("DS4_METAL_SELECTED_PROFILE");
if (!selected_profile_env) {
selected_profile_env = getenv("DS4_METAL_Q4_SELECTED_PROFILE");
}
const char *selected_profile_layer_env = getenv("DS4_METAL_SELECTED_PROFILE_LAYER");
if (!selected_profile_layer_env) {
selected_profile_layer_env = getenv("DS4_METAL_Q4_SELECTED_PROFILE_LAYER");
}
bool selected_profile_layer_match = true;
if (selected_profile_layer_env && selected_profile_layer_env[0]) {
char *end = NULL;
long layer = strtol(selected_profile_layer_env, &end, 10);
selected_profile_layer_match =
end && *end == '\0' && layer >= 0 && (uint32_t)layer == layer_index;
}
const bool selected_profile =
use_selected_slots &&
selected_profile_env != NULL &&
selected_profile_layer_match;
const bool q4_selected_shared_event =
use_q4_selected_slots &&
getenv("DS4_METAL_Q4_SELECTED_SHARED_EVENT") != NULL;
const bool q4_selected_base_views =
use_q4_selected_slots &&
getenv("DS4_METAL_Q4_SELECTED_USE_BASE_VIEWS") != NULL &&
getenv("DS4_METAL_Q4_SELECTED_EXACT_VIEWS") == NULL;
const bool q4_selected_transient_views =
use_q4_selected_slots &&
!q4_selected_base_views &&
getenv("DS4_METAL_Q4_SELECTED_TRANSIENT_VIEWS") != NULL;
const char *q4_selected_view_mode =
q4_selected_base_views ? "base" :
(q4_selected_transient_views ? "transient" : "cached");
if (!use_selected_slots) {
g_routed_moe_selected_override_n = 0;
}
if (use_q4_expert_address_table) {
gate_table = ds4_gpu_q4_expert_address_table(model_map,
model_size,
gate_offset,
gate_expert_bytes,
n_total_expert);
up_table = ds4_gpu_q4_expert_address_table(model_map,
model_size,
up_offset,
gate_expert_bytes,
n_total_expert);
down_table = ds4_gpu_q4_expert_address_table(model_map,
model_size,
down_offset,
down_expert_bytes,
n_total_expert);
if (!gate_table || !up_table || !down_table) {
if (getenv("DS4_GLM_TP_DEBUG")) fprintf(stderr, "ds4: routed_moe_one silent return at line %d\n", 32546);
return 0;
}
q4_table_layer_residency =
ds4_gpu_q4_expert_layer_residency_set(gate_table,
up_table,
down_table,
q4_expert_address_auto);
} else if (use_q4_expert_table) {
gate_table = ds4_gpu_q4_expert_table(model_map,
model_size,
gate_offset,
gate_expert_bytes,
n_total_expert,
g_moe_table_q4_pair_gate_encoder);
up_table = ds4_gpu_q4_expert_table(model_map,
model_size,
up_offset,
gate_expert_bytes,
n_total_expert,
g_moe_table_q4_pair_up_encoder);
down_table = ds4_gpu_q4_expert_table(model_map,
model_size,
down_offset,
down_expert_bytes,
n_total_expert,
g_moe_table_q4_sum_down_encoder);
if (!gate_table || !up_table || !down_table) {
if (getenv("DS4_GLM_TP_DEBUG")) fprintf(stderr, "ds4: routed_moe_one silent return at line %d\n", 32573);
return 0;
}
q4_table_layer_residency =
ds4_gpu_q4_expert_layer_residency_set(gate_table,
up_table,
down_table,
q4_expert_table_auto);
} else if (use_q4_exact_tensor_id) {
gate_buf = ds4_gpu_wrap_model_exact_range(model_map,
model_size,
gate_offset,
gate_tensor_bytes,
&gate_inner);
up_buf = ds4_gpu_wrap_model_exact_range(model_map,
model_size,
up_offset,
gate_tensor_bytes,
&up_inner);
down_buf = ds4_gpu_wrap_model_exact_range(model_map,
model_size,
down_offset,
down_tensor_bytes,
&down_inner);
if (!gate_buf || !up_buf || !down_buf) { if (getenv("DS4_GLM_TP_DEBUG")) fprintf(stderr, "ds4: routed_moe_one silent return at line %d\n", 32601); return 0; }
} else if (use_q4_group6_experts || use_q4_gather_slots) {
if ((uint64_t)q4_group6_expert_group_size > UINT64_MAX / gate_expert_bytes ||
(uint64_t)q4_group6_expert_group_size > UINT64_MAX / down_expert_bytes) {
fprintf(stderr, "ds4: Metal routed MoE Q4 group6 byte size overflow\n");
return 0;
}
const uint64_t gate_group_bytes =
(uint64_t)q4_group6_expert_group_size * gate_expert_bytes;
const uint64_t down_group_bytes =
(uint64_t)q4_group6_expert_group_size * down_expert_bytes;
for (uint32_t i = 0; i < 6; i++) {
const uint64_t gate_rel = (uint64_t)i * gate_group_bytes;
const uint64_t down_rel = (uint64_t)i * down_group_bytes;
if (gate_rel > gate_tensor_bytes ||
gate_group_bytes > gate_tensor_bytes - gate_rel ||
down_rel > down_tensor_bytes ||
down_group_bytes > down_tensor_bytes - down_rel ||
gate_rel > UINT64_MAX - gate_offset ||
gate_rel > UINT64_MAX - up_offset ||
down_rel > UINT64_MAX - down_offset) {
fprintf(stderr, "ds4: Metal routed MoE Q4 group6 offset overflow\n");
return 0;
}
uint64_t group_inner = 0;
gate_group6_bufs[i] = ds4_gpu_wrap_model_range(model_map,
model_size,
gate_offset + gate_rel,
gate_group_bytes,
&group_inner);
gate_group6_offsets[i] = (NSUInteger)group_inner;
group_inner = 0;
up_group6_bufs[i] = ds4_gpu_wrap_model_range(model_map,
model_size,
up_offset + gate_rel,
gate_group_bytes,
&group_inner);
up_group6_offsets[i] = (NSUInteger)group_inner;
group_inner = 0;
down_group6_bufs[i] = ds4_gpu_wrap_model_range(model_map,
model_size,
down_offset + down_rel,
down_group_bytes,
&group_inner);
down_group6_offsets[i] = (NSUInteger)group_inner;
if (!gate_group6_bufs[i] || !up_group6_bufs[i] || !down_group6_bufs[i]) {
if (getenv("DS4_GLM_TP_DEBUG")) fprintf(stderr, "ds4: routed_moe_one silent return at line %d\n", 32643);
return 0;
}
}
if (use_q4_gather_slots) {
if (gate_expert_bytes > NSUIntegerMax ||
down_expert_bytes > NSUIntegerMax ||
gate_expert_bytes > UINT64_MAX / 6u ||
down_expert_bytes > UINT64_MAX / 6u ||
6ull * gate_expert_bytes > NSUIntegerMax ||
6ull * down_expert_bytes > NSUIntegerMax) {
fprintf(stderr, "ds4: Metal routed MoE Q4 gather scratch byte size overflow\n");
return 0;
}
const NSUInteger gate_slots_bytes = (NSUInteger)(6ull * gate_expert_bytes);
const NSUInteger down_slots_bytes = (NSUInteger)(6ull * down_expert_bytes);
if (!ds4_gpu_ensure_scratch_buffer(&g_moe_q4_gate_slots_buffer,
&g_moe_q4_gate_slots_bytes,
gate_slots_bytes,
"ds4_moe_q4_gate_slots") ||
!ds4_gpu_ensure_scratch_buffer(&g_moe_q4_up_slots_buffer,
&g_moe_q4_up_slots_bytes,
gate_slots_bytes,
"ds4_moe_q4_up_slots") ||
!ds4_gpu_ensure_scratch_buffer(&g_moe_q4_down_slots_buffer,
&g_moe_q4_down_slots_bytes,
down_slots_bytes,
"ds4_moe_q4_down_slots")) {
if (getenv("DS4_GLM_TP_DEBUG")) fprintf(stderr, "ds4: routed_moe_one silent return at line %d\n", 32670);
return 0;
}
for (uint32_t i = 0; i < 6; i++) {
gate_slot_bufs[i] = g_moe_q4_gate_slots_buffer;
up_slot_bufs[i] = g_moe_q4_up_slots_buffer;
down_slot_bufs[i] = g_moe_q4_down_slots_buffer;
gate_slot_offsets[i] = (NSUInteger)((uint64_t)i * gate_expert_bytes);
up_slot_offsets[i] = (NSUInteger)((uint64_t)i * gate_expert_bytes);
down_slot_offsets[i] = (NSUInteger)((uint64_t)i * down_expert_bytes);
}
}
} else if (use_q4_group8_experts) {
if ((uint64_t)q4_group8_expert_group_size > UINT64_MAX / gate_expert_bytes ||
(uint64_t)q4_group8_expert_group_size > UINT64_MAX / down_expert_bytes) {
fprintf(stderr, "ds4: Metal routed MoE Q4 group8 byte size overflow\n");
return 0;
}
const uint64_t gate_group_bytes =
(uint64_t)q4_group8_expert_group_size * gate_expert_bytes;
const uint64_t down_group_bytes =
(uint64_t)q4_group8_expert_group_size * down_expert_bytes;
for (uint32_t i = 0; i < 8; i++) {
const uint64_t gate_rel = (uint64_t)i * gate_group_bytes;
const uint64_t down_rel = (uint64_t)i * down_group_bytes;
if (gate_rel > gate_tensor_bytes ||
gate_group_bytes > gate_tensor_bytes - gate_rel ||
down_rel > down_tensor_bytes ||
down_group_bytes > down_tensor_bytes - down_rel ||
gate_rel > UINT64_MAX - gate_offset ||
gate_rel > UINT64_MAX - up_offset ||
down_rel > UINT64_MAX - down_offset) {
fprintf(stderr, "ds4: Metal routed MoE Q4 group8 offset overflow\n");
return 0;
}
uint64_t group_inner = 0;
gate_group8_bufs[i] = ds4_gpu_wrap_model_range(model_map,
model_size,
gate_offset + gate_rel,
gate_group_bytes,
&group_inner);
gate_group8_offsets[i] = (NSUInteger)group_inner;
group_inner = 0;
up_group8_bufs[i] = ds4_gpu_wrap_model_range(model_map,
model_size,
up_offset + gate_rel,
gate_group_bytes,
&group_inner);
up_group8_offsets[i] = (NSUInteger)group_inner;
group_inner = 0;
down_group8_bufs[i] = ds4_gpu_wrap_model_range(model_map,
model_size,
down_offset + down_rel,
down_group_bytes,
&group_inner);
down_group8_offsets[i] = (NSUInteger)group_inner;
if (!gate_group8_bufs[i] || !up_group8_bufs[i] || !down_group8_bufs[i]) {
if (getenv("DS4_GLM_TP_DEBUG")) fprintf(stderr, "ds4: routed_moe_one silent return at line %d\n", 32727);
return 0;
}
}
} else if (use_q4_group24_experts) {
if ((uint64_t)q4_group24_expert_group_size > UINT64_MAX / gate_expert_bytes ||
(uint64_t)q4_group24_expert_group_size > UINT64_MAX / down_expert_bytes) {
fprintf(stderr, "ds4: Metal routed MoE Q4 group24 byte size overflow\n");
return 0;
}
const uint64_t gate_group_bytes =
(uint64_t)q4_group24_expert_group_size * gate_expert_bytes;
const uint64_t down_group_bytes =
(uint64_t)q4_group24_expert_group_size * down_expert_bytes;
for (uint32_t i = 0; i < 24; i++) {
const uint64_t gate_rel = (uint64_t)i * gate_group_bytes;
const uint64_t down_rel = (uint64_t)i * down_group_bytes;
if (gate_rel > gate_tensor_bytes ||
gate_group_bytes > gate_tensor_bytes - gate_rel ||
down_rel > down_tensor_bytes ||
down_group_bytes > down_tensor_bytes - down_rel ||
gate_rel > UINT64_MAX - gate_offset ||
gate_rel > UINT64_MAX - up_offset ||
down_rel > UINT64_MAX - down_offset) {
fprintf(stderr, "ds4: Metal routed MoE Q4 group24 offset overflow\n");
return 0;
}
uint64_t group_inner = 0;
gate_group24_bufs[i] = q4_group24_exact_views ?
ds4_gpu_wrap_model_exact_range(model_map,
model_size,
gate_offset + gate_rel,
gate_group_bytes,
&group_inner) :
ds4_gpu_wrap_model_range(model_map,
model_size,
gate_offset + gate_rel,
gate_group_bytes,
&group_inner);
gate_group24_offsets[i] = (NSUInteger)group_inner;
group_inner = 0;
up_group24_bufs[i] = q4_group24_exact_views ?
ds4_gpu_wrap_model_exact_range(model_map,
model_size,
up_offset + gate_rel,
gate_group_bytes,
&group_inner) :
ds4_gpu_wrap_model_range(model_map,
model_size,
up_offset + gate_rel,
gate_group_bytes,
&group_inner);
up_group24_offsets[i] = (NSUInteger)group_inner;
group_inner = 0;
down_group24_bufs[i] = q4_group24_exact_views ?
ds4_gpu_wrap_model_exact_range(model_map,
model_size,
down_offset + down_rel,
down_group_bytes,
&group_inner) :
ds4_gpu_wrap_model_range(model_map,
model_size,
down_offset + down_rel,
down_group_bytes,
&group_inner);
down_group24_offsets[i] = (NSUInteger)group_inner;
if (!gate_group24_bufs[i] || !up_group24_bufs[i] || !down_group24_bufs[i]) {
if (getenv("DS4_GLM_TP_DEBUG")) fprintf(stderr, "ds4: routed_moe_one silent return at line %d\n", 32794);
return 0;
}
}
} else if (use_selected_slots) {
const bool selected_timing =
selected_profile ||
ds4_gpu_stream_expert_timing_summary_enabled();
double selected_t0 = selected_timing ? ds4_gpu_now_ms() : 0.0;
double selected_read_ms = 0.0;
double selected_sync_ms = 0.0;
double selected_copy_ms = 0.0;
double selected_wrap_ms = 0.0;
uint64_t selected_cache_hits0 = g_stream_expert_cache_hits;
uint64_t selected_cache_misses0 = g_stream_expert_cache_misses;
uint64_t selected_cache_wraps0 = g_stream_expert_cache_wraps;
uint64_t selected_cache_evictions0 = g_stream_expert_cache_evictions;
const char *selected_id_source = "readback";
bool selected_ids_available = true;
bool selected_exec_ids_from_host = false;
const int stream_expert_cache_size_known =
ds4_gpu_stream_expert_cache_note_expert_size(gate_expert_bytes,
down_expert_bytes);
const bool use_iq2_full_expert_addr_table =
use_iq2_selected_slots &&
ds4_gpu_stream_full_expert_addr_table_requested() &&
g_moe_mul_mv_addr_iq2_xxs_pair_swiglu_pipeline != nil &&
g_moe_mul_mv_addr_q2_k_sum6_pipeline != nil;
use_stream_expert_cache =
!use_iq2_full_expert_addr_table &&
(use_iq2_selected_slots || use_iq2_stream_addr_table ||
use_q4_selected_slots || use_mxfp4_selected_slots) &&
stream_expert_cache_size_known &&
ds4_gpu_stream_expert_cache_effective_cap(layer_index,
n_total_expert,
n_expert) != 0;
if (use_iq2_stream_addr_table && !use_stream_expert_cache) {
fprintf(stderr,
"ds4: Metal IQ2/IQ2 streaming decode requires a non-empty expert cache\n");
if (getenv("DS4_GLM_TP_DEBUG")) fprintf(stderr, "ds4: routed_moe_one silent return at line %d\n", 32831);
return 0;
}
const bool stream_split_ready =
use_stream_expert_cache &&
ds4_gpu_stream_expert_split_ready();
const bool use_stream_compact_addr =
use_stream_expert_cache &&
use_iq2_selected_slots &&
ds4_gpu_stream_compact_addr_requested() &&
!stream_split_ready &&
!ds4_gpu_stream_expert_masked_addr_requested() &&
g_moe_mul_mv_addr_iq2_xxs_pair_swiglu_pipeline != nil &&
g_moe_mul_mv_addr_q2_k_sum6_pipeline != nil;
use_stream_expert_split_candidate =
use_stream_expert_cache &&
use_iq2_selected_slots &&
!use_stream_compact_addr &&
stream_split_ready &&
g_moe_mul_mv_addr_iq2_xxs_pair_swiglu_masked_pipeline != nil &&
g_moe_mul_mv_addr_q2_k_sum6_masked_pipeline != nil;
const bool use_stream_hit_validator =
use_stream_expert_cache &&
use_iq2_selected_slots &&
ds4_gpu_stream_expert_hit_validator_requested() &&
g_moe_stream_expert_cache_validate_pipeline != nil &&
g_moe_mul_mv_addr_iq2_xxs_pair_swiglu_pipeline != nil &&
g_moe_mul_mv_addr_q2_k_sum6_pipeline != nil &&
ds4_gpu_stream_expert_cache_addr_buffers(layer_index,
&stream_gate_addr_buf,
&stream_up_addr_buf,
&stream_down_addr_buf);
if (use_iq2_full_expert_addr_table) {
selected_id_source = "gpu-full-addr";
selected_ids_available = false;
g_routed_moe_selected_override_n = 0;
ds4_gpu_stream_expert_cache_entry *full_entry = NULL;
if (!ds4_gpu_stream_full_expert_addr_table_prepare(model_map,
model_size,
layer_index,
n_total_expert,
gate_offset,
up_offset,
down_offset,
gate_expert_bytes,
down_expert_bytes,
&stream_gate_addr_buf,
&stream_up_addr_buf,
&stream_down_addr_buf,
&full_entry)) {
if (getenv("DS4_GLM_TP_DEBUG")) fprintf(stderr, "ds4: routed_moe_one silent return at line %d\n", 32880);
return 0;
}
for (uint32_t i = 0; i < n_expert; i++) {
stream_slot_entries[i] = full_entry;
}
use_stream_expert_addr_table = true;
} else {
const int replayed_selected_ids =
ds4_gpu_moe_selected_trace_replay(selected_ids, n_expert);
if (replayed_selected_ids < 0) {
if (getenv("DS4_GLM_TP_DEBUG")) fprintf(stderr, "ds4: routed_moe_one silent return at line %d\n", 32890);
return 0;
} else if (replayed_selected_ids > 0) {
selected_id_source = "replay";
selected_exec_ids_from_host = true;
g_routed_moe_selected_override_n = 0;
} else if (g_routed_moe_selected_override_n == n_expert) {
memcpy(selected_ids,
g_routed_moe_selected_override,
(size_t)n_expert * sizeof(selected_ids[0]));
selected_id_source = "override";
selected_exec_ids_from_host = true;
g_routed_moe_selected_override_n = 0;
} else if (use_stream_expert_cache &&
ds4_gpu_glm_stream_selected_prefetch_take(
model_map, model_size, layer_index,
n_total_expert, n_expert,
gate_offset, up_offset, down_offset,
gate_expert_bytes, down_expert_bytes,
selected_ids)) {
/* GLM already read these IDs to start loading experts.
* Submit the shared expert work while the reads finish. */
selected_id_source = "prefetched";
if (g_batch_cb && g_batch_has_work &&
g_stream_expert_pending_load.active &&
!ds4_gpu_flush_commands()) return 0;
} else if (use_stream_hit_validator) {
g_routed_moe_selected_override_n = 0;
uint32_t validator_all_cached = 0;
uint32_t validator_miss_mask = 0;
uint32_t validator_invalid_mask = 0;
if (!ds4_gpu_stream_expert_cache_validate_selected(selected,
stream_gate_addr_buf,
stream_up_addr_buf,
stream_down_addr_buf,
n_total_expert,
n_expert,
selected_ids,
&validator_all_cached,
&validator_miss_mask,
&validator_invalid_mask)) {
if (getenv("DS4_GLM_TP_DEBUG")) fprintf(stderr, "ds4: routed_moe_one silent return at line %d\n", 32917);
return 0;
}
selected_id_source =
validator_invalid_mask != 0 ? "validator-invalid" :
(validator_miss_mask != 0 ? "validator-miss" :
(validator_all_cached != 0 ? "validator-hit" : "validator-miss"));
} else {
g_routed_moe_selected_override_n = 0;
if (g_batch_cb != nil) {
double selected_boundary_t0 =
selected_timing ? ds4_gpu_now_ms() : 0.0;
if (q4_selected_shared_event) {
if (ds4_gpu_signal_batch_and_wait_event("selected-id readback") == 0) { if (getenv("DS4_GLM_TP_DEBUG")) fprintf(stderr, "ds4: routed_moe_one silent return at line %d\n", 32942); return 0; }
} else if (ds4_gpu_end_commands() == 0) {
if (getenv("DS4_GLM_TP_DEBUG")) fprintf(stderr, "ds4: routed_moe_one silent return at line %d\n", 32931);
return 0;
}
if (selected_timing) {
selected_sync_ms +=
ds4_gpu_now_ms() - selected_boundary_t0;
}
double selected_copy_t0 =
selected_timing ? ds4_gpu_now_ms() : 0.0;
if (ds4_gpu_tensor_read(selected,
0,
selected_ids,
(uint64_t)n_expert * sizeof(selected_ids[0])) == 0) {
if (getenv("DS4_GLM_TP_DEBUG")) fprintf(stderr, "ds4: routed_moe_one silent return at line %d\n", 32943);
return 0;
}
if (selected_timing) {
selected_copy_ms +=
ds4_gpu_now_ms() - selected_copy_t0;
}
if (!q4_selected_shared_event) {
selected_boundary_t0 =
selected_timing ? ds4_gpu_now_ms() : 0.0;
if (ds4_gpu_begin_commands() == 0) { if (getenv("DS4_GLM_TP_DEBUG")) fprintf(stderr, "ds4: routed_moe_one silent return at line %d\n", 32967); return 0; }
if (selected_timing) {
selected_sync_ms +=
ds4_gpu_now_ms() - selected_boundary_t0;
}
}
} else {
double selected_copy_t0 =
selected_timing ? ds4_gpu_now_ms() : 0.0;
if (ds4_gpu_tensor_read(selected,
0,
selected_ids,
(uint64_t)n_expert * sizeof(selected_ids[0])) == 0) {
if (getenv("DS4_GLM_TP_DEBUG")) fprintf(stderr, "ds4: routed_moe_one silent return at line %d\n", 32965);
return 0;
}
if (selected_timing) {
selected_copy_ms +=
ds4_gpu_now_ms() - selected_copy_t0;
}
}
}
}
if (selected_timing) {
selected_read_ms = selected_sync_ms + selected_copy_ms;
selected_t0 = ds4_gpu_now_ms();
}
if (selected_ids_available) {
for (uint32_t i = 0; i < n_expert; i++) {
if (selected_ids[i] < 0 || (uint32_t)selected_ids[i] >= n_total_expert) {
fprintf(stderr,
"ds4: Metal routed MoE selected expert id %d is outside 0..%u\n",
selected_ids[i],
n_total_expert);
if (getenv("DS4_GLM_TP_DEBUG")) fprintf(stderr, "ds4: routed_moe_one silent return at line %d\n", 32986);
return 0;
}
}
ds4_gpu_stream_expert_cache_note_selected_hotness(layer_index,
selected_ids,
n_expert);
if (!ds4_gpu_moe_selected_trace_record(selected_ids, n_expert) ||
!ds4_gpu_moe_selected_hotlist_record(layer_index,
selected_ids,
n_expert,
n_total_expert)) {
if (getenv("DS4_GLM_TP_DEBUG")) fprintf(stderr, "ds4: routed_moe_one silent return at line %d\n", 32997);
return 0;
}
for (uint32_t i = 0; i < n_expert; i++) {
const uint64_t expert_id = (uint64_t)(uint32_t)selected_ids[i];
if (expert_id > UINT64_MAX / gate_expert_bytes ||
expert_id > UINT64_MAX / down_expert_bytes) {
fprintf(stderr, "ds4: Metal routed MoE selected expert offset overflow\n");
return 0;
}
const uint64_t gate_rel = expert_id * gate_expert_bytes;
const uint64_t down_rel = expert_id * down_expert_bytes;
if (gate_rel > UINT64_MAX - gate_offset ||
gate_rel > UINT64_MAX - up_offset ||
down_rel > UINT64_MAX - down_offset) {
fprintf(stderr, "ds4: Metal routed MoE selected expert offset overflow\n");
return 0;
}
stream_gate_abs_offsets[i] = gate_offset + gate_rel;
stream_up_abs_offsets[i] = up_offset + gate_rel;
stream_down_abs_offsets[i] = down_offset + down_rel;
if (use_stream_expert_cache) {
ds4_gpu_stream_expert_cache_entry *entry = NULL;
entry = ds4_gpu_stream_expert_cache_peek(model_map,
model_size,
layer_index,
(uint32_t)selected_ids[i],
n_total_expert,
n_expert,
stream_gate_abs_offsets[i],
stream_up_abs_offsets[i],
stream_down_abs_offsets[i],
gate_expert_bytes,
down_expert_bytes);
if (!entry) {
stream_expert_missing_mask |= 1u << i;
continue;
}
stream_expert_resident_mask |= 1u << i;
stream_slot_entries[i] = entry;
gate_slot_bufs[i] = entry->gate_buffer;
gate_slot_offsets[i] = entry->gate_inner;
up_slot_bufs[i] = entry->up_buffer;
up_slot_offsets[i] = entry->up_inner;
down_slot_bufs[i] = entry->down_buffer;
down_slot_offsets[i] = entry->down_inner;
continue;
}
uint64_t slot_inner = 0;
gate_slot_bufs[i] = q4_selected_base_views ?
ds4_gpu_wrap_model_range(model_map,
model_size,
gate_offset + gate_rel,
gate_expert_bytes,
&slot_inner) :
(q4_selected_transient_views ?
ds4_gpu_wrap_model_exact_range_transient(model_map,
model_size,
gate_offset + gate_rel,
gate_expert_bytes,
&slot_inner) :
ds4_gpu_wrap_model_exact_range(model_map,
model_size,
gate_offset + gate_rel,
gate_expert_bytes,
&slot_inner));
gate_slot_offsets[i] = (NSUInteger)slot_inner;
slot_inner = 0;
up_slot_bufs[i] = q4_selected_base_views ?
ds4_gpu_wrap_model_range(model_map,
model_size,
up_offset + gate_rel,
gate_expert_bytes,
&slot_inner) :
(q4_selected_transient_views ?
ds4_gpu_wrap_model_exact_range_transient(model_map,
model_size,
up_offset + gate_rel,
gate_expert_bytes,
&slot_inner) :
ds4_gpu_wrap_model_exact_range(model_map,
model_size,
up_offset + gate_rel,
gate_expert_bytes,
&slot_inner));
up_slot_offsets[i] = (NSUInteger)slot_inner;
slot_inner = 0;
down_slot_bufs[i] = q4_selected_base_views ?
ds4_gpu_wrap_model_range(model_map,
model_size,
down_offset + down_rel,
down_expert_bytes,
&slot_inner) :
(q4_selected_transient_views ?
ds4_gpu_wrap_model_exact_range_transient(model_map,
model_size,
down_offset + down_rel,
down_expert_bytes,
&slot_inner) :
ds4_gpu_wrap_model_exact_range(model_map,
model_size,
down_offset + down_rel,
down_expert_bytes,
&slot_inner));
down_slot_offsets[i] = (NSUInteger)slot_inner;
if (!gate_slot_bufs[i] || !up_slot_bufs[i] || !down_slot_bufs[i]) {
if (getenv("DS4_GLM_TP_DEBUG")) fprintf(stderr, "ds4: routed_moe_one silent return at line %d\n", 33105);
return 0;
}
}
}
if (use_stream_expert_cache) {
use_stream_expert_addr_table =
((use_iq2_selected_slots &&
ds4_gpu_stream_expert_addr_table_kernel_requested() &&
g_moe_mul_mv_addr_iq2_xxs_pair_swiglu_pipeline != nil &&
g_moe_mul_mv_addr_q2_k_sum6_pipeline != nil) ||
use_iq2_stream_addr_table) &&
ds4_gpu_stream_expert_cache_addr_buffers(layer_index,
&stream_gate_addr_buf,
&stream_up_addr_buf,
&stream_down_addr_buf);
if (use_iq2_stream_addr_table && !use_stream_expert_addr_table) {
fprintf(stderr,
"ds4: Metal IQ2/IQ2 streaming decode could not prepare expert address buffers\n");
if (getenv("DS4_GLM_TP_DEBUG")) fprintf(stderr, "ds4: routed_moe_one silent return at line %d\n", 33123);
return 0;
}
use_stream_expert_masked_addr_table =
use_stream_expert_addr_table &&
use_iq2_selected_slots &&
ds4_gpu_stream_expert_masked_addr_requested() &&
g_moe_mul_mv_addr_iq2_xxs_pair_swiglu_masked_pipeline != nil &&
g_moe_mul_mv_addr_q2_k_sum6_masked_pipeline != nil;
use_stream_expert_split_deferred =
use_stream_expert_split_candidate &&
use_stream_expert_masked_addr_table &&
stream_expert_resident_mask != 0 &&
stream_expert_missing_mask != 0 &&
ds4_gpu_stream_expert_split_worthwhile(stream_expert_resident_mask,
stream_expert_missing_mask) &&
g_batch_cb != nil &&
getenv("DS4_METAL_MOE_ONE_STAGE_PROFILE") == NULL;
if (use_stream_expert_split_deferred) {
const ds4_gpu_stream_expert_table table = {
.model_map = model_map,
.model_size = model_size,
.layer = layer_index,
.n_total_expert = n_total_expert,
.gate_offset = gate_offset,
.up_offset = up_offset,
.down_offset = down_offset,
.gate_expert_bytes = gate_expert_bytes,
.down_expert_bytes = down_expert_bytes,
};
if (!ds4_gpu_stream_expert_cache_begin_selected_load(
&table,
selected_ids,
n_expert)) {
if (getenv("DS4_GLM_TP_DEBUG")) fprintf(stderr, "ds4: routed_moe_one silent return at line %d\n", 33156);
return 0;
}
}
if (stream_expert_missing_mask != 0 &&
!use_stream_expert_split_deferred) {
if (!ds4_gpu_stream_expert_cache_load_selected_missing(
model_map,
model_size,
layer_index,
selected_ids,
n_total_expert,
n_expert,
stream_gate_abs_offsets,
stream_up_abs_offsets,
stream_down_abs_offsets,
gate_expert_bytes,
down_expert_bytes,
stream_expert_missing_mask,
stream_slot_entries)) {
if (getenv("DS4_GLM_TP_DEBUG")) fprintf(stderr, "ds4: routed_moe_one silent return at line %d\n", 33175);
return 0;
}
for (uint32_t i = 0; i < n_expert; i++) {
if ((stream_expert_missing_mask & (1u << i)) == 0) continue;
ds4_gpu_stream_expert_cache_entry *entry = stream_slot_entries[i];
if (!entry) { if (getenv("DS4_GLM_TP_DEBUG")) fprintf(stderr, "ds4: routed_moe_one silent return at line %d\n", 33202); return 0; }
gate_slot_bufs[i] = entry->gate_buffer;
gate_slot_offsets[i] = entry->gate_inner;
up_slot_bufs[i] = entry->up_buffer;
up_slot_offsets[i] = entry->up_inner;
down_slot_bufs[i] = entry->down_buffer;
down_slot_offsets[i] = entry->down_inner;
}
}
if (use_iq2_stream_addr_table && use_stream_expert_addr_table) {
for (uint32_t i = 0; i < n_expert; i++) {
ds4_gpu_stream_expert_cache_entry *entry = stream_slot_entries[i];
if (!entry) { if (getenv("DS4_GLM_TP_DEBUG")) fprintf(stderr, "ds4: routed_moe_one silent return at line %d\n", 33214); return 0; }
if (!ds4_gpu_stream_expert_cache_set_addr_slot_raw(
layer_index,
(uint32_t)selected_ids[i],
entry->gate_buffer,
entry->gate_inner,
entry->up_buffer,
entry->up_inner,
entry->down_buffer,
entry->down_inner)) {
if (getenv("DS4_GLM_TP_DEBUG")) fprintf(stderr, "ds4: routed_moe_one silent return at line %d\n", 33202);
return 0;
}
}
}
ds4_gpu_stream_expert_cache_prune_layer(layer_index,
n_total_expert,
n_expert,
selected_ids,
n_expert);
ds4_gpu_stream_expert_cache_prune_global(layer_index,
selected_ids,
n_expert);
if (use_stream_compact_addr) {
id<MTLBuffer> compact_selected = nil;
if (!ds4_gpu_stream_compact_addr_prepare(layer_index,
stream_slot_entries,
n_expert,
&stream_gate_addr_buf,
&stream_up_addr_buf,
&stream_down_addr_buf,
&compact_selected)) {
if (getenv("DS4_GLM_TP_DEBUG")) fprintf(stderr, "ds4: routed_moe_one silent return at line %d\n", 33223);
return 0;
}
selected_exec_buf = compact_selected;
selected_exec_off = 0;
use_stream_expert_addr_table = true;
use_stream_expert_masked_addr_table = false;
use_stream_compact_addr_table = true;
}
if (use_stream_expert_addr_table &&
!use_stream_compact_addr_table &&
selected_exec_ids_from_host) {
if (!ds4_gpu_stream_selected_ids_prepare(layer_index,
selected_ids,
n_expert,
&selected_exec_buf,
&selected_exec_off)) {
if (getenv("DS4_GLM_TP_DEBUG")) fprintf(stderr, "ds4: routed_moe_one silent return at line %d\n", 33239);
return 0;
}
}
}
if (selected_timing) {
selected_wrap_ms = ds4_gpu_now_ms() - selected_t0;
if (use_stream_expert_cache) {
ds4_gpu_stream_expert_timing_note_cache_class(
stream_expert_resident_mask,
stream_expert_missing_mask);
}
ds4_gpu_stream_expert_timing_note_selected(selected_sync_ms,
selected_copy_ms,
selected_wrap_ms);
}
if (selected_profile) {
const uint64_t selected_cache_hits =
g_stream_expert_cache_hits - selected_cache_hits0;
const uint64_t selected_cache_misses =
g_stream_expert_cache_misses - selected_cache_misses0;
const uint64_t selected_cache_wraps =
g_stream_expert_cache_wraps - selected_cache_wraps0;
const uint64_t selected_cache_evictions =
g_stream_expert_cache_evictions - selected_cache_evictions0;
const char *selected_path =
use_iq2_stream_addr_table ? "iq2/iq2" :
(use_iq2_selected_slots ? "iq2/q2" :
(use_mxfp4_selected_slots ? "mxfp4/mxfp4" : "q4/q4"));
const char *selected_view_mode =
use_stream_expert_split_deferred ? "stream-split" :
use_stream_expert_masked_addr_table ? "stream-addr-mask" :
use_stream_compact_addr_table ? "stream-compact-addr" :
use_stream_expert_addr_table ? "stream-addr" :
(use_stream_expert_cache ? "stream-cache" :
(use_iq2_selected_slots ? "exact-cache" : q4_selected_view_mode));
fprintf(stderr,
"ds4: Metal selected views layer=%u path=%s mode=%s ids=%s "
"experts=",
layer_index, selected_path, selected_view_mode,
selected_id_source);
for (uint32_t i = 0; i < n_expert; i++)
fprintf(stderr, "%s%d", i ? "," : "",
selected_ids_available ? selected_ids[i] : -1);
fprintf(stderr,
" expert_gate=%.2f MiB "
"expert_down=%.2f MiB read=%.3f ms bind=%.3f ms "
"cache_hits=%llu cache_misses=%llu cache_wraps=%llu cache_evictions=%llu\n",
ds4_gpu_mib(gate_expert_bytes),
ds4_gpu_mib(down_expert_bytes),
selected_read_ms,
selected_wrap_ms,
(unsigned long long)selected_cache_hits,
(unsigned long long)selected_cache_misses,
(unsigned long long)selected_cache_wraps,
(unsigned long long)selected_cache_evictions);
}
} else if (!use_q4_grouped_experts) {
gate_buf = ds4_gpu_wrap_model_range(model_map, model_size, gate_offset, gate_tensor_bytes, &gate_inner);
up_buf = ds4_gpu_wrap_model_range(model_map, model_size, up_offset, gate_tensor_bytes, &up_inner);
down_buf = ds4_gpu_wrap_model_range(model_map, model_size, down_offset, down_tensor_bytes, &down_inner);
if (!gate_buf || !up_buf || !down_buf) { if (getenv("DS4_GLM_TP_DEBUG")) fprintf(stderr, "ds4: routed_moe_one silent return at line %d\n", 33326); return 0; }
}
if (q4_grouped_boundary || q4_exact_boundary || q4_table_boundary) {
if (ds4_gpu_end_commands() == 0 || ds4_gpu_begin_commands() == 0) {
if (getenv("DS4_GLM_TP_DEBUG")) fprintf(stderr, "ds4: routed_moe_one silent return at line %d\n", 33305);
return 0;
}
}
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) { if (getenv("DS4_GLM_TP_DEBUG")) fprintf(stderr, "ds4: routed_moe_one silent return at line %d\n", 33337); return 0; }
if ((use_q4_expert_address_table || use_q4_expert_table) &&
!ds4_gpu_use_model_residency_set(cb)) {
if (getenv("DS4_GLM_TP_DEBUG")) fprintf(stderr, "ds4: routed_moe_one silent return at line %d\n", 33314);
return 0;
}
if (!q4_table_queue_residency &&
q4_table_layer_residency &&
[cb respondsToSelector:@selector(useResidencySet:)]) {
[cb useResidencySet:q4_table_layer_residency];
}
const bool moe_one_stage_profile =
g_batch_cb != nil &&
ds4_gpu_stage_profile_enabled_for_layer("DS4_METAL_MOE_ONE_STAGE_PROFILE",
"DS4_METAL_MOE_ONE_STAGE_PROFILE_LAYER",
layer_index);
const char *moe_one_stage_filter = getenv("DS4_METAL_MOE_STAGE_PROFILE_FILTER");
const char *moe_one_path =
use_q4_grouped_experts ? "q4_grouped_pair_swiglu" :
use_q4_group6_experts ? "q4_group6_pair_swiglu" :
use_q4_group8_experts ? "q4_group8_pair_swiglu" :
use_q4_group24_experts ? "q4_group24_split_gate_up" :
use_q4_exact_tensor_id ? "q4_exact_pair_swiglu" :
use_q4_expert_address_table ? "q4_addr_pair_swiglu" :
use_q4_expert_table ? "q4_table_pair_swiglu" :
use_q4_gather_slots ? "q4_gather_slots6_pair_swiglu" :
use_stream_expert_split_deferred ? "iq2_stream_split_pair_swiglu" :
use_stream_expert_masked_addr_table ? "iq2_stream_addr_mask_pair_swiglu" :
use_stream_expert_addr_table ? "iq2_stream_addr_pair_swiglu" :
use_iq2_selected_slots ? "iq2_slots6_pair_swiglu" :
use_mxfp4_selected_slots ? "mxfp4_slots6_pair_swiglu" :
use_q4_selected_slots ? "q4_slots6_pair_swiglu" :
(fuse_pair_swiglu ? "pair_swiglu" :
((!g_quality_mode &&
((gate_type == DS4_METAL_TENSOR_IQ2_XXS && g_moe_mul_mv_id_iq2_xxs_pair_pipeline) ||
(gate_type == DS4_METAL_TENSOR_Q4_K && g_moe_mul_mv_id_q4_k_pair_pipeline))) ? "pair" : "single"));
double moe_one_stage_t0 = moe_one_stage_profile ? ds4_gpu_now_ms() : 0.0;
if (moe_one_stage_profile) {
if (ds4_gpu_end_commands() == 0 || ds4_gpu_begin_commands() == 0) {
if (getenv("DS4_GLM_TP_DEBUG")) fprintf(stderr, "ds4: routed_moe_one silent return at line %d\n", 33349);
return 0;
}
cb = ds4_gpu_command_buffer(&owned);
if (!cb) { if (getenv("DS4_GLM_TP_DEBUG")) fprintf(stderr, "ds4: routed_moe_one silent return at line %d\n", 33380); return 0; }
moe_one_stage_t0 = ds4_gpu_now_ms();
}
#define DS4_METAL_PROFILE_MOE_ONE_STAGE(name) do { \
if (ok && moe_one_stage_profile) { \
if (ds4_gpu_end_commands() == 0) { \
ok = 0; \
} else { \
const char *stage_name = (name); \
const double now_ms = ds4_gpu_now_ms(); \
const int print_stage = \
!moe_one_stage_filter || !moe_one_stage_filter[0] || \
strstr(stage_name, moe_one_stage_filter) != NULL; \
if (print_stage) { \
fprintf(stderr, \
"ds4: Metal routed MoE one stage layer=%u pairs=%u experts=%u " \
"gate=%s down=%s path=%s %s=%.3f ms\n", \
layer_index, pair_rows, n_expert, \
ds4_gpu_metal_tensor_type_name(gate_type), \
ds4_gpu_metal_tensor_type_name(down_type), \
moe_one_path, \
stage_name, now_ms - moe_one_stage_t0); \
} \
moe_one_stage_t0 = now_ms; \
if (ds4_gpu_begin_commands() == 0) { \
ok = 0; \
} else { \
cb = ds4_gpu_command_buffer(&owned); \
if (!cb) ok = 0; \
} \
} \
} \
} while (0)
if (use_q4_gather_slots) {
ds4_gpu_q4_gather_slots6_args gate_gather_args = {
.expert_bytes = gate_expert_bytes,
.group_size = q4_group6_expert_group_size,
.n_slots = n_expert,
};
ds4_gpu_q4_gather_slots6_args down_gather_args = {
.expert_bytes = down_expert_bytes,
.group_size = q4_group6_expert_group_size,
.n_slots = n_expert,
};
ok = ds4_gpu_encode_q4_gather_slots6(cb,
g_moe_q4_gather_slots6_pipeline,
&gate_gather_args,
gate_group6_bufs,
gate_group6_offsets,
selectedbuf,
ds4_gpu_tensor_offset(selected),
g_moe_q4_gate_slots_buffer,
0) &&
ds4_gpu_encode_q4_gather_slots6(cb,
g_moe_q4_gather_slots6_pipeline,
&gate_gather_args,
up_group6_bufs,
up_group6_offsets,
selectedbuf,
ds4_gpu_tensor_offset(selected),
g_moe_q4_up_slots_buffer,
0) &&
ds4_gpu_encode_q4_gather_slots6(cb,
g_moe_q4_gather_slots6_pipeline,
&down_gather_args,
down_group6_bufs,
down_group6_offsets,
selectedbuf,
ds4_gpu_tensor_offset(selected),
g_moe_q4_down_slots_buffer,
0);
}
if (use_q4_gather_slots) {
DS4_METAL_PROFILE_MOE_ONE_STAGE("q4_gather");
}
if (!ok) { if (getenv("DS4_GLM_TP_DEBUG")) fprintf(stderr, "ds4: routed_moe_one silent return at line %d\n", 33455); return 0; }
if (use_q4_grouped_experts) {
ds4_gpu_dsv4_moe_swiglu_weight_args act_args = {
.width = expert_mid_dim,
.rows = pair_rows,
.gate_row_stride = (uint64_t)expert_mid_dim * sizeof(float),
.up_row_stride = (uint64_t)expert_mid_dim * sizeof(float),
.mid_row_stride = (uint64_t)expert_mid_dim * sizeof(float),
.weight_stride = sizeof(float),
.write_clamped = 0,
.clamp_value = clamp,
};
for (uint32_t expert_base = 0; ok && expert_base < n_total_expert; expert_base += q4_expert_group_size) {
const uint32_t expert_count =
q4_expert_group_size < n_total_expert - expert_base ?
q4_expert_group_size : n_total_expert - expert_base;
if ((uint64_t)expert_base > UINT64_MAX / gate_expert_bytes ||
(uint64_t)expert_count > UINT64_MAX / gate_expert_bytes) {
ok = 0;
break;
}
const uint64_t group_rel = (uint64_t)expert_base * gate_expert_bytes;
const uint64_t group_bytes = (uint64_t)expert_count * gate_expert_bytes;
if (group_rel > UINT64_MAX - gate_offset ||
group_rel > UINT64_MAX - up_offset) {
ok = 0;
break;
}
uint64_t gate_group_inner = 0;
uint64_t up_group_inner = 0;
id<MTLBuffer> gate_group_buf =
q4_grouped_cache_views ?
ds4_gpu_wrap_model_exact_range(model_map,
model_size,
gate_offset + group_rel,
group_bytes,
&gate_group_inner) :
ds4_gpu_wrap_model_exact_range_transient(model_map,
model_size,
gate_offset + group_rel,
group_bytes,
&gate_group_inner);
id<MTLBuffer> up_group_buf =
q4_grouped_cache_views ?
ds4_gpu_wrap_model_exact_range(model_map,
model_size,
up_offset + group_rel,
group_bytes,
&up_group_inner) :
ds4_gpu_wrap_model_exact_range_transient(model_map,
model_size,
up_offset + group_rel,
group_bytes,
&up_group_inner);
if (!gate_group_buf || !up_group_buf) {
ok = 0;
break;
}
ds4_gpu_moe_expert_group_args group_args = {
.expert_base = expert_base,
.expert_count = expert_count,
.accumulate = 0,
.pad0 = 0,
};
ok = ds4_gpu_encode_mul_mv_group_q4_pair_swiglu(cb,
g_moe_mul_mv_group_q4_k_pair_swiglu_pipeline,
&gate_args,
&act_args,
&group_args,
gate_group_buf,
(NSUInteger)gate_group_inner,
up_group_buf,
(NSUInteger)up_group_inner,
xbuf,
ds4_gpu_tensor_offset(x),
gatebuf,
ds4_gpu_tensor_offset(gate),
upbuf,
ds4_gpu_tensor_offset(up),
midbuf,
ds4_gpu_tensor_offset(mid),
selectedbuf,
ds4_gpu_tensor_offset(selected),
weightsbuf,
ds4_gpu_tensor_offset(weights),
gate_smem,
2,
false);
}
} else if (use_q4_expert_address_table) {
ds4_gpu_dsv4_moe_swiglu_weight_args act_args = {
.width = expert_mid_dim,
.rows = pair_rows,
.gate_row_stride = (uint64_t)expert_mid_dim * sizeof(float),
.up_row_stride = (uint64_t)expert_mid_dim * sizeof(float),
.mid_row_stride = (uint64_t)expert_mid_dim * sizeof(float),
.weight_stride = sizeof(float),
.write_clamped = 0,
.clamp_value = clamp,
};
ok = ds4_gpu_encode_mul_mv_addr_q4_pair_swiglu(cb,
g_moe_mul_mv_addr_q4_k_pair_swiglu_pipeline,
&gate_args,
&act_args,
gate_table,
up_table,
xbuf,
ds4_gpu_tensor_offset(x),
gatebuf,
ds4_gpu_tensor_offset(gate),
upbuf,
ds4_gpu_tensor_offset(up),
midbuf,
ds4_gpu_tensor_offset(mid),
selectedbuf,
ds4_gpu_tensor_offset(selected),
weightsbuf,
ds4_gpu_tensor_offset(weights),
gate_smem,
2,
false);
} else if (use_q4_expert_table) {
ds4_gpu_dsv4_moe_swiglu_weight_args act_args = {
.width = expert_mid_dim,
.rows = pair_rows,
.gate_row_stride = (uint64_t)expert_mid_dim * sizeof(float),
.up_row_stride = (uint64_t)expert_mid_dim * sizeof(float),
.mid_row_stride = (uint64_t)expert_mid_dim * sizeof(float),
.weight_stride = sizeof(float),
.write_clamped = 0,
.clamp_value = clamp,
};
ok = ds4_gpu_encode_mul_mv_table_q4_pair_swiglu(cb,
g_moe_mul_mv_table_q4_k_pair_swiglu_pipeline,
&gate_args,
&act_args,
gate_table,
up_table,
xbuf,
ds4_gpu_tensor_offset(x),
gatebuf,
ds4_gpu_tensor_offset(gate),
upbuf,
ds4_gpu_tensor_offset(up),
midbuf,
ds4_gpu_tensor_offset(mid),
selectedbuf,
ds4_gpu_tensor_offset(selected),
weightsbuf,
ds4_gpu_tensor_offset(weights),
gate_smem,
2,
false,
q4_table_queue_residency);
} else if (use_q4_group6_experts) {
ds4_gpu_dsv4_moe_swiglu_weight_args act_args = {
.width = expert_mid_dim,
.rows = pair_rows,
.gate_row_stride = (uint64_t)expert_mid_dim * sizeof(float),
.up_row_stride = (uint64_t)expert_mid_dim * sizeof(float),
.mid_row_stride = (uint64_t)expert_mid_dim * sizeof(float),
.weight_stride = sizeof(float),
.write_clamped = 0,
.clamp_value = clamp,
};
ok = ds4_gpu_encode_mul_mv_group6_pair_swiglu(cb,
g_moe_mul_mv_group6_q4_k_pair_swiglu_pipeline,
&gate_args,
&act_args,
gate_group6_bufs,
gate_group6_offsets,
up_group6_bufs,
up_group6_offsets,
xbuf,
ds4_gpu_tensor_offset(x),
gatebuf,
ds4_gpu_tensor_offset(gate),
upbuf,
ds4_gpu_tensor_offset(up),
midbuf,
ds4_gpu_tensor_offset(mid),
selectedbuf,
ds4_gpu_tensor_offset(selected),
weightsbuf,
ds4_gpu_tensor_offset(weights),
gate_smem,
2,
false);
} else if (use_q4_group8_experts) {
ds4_gpu_dsv4_moe_swiglu_weight_args act_args = {
.width = expert_mid_dim,
.rows = pair_rows,
.gate_row_stride = (uint64_t)expert_mid_dim * sizeof(float),
.up_row_stride = (uint64_t)expert_mid_dim * sizeof(float),
.mid_row_stride = (uint64_t)expert_mid_dim * sizeof(float),
.weight_stride = sizeof(float),
.write_clamped = 0,
.clamp_value = clamp,
};
ok = ds4_gpu_encode_mul_mv_group8_pair_swiglu(cb,
g_moe_mul_mv_group8_q4_k_pair_swiglu_pipeline,
&gate_args,
&act_args,
gate_group8_bufs,
gate_group8_offsets,
up_group8_bufs,
up_group8_offsets,
xbuf,
ds4_gpu_tensor_offset(x),
gatebuf,
ds4_gpu_tensor_offset(gate),
upbuf,
ds4_gpu_tensor_offset(up),
midbuf,
ds4_gpu_tensor_offset(mid),
selectedbuf,
ds4_gpu_tensor_offset(selected),
weightsbuf,
ds4_gpu_tensor_offset(weights),
gate_smem,
2,
false);
} else if (use_q4_group24_experts) {
ok = ds4_gpu_encode_mul_mv_group24_id(cb,
g_moe_mul_mv_group24_q4_k_id_pipeline,
&gate_args,
gate_group24_bufs,
gate_group24_offsets,
xbuf,
ds4_gpu_tensor_offset(x),
gatebuf,
ds4_gpu_tensor_offset(gate),
selectedbuf,
ds4_gpu_tensor_offset(selected),
gate_smem,
2,
false) &&
ds4_gpu_encode_mul_mv_group24_id(cb,
g_moe_mul_mv_group24_q4_k_id_pipeline,
&gate_args,
up_group24_bufs,
up_group24_offsets,
xbuf,
ds4_gpu_tensor_offset(x),
upbuf,
ds4_gpu_tensor_offset(up),
selectedbuf,
ds4_gpu_tensor_offset(selected),
gate_smem,
2,
false);
} else if (use_q4_gather_slots || use_selected_slots) {
ds4_gpu_dsv4_moe_swiglu_weight_args act_args = {
.width = expert_mid_dim,
.rows = pair_rows,
.gate_row_stride = (uint64_t)expert_mid_dim * sizeof(float),
.up_row_stride = (uint64_t)expert_mid_dim * sizeof(float),
.mid_row_stride = (uint64_t)expert_mid_dim * sizeof(float),
.weight_stride = sizeof(float),
.write_clamped = 0,
.clamp_value = clamp,
};
if (use_stream_expert_addr_table) {
if (use_stream_expert_masked_addr_table) {
if (use_stream_expert_split_deferred) {
const bool stream_split_profile =
getenv("DS4_METAL_STREAMING_EXPERT_SPLIT_PROFILE") != NULL;
const bool stream_split_timing =
stream_split_profile ||
ds4_gpu_stream_expert_timing_summary_enabled();
double stream_split_t0 =
stream_split_timing ? ds4_gpu_now_ms() : 0.0;
ds4_gpu_stream_expert_split_args resident_pair_args = {
.active_mask = stream_expert_resident_mask,
.accumulate = 0u,
};
ok = ds4_gpu_encode_mul_mv_addr_iq2_pair_swiglu_masked(cb,
g_moe_mul_mv_addr_iq2_xxs_pair_swiglu_masked_pipeline,
&gate_args,
&act_args,
&resident_pair_args,
stream_slot_entries,
stream_gate_addr_buf,
stream_up_addr_buf,
xbuf,
ds4_gpu_tensor_offset(x),
gatebuf,
ds4_gpu_tensor_offset(gate),
upbuf,
ds4_gpu_tensor_offset(up),
midbuf,
ds4_gpu_tensor_offset(mid),
selected_exec_buf,
selected_exec_off,
weightsbuf,
ds4_gpu_tensor_offset(weights),
gate_smem,
2,
false);
if (ok) {
ok = ds4_gpu_flush_commands();
if (ok) {
cb = ds4_gpu_command_buffer(&owned);
if (!cb) ok = 0;
}
}
const double stream_split_resident_ms =
stream_split_timing ? ds4_gpu_now_ms() - stream_split_t0 : 0.0;
if (stream_split_timing) stream_split_t0 = ds4_gpu_now_ms();
const double stream_split_missing_start_ms = stream_split_t0;
double stream_split_missing_load_ms = 0.0;
double stream_split_missing_slot_ms = 0.0;
double stream_split_missing_prune_ms = 0.0;
double stream_split_missing_addr_ms = 0.0;
double stream_split_missing_wait_ms = 0.0;
if (ok) {
ok = ds4_gpu_stream_expert_cache_load_selected_missing(
model_map,
model_size,
layer_index,
selected_ids,
n_total_expert,
n_expert,
stream_gate_abs_offsets,
stream_up_abs_offsets,
stream_down_abs_offsets,
gate_expert_bytes,
down_expert_bytes,
stream_expert_missing_mask,
stream_slot_entries);
if (stream_split_timing) {
const double now_ms = ds4_gpu_now_ms();
stream_split_missing_load_ms =
now_ms - stream_split_t0;
stream_split_t0 = now_ms;
}
if (ok) {
for (uint32_t i = 0; i < n_expert; i++) {
if ((stream_expert_missing_mask & (1u << i)) == 0) continue;
ds4_gpu_stream_expert_cache_entry *entry =
stream_slot_entries[i];
if (!entry) {
ok = 0;
break;
}
gate_slot_bufs[i] = entry->gate_buffer;
gate_slot_offsets[i] = entry->gate_inner;
up_slot_bufs[i] = entry->up_buffer;
up_slot_offsets[i] = entry->up_inner;
down_slot_bufs[i] = entry->down_buffer;
down_slot_offsets[i] = entry->down_inner;
}
}
}
if (stream_split_timing) {
const double now_ms = ds4_gpu_now_ms();
stream_split_missing_slot_ms =
now_ms - stream_split_t0;
stream_split_t0 = now_ms;
}
if (ok) {
ds4_gpu_stream_expert_cache_prune_layer(layer_index,
n_total_expert,
n_expert,
selected_ids,
n_expert);
ds4_gpu_stream_expert_cache_prune_global(layer_index,
selected_ids,
n_expert);
if (stream_split_timing) {
const double now_ms = ds4_gpu_now_ms();
stream_split_missing_prune_ms =
now_ms - stream_split_t0;
stream_split_t0 = now_ms;
}
ok = ds4_gpu_stream_expert_cache_addr_buffers(layer_index,
&stream_gate_addr_buf,
&stream_up_addr_buf,
&stream_down_addr_buf);
if (stream_split_timing) {
const double now_ms = ds4_gpu_now_ms();
stream_split_missing_addr_ms =
now_ms - stream_split_t0;
stream_split_t0 = now_ms;
}
}
if (ok) {
/*
* The resident stage was submitted before the
* CPU read of missing experts so I/O can overlap
* with GPU work. The missing stage reuses the same
* gate/up/mid scratch buffers, so it must not
* execute until the resident command buffer has
* finished. The down/sum pass is issued once after
* all six mid slots exist; this keeps the final
* accumulation order stable regardless of the
* resident/missing split.
*/
ok = ds4_gpu_wait_pending_command_buffers(
"streaming expert split resident");
if (stream_split_timing) {
const double now_ms = ds4_gpu_now_ms();
stream_split_missing_wait_ms =
now_ms - stream_split_t0;
stream_split_t0 = now_ms;
}
}
const double stream_split_missing_ms =
stream_split_timing ?
ds4_gpu_now_ms() - stream_split_missing_start_ms :
0.0;
ds4_gpu_stream_expert_split_args missing_pair_args = {
.active_mask = stream_expert_missing_mask,
.accumulate = 0u,
};
ds4_gpu_stream_expert_split_args all_down_args = {
.active_mask = 0x3fu,
.accumulate = 0u,
};
if (ok) {
ok = ds4_gpu_encode_mul_mv_addr_iq2_pair_swiglu_masked(cb,
g_moe_mul_mv_addr_iq2_xxs_pair_swiglu_masked_pipeline,
&gate_args,
&act_args,
&missing_pair_args,
stream_slot_entries,
stream_gate_addr_buf,
stream_up_addr_buf,
xbuf,
ds4_gpu_tensor_offset(x),
gatebuf,
ds4_gpu_tensor_offset(gate),
upbuf,
ds4_gpu_tensor_offset(up),
midbuf,
ds4_gpu_tensor_offset(mid),
selected_exec_buf,
selected_exec_off,
weightsbuf,
ds4_gpu_tensor_offset(weights),
gate_smem,
2,
false);
}
if (ok) {
ok = ds4_gpu_encode_mul_mv_addr_q2_sum6_masked(cb,
g_moe_mul_mv_addr_q2_k_sum6_masked_pipeline,
&down_args,
&all_down_args,
stream_slot_entries,
stream_down_addr_buf,
midbuf,
ds4_gpu_tensor_offset(mid),
outbuf,
ds4_gpu_tensor_offset(out),
selected_exec_buf,
selected_exec_off,
down_smem,
2);
}
stream_expert_split_completed = ok;
if (stream_split_timing) {
ds4_gpu_stream_expert_timing_note_split(
stream_expert_resident_mask,
stream_expert_missing_mask,
stream_split_resident_ms,
stream_split_missing_ms);
ds4_gpu_stream_expert_timing_note_split_missing_detail(
stream_split_missing_load_ms,
stream_split_missing_slot_ms,
stream_split_missing_prune_ms,
stream_split_missing_addr_ms,
stream_split_missing_wait_ms);
}
if (stream_split_profile) {
fprintf(stderr,
"ds4: Metal streaming expert split layer=%u "
"resident=0x%02x missing=0x%02x resident_submit=%.3f ms "
"missing_bind=%.3f ms\n",
layer_index,
stream_expert_resident_mask,
stream_expert_missing_mask,
stream_split_resident_ms,
stream_split_missing_ms);
}
} else {
ds4_gpu_stream_expert_split_args split_args = {
.active_mask = 0x3fu,
.accumulate = 0u,
};
ok = ds4_gpu_encode_mul_mv_addr_iq2_pair_swiglu_masked(cb,
g_moe_mul_mv_addr_iq2_xxs_pair_swiglu_masked_pipeline,
&gate_args,
&act_args,
&split_args,
stream_slot_entries,
stream_gate_addr_buf,
stream_up_addr_buf,
xbuf,
ds4_gpu_tensor_offset(x),
gatebuf,
ds4_gpu_tensor_offset(gate),
upbuf,
ds4_gpu_tensor_offset(up),
midbuf,
ds4_gpu_tensor_offset(mid),
selected_exec_buf,
selected_exec_off,
weightsbuf,
ds4_gpu_tensor_offset(weights),
gate_smem,
2,
false);
}
} else {
ok = ds4_gpu_encode_mul_mv_addr_iq2_pair_swiglu(cb,
g_moe_mul_mv_addr_iq2_xxs_pair_swiglu_pipeline,
&gate_args,
&act_args,
stream_slot_entries,
n_expert,
stream_gate_addr_buf,
stream_up_addr_buf,
xbuf,
ds4_gpu_tensor_offset(x),
gatebuf,
ds4_gpu_tensor_offset(gate),
upbuf,
ds4_gpu_tensor_offset(up),
midbuf,
ds4_gpu_tensor_offset(mid),
selected_exec_buf,
selected_exec_off,
weightsbuf,
ds4_gpu_tensor_offset(weights),
gate_smem,
2,
false,
nil,
nil);
}
} else {
ok = (!use_stream_expert_cache ||
ds4_gpu_stream_expert_cache_mark_entries_inflight(
stream_slot_entries,
n_expert,
0)) &&
ds4_gpu_encode_mul_mv_slots6_pair_swiglu(cb,
slots_pair_swiglu_pipeline,
&gate_args,
&act_args,
gate_slot_bufs,
gate_slot_offsets,
up_slot_bufs,
up_slot_offsets,
xbuf,
ds4_gpu_tensor_offset(x),
gatebuf,
ds4_gpu_tensor_offset(gate),
upbuf,
ds4_gpu_tensor_offset(up),
midbuf,
ds4_gpu_tensor_offset(mid),
weightsbuf,
ds4_gpu_tensor_offset(weights),
gate_smem,
2,
false);
}
} else if (fuse_pair_swiglu) {
ds4_gpu_dsv4_moe_swiglu_weight_args act_args = {
.width = expert_mid_dim,
.rows = pair_rows,
.gate_row_stride = (uint64_t)expert_mid_dim * sizeof(float),
.up_row_stride = (uint64_t)expert_mid_dim * sizeof(float),
.mid_row_stride = (uint64_t)expert_mid_dim * sizeof(float),
.weight_stride = sizeof(float),
.write_clamped = 0,
.clamp_value = clamp,
};
ok = ds4_gpu_encode_mul_mv_id_pair_swiglu(cb,
pair_swiglu_pipeline,
&gate_args,
&act_args,
gate_buf,
(NSUInteger)gate_inner,
up_buf,
(NSUInteger)up_inner,
xbuf,
ds4_gpu_tensor_offset(x),
gatebuf,
ds4_gpu_tensor_offset(gate),
upbuf,
ds4_gpu_tensor_offset(up),
midbuf,
ds4_gpu_tensor_offset(mid),
selectedbuf,
ds4_gpu_tensor_offset(selected),
weightsbuf,
ds4_gpu_tensor_offset(weights),
gate_smem,
pair_swiglu_nsg,
false);
} else if (!g_quality_mode &&
gate_type == DS4_METAL_TENSOR_IQ2_XXS &&
g_moe_mul_mv_id_iq2_xxs_pair_pipeline) {
ok = ds4_gpu_encode_mul_mv_id_pair(cb,
g_moe_mul_mv_id_iq2_xxs_pair_pipeline,
&gate_args,
gate_buf,
(NSUInteger)gate_inner,
up_buf,
(NSUInteger)up_inner,
xbuf,
ds4_gpu_tensor_offset(x),
gatebuf,
ds4_gpu_tensor_offset(gate),
upbuf,
ds4_gpu_tensor_offset(up),
selectedbuf,
ds4_gpu_tensor_offset(selected),
gate_smem,
2,
false);
} else if (!g_quality_mode &&
gate_type == DS4_METAL_TENSOR_Q4_K &&
g_moe_mul_mv_id_q4_k_pair_pipeline) {
ok = ds4_gpu_encode_mul_mv_id_pair(cb,
g_moe_mul_mv_id_q4_k_pair_pipeline,
&gate_args,
gate_buf,
(NSUInteger)gate_inner,
up_buf,
(NSUInteger)up_inner,
xbuf,
ds4_gpu_tensor_offset(x),
gatebuf,
ds4_gpu_tensor_offset(gate),
upbuf,
ds4_gpu_tensor_offset(up),
selectedbuf,
ds4_gpu_tensor_offset(selected),
gate_smem,
2,
false);
} else {
ok = ds4_gpu_encode_mul_mv_id(cb,
gate_mv_pipeline,
&gate_args,
gate_buf,
(NSUInteger)gate_inner,
xbuf,
ds4_gpu_tensor_offset(x),
gatebuf,
ds4_gpu_tensor_offset(gate),
selectedbuf,
ds4_gpu_tensor_offset(selected),
gate_smem,
gate_nsg,
gate_rows_per_group_is_nr0) &&
ds4_gpu_encode_mul_mv_id(cb,
gate_mv_pipeline,
&gate_args,
up_buf,
(NSUInteger)up_inner,
xbuf,
ds4_gpu_tensor_offset(x),
upbuf,
ds4_gpu_tensor_offset(up),
selectedbuf,
ds4_gpu_tensor_offset(selected),
gate_smem,
gate_nsg,
gate_rows_per_group_is_nr0);
}
DS4_METAL_PROFILE_MOE_ONE_STAGE("gate_up");
if (ok && (!fuse_pair_swiglu || use_q4_group24_experts)) {
ok = ds4_gpu_encode_moe_swiglu_weight(cb,
gatebuf,
ds4_gpu_tensor_offset(gate),
upbuf,
ds4_gpu_tensor_offset(up),
midbuf,
ds4_gpu_tensor_offset(mid),
weightsbuf,
ds4_gpu_tensor_offset(weights),
expert_mid_dim,
pair_rows,
clamp,
false);
}
DS4_METAL_PROFILE_MOE_ONE_STAGE("activation_weight");
if (ok && g_parallel_q8_pending) {
ok = ds4_gpu_parallel_q8_matvec_encode_pending(cb, midbuf) != 0;
}
id<MTLBuffer> down_dst = n_expert == 1 ? outbuf : (expertsbuf ? expertsbuf : g_moe_down_scratch_buffer);
NSUInteger down_dst_off = n_expert == 1 ? ds4_gpu_tensor_offset(out) :
(expertsbuf ? ds4_gpu_tensor_offset(experts) : 0);
if (ok && stream_expert_split_completed) {
/* The split path already wrote the resident partial output and
* accumulated the missing experts into out. */
} else if (ok && use_q4_grouped_experts) {
bool first_group = true;
for (uint32_t expert_base = 0; ok && expert_base < n_total_expert; expert_base += q4_expert_group_size) {
const uint32_t expert_count =
q4_expert_group_size < n_total_expert - expert_base ?
q4_expert_group_size : n_total_expert - expert_base;
if ((uint64_t)expert_base > UINT64_MAX / down_expert_bytes ||
(uint64_t)expert_count > UINT64_MAX / down_expert_bytes) {
ok = 0;
break;
}
const uint64_t group_rel = (uint64_t)expert_base * down_expert_bytes;
const uint64_t group_bytes = (uint64_t)expert_count * down_expert_bytes;
if (group_rel > UINT64_MAX - down_offset) {
ok = 0;
break;
}
uint64_t down_group_inner = 0;
id<MTLBuffer> down_group_buf =
q4_grouped_cache_views ?
ds4_gpu_wrap_model_exact_range(model_map,
model_size,
down_offset + group_rel,
group_bytes,
&down_group_inner) :
ds4_gpu_wrap_model_exact_range_transient(model_map,
model_size,
down_offset + group_rel,
group_bytes,
&down_group_inner);
if (!down_group_buf) {
ok = 0;
break;
}
ds4_gpu_moe_expert_group_args group_args = {
.expert_base = expert_base,
.expert_count = expert_count,
.accumulate = first_group ? 0u : 1u,
.pad0 = 0,
};
ok = ds4_gpu_encode_mul_mv_group_q4_sum6(cb,
g_moe_mul_mv_group_q4_k_sum6_pipeline,
&down_args,
&group_args,
down_group_buf,
(NSUInteger)down_group_inner,
midbuf,
ds4_gpu_tensor_offset(mid),
outbuf,
ds4_gpu_tensor_offset(out),
selectedbuf,
ds4_gpu_tensor_offset(selected),
down_smem,
2);
first_group = false;
}
} else if (ok && use_q4_expert_address_table) {
ok = ds4_gpu_encode_mul_mv_addr_q4_sum6(cb,
g_moe_mul_mv_addr_q4_k_sum6_pipeline,
&down_args,
down_table,
midbuf,
ds4_gpu_tensor_offset(mid),
outbuf,
ds4_gpu_tensor_offset(out),
selectedbuf,
ds4_gpu_tensor_offset(selected),
down_smem,
2);
} else if (ok && use_q4_expert_table) {
ok = ds4_gpu_encode_mul_mv_table_q4_sum6(cb,
g_moe_mul_mv_table_q4_k_sum6_pipeline,
&down_args,
down_table,
midbuf,
ds4_gpu_tensor_offset(mid),
outbuf,
ds4_gpu_tensor_offset(out),
selectedbuf,
ds4_gpu_tensor_offset(selected),
down_smem,
2,
q4_table_queue_residency);
} else if (ok && use_q4_group6_experts) {
ok = ds4_gpu_encode_mul_mv_group6_sum6(cb,
g_moe_mul_mv_group6_q4_k_sum6_pipeline,
&down_args,
down_group6_bufs,
down_group6_offsets,
midbuf,
ds4_gpu_tensor_offset(mid),
outbuf,
ds4_gpu_tensor_offset(out),
selectedbuf,
ds4_gpu_tensor_offset(selected),
down_smem,
2);
} else if (ok && use_q4_group8_experts) {
ok = ds4_gpu_encode_mul_mv_group8_sum6(cb,
g_moe_mul_mv_group8_q4_k_sum6_pipeline,
&down_args,
down_group8_bufs,
down_group8_offsets,
midbuf,
ds4_gpu_tensor_offset(mid),
outbuf,
ds4_gpu_tensor_offset(out),
selectedbuf,
ds4_gpu_tensor_offset(selected),
down_smem,
2);
} else if (ok && use_q4_group24_experts) {
ok = ds4_gpu_encode_mul_mv_group24_sum6(cb,
g_moe_mul_mv_group24_q4_k_sum6_pipeline,
&down_args,
down_group24_bufs,
down_group24_offsets,
midbuf,
ds4_gpu_tensor_offset(mid),
outbuf,
ds4_gpu_tensor_offset(out),
selectedbuf,
ds4_gpu_tensor_offset(selected),
down_smem,
2);
} else if (ok && (use_q4_gather_slots || use_selected_slots)) {
if (use_stream_expert_addr_table) {
if (down_type == DS4_METAL_TENSOR_IQ2_XXS) {
ok = ds4_gpu_encode_mul_mv_addr_iq2(cb,
g_moe_mul_mv_addr_iq2_xxs_pipeline,
&down_args,
stream_slot_entries,
n_expert,
stream_down_addr_buf,
midbuf,
ds4_gpu_tensor_offset(mid),
down_dst,
down_dst_off,
selected_exec_buf,
selected_exec_off,
down_smem,
2,
false, 2, nil);
} else if (use_stream_expert_masked_addr_table) {
ds4_gpu_stream_expert_split_args split_args = {
.active_mask = 0x3fu,
.accumulate = 0u,
};
ok = ds4_gpu_encode_mul_mv_addr_q2_sum6_masked(cb,
g_moe_mul_mv_addr_q2_k_sum6_masked_pipeline,
&down_args,
&split_args,
stream_slot_entries,
stream_down_addr_buf,
midbuf,
ds4_gpu_tensor_offset(mid),
outbuf,
ds4_gpu_tensor_offset(out),
selected_exec_buf,
selected_exec_off,
down_smem,
2);
} else {
ok = ds4_gpu_encode_mul_mv_addr_q2_sum6(cb,
g_moe_mul_mv_addr_q2_k_sum6_pipeline,
&down_args,
stream_slot_entries,
n_expert,
stream_down_addr_buf,
midbuf,
ds4_gpu_tensor_offset(mid),
outbuf,
ds4_gpu_tensor_offset(out),
selected_exec_buf,
selected_exec_off,
down_smem,
2,
nil);
}
} else {
ok = (!use_stream_expert_cache ||
ds4_gpu_stream_expert_cache_mark_entries_inflight(
stream_slot_entries,
n_expert,
0)) &&
ds4_gpu_encode_mul_mv_slots6_sum6(cb,
slots_sum6_pipeline,
&down_args,
down_slot_bufs,
down_slot_offsets,
midbuf,
ds4_gpu_tensor_offset(mid),
outbuf,
ds4_gpu_tensor_offset(out),
down_smem,
2);
}
} else if (ok && direct_down_sum) {
ok = ds4_gpu_encode_mul_mv_id_sum6(cb,
down_sum6_pipeline,
&down_args,
down_buf,
(NSUInteger)down_inner,
midbuf,
ds4_gpu_tensor_offset(mid),
outbuf,
ds4_gpu_tensor_offset(out),
selectedbuf,
ds4_gpu_tensor_offset(selected),
add_in ? ds4_gpu_tensor_buffer(add_in) : nil,
add_in ? ds4_gpu_tensor_offset(add_in) : 0,
down_smem,
down_sum6_nsg);
} else if (ok) {
ok = ds4_gpu_encode_mul_mv_id(cb,
down_mv_pipeline,
&down_args,
down_buf,
(NSUInteger)down_inner,
midbuf,
ds4_gpu_tensor_offset(mid),
down_dst,
down_dst_off,
selectedbuf,
ds4_gpu_tensor_offset(selected),
down_smem,
down_nsg,
down_rows_per_group_is_nr0);
}
DS4_METAL_PROFILE_MOE_ONE_STAGE("down");
if (ok && n_expert > 1 && !direct_down_sum && !stream_expert_split_completed) {
ok = ds4_gpu_encode_moe_sum_experts(cb,
down_dst,
down_dst_off,
outbuf,
ds4_gpu_tensor_offset(out),
out_dim,
n_expert,
n_tokens);
}
DS4_METAL_PROFILE_MOE_ONE_STAGE("sum");
if (ok && g_parallel_ffn_mode == 2 && g_parallel_ffn_stage == 1) {
ok = ds4_gpu_parallel_ffn_encode_second_stage(cb) != 0;
}
if (!ok) { if (getenv("DS4_GLM_TP_DEBUG")) fprintf(stderr, "ds4: routed_moe_one silent return at line %d\n", 34395); return 0; }
if (!ds4_gpu_finish_command_buffer(cb, owned, "routed tensor MoE")) { if (getenv("DS4_GLM_TP_DEBUG")) fprintf(stderr, "ds4: routed_moe_one silent return at line %d\n", 34397); return 0; }
if (q4_grouped_boundary || q4_exact_boundary || q4_table_boundary) {
if (ds4_gpu_end_commands() == 0 || ds4_gpu_begin_commands() == 0) {
if (getenv("DS4_GLM_TP_DEBUG")) fprintf(stderr, "ds4: routed_moe_one silent return at line %d\n", 34372);
return 0;
}
}
#undef DS4_METAL_PROFILE_MOE_ONE_STAGE
}
parallel_ffn_scope = NO;
return 1;
}
int ds4_gpu_routed_moe_batch_tensor(
ds4_gpu_tensor *out,
ds4_gpu_tensor *gate,
ds4_gpu_tensor *up,
ds4_gpu_tensor *mid,
ds4_gpu_tensor *experts,
const void *model_map,
uint64_t model_size,
uint64_t gate_offset,
uint64_t up_offset,
uint64_t down_offset,
uint32_t gate_type,
uint32_t down_type,
uint64_t gate_expert_bytes,
uint64_t gate_row_bytes,
uint64_t down_expert_bytes,
uint64_t down_row_bytes,
uint32_t expert_in_dim,
uint32_t expert_mid_dim,
uint32_t out_dim,
const ds4_gpu_tensor *selected,
const ds4_gpu_tensor *weights,
uint32_t n_total_expert,
uint32_t n_expert,
float clamp,
const ds4_gpu_tensor *x,
uint32_t layer_index,
uint32_t n_tokens,
bool *mid_is_f16,
bool force_resident) {
if (!g_initialized && !ds4_gpu_init()) return 0;
/* TP sharding (see ds4_gpu_routed_moe_one_tensor): bind from the owned
* expert range and rebase ids in the kernels. */
uint32_t first_expert = 0;
uint32_t n_bind_expert = 0;
ds4_gpu_tp_expert_range(n_total_expert, &first_expert, &n_bind_expert);
const int32_t tp_expert_base_host = (int32_t)first_expert;
const int32_t tp_world_host = g_tp_full_expert_bind ? 1 : (int32_t)g_tp_split_world;
gate_offset += (uint64_t)first_expert * gate_expert_bytes;
up_offset += (uint64_t)first_expert * gate_expert_bytes;
down_offset += (uint64_t)first_expert * down_expert_bytes;
if (!out || !gate || !up || !mid || !x || !model_map || !selected || !weights ||
n_tokens == 0 || n_total_expert == 0 || n_expert == 0 ||
n_expert > DS4_METAL_MAX_ROUTED_EXPERT_USED) {
return 0;
}
if (gate_expert_bytes == 0 || down_expert_bytes == 0 ||
gate_row_bytes == 0 || down_row_bytes == 0) {
return 0;
}
if ((expert_in_dim % 256u) != 0 || (expert_mid_dim % 256u) != 0) return 0;
if ((uint64_t)n_total_expert > UINT64_MAX / gate_expert_bytes ||
(uint64_t)n_total_expert > UINT64_MAX / down_expert_bytes) {
fprintf(stderr, "ds4: Metal routed batch MoE tensor byte size overflow\n");
return 0;
}
const uint64_t gate_tensor_bytes = (uint64_t)n_bind_expert * gate_expert_bytes;
const uint64_t down_tensor_bytes = (uint64_t)n_bind_expert * down_expert_bytes;
/*
* PRO Q4 routed expert tensors are multi-GiB per layer. A one-token
* layer-slice prefill should use the one-token path so it can either bind
* exact tensor views with GPU-selected IDs or fall back to selected-expert
* views. Keep this guarded by tensor size so Flash and mixed-Flash Q4 keep
* their existing fast path.
*/
const uint64_t q4_selected_min_tensor_bytes = 2ull * 1024ull * 1024ull * 1024ull;
const bool can_single_token_q4_grouped =
getenv("DS4_METAL_ENABLE_Q4_GROUPED_EXPERTS") != NULL &&
getenv("DS4_METAL_DISABLE_Q4_GROUPED_EXPERTS") == NULL &&
g_moe_mul_mv_group_q4_k_pair_swiglu_pipeline != nil &&
g_moe_mul_mv_group_q4_k_sum6_pipeline != nil;
const bool can_single_token_q4_selected_slots =
ds4_gpu_q4_selected_paths_allowed() &&
getenv("DS4_METAL_DISABLE_Q4_SELECTED_EXPERT_VIEWS") == NULL &&
g_moe_mul_mv_slots6_q4_k_pair_swiglu_pipeline != nil &&
g_moe_mul_mv_slots6_q4_k_sum6_pipeline != nil;
const bool can_single_token_q4_group6 =
n_total_expert == 384 &&
getenv("DS4_METAL_ENABLE_Q4_GROUP6_EXPERT_TABLE") != NULL &&
getenv("DS4_METAL_DISABLE_Q4_GROUP6_EXPERT_TABLE") == NULL &&
g_moe_mul_mv_group6_q4_k_pair_swiglu_pipeline != nil &&
g_moe_mul_mv_group6_q4_k_sum6_pipeline != nil;
const bool can_single_token_q4_group8 =
n_total_expert == 384 &&
getenv("DS4_METAL_ENABLE_Q4_GROUP8_EXPERT_TABLE") != NULL &&
getenv("DS4_METAL_DISABLE_Q4_GROUP8_EXPERT_TABLE") == NULL &&
g_moe_mul_mv_group8_q4_k_pair_swiglu_pipeline != nil &&
g_moe_mul_mv_group8_q4_k_sum6_pipeline != nil;
const bool can_single_token_q4_group24 =
n_total_expert == 384 &&
getenv("DS4_METAL_ENABLE_Q4_GROUP24_EXPERT_TABLE") != NULL &&
getenv("DS4_METAL_DISABLE_Q4_GROUP24_EXPERT_TABLE") == NULL &&
g_moe_mul_mv_group24_q4_k_id_pipeline != nil &&
g_moe_mul_mv_group24_q4_k_sum6_pipeline != nil;
const uint64_t max_buffer_len = g_device ? (uint64_t)[g_device maxBufferLength] : 0;
const bool can_single_token_q4_exact_tensor_id =
n_total_expert == 384 &&
max_buffer_len != 0 &&
gate_tensor_bytes <= max_buffer_len &&
down_tensor_bytes <= max_buffer_len &&
getenv("DS4_METAL_ENABLE_Q4_EXACT_TENSOR_ID") != NULL &&
getenv("DS4_METAL_DISABLE_Q4_EXACT_TENSOR_ID") == NULL;
const bool enable_single_token_q4_expert_table =
getenv("DS4_METAL_ENABLE_Q4_EXPERT_TABLE") != NULL ||
ds4_gpu_pro_q4_expert_table_auto_enabled(n_total_expert,
n_expert,
gate_tensor_bytes,
down_tensor_bytes);
const bool can_single_token_q4_expert_table =
n_total_expert == 384 &&
enable_single_token_q4_expert_table &&
getenv("DS4_METAL_DISABLE_Q4_EXPERT_TABLE") == NULL &&
g_moe_mul_mv_table_q4_k_pair_swiglu_pipeline != nil &&
g_moe_mul_mv_table_q4_k_sum6_pipeline != nil &&
g_moe_table_q4_pair_gate_encoder != nil &&
g_moe_table_q4_pair_up_encoder != nil &&
g_moe_table_q4_sum_down_encoder != nil;
const bool can_single_token_q4_expert_address_table =
n_total_expert == 384 &&
getenv("DS4_METAL_ENABLE_Q4_EXPERT_ADDRESS_TABLE") != NULL &&
getenv("DS4_METAL_DISABLE_Q4_EXPERT_ADDRESS_TABLE") == NULL &&
g_moe_mul_mv_addr_q4_k_pair_swiglu_pipeline != nil &&
g_moe_mul_mv_addr_q4_k_sum6_pipeline != nil;
const bool use_single_token_q4_one_tensor =
gate_type == DS4_METAL_TENSOR_Q4_K &&
down_type == DS4_METAL_TENSOR_Q4_K &&
n_tokens == 1 &&
n_expert == 6 &&
n_total_expert >= 128 &&
(g_ssd_streaming_mode ||
(gate_tensor_bytes >= q4_selected_min_tensor_bytes &&
down_tensor_bytes >= q4_selected_min_tensor_bytes)) &&
!g_quality_mode &&
getenv("DS4_METAL_MOE_WRITE_CLAMPED_ACT") == NULL &&
getenv("DS4_METAL_DISABLE_ROUTED_PAIR_SWIGLU_FUSION") == NULL &&
(can_single_token_q4_grouped ||
can_single_token_q4_group6 ||
can_single_token_q4_group8 ||
can_single_token_q4_group24 ||
can_single_token_q4_exact_tensor_id ||
can_single_token_q4_expert_address_table ||
can_single_token_q4_expert_table ||
can_single_token_q4_selected_slots);
const bool use_single_token_mxfp4_one_tensor =
gate_type == DS4_METAL_TENSOR_MXFP4 &&
down_type == DS4_METAL_TENSOR_MXFP4 &&
n_tokens == 1 &&
n_expert == 6 &&
n_total_expert >= 128 &&
!g_quality_mode &&
getenv("DS4_METAL_MOE_WRITE_CLAMPED_ACT") == NULL &&
getenv("DS4_METAL_DISABLE_ROUTED_PAIR_SWIGLU_FUSION") == NULL &&
g_moe_mul_mv_id_mxfp4_pair_swiglu_pipeline != nil &&
g_moe_mul_mv_id_mxfp4_sum6_pipeline != nil &&
(!g_ssd_streaming_mode ||
(g_moe_mul_mv_slots6_mxfp4_pair_swiglu_pipeline != nil &&
g_moe_mul_mv_slots6_mxfp4_sum6_pipeline != nil));
if (use_single_token_q4_one_tensor || use_single_token_mxfp4_one_tensor) {
if (mid_is_f16) *mid_is_f16 = false;
/* the one-token path binds the owned expert range itself */
return ds4_gpu_routed_moe_one_tensor(out,
gate,
up,
mid,
experts,
model_map,
model_size,
gate_offset - (uint64_t)first_expert * gate_expert_bytes,
up_offset - (uint64_t)first_expert * gate_expert_bytes,
down_offset - (uint64_t)first_expert * down_expert_bytes,
gate_type,
down_type,
gate_expert_bytes,
gate_row_bytes,
down_expert_bytes,
down_row_bytes,
expert_in_dim,
expert_mid_dim,
out_dim,
selected,
weights,
n_total_expert,
n_expert,
clamp,
x,
NULL,
layer_index,
force_resident);
}
@autoreleasepool {
id<MTLBuffer> xbuf = ds4_gpu_tensor_buffer(x);
id<MTLBuffer> gatebuf = ds4_gpu_tensor_buffer(gate);
id<MTLBuffer> upbuf = ds4_gpu_tensor_buffer(up);
id<MTLBuffer> midbuf = ds4_gpu_tensor_buffer(mid);
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out);
id<MTLBuffer> expertsbuf = ds4_gpu_tensor_buffer(experts);
id<MTLBuffer> selectedbuf = ds4_gpu_tensor_buffer(selected);
id<MTLBuffer> weightsbuf = ds4_gpu_tensor_buffer(weights);
const uint64_t x_bytes = (uint64_t)n_tokens * expert_in_dim * sizeof(float);
const uint64_t mid_bytes = (uint64_t)n_tokens * n_expert * expert_mid_dim * sizeof(float);
const uint64_t out_bytes = (uint64_t)n_tokens * out_dim * sizeof(float);
const uint64_t selected_bytes = (uint64_t)n_tokens * n_expert * sizeof(int);
const uint64_t weights_bytes = (uint64_t)n_tokens * n_expert * sizeof(float);
if (!xbuf || !gatebuf || !upbuf || !midbuf || !outbuf || !selectedbuf || !weightsbuf ||
ds4_gpu_tensor_bytes(x) < x_bytes ||
ds4_gpu_tensor_bytes(gate) < mid_bytes ||
ds4_gpu_tensor_bytes(up) < mid_bytes ||
ds4_gpu_tensor_bytes(mid) < mid_bytes ||
ds4_gpu_tensor_bytes(out) < out_bytes ||
ds4_gpu_tensor_bytes(selected) < selected_bytes ||
ds4_gpu_tensor_bytes(weights) < weights_bytes) {
fprintf(stderr, "ds4: Metal routed batch MoE received undersized activation buffers\n");
return 0;
}
uint64_t gate_inner = 0;
uint64_t up_inner = 0;
uint64_t down_inner = 0;
id<MTLBuffer> gate_buf = nil;
id<MTLBuffer> up_buf = nil;
id<MTLBuffer> down_buf = nil;
DS4MetalQ4ExpertTable *gate_table = nil;
DS4MetalQ4ExpertTable *up_table = nil;
DS4MetalQ4ExpertTable *down_table = nil;
id q4_table_layer_residency = nil;
id<MTLBuffer> stream_gate_addr_buf = nil;
id<MTLBuffer> stream_up_addr_buf = nil;
id<MTLBuffer> stream_down_addr_buf = nil;
id<MTLBuffer> stream_overflow_gate = nil;
id<MTLBuffer> stream_overflow_up = nil;
id<MTLBuffer> stream_overflow_down = nil;
ds4_gpu_stream_expert_cache_entry
*stream_resources[DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT] = { NULL };
uint32_t stream_resource_count = 0;
uint32_t stream_unique = 0;
const uint32_t pair_rows = n_tokens * n_expert;
const uint64_t down_scratch_bytes = (uint64_t)pair_rows * out_dim * sizeof(float);
const uint32_t gate_nr0 = ds4_gpu_routed_mv_nr0(gate_type);
const uint32_t down_nr0 = ds4_gpu_routed_mv_nr0(down_type);
id<MTLComputePipelineState> gate_mv_pipeline = ds4_gpu_routed_mv_pipeline(gate_type);
id<MTLComputePipelineState> down_mv_pipeline = ds4_gpu_routed_mv_pipeline(down_type);
id<MTLComputePipelineState> gate_mm_pipeline = nil;
id<MTLComputePipelineState> up_mm_pipeline = nil;
id<MTLComputePipelineState> down_mm_pipeline = nil;
id<MTLComputePipelineState> pair_swiglu_mm_pipeline = nil;
if (gate_nr0 == 0 || down_nr0 == 0 || !gate_mv_pipeline || !down_mv_pipeline) {
fprintf(stderr, "ds4: unsupported Metal routed batch MoE quant types gate=%u down=%u\n",
gate_type, down_type);
return 0;
}
const bool use_iq2_batch_selected_addr =
!force_resident &&
ds4_gpu_stream_prefill_batch_selected_addr_enabled(n_tokens,
n_total_expert,
n_expert,
gate_type,
down_type) &&
n_tokens > 1 &&
n_total_expert <= DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT &&
!g_quality_mode &&
getenv("DS4_METAL_MOE_WRITE_CLAMPED_ACT") == NULL &&
getenv("DS4_METAL_DISABLE_ROUTED_PAIR_SWIGLU_FUSION") == NULL &&
g_moe_mul_mv_addr_iq2_xxs_pair_swiglu_pipeline != nil &&
g_moe_mul_mv_addr_q2_k_sum6_pipeline != nil;
/* Small full-GLM appends reuse cached bytes with the same MV/MM
* arithmetic. Large prefills retain sequential whole-layer reads. */
const bool use_iq2_cached_batch =
g_ssd_streaming_mode && !force_resident && g_tp_split_world == 1 &&
!ds4_gpu_glm_streaming_prefill_full_layer_active() &&
gate_type == DS4_METAL_TENSOR_IQ2_XXS &&
down_type == DS4_METAL_TENSOR_IQ2_XXS && n_expert == 8 &&
n_tokens < 256 &&
n_total_expert <= DS4_METAL_STREAM_EXPERT_CACHE_MAX_EXPERT &&
ds4_gpu_stream_expert_cache_note_expert_size(gate_expert_bytes,
down_expert_bytes) &&
ds4_gpu_stream_expert_cache_configured_count() >= n_total_expert &&
getenv("DS4_METAL_GLM_DISABLE_STREAMING_EXPERT_CACHE") == NULL &&
getenv("DS4_METAL_DISABLE_IQ2_STREAM_ADDR_TABLE") == NULL &&
getenv("DS4_METAL_DISABLE_STREAMING_EXPERT_ADDR_TABLE") == NULL &&
getenv("DS4_METAL_DISABLE_ROUTED_PAIR_SWIGLU_FUSION") == NULL &&
getenv("DS4_METAL_DISABLE_TINY_PAIR_SWIGLU_FUSION") == NULL &&
getenv("DS4_METAL_MOE_WRITE_CLAMPED_ACT") == NULL &&
getenv("DS4_METAL_DISABLE_STREAMING_PREFILL_BATCH_SELECTED_ADDR") == NULL;
const bool use_cached_batch = use_iq2_batch_selected_addr || use_iq2_cached_batch;
ds4_gpu_mul_mv_id_args gate_args =
ds4_gpu_make_mul_mv_id_args(expert_in_dim, expert_mid_dim, n_total_expert,
gate_row_bytes, gate_expert_bytes,
1, n_expert, n_tokens, gate_nr0);
gate_args.tp_rank = g_tp_split_rank;
gate_args.tp_world = tp_world_host;
gate_args.tp_expert_base = tp_expert_base_host;
ds4_gpu_mul_mv_id_args down_args =
ds4_gpu_make_mul_mv_id_args(expert_mid_dim, out_dim, n_total_expert,
down_row_bytes, down_expert_bytes,
n_expert, n_expert, n_tokens, down_nr0);
down_args.tp_rank = g_tp_split_rank;
down_args.tp_world = tp_world_host;
down_args.tp_expert_base = tp_expert_base_host;
const bool q4_batch_expert_table_auto =
gate_type == DS4_METAL_TENSOR_Q4_K &&
down_type == DS4_METAL_TENSOR_Q4_K &&
ds4_gpu_pro_q4_expert_table_auto_enabled(n_total_expert,
n_expert,
gate_tensor_bytes,
down_tensor_bytes);
const bool q4_batch_table_queue_residency =
ds4_gpu_q4_table_queue_residency_enabled(q4_batch_expert_table_auto);
const bool enable_q4_batch_expert_table =
getenv("DS4_METAL_ENABLE_Q4_BATCH_EXPERT_TABLE") != NULL ||
getenv("DS4_METAL_ENABLE_Q4_EXPERT_TABLE") != NULL ||
q4_batch_expert_table_auto;
const bool use_q4_batch_expert_table =
!g_ssd_streaming_mode &&
gate_type == DS4_METAL_TENSOR_Q4_K &&
down_type == DS4_METAL_TENSOR_Q4_K &&
n_expert == 6 &&
n_tokens > 1 &&
n_total_expert == 384 &&
gate_tensor_bytes >= q4_selected_min_tensor_bytes &&
down_tensor_bytes >= q4_selected_min_tensor_bytes &&
!g_quality_mode &&
getenv("DS4_METAL_MOE_WRITE_CLAMPED_ACT") == NULL &&
getenv("DS4_METAL_DISABLE_ROUTED_PAIR_SWIGLU_FUSION") == NULL &&
getenv("DS4_METAL_DISABLE_Q4_BATCH_EXPERT_TABLE") == NULL &&
getenv("DS4_METAL_DISABLE_Q4_EXPERT_TABLE") == NULL &&
g_moe_mul_mv_table_q4_k_pair_swiglu_pipeline != nil &&
g_moe_mul_mv_table_q4_k_sum6_pipeline != nil &&
g_moe_table_q4_pair_gate_encoder != nil &&
g_moe_table_q4_pair_up_encoder != nil &&
g_moe_table_q4_sum_down_encoder != nil &&
enable_q4_batch_expert_table &&
(getenv("DS4_METAL_Q4_TABLE_USE_RESOURCES") != NULL ||
getenv("DS4_METAL_Q4_TABLE_RESIDENCY_SET") != NULL ||
q4_batch_table_queue_residency ||
ds4_gpu_q4_table_model_residency_enabled());
const bool use_mm_id =
!use_q4_batch_expert_table &&
!use_iq2_batch_selected_addr &&
n_tokens >= 32u &&
ds4_gpu_mul_mm_id_map0_name(n_expert) != NULL;
bool mm_id_mpp_active = false;
/* Reuse half activations across gate/up and all output tiles. V4.1
* admits 512 MiB for 8k tiles; other shapes retain the 256 MiB limit. */
const uint64_t packed_rows = (uint64_t)n_tokens * n_expert;
const uint64_t packed_header = ((uint64_t)n_total_expert + 31u) / 32u * 32u * sizeof(uint32_t);
const uint64_t packed_width = expert_in_dim > expert_mid_dim ? expert_in_dim : expert_mid_dim;
const bool v41_packed = n_total_expert == 384u && expert_in_dim == 5120u &&
expert_mid_dim == 2304u;
const uint64_t packed_limit = (v41_packed ? UINT64_C(512) : UINT64_C(256)) << 20;
const bool use_packed_mpp = use_mm_id && !g_quality_mode &&
(!g_ssd_streaming_mode || force_resident) && n_tokens >= 512u &&
n_tokens <= (v41_packed ? 8192u : 4096u) &&
n_expert == 6u && n_total_expert <= 384u &&
expert_in_dim <= 5120u && expert_mid_dim <= 4096u &&
packed_header + packed_rows * packed_width * sizeof(uint16_t) <= packed_limit &&
ds4_gpu_routed_mm_mpp_mask() == 7 &&
((gate_type == DS4_METAL_TENSOR_IQ2_XXS && down_type == DS4_METAL_TENSOR_Q2_K) ||
(g_tp_split_world == 2 && gate_type == DS4_METAL_TENSOR_MXFP4 &&
down_type == DS4_METAL_TENSOR_MXFP4 &&
getenv("DS4_METAL_DISABLE_MXFP4_MM_ID_MPP") == NULL)) &&
getenv("DS4_METAL_DISABLE_ROUTED_MPP_PACKED") == NULL;
const bool packed_m32n128 = use_packed_mpp && n_tokens >= 8192u &&
n_total_expert == 384u && expert_in_dim == 5120u && expert_mid_dim == 2304u &&
gate_type == DS4_METAL_TENSOR_IQ2_XXS && down_type == DS4_METAL_TENSOR_Q2_K &&
!getenv("DS4_METAL_DISABLE_V41_PACKED_M32N128");
/* Tiny verification and session batches reuse the fused decode kernels.
* V4.1 needs scalar reduction order before its BF16/router boundaries
* through eight rows, for both routed recipes. Larger prefills retain
* the grouped matmul path. */
static int tiny_batch_off = -1;
if (tiny_batch_off < 0) tiny_batch_off = getenv("DS4_METAL_DISABLE_V41_MXFP4_TINY_BATCH") != NULL;
const bool v41_decode_batch = n_tokens <= 8u && n_total_expert == 384u &&
n_expert == 6u && expert_in_dim == 5120u && expert_mid_dim == 2304u &&
out_dim == 5120u &&
((gate_type == DS4_METAL_TENSOR_IQ2_XXS && down_type == DS4_METAL_TENSOR_Q2_K) ||
(gate_type == DS4_METAL_TENSOR_Q4_K && down_type == DS4_METAL_TENSOR_Q4_K) ||
(gate_type == DS4_METAL_TENSOR_MXFP4 && down_type == DS4_METAL_TENSOR_MXFP4 && !tiny_batch_off));
const bool use_tiny_pair_mv =
!g_quality_mode &&
(n_tokens <= 5u || v41_decode_batch ||
(n_tokens == 6u && ds4_gpu_device_is_m5_apple_silicon() &&
(gate_type == DS4_METAL_TENSOR_IQ2_XXS ||
gate_type == DS4_METAL_TENSOR_Q4_K ||
(g_tp_split_world == 2 && gate_type == DS4_METAL_TENSOR_MXFP4)))) &&
!use_q4_batch_expert_table &&
!use_mm_id &&
((gate_type == DS4_METAL_TENSOR_IQ2_XXS && g_moe_mul_mv_id_iq2_xxs_pair_pipeline) ||
(gate_type == DS4_METAL_TENSOR_Q4_K && g_moe_mul_mv_id_q4_k_pair_pipeline) ||
(gate_type == DS4_METAL_TENSOR_MXFP4 &&
g_moe_mul_mv_id_mxfp4_pair_swiglu_pipeline &&
getenv("DS4_METAL_DISABLE_TINY_PAIR_SWIGLU_FUSION") == NULL &&
getenv("DS4_METAL_MOE_WRITE_CLAMPED_ACT") == NULL));
id<MTLComputePipelineState> tiny_pair_swiglu_pipeline = nil;
const bool use_tp_mxfp4_static_batch = use_tiny_pair_mv &&
ds4_gpu_device_is_m5_apple_silicon() && g_tp_split_world == 2 &&
gate_type == DS4_METAL_TENSOR_MXFP4 && down_type == DS4_METAL_TENSOR_MXFP4 &&
n_expert == 6 && n_total_expert == 256 &&
expert_in_dim == 4096 && expert_mid_dim == 2048 && out_dim == 4096 &&
gate_row_bytes == 2176 && gate_expert_bytes == 4456448 &&
down_row_bytes == 1088 && down_expert_bytes == 4456448 &&
!g_ssd_streaming_mode &&
getenv("DS4_METAL_DISABLE_M5_TP_MXFP4_STATIC") == NULL &&
g_moe_mul_mv_id_mxfp4_pair_swiglu_tp_static_pipeline_nsg1 != nil &&
g_moe_mul_mv_id_mxfp4_sum6_tp_full_rows_static_pipeline_nsg1 != nil;
if (gate_type == DS4_METAL_TENSOR_IQ2_XXS) {
tiny_pair_swiglu_pipeline = g_moe_mul_mv_id_iq2_xxs_pair_swiglu_pipeline;
} else if (gate_type == DS4_METAL_TENSOR_Q4_K) {
tiny_pair_swiglu_pipeline = g_moe_mul_mv_id_q4_k_pair_swiglu_pipeline;
} else if (gate_type == DS4_METAL_TENSOR_MXFP4) {
tiny_pair_swiglu_pipeline = use_tp_mxfp4_static_batch ?
g_moe_mul_mv_id_mxfp4_pair_swiglu_tp_static_pipeline_nsg1 :
g_moe_mul_mv_id_mxfp4_pair_swiglu_pipeline;
}
const bool use_tiny_pair_swiglu =
use_tiny_pair_mv &&
tiny_pair_swiglu_pipeline != nil &&
getenv("DS4_METAL_DISABLE_TINY_PAIR_SWIGLU_FUSION") == NULL &&
getenv("DS4_METAL_MOE_WRITE_CLAMPED_ACT") == NULL;
ds4_gpu_mul_mm_id_map_args gate_map_args = { 0 };
ds4_gpu_mul_mm_id_args gate_mm_args = { 0 };
ds4_gpu_mul_mm_id_args down_mm_args = { 0 };
id<MTLComputePipelineState> map_pipeline = nil;
/*
* The grouped routed-MoE matmul loads activation tiles as half before
* using SIMD-group MMA. Store the SwiGLU/route-weight intermediate in
* that same precision so the down projection avoids a large F32 mid
* write/read. --quality keeps the older F32 intermediate.
*/
const bool request_mid_f16 =
!g_quality_mode &&
!use_q4_batch_expert_table &&
!use_iq2_batch_selected_addr;
/*
* Fused gate+up grouped matmul with the SwiGLU epilogue. Both IQ2 and
* Q4_K use the compact expert work list, the same MMA accumulation
* order, and the same epilogue math as separate GEMMs + SwiGLU, so the
* mid tensor is bit-identical.
*/
const bool use_mm_id_pair_swiglu =
use_mm_id &&
!(gate_type == DS4_METAL_TENSOR_IQ2_XXS &&
(ds4_gpu_routed_mm_mpp_mask() & 3) == 3) &&
g_tp_split_world != 2 && /* pair-swiglu mm kernel lacks expert ownership */
request_mid_f16 &&
n_expert == 6 &&
((gate_type == DS4_METAL_TENSOR_IQ2_XXS &&
down_type == DS4_METAL_TENSOR_Q2_K) ||
(gate_type == DS4_METAL_TENSOR_Q4_K &&
down_type == DS4_METAL_TENSOR_Q4_K) ||
(gate_type == DS4_METAL_TENSOR_MXFP4 &&
down_type == DS4_METAL_TENSOR_MXFP4)) &&
getenv("DS4_METAL_DISABLE_MOE_MM_ID_PAIR_SWIGLU") == NULL &&
getenv("DS4_METAL_MOE_WRITE_CLAMPED_ACT") == NULL &&
getenv("DS4_METAL_GRAPH_DUMP_PREFIX") == NULL;
/*
* The MXFP4 32x32 specialization uses two SIMDgroups and 8 KiB of
* threadgroup memory, and exactly culls SIMDgroup 1 on at-most-16-row
* final work items. Balanced full-model A/B at both 2K and 8K makes
* it the resident pre-M5 default for large prefill.
*/
const bool resident_tile_types =
(gate_type == DS4_METAL_TENSOR_MXFP4 && down_type == DS4_METAL_TENSOR_MXFP4) ||
(v41_packed && /* Q4_K verified on the V4.1 shape only */
gate_type == DS4_METAL_TENSOR_Q4_K && down_type == DS4_METAL_TENSOR_Q4_K);
const bool use_pre_m5_mxfp4_mm_id_pair_swiglu_compact_tile_default =
ds4_gpu_device_is_pre_m5_apple_silicon() &&
!g_ssd_streaming_mode &&
n_tokens >= 2048u &&
getenv("DS4_METAL_DISABLE_PRE_M5_MXFP4_MOE_MM_ID_PAIR_SWIGLU_COMPACT_TILE") == NULL;
const bool use_mxfp4_mm_id_pair_swiglu_compact_tile =
use_mm_id_pair_swiglu &&
resident_tile_types &&
g_tp_split_world == 1 &&
(use_pre_m5_mxfp4_mm_id_pair_swiglu_compact_tile_default ||
(g_test_flags & DS4_GPU_TEST_MXFP4_PAIR_COMPACT_TILE) != 0u);
/*
* Token-centric map construction for the compact resident pre-M5 MXFP4
* path. It emits the established work_count/uint2 trailer consumed
* by the existing padded direct launches and changes no arithmetic.
*/
const bool use_pre_m5_mxfp4_mm_id_map_scatter_default =
ds4_gpu_device_is_pre_m5_apple_silicon() &&
n_tokens >= 2048u &&
getenv("DS4_METAL_DISABLE_PRE_M5_MXFP4_MOE_MM_ID_MAP_SCATTER") == NULL;
const bool use_mxfp4_mm_id_map_scatter =
use_mxfp4_mm_id_pair_swiglu_compact_tile &&
resident_tile_types &&
n_expert == 6 &&
!g_ssd_streaming_mode &&
g_tp_split_world == 1 &&
(use_pre_m5_mxfp4_mm_id_map_scatter_default ||
(g_test_flags & DS4_GPU_TEST_MXFP4_MAP_SCATTER) != 0u);
/*
* On a final occupied 32-row work tile with at most 16 rows, the
* second SIMDgroup pair has no valid MMA output. The specialized
* kernel still keeps every thread in staging, barriers, and the
* scalar SwiGLU epilogue. Keep the automatic path to the resident pre-M5
* shape and prefixes covered by the full-model A/B gate.
*/
const bool use_pre_m5_mxfp4_mm_id_pair_tail_simdgroup_cull_default =
ds4_gpu_device_is_pre_m5_apple_silicon() &&
!g_ssd_streaming_mode &&
n_tokens >= 2048u &&
getenv("DS4_METAL_DISABLE_PRE_M5_MXFP4_MOE_MM_ID_PAIR_TAIL_SIMDGROUP_CULL") == NULL;
const bool use_mxfp4_mm_id_pair_tail_simdgroup_cull =
use_mm_id_pair_swiglu &&
resident_tile_types &&
g_tp_split_world == 1 &&
(use_pre_m5_mxfp4_mm_id_pair_tail_simdgroup_cull_default ||
(g_test_flags & DS4_GPU_TEST_MXFP4_PAIR_TAIL_CULL) != 0u);
const bool use_pre_m5_mxfp4_mm_id_down_tail_simdgroup_cull_default =
ds4_gpu_device_is_pre_m5_apple_silicon() &&
!g_ssd_streaming_mode &&
n_tokens >= 2048u &&
getenv("DS4_METAL_DISABLE_PRE_M5_MXFP4_MOE_MM_ID_DOWN_TAIL_SIMDGROUP_CULL") == NULL;
const bool use_mxfp4_mm_id_down_tail_simdgroup_cull =
use_mm_id &&
request_mid_f16 &&
resident_tile_types &&
g_tp_split_world == 1 &&
(use_pre_m5_mxfp4_mm_id_down_tail_simdgroup_cull_default ||
(g_test_flags & DS4_GPU_TEST_MXFP4_DOWN_TAIL_CULL) != 0u);
/*
* Exact half-result dequantization table for the resident MXFP4 down
* projection. Balanced full-model A/B at 2K and 8K makes it the
* pre-M5 Apple-Silicon default for large prefill.
*/
const bool use_pre_m5_mxfp4_mm_id_down_half_lut_default =
ds4_gpu_device_is_pre_m5_apple_silicon() &&
n_tokens >= 2048u &&
getenv("DS4_METAL_DISABLE_PRE_M5_MXFP4_MOE_MM_ID_DOWN_HALF_LUT") == NULL;
const bool use_mxfp4_mm_id_down_half_lut =
use_mm_id &&
request_mid_f16 &&
gate_type == DS4_METAL_TENSOR_MXFP4 &&
down_type == DS4_METAL_TENSOR_MXFP4 &&
n_expert == 6 &&
!g_ssd_streaming_mode &&
g_tp_split_world == 1 &&
(use_pre_m5_mxfp4_mm_id_down_half_lut_default ||
(g_test_flags & DS4_GPU_TEST_MXFP4_DOWN_HALF_LUT) != 0u);
if (use_mm_id) {
gate_map_args =
ds4_gpu_make_mul_mm_id_map_args(expert_in_dim, n_total_expert, 1, n_expert, n_tokens);
gate_mm_args =
ds4_gpu_make_mul_mm_id_args(expert_in_dim, expert_mid_dim, n_total_expert,
gate_row_bytes, gate_expert_bytes,
1, n_expert, n_tokens);
down_mm_args =
ds4_gpu_make_mul_mm_id_args_src1_size(expert_mid_dim, out_dim, n_total_expert,
down_row_bytes, down_expert_bytes,
n_expert, n_expert, n_tokens,
request_mid_f16 ? sizeof(uint16_t) : sizeof(float));
gate_mm_args.tp_rank = g_tp_split_rank;
gate_mm_args.tp_world = tp_world_host;
gate_mm_args.tp_expert_base = tp_expert_base_host;
down_mm_args.tp_rank = g_tp_split_rank;
down_mm_args.tp_world = tp_world_host;
down_mm_args.tp_expert_base = tp_expert_base_host;
map_pipeline = ds4_gpu_get_pipeline(
use_mxfp4_mm_id_map_scatter ?
"kernel_mul_mm_id_map_scatter_work_ne20_6" :
ds4_gpu_mul_mm_id_map0_name(n_expert));
gate_mm_pipeline = ds4_gpu_routed_mm_pipeline(gate_type);
up_mm_pipeline = ds4_gpu_routed_mm_pipeline(gate_type);
down_mm_pipeline = use_mxfp4_mm_id_down_half_lut ?
ds4_gpu_get_mul_mm_id_pipeline(
use_mxfp4_mm_id_down_tail_simdgroup_cull ?
"kernel_mul_mm_id_mxfp4_f16_half_lut_tail_cull" :
"kernel_mul_mm_id_mxfp4_f16_half_lut",
false) :
use_mxfp4_mm_id_down_tail_simdgroup_cull ?
ds4_gpu_get_mul_mm_id_pipeline(
gate_type == DS4_METAL_TENSOR_Q4_K ?
"kernel_mul_mm_id_q4_K_f16_tail_cull" :
"kernel_mul_mm_id_mxfp4_f16_tail_cull", false) :
request_mid_f16 ?
ds4_gpu_routed_mm_f16_rhs_pipeline(down_type) :
ds4_gpu_routed_mm_pipeline(down_type);
const int mpp_mask = ds4_gpu_routed_mm_mpp_mask();
if (mpp_mask && gate_type == DS4_METAL_TENSOR_IQ2_XXS) {
id<MTLComputePipelineState> mpp =
ds4_gpu_get_mul_mm_id_pipeline("kernel_mul_mm_id_iq2_xxs_f32_mpp", false);
if (mpp) {
if (mpp_mask & 1) gate_mm_pipeline = mpp;
if (mpp_mask & 2) up_mm_pipeline = mpp;
}
}
/* MXFP4 routed experts on the TensorOps pipeline (M5 nax units):
* same half weights x half activations with f32 accumulation as
* the simdgroup kernel, different accumulation order.
* DS4_METAL_DISABLE_MXFP4_MM_ID_MPP=1 keeps the simdgroup kernels. */
static int mxfp4_mpp = -1;
if (mxfp4_mpp < 0)
mxfp4_mpp = getenv("DS4_METAL_DISABLE_MXFP4_MM_ID_MPP") == NULL;
if (mpp_mask && mxfp4_mpp && gate_type == DS4_METAL_TENSOR_MXFP4) {
id<MTLComputePipelineState> mpp =
ds4_gpu_get_mul_mm_id_pipeline("kernel_mul_mm_id_mxfp4_f32_mpp", false);
if (mpp) {
if (mpp_mask & 1) gate_mm_pipeline = mpp;
if (mpp_mask & 2) up_mm_pipeline = mpp;
}
}
/* Q4_K routed gate/up on the TensorOps pipeline: same operand
* domains as the simdgroup kernel, different accumulation order.
* DS4_METAL_DISABLE_Q4_K_MM_ID_MPP=1 keeps the simdgroup kernels. */
static int q4_k_mpp = -1;
if (q4_k_mpp < 0)
q4_k_mpp = getenv("DS4_METAL_DISABLE_Q4_K_MM_ID_MPP") == NULL;
if (mpp_mask && q4_k_mpp && gate_type == DS4_METAL_TENSOR_Q4_K) {
id<MTLComputePipelineState> mpp =
ds4_gpu_get_mul_mm_id_pipeline("kernel_mul_mm_id_q4_K_f32_dbuf_mpp", false);
if (mpp) {
if (mpp_mask & 1) gate_mm_pipeline = mpp;
if (mpp_mask & 2) up_mm_pipeline = mpp;
mm_id_mpp_active = true;
}
}
if ((mpp_mask & 4) && mxfp4_mpp && request_mid_f16 &&
down_type == DS4_METAL_TENSOR_MXFP4) {
id<MTLComputePipelineState> mpp =
ds4_gpu_get_mul_mm_id_pipeline("kernel_mul_mm_id_mxfp4_f16_mpp", false);
if (mpp) {
down_mm_pipeline = mpp;
}
}
if ((mpp_mask & 4) && q4_k_mpp && request_mid_f16 &&
down_type == DS4_METAL_TENSOR_Q4_K) {
id<MTLComputePipelineState> mpp =
ds4_gpu_get_mul_mm_id_pipeline("kernel_mul_mm_id_q4_K_f16_dbuf_mpp", false);
if (mpp) {
down_mm_pipeline = mpp;
mm_id_mpp_active = true;
}
}
if ((mpp_mask & 4) && request_mid_f16 &&
(down_type == DS4_METAL_TENSOR_Q2_K || down_type == DS4_METAL_TENSOR_IQ2_XXS)) {
id<MTLComputePipelineState> mpp = ds4_gpu_get_mul_mm_id_pipeline(
down_type == DS4_METAL_TENSOR_Q2_K ?
"kernel_mul_mm_id_q2_K_f16_mpp" :
"kernel_mul_mm_id_iq2_xxs_f16_mpp", false);
if (mpp) {
down_mm_pipeline = mpp;
}
}
if (use_packed_mpp) {
gate_mm_pipeline = up_mm_pipeline =
ds4_gpu_get_pipeline(gate_type == DS4_METAL_TENSOR_MXFP4 ?
"kernel_mul_mm_id_mxfp4_mpp_packed" : packed_m32n128 ?
"kernel_mul_mm_id_iq2_xxs_mpp_packed_m32n128" : "kernel_mul_mm_id_iq2_xxs_mpp_packed");
down_mm_pipeline = ds4_gpu_get_pipeline(down_type == DS4_METAL_TENSOR_MXFP4 ?
"kernel_mul_mm_id_mxfp4_mpp_packed" : packed_m32n128 ?
"kernel_mul_mm_id_q2_K_mpp_packed_m32n128" : "kernel_mul_mm_id_q2_K_mpp_packed");
}
if (use_packed_mpp) mm_id_mpp_active = false;
if (use_mm_id_pair_swiglu) {
/* Exact half-domain block scaling for the resident MXFP4 pair
* tile: E8M0 is a power of two and every E2M1 magnitude is
* exact in binary16, so inside the guarded exponent band the
* half product is the identical once-rounded value while the
* per-element f32->f16 narrowing and the 64-byte float table
* disappear. Outside the band the kernel keeps the established
* float arithmetic. */
const bool use_mxfp4_mm_id_pair_half_scale =
gate_type == DS4_METAL_TENSOR_MXFP4 &&
ds4_gpu_device_is_pre_m5_apple_silicon() &&
getenv("DS4_METAL_DISABLE_PRE_M5_MXFP4_MM_ID_PAIR_HALF_SCALE") == NULL &&
!g_quality_mode &&
!g_ssd_streaming_mode &&
g_tp_split_world == 1;
pair_swiglu_mm_pipeline =
ds4_gpu_get_pipeline(
gate_type == DS4_METAL_TENSOR_Q4_K ?
(use_mxfp4_mm_id_pair_swiglu_compact_tile ?
"kernel_mul_mm_id_q4_K_pair_swiglu_f16_compact_tail_cull" :
use_mxfp4_mm_id_pair_tail_simdgroup_cull ?
"kernel_mul_mm_id_q4_K_pair_swiglu_f16_tail_cull" :
"kernel_mul_mm_id_q4_K_pair_swiglu_f16") :
gate_type == DS4_METAL_TENSOR_MXFP4 ?
(use_mxfp4_mm_id_pair_swiglu_compact_tile ?
(use_mxfp4_mm_id_pair_half_scale ?
"kernel_mul_mm_id_mxfp4_pair_swiglu_f16_compact_tail_cull_half_scale" :
"kernel_mul_mm_id_mxfp4_pair_swiglu_f16_compact_tail_cull") :
use_mxfp4_mm_id_pair_tail_simdgroup_cull ?
(use_mxfp4_mm_id_pair_half_scale ?
"kernel_mul_mm_id_mxfp4_pair_swiglu_f16_tail_cull_half_scale" :
"kernel_mul_mm_id_mxfp4_pair_swiglu_f16_tail_cull") :
(use_mxfp4_mm_id_pair_half_scale ?
"kernel_mul_mm_id_mxfp4_pair_swiglu_f16_half_scale" :
"kernel_mul_mm_id_mxfp4_pair_swiglu_f16")) :
"kernel_mul_mm_id_iq2_xxs_pair_swiglu_f16");
}
if (!map_pipeline || !gate_mm_pipeline || !up_mm_pipeline || !down_mm_pipeline ||
(use_mm_id_pair_swiglu && !pair_swiglu_mm_pipeline)) {
return 0;
}
}
if (use_iq2_cached_batch && use_mm_id) {
const int mpp = ds4_gpu_routed_mm_mpp_mask();
gate_mm_pipeline = up_mm_pipeline = ds4_gpu_get_mul_mm_id_pipeline(
mpp ? "kernel_mul_mm_id_iq2_xxs_cached_f32_mpp" :
"kernel_mul_mm_id_iq2_xxs_cached_f32", false);
down_mm_pipeline = ds4_gpu_get_mul_mm_id_pipeline(
request_mid_f16 ? (mpp ? "kernel_mul_mm_id_iq2_xxs_cached_f16_mpp" :
"kernel_mul_mm_id_iq2_xxs_cached_f16") :
"kernel_mul_mm_id_iq2_xxs_cached_f32", false);
if (!gate_mm_pipeline || !down_mm_pipeline) return 0;
}
if (use_cached_batch) {
const int had_batch = g_batch_cb != nil;
if (had_batch && ds4_gpu_end_commands() == 0) {
return 0;
}
g_stream_prefill_batch_selected_addr_building++;
if (!ds4_gpu_stream_expert_cache_prepare_selected_batch(
model_map,
model_size,
layer_index,
selected,
n_tokens,
n_total_expert,
n_expert,
gate_offset,
up_offset,
down_offset,
gate_expert_bytes,
down_expert_bytes,
&stream_gate_addr_buf,
&stream_up_addr_buf,
&stream_down_addr_buf,
stream_resources,
&stream_resource_count,
&stream_unique,
&stream_overflow_gate,
&stream_overflow_up,
&stream_overflow_down)) {
g_stream_prefill_batch_selected_addr_building--;
return 0;
}
g_stream_prefill_batch_selected_addr_building--;
if (stream_unique == 0) {
ds4_gpu_stream_expert_cache_clear_layer(layer_index);
fprintf(stderr,
"ds4: Metal streaming prefill batch selected addr layer=%u "
"produced no resident experts\n",
layer_index);
return 0;
}
for (uint32_t i = 0; i < stream_resource_count; i++) {
ds4_gpu_stream_expert_cache_entry *entry = stream_resources[i];
if (!entry ||
!entry->valid ||
!entry->gate_buffer ||
!entry->up_buffer ||
!entry->down_buffer) {
fprintf(stderr,
"ds4: Metal streaming prefill batch selected addr layer=%u "
"lost resident expert resource %u/%u during preparation\n",
layer_index,
i,
stream_resource_count);
ds4_gpu_stream_expert_cache_clear_layer(layer_index);
return 0;
}
}
if (had_batch && ds4_gpu_begin_commands() == 0) {
fprintf(stderr,
"ds4: Metal streaming prefill batch selected addr layer=%u "
"failed to reopen command batch after preparation\n",
layer_index);
ds4_gpu_stream_expert_cache_clear_layer(layer_index);
return 0;
}
}
if (use_q4_batch_expert_table) {
gate_table = ds4_gpu_q4_expert_table(model_map,
model_size,
gate_offset,
gate_expert_bytes,
n_total_expert,
g_moe_table_q4_pair_gate_encoder);
up_table = ds4_gpu_q4_expert_table(model_map,
model_size,
up_offset,
gate_expert_bytes,
n_total_expert,
g_moe_table_q4_pair_up_encoder);
down_table = ds4_gpu_q4_expert_table(model_map,
model_size,
down_offset,
down_expert_bytes,
n_total_expert,
g_moe_table_q4_sum_down_encoder);
if (!gate_table || !up_table || !down_table) {
return 0;
}
q4_table_layer_residency =
ds4_gpu_q4_expert_layer_residency_set(gate_table,
up_table,
down_table,
q4_batch_expert_table_auto);
if ((q4_batch_table_queue_residency ||
getenv("DS4_METAL_Q4_TABLE_RESIDENCY_SET") != NULL) &&
!q4_table_layer_residency &&
!ds4_gpu_q4_table_model_residency_enabled()) {
fprintf(stderr, "ds4: Metal Q4 batch expert table residency set is not available\n");
return 0;
}
} else if (use_cached_batch) {
if (n_expert > 1 && (!expertsbuf ||
ds4_gpu_tensor_bytes(experts) < down_scratch_bytes)) {
if (!ds4_gpu_ensure_scratch_buffer(&g_moe_down_scratch_buffer,
&g_moe_down_scratch_bytes,
(NSUInteger)down_scratch_bytes,
"ds4_moe_down_scratch")) {
ds4_gpu_stream_expert_cache_clear_layer(layer_index);
return 0;
}
}
} else {
if (n_expert > 1 && (!expertsbuf ||
ds4_gpu_tensor_bytes(experts) < down_scratch_bytes)) {
if (!ds4_gpu_ensure_scratch_buffer(&g_moe_down_scratch_buffer,
&g_moe_down_scratch_bytes,
(NSUInteger)down_scratch_bytes,
"ds4_moe_down_scratch")) {
return 0;
}
}
gate_buf = ds4_gpu_wrap_model_range(model_map,
model_size,
gate_offset,
gate_tensor_bytes,
&gate_inner);
up_buf = ds4_gpu_wrap_model_range(model_map,
model_size,
up_offset,
gate_tensor_bytes,
&up_inner);
down_buf = ds4_gpu_wrap_model_range(model_map,
model_size,
down_offset,
down_tensor_bytes,
&down_inner);
if (!gate_buf || !up_buf || !down_buf) return 0;
if (getenv("DS4_GLM_TP_DEBUG") && layer_index == 3) {
fprintf(stderr,
"ds4: batch mv binds l=%u rank=%d base=%d gate=%llu+%llu "
"up=%llu down=%llu inner=%llu/%llu/%llu ne02=%d nei0=%d nr0=%d\n",
layer_index, g_tp_split_rank, tp_expert_base_host,
(unsigned long long)gate_offset,
(unsigned long long)gate_tensor_bytes,
(unsigned long long)up_offset,
(unsigned long long)down_offset,
(unsigned long long)gate_inner,
(unsigned long long)up_inner,
(unsigned long long)down_inner,
gate_args.ne02, gate_args.nei0, gate_args.nr0);
}
}
const bool q4_batch_table_boundary =
use_q4_batch_expert_table &&
g_batch_cb != nil &&
getenv("DS4_METAL_Q4_TABLE_RESIDENCY_SET") != NULL &&
!q4_batch_table_queue_residency &&
getenv("DS4_METAL_DISABLE_Q4_TABLE_BOUNDARY") == NULL;
if (q4_batch_table_boundary) {
if (ds4_gpu_end_commands() == 0 || ds4_gpu_begin_commands() == 0) {
return 0;
}
}
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
if (use_q4_batch_expert_table && !ds4_gpu_use_model_residency_set(cb)) {
return 0;
}
if (!q4_batch_table_queue_residency &&
q4_table_layer_residency &&
[cb respondsToSelector:@selector(useResidencySet:)]) {
[cb useResidencySet:q4_table_layer_residency];
}
const bool moe_stage_profile =
g_batch_cb != nil &&
ds4_gpu_stage_profile_enabled_for_layer("DS4_METAL_MOE_STAGE_PROFILE",
"DS4_METAL_MOE_STAGE_PROFILE_LAYER",
layer_index);
const char *moe_stage_filter = getenv("DS4_METAL_MOE_STAGE_PROFILE_FILTER");
const char *moe_path =
use_q4_batch_expert_table ? "q4_table_pair_swiglu" :
use_iq2_batch_selected_addr ? "iq2_batch_stream_addr" :
use_iq2_cached_batch ? (use_mm_id ? "iq2_cached_mm" : "iq2_cached_mv") :
use_mm_id_pair_swiglu ? "mm_id_pair_swiglu" :
use_mm_id ? "mm_id" :
use_tiny_pair_swiglu ? "tiny_pair_swiglu" :
(use_tiny_pair_mv ? "tiny_pair_mv" : "mv");
double moe_stage_t0 = moe_stage_profile ? ds4_gpu_now_ms() : 0.0;
if (moe_stage_profile) {
if (ds4_gpu_end_commands() == 0 || ds4_gpu_begin_commands() == 0) {
return 0;
}
cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
moe_stage_t0 = ds4_gpu_now_ms();
}
if (use_iq2_cached_batch && stream_resource_count &&
!ds4_gpu_stream_expert_cache_mark_entries_inflight(
stream_resources, stream_resource_count, 0)) return 0;
#define DS4_METAL_PROFILE_MOE_STAGE(name) do { \
if (ok && moe_stage_profile) { \
if (ds4_gpu_end_commands() == 0) { \
ok = 0; \
} else { \
const char *stage_name = (name); \
const double now_ms = ds4_gpu_now_ms(); \
const int print_stage = \
!moe_stage_filter || !moe_stage_filter[0] || \
strstr(stage_name, moe_stage_filter) != NULL; \
if (print_stage) { \
fprintf(stderr, \
"ds4: Metal routed MoE stage layer=%u tokens=%u pairs=%u experts=%u " \
"gate=%s down=%s path=%s mid=%s %s=%.3f ms\n", \
layer_index, n_tokens, pair_rows, n_expert, \
ds4_gpu_metal_tensor_type_name(gate_type), \
ds4_gpu_metal_tensor_type_name(down_type), \
moe_path, \
request_mid_f16 ? "f16" : "f32", \
stage_name, now_ms - moe_stage_t0); \
} \
moe_stage_t0 = now_ms; \
if (ds4_gpu_begin_commands() == 0) { \
ok = 0; \
} else { \
cb = ds4_gpu_command_buffer(&owned); \
if (!cb) ok = 0; \
} \
} \
} \
} while (0)
const NSUInteger gate_smem = ds4_gpu_routed_mv_smem(gate_type);
const NSUInteger down_smem = ds4_gpu_routed_mv_smem(down_type);
const NSUInteger gate_nsg = ds4_gpu_routed_mv_nsg(gate_type);
const NSUInteger down_nsg = ds4_gpu_routed_mv_nsg(down_type);
const bool gate_rows_per_group_is_nr0 = ds4_gpu_routed_mv_rows_per_group_is_nr0(gate_type);
const bool down_rows_per_group_is_nr0 = ds4_gpu_routed_mv_rows_per_group_is_nr0(down_type);
id<MTLComputePipelineState> down_sum6_pipeline = nil;
if (down_type == DS4_METAL_TENSOR_Q2_K) {
down_sum6_pipeline = g_moe_mul_mv_id_q2_k_sum6_pipeline;
} else if (down_type == DS4_METAL_TENSOR_Q4_K) {
down_sum6_pipeline = g_moe_mul_mv_id_q4_k_sum6_pipeline;
} else if (down_type == DS4_METAL_TENSOR_MXFP4) {
down_sum6_pipeline = use_tp_mxfp4_static_batch ?
g_moe_mul_mv_id_mxfp4_sum6_tp_full_rows_static_pipeline_nsg1 :
g_moe_mul_mv_id_mxfp4_sum6_pipeline;
}
const bool direct_down_sum =
!g_quality_mode &&
!use_q4_batch_expert_table &&
!use_mm_id &&
n_expert == 6 &&
(n_tokens <= 4u || v41_decode_batch || use_tp_mxfp4_static_batch) &&
down_sum6_pipeline != nil;
int ok = 0;
if (use_iq2_batch_selected_addr) {
ds4_gpu_dsv4_moe_swiglu_weight_args act_args = {
.width = expert_mid_dim,
.rows = pair_rows,
.gate_row_stride = (uint64_t)expert_mid_dim * sizeof(float),
.up_row_stride = (uint64_t)expert_mid_dim * sizeof(float),
.mid_row_stride = (uint64_t)expert_mid_dim * sizeof(float),
.weight_stride = sizeof(float),
.write_clamped = 0,
.clamp_value = clamp,
};
ok = ds4_gpu_encode_mul_mv_addr_iq2_pair_swiglu(
cb,
g_moe_mul_mv_addr_iq2_xxs_pair_swiglu_pipeline,
&gate_args,
&act_args,
stream_resources,
stream_resource_count,
stream_gate_addr_buf,
stream_up_addr_buf,
xbuf,
ds4_gpu_tensor_offset(x),
gatebuf,
ds4_gpu_tensor_offset(gate),
upbuf,
ds4_gpu_tensor_offset(up),
midbuf,
ds4_gpu_tensor_offset(mid),
selectedbuf,
ds4_gpu_tensor_offset(selected),
weightsbuf,
ds4_gpu_tensor_offset(weights),
gate_smem,
2,
false,
stream_overflow_gate,
stream_overflow_up);
if (!ok) {
fprintf(stderr,
"ds4: Metal streaming prefill batch selected addr layer=%u "
"failed to encode gate/up path tokens=%u unique=%u\n",
layer_index,
n_tokens,
stream_unique);
}
} else if (use_q4_batch_expert_table) {
ds4_gpu_dsv4_moe_swiglu_weight_args act_args = {
.width = expert_mid_dim,
.rows = pair_rows,
.gate_row_stride = (uint64_t)expert_mid_dim * sizeof(float),
.up_row_stride = (uint64_t)expert_mid_dim * sizeof(float),
.mid_row_stride = (uint64_t)expert_mid_dim * sizeof(float),
.weight_stride = sizeof(float),
.write_clamped = 0,
.clamp_value = clamp,
};
ok = ds4_gpu_encode_mul_mv_table_q4_pair_swiglu(cb,
g_moe_mul_mv_table_q4_k_pair_swiglu_pipeline,
&gate_args,
&act_args,
gate_table,
up_table,
xbuf,
ds4_gpu_tensor_offset(x),
gatebuf,
ds4_gpu_tensor_offset(gate),
upbuf,
ds4_gpu_tensor_offset(up),
midbuf,
ds4_gpu_tensor_offset(mid),
selectedbuf,
ds4_gpu_tensor_offset(selected),
weightsbuf,
ds4_gpu_tensor_offset(weights),
gate_smem,
2,
false,
q4_batch_table_queue_residency);
} else if (use_mm_id) {
/*
* The routed pair ids are the same for gate, up, and down. Build
* the expert-major work map once, then reuse it for all three
* batched expert matmuls.
*/
ok = ds4_gpu_encode_mul_mm_id_map(cb,
map_pipeline,
&gate_map_args,
&gate_mm_args,
selectedbuf,
ds4_gpu_tensor_offset(selected));
if (ok && use_packed_mpp)
ok = ds4_gpu_encode_moe_packed_rhs(cb, &gate_mm_args,
xbuf, ds4_gpu_tensor_offset(x), false, packed_limit);
DS4_METAL_PROFILE_MOE_STAGE("map");
if (ok && use_mm_id_pair_swiglu) {
ds4_gpu_dsv4_moe_swiglu_weight_args act_args = {
.width = expert_mid_dim,
.rows = pair_rows,
.gate_row_stride = (uint64_t)expert_mid_dim * sizeof(float),
.up_row_stride = (uint64_t)expert_mid_dim * sizeof(float),
.mid_row_stride = (uint64_t)expert_mid_dim * sizeof(uint16_t),
.weight_stride = sizeof(float),
.write_clamped = 0,
.clamp_value = clamp,
};
ok = ds4_gpu_encode_mul_mm_id_iq2_pair_swiglu_f16(cb,
pair_swiglu_mm_pipeline,
use_mxfp4_mm_id_pair_swiglu_compact_tile,
&gate_mm_args,
&act_args,
gate_buf,
(NSUInteger)gate_inner,
up_buf,
(NSUInteger)up_inner,
xbuf,
ds4_gpu_tensor_offset(x),
midbuf,
ds4_gpu_tensor_offset(mid),
weightsbuf,
ds4_gpu_tensor_offset(weights));
DS4_METAL_PROFILE_MOE_STAGE("gate_up_fused");
} else if (ok) {
ok = ds4_gpu_encode_mul_mm_id_mapped_tile_resources(cb,
gate_mm_pipeline,
&gate_mm_args,
use_iq2_cached_batch ? stream_gate_addr_buf : gate_buf,
(NSUInteger)gate_inner,
use_packed_mpp ? g_moe_packed_rhs_buffer : xbuf,
use_packed_mpp ? 0 : ds4_gpu_tensor_offset(x),
gatebuf,
ds4_gpu_tensor_offset(gate),
packed_m32n128 ? 16384u : mm_id_mpp_active ? 12288u : 8192u,
use_iq2_cached_batch ? stream_resources : NULL,
stream_resource_count, 0, stream_overflow_gate,
packed_m32n128 ? 32u : 64u);
DS4_METAL_PROFILE_MOE_STAGE("gate");
}
if (ok && !use_mm_id_pair_swiglu) {
ok = ds4_gpu_encode_mul_mm_id_mapped_tile_resources(cb,
up_mm_pipeline,
&gate_mm_args,
use_iq2_cached_batch ? stream_up_addr_buf : up_buf,
(NSUInteger)up_inner,
use_packed_mpp ? g_moe_packed_rhs_buffer : xbuf,
use_packed_mpp ? 0 : ds4_gpu_tensor_offset(x),
upbuf,
ds4_gpu_tensor_offset(up),
packed_m32n128 ? 16384u : mm_id_mpp_active ? 12288u : 8192u,
use_iq2_cached_batch ? stream_resources : NULL,
stream_resource_count, 1, stream_overflow_up,
packed_m32n128 ? 32u : 64u);
DS4_METAL_PROFILE_MOE_STAGE("up");
}
} else if (use_iq2_cached_batch && use_tiny_pair_swiglu) {
ds4_gpu_dsv4_moe_swiglu_weight_args act_args = {
.width = expert_mid_dim, .rows = pair_rows,
.gate_row_stride = (uint64_t)expert_mid_dim * sizeof(float),
.up_row_stride = (uint64_t)expert_mid_dim * sizeof(float),
.mid_row_stride = (uint64_t)expert_mid_dim * sizeof(float),
.weight_stride = sizeof(float), .clamp_value = clamp,
};
ok = ds4_gpu_encode_mul_mv_addr_iq2_pair_swiglu(cb,
g_moe_mul_mv_addr_iq2_xxs_pair_swiglu_pipeline, &gate_args, &act_args,
stream_resources, stream_resource_count, stream_gate_addr_buf, stream_up_addr_buf,
xbuf, ds4_gpu_tensor_offset(x), gatebuf, ds4_gpu_tensor_offset(gate),
upbuf, ds4_gpu_tensor_offset(up), midbuf, ds4_gpu_tensor_offset(mid),
selectedbuf, ds4_gpu_tensor_offset(selected), weightsbuf, ds4_gpu_tensor_offset(weights),
gate_smem, 2, false, stream_overflow_gate, stream_overflow_up);
} else if (use_iq2_cached_batch) {
ok = ds4_gpu_encode_mul_mv_addr_iq2(cb,
g_moe_mul_mv_addr_iq2_xxs_pipeline, &gate_args,
stream_resources, stream_resource_count, stream_gate_addr_buf,
xbuf, ds4_gpu_tensor_offset(x), gatebuf, ds4_gpu_tensor_offset(gate),
selectedbuf, ds4_gpu_tensor_offset(selected), gate_smem, gate_nsg,
gate_rows_per_group_is_nr0, 0, stream_overflow_gate) &&
ds4_gpu_encode_mul_mv_addr_iq2(cb,
g_moe_mul_mv_addr_iq2_xxs_pipeline, &gate_args,
stream_resources, stream_resource_count, stream_up_addr_buf,
xbuf, ds4_gpu_tensor_offset(x), upbuf, ds4_gpu_tensor_offset(up),
selectedbuf, ds4_gpu_tensor_offset(selected), gate_smem, gate_nsg,
gate_rows_per_group_is_nr0, 1, stream_overflow_up);
} else if (use_tiny_pair_swiglu) {
ds4_gpu_dsv4_moe_swiglu_weight_args act_args = {
.width = expert_mid_dim,
.rows = pair_rows,
.gate_row_stride = (uint64_t)expert_mid_dim * sizeof(float),
.up_row_stride = (uint64_t)expert_mid_dim * sizeof(float),
.mid_row_stride = (uint64_t)expert_mid_dim * sizeof(float),
.weight_stride = sizeof(float),
.write_clamped = 0,
.clamp_value = clamp,
};
/* a few single-node rows: every distinct expert read once */
static int dedup_off = -1;
if (dedup_off < 0) dedup_off = getenv("DS4_METAL_DISABLE_V41_MOE_DEDUP") != NULL;
const bool dedup = gate_type == DS4_METAL_TENSOR_MXFP4 && n_tokens >= 2u && n_tokens <= 8u &&
!use_tp_mxfp4_static_batch && g_tp_split_world == 1 && !dedup_off;
ok = dedup ? ds4_gpu_encode_mul_mv_id_pair_swiglu_dedup(cb, &gate_args, &act_args,
gate_buf, (NSUInteger)gate_inner, up_buf, (NSUInteger)up_inner,
xbuf, ds4_gpu_tensor_offset(x), gatebuf, ds4_gpu_tensor_offset(gate),
upbuf, ds4_gpu_tensor_offset(up), midbuf, ds4_gpu_tensor_offset(mid),
selectedbuf, ds4_gpu_tensor_offset(selected), weightsbuf, ds4_gpu_tensor_offset(weights),
gate_smem, n_tokens) :
ds4_gpu_encode_mul_mv_id_pair_swiglu(cb,
tiny_pair_swiglu_pipeline,
&gate_args,
&act_args,
gate_buf,
(NSUInteger)gate_inner,
up_buf,
(NSUInteger)up_inner,
xbuf,
ds4_gpu_tensor_offset(x),
gatebuf,
ds4_gpu_tensor_offset(gate),
upbuf,
ds4_gpu_tensor_offset(up),
midbuf,
ds4_gpu_tensor_offset(mid),
selectedbuf,
ds4_gpu_tensor_offset(selected),
weightsbuf,
ds4_gpu_tensor_offset(weights),
gate_smem,
use_tp_mxfp4_static_batch ? 1 : 2,
false);
} else if (use_tiny_pair_mv) {
id<MTLComputePipelineState> pair_pipeline =
gate_type == DS4_METAL_TENSOR_IQ2_XXS ?
g_moe_mul_mv_id_iq2_xxs_pair_pipeline :
g_moe_mul_mv_id_q4_k_pair_pipeline;
ok = ds4_gpu_encode_mul_mv_id_pair(cb,
pair_pipeline,
&gate_args,
gate_buf,
(NSUInteger)gate_inner,
up_buf,
(NSUInteger)up_inner,
xbuf,
ds4_gpu_tensor_offset(x),
gatebuf,
ds4_gpu_tensor_offset(gate),
upbuf,
ds4_gpu_tensor_offset(up),
selectedbuf,
ds4_gpu_tensor_offset(selected),
gate_smem,
2,
false);
} else {
ok = ds4_gpu_encode_mul_mv_id(cb,
gate_mv_pipeline,
&gate_args,
gate_buf,
(NSUInteger)gate_inner,
xbuf,
ds4_gpu_tensor_offset(x),
gatebuf,
ds4_gpu_tensor_offset(gate),
selectedbuf,
ds4_gpu_tensor_offset(selected),
gate_smem,
gate_nsg,
gate_rows_per_group_is_nr0) &&
ds4_gpu_encode_mul_mv_id(cb,
gate_mv_pipeline,
&gate_args,
up_buf,
(NSUInteger)up_inner,
xbuf,
ds4_gpu_tensor_offset(x),
upbuf,
ds4_gpu_tensor_offset(up),
selectedbuf,
ds4_gpu_tensor_offset(selected),
gate_smem,
gate_nsg,
gate_rows_per_group_is_nr0);
}
DS4_METAL_PROFILE_MOE_STAGE("gate_up");
const bool use_fused_activation = !g_quality_mode && !use_q4_batch_expert_table;
const bool use_mid_f16 =
use_mm_id &&
use_fused_activation &&
request_mid_f16;
if (mid_is_f16) *mid_is_f16 = use_mid_f16;
if (ok && use_iq2_batch_selected_addr) {
/* The address-table pair kernel already wrote weighted SwiGLU rows into mid. */
} else if (ok && use_q4_batch_expert_table) {
/* The table pair kernel already wrote weighted SwiGLU rows into mid. */
} else if (ok && use_mm_id_pair_swiglu) {
/* The fused batch mm_id pair kernel already wrote weighted f16 SwiGLU rows into mid. */
} else if (ok && use_tiny_pair_swiglu) {
/* The fused tiny pair kernel already wrote weighted F32 SwiGLU rows into mid. */
} else if (ok && use_fused_activation) {
ok = ds4_gpu_encode_moe_swiglu_weight(cb,
gatebuf,
ds4_gpu_tensor_offset(gate),
upbuf,
ds4_gpu_tensor_offset(up),
midbuf,
ds4_gpu_tensor_offset(mid),
weightsbuf,
ds4_gpu_tensor_offset(weights),
expert_mid_dim,
pair_rows,
clamp,
use_mid_f16);
} else if (ok && clamp > 1.0e-6f) {
ok = ds4_gpu_encode_unary_f32_rows(cb,
g_unary_clamp_pipeline,
gatebuf,
ds4_gpu_tensor_offset(gate),
gatebuf,
ds4_gpu_tensor_offset(gate),
expert_mid_dim,
pair_rows,
0,
-FLT_MAX,
clamp);
if (ok) {
ok = ds4_gpu_encode_unary_f32_rows(cb,
g_unary_silu_pipeline,
gatebuf,
ds4_gpu_tensor_offset(gate),
midbuf,
ds4_gpu_tensor_offset(mid),
expert_mid_dim,
pair_rows,
1,
0.0f,
0.0f);
}
if (ok) {
ok = ds4_gpu_encode_unary_f32_rows(cb,
g_unary_clamp_pipeline,
upbuf,
ds4_gpu_tensor_offset(up),
upbuf,
ds4_gpu_tensor_offset(up),
expert_mid_dim,
pair_rows,
0,
-clamp,
clamp);
}
if (ok) {
ds4_gpu_bin_args mul_args =
ds4_gpu_make_bin_same_rows_args(expert_mid_dim, pair_rows);
ok = ds4_gpu_encode_bin_f32_rows(cb,
g_mul_pipeline,
&mul_args,
midbuf,
ds4_gpu_tensor_offset(mid),
upbuf,
ds4_gpu_tensor_offset(up),
midbuf,
ds4_gpu_tensor_offset(mid));
}
} else if (ok) {
ok = ds4_gpu_encode_swiglu_flat(cb,
gatebuf,
ds4_gpu_tensor_offset(gate),
upbuf,
ds4_gpu_tensor_offset(up),
midbuf,
ds4_gpu_tensor_offset(mid),
(uint32_t)((uint64_t)pair_rows * expert_mid_dim));
}
if (ok && !use_fused_activation && !use_q4_batch_expert_table) {
ds4_gpu_bin_args weight_args =
ds4_gpu_make_bin_rowwise_scalar_args(expert_mid_dim, pair_rows);
ok = ds4_gpu_encode_bin_f32_rows(cb,
g_bin_mul_scalar_pipeline,
&weight_args,
midbuf,
ds4_gpu_tensor_offset(mid),
weightsbuf,
ds4_gpu_tensor_offset(weights),
midbuf,
ds4_gpu_tensor_offset(mid));
}
DS4_METAL_PROFILE_MOE_STAGE("activation_weight");
id<MTLBuffer> down_dst = n_expert == 1 ? outbuf : (expertsbuf ? expertsbuf : g_moe_down_scratch_buffer);
NSUInteger down_dst_off = n_expert == 1 ? ds4_gpu_tensor_offset(out) :
(expertsbuf ? ds4_gpu_tensor_offset(experts) : 0);
if (ok) {
if (use_iq2_batch_selected_addr) {
ok = ds4_gpu_encode_mul_mv_addr_q2_sum6(
cb,
g_moe_mul_mv_addr_q2_k_sum6_pipeline,
&down_args,
stream_resources,
stream_resource_count,
stream_down_addr_buf,
midbuf,
ds4_gpu_tensor_offset(mid),
outbuf,
ds4_gpu_tensor_offset(out),
selectedbuf,
ds4_gpu_tensor_offset(selected),
down_smem,
2,
stream_overflow_down);
if (!ok) {
fprintf(stderr,
"ds4: Metal streaming prefill batch selected addr layer=%u "
"failed to encode down path tokens=%u unique=%u\n",
layer_index,
n_tokens,
stream_unique);
}
} else if (use_q4_batch_expert_table) {
ok = ds4_gpu_encode_mul_mv_table_q4_sum6(cb,
g_moe_mul_mv_table_q4_k_sum6_pipeline,
&down_args,
down_table,
midbuf,
ds4_gpu_tensor_offset(mid),
outbuf,
ds4_gpu_tensor_offset(out),
selectedbuf,
ds4_gpu_tensor_offset(selected),
down_smem,
2,
q4_batch_table_queue_residency);
} else if (direct_down_sum) {
ok = ds4_gpu_encode_mul_mv_id_sum6(cb,
down_sum6_pipeline,
&down_args,
down_buf,
(NSUInteger)down_inner,
midbuf,
ds4_gpu_tensor_offset(mid),
outbuf,
ds4_gpu_tensor_offset(out),
selectedbuf,
ds4_gpu_tensor_offset(selected),
nil,
0,
down_smem,
use_tp_mxfp4_static_batch ? 1 : 2);
} else if (use_mm_id) {
if (use_packed_mpp)
ok = ds4_gpu_encode_moe_packed_rhs(cb, &down_mm_args,
midbuf, ds4_gpu_tensor_offset(mid), true, packed_limit);
if (ok) ok = ds4_gpu_encode_mul_mm_id_mapped_tile_resources(cb,
down_mm_pipeline,
&down_mm_args,
use_iq2_cached_batch ? stream_down_addr_buf : down_buf,
(NSUInteger)down_inner,
use_packed_mpp ? g_moe_packed_rhs_buffer : midbuf,
use_packed_mpp ? 0 : ds4_gpu_tensor_offset(mid),
down_dst,
down_dst_off,
packed_m32n128 ? 16384u : mm_id_mpp_active ? 12288u : 8192u,
use_iq2_cached_batch ? stream_resources : NULL,
stream_resource_count, 2, stream_overflow_down,
packed_m32n128 ? 32u : 64u);
} else if (use_iq2_cached_batch) {
ok = ds4_gpu_encode_mul_mv_addr_iq2(cb,
g_moe_mul_mv_addr_iq2_xxs_pipeline, &down_args,
stream_resources, stream_resource_count, stream_down_addr_buf,
midbuf, ds4_gpu_tensor_offset(mid), down_dst, down_dst_off,
selectedbuf, ds4_gpu_tensor_offset(selected), down_smem, down_nsg,
down_rows_per_group_is_nr0, 2, stream_overflow_down);
} else {
ok = ds4_gpu_encode_mul_mv_id(cb,
down_mv_pipeline,
&down_args,
down_buf,
(NSUInteger)down_inner,
midbuf,
ds4_gpu_tensor_offset(mid),
down_dst,
down_dst_off,
selectedbuf,
ds4_gpu_tensor_offset(selected),
down_smem,
down_nsg,
down_rows_per_group_is_nr0);
}
}
DS4_METAL_PROFILE_MOE_STAGE("down");
if (ok &&
n_expert > 1 &&
!direct_down_sum &&
!use_q4_batch_expert_table &&
!use_iq2_batch_selected_addr) {
ok = ds4_gpu_encode_moe_sum_experts(cb,
down_dst,
down_dst_off,
outbuf,
ds4_gpu_tensor_offset(out),
out_dim,
n_expert,
n_tokens);
}
DS4_METAL_PROFILE_MOE_STAGE("sum");
if (!ok) {
fprintf(stderr,
"ds4: Metal routed batch MoE failed before submit layer=%u tokens=%u "
"gate=%s down=%s path=%s\n",
layer_index,
n_tokens,
ds4_gpu_metal_tensor_type_name(gate_type),
ds4_gpu_metal_tensor_type_name(down_type),
moe_path);
return 0;
}
if (!ds4_gpu_finish_command_buffer(cb, owned, "routed batch MoE")) {
if (use_cached_batch) {
ds4_gpu_stream_expert_cache_clear_layer(layer_index);
}
return 0;
}
if (use_cached_batch) {
if (!owned) {
if (ds4_gpu_end_commands() == 0) {
ds4_gpu_stream_expert_cache_clear_layer(layer_index);
return 0;
}
if (ds4_gpu_begin_commands() == 0) {
fprintf(stderr,
"ds4: Metal streaming prefill batch selected addr layer=%u "
"failed to reopen command batch after execution\n",
layer_index);
return 0;
}
}
}
if (q4_batch_table_boundary) {
if (ds4_gpu_end_commands() == 0 || ds4_gpu_begin_commands() == 0) {
return 0;
}
}
#undef DS4_METAL_PROFILE_MOE_STAGE
}
return 1;
}
int ds4_gpu_hc_split_sinkhorn_tensor(
ds4_gpu_tensor *out,
const ds4_gpu_tensor *mix,
const void *model_map,
uint64_t model_size,
uint64_t scale_offset,
uint64_t base_offset,
uint32_t n_hc,
uint32_t sinkhorn_iters,
float eps) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (n_hc == 0 || n_hc > 16) return 0;
const uint64_t mix_hc = 2ull * n_hc + (uint64_t)n_hc * n_hc;
const uint64_t mix_bytes = mix_hc * sizeof(float);
const uint64_t scale_bytes = 3ull * sizeof(float);
@autoreleasepool {
id<MTLBuffer> mixbuf = ds4_gpu_tensor_buffer(mix);
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out);
const uint64_t mix_tensor_bytes = ds4_gpu_tensor_bytes(mix);
const uint64_t out_tensor_bytes = ds4_gpu_tensor_bytes(out);
if (!mixbuf || !outbuf ||
mix_tensor_bytes < mix_bytes ||
out_tensor_bytes < mix_bytes) {
fprintf(stderr, "ds4: Metal HC split received undersized activation buffers\n");
return 0;
}
if (scale_offset > model_size || scale_bytes > model_size - scale_offset ||
base_offset > model_size || mix_bytes > model_size - base_offset) {
fprintf(stderr, "ds4: Metal HC split parameter range is outside the mapped model\n");
return 0;
}
uint64_t scale_inner = 0;
uint64_t base_inner = 0;
id<MTLBuffer> scalebuf = ds4_gpu_wrap_model_range(model_map, model_size, scale_offset, scale_bytes, &scale_inner);
id<MTLBuffer> basebuf = ds4_gpu_wrap_model_range(model_map, model_size, base_offset, mix_bytes, &base_inner);
if (!scalebuf || !basebuf) return 0;
uint64_t n_rows64 = mix_tensor_bytes / mix_bytes;
const uint64_t out_rows64 = out_tensor_bytes / mix_bytes;
if (out_rows64 < n_rows64) n_rows64 = out_rows64;
if (n_rows64 == 0 || n_rows64 > UINT32_MAX) {
fprintf(stderr, "ds4: Metal HC split row count is outside supported range\n");
return 0;
}
ds4_gpu_hc_split_args args = {
.n_hc = (int32_t)n_hc,
.sinkhorn_iters = (int32_t)sinkhorn_iters,
.n_rows = (int64_t)n_rows64,
.mix_hc = (int64_t)mix_hc,
.nb01 = mix_bytes,
.nb1 = mix_bytes,
.eps = eps,
};
const NSUInteger nth = MIN((NSUInteger)256, MAX((NSUInteger)1, (NSUInteger)n_rows64));
const NSUInteger n_tg = ((NSUInteger)n_rows64 + nth - 1u) / nth;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:g_hc_split_sinkhorn_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:mixbuf offset:ds4_gpu_tensor_offset(mix) atIndex:1];
[enc setBuffer:scalebuf offset:(NSUInteger)scale_inner atIndex:2];
[enc setBuffer:basebuf offset:(NSUInteger)base_inner atIndex:3];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(out) atIndex:4];
[enc dispatchThreadgroups:MTLSizeMake(n_tg, 1, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "HC split/sinkhorn")) return 0;
}
return 1;
}
static int ds4_gpu_hc_weighted_sum_strided(
ds4_gpu_tensor *out,
const ds4_gpu_tensor *residual_hc,
const ds4_gpu_tensor *weights,
uint64_t weight_offset,
uint64_t weight_row_stride,
uint32_t n_embd,
uint32_t n_hc,
int round_bf16,
const char *label) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!out || !residual_hc || !weights || n_embd == 0 || n_hc == 0 ||
weight_row_stride < (uint64_t)n_hc * sizeof(float)) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> xbuf = ds4_gpu_tensor_buffer(residual_hc);
id<MTLBuffer> wbuf = ds4_gpu_tensor_buffer(weights);
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out);
const uint64_t out_row_bytes = (uint64_t)n_embd * sizeof(float);
const uint64_t out_tensor_bytes = ds4_gpu_tensor_bytes(out);
if (out_row_bytes == 0 || out_tensor_bytes < out_row_bytes || out_tensor_bytes % out_row_bytes != 0) {
fprintf(stderr, "ds4: Metal HC weighted sum output size is not a whole token row\n");
return 0;
}
const uint64_t n_tokens64 = out_tensor_bytes / out_row_bytes;
if (n_tokens64 == 0 || n_tokens64 > UINT32_MAX) {
fprintf(stderr, "ds4: Metal HC weighted sum token count is outside supported range\n");
return 0;
}
const uint64_t x_row_values = (uint64_t)n_hc * n_embd;
if (x_row_values == 0 ||
x_row_values > UINT64_MAX / sizeof(float) ||
n_tokens64 > UINT64_MAX / (x_row_values * sizeof(float)) ||
n_tokens64 > UINT64_MAX / ((uint64_t)n_hc * sizeof(float))) {
fprintf(stderr, "ds4: Metal HC weighted sum activation size overflow\n");
return 0;
}
const uint64_t x_bytes = n_tokens64 * x_row_values * sizeof(float);
const uint64_t w_last = weight_offset +
(n_tokens64 - 1u) * weight_row_stride +
(uint64_t)n_hc * sizeof(float);
if (!xbuf || !wbuf || !outbuf ||
ds4_gpu_tensor_bytes(residual_hc) < x_bytes ||
ds4_gpu_tensor_bytes(weights) < w_last) {
fprintf(stderr, "ds4: Metal HC weighted sum received undersized activation buffers\n");
return 0;
}
ds4_gpu_hc_weighted_sum_args args = {
.n_embd = n_embd,
.n_hc = n_hc,
.n_tokens = (int64_t)n_tokens64,
.nb_x0 = sizeof(float),
.nb_x1 = (uint64_t)n_embd * sizeof(float),
.nb_x2 = (uint64_t)n_hc * n_embd * sizeof(float),
.nb_w0 = sizeof(float),
.nb_w1 = weight_row_stride,
.nb0 = sizeof(float),
.nb1 = (uint64_t)n_embd * sizeof(float),
.round_bf16 = (uint64_t)(round_bf16 != 0),
};
const uint64_t n_elem = (uint64_t)n_embd * n_tokens64;
const NSUInteger nth = MIN((NSUInteger)256, MAX((NSUInteger)1, (NSUInteger)n_elem));
const NSUInteger n_tg = ((NSUInteger)n_elem + nth - 1u) / nth;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:g_hc_weighted_sum_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(residual_hc) atIndex:1];
[enc setBuffer:wbuf offset:ds4_gpu_tensor_offset(weights) + (NSUInteger)weight_offset atIndex:2];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(out) atIndex:3];
[enc dispatchThreadgroups:MTLSizeMake(n_tg, 1, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, label)) return 0;
}
return 1;
}
int ds4_gpu_hc_weighted_sum_tensor(
ds4_gpu_tensor *out,
const ds4_gpu_tensor *residual_hc,
const ds4_gpu_tensor *weights,
uint32_t n_embd,
uint32_t n_hc) {
return ds4_gpu_hc_weighted_sum_strided(out,
residual_hc,
weights,
0,
(uint64_t)n_hc * sizeof(float),
n_embd,
n_hc,
0,
"HC weighted sum");
}
int ds4_gpu_hc_weighted_sum_bf16_tensor(
ds4_gpu_tensor *out,
const ds4_gpu_tensor *residual_hc,
const ds4_gpu_tensor *weights,
uint32_t n_embd,
uint32_t n_hc) {
return ds4_gpu_hc_weighted_sum_strided(out, residual_hc, weights, 0,
(uint64_t)n_hc * sizeof(float), n_embd, n_hc, 1,
"HC weighted sum");
}
int ds4_gpu_hc_weighted_sum_split_tensor(
ds4_gpu_tensor *out,
const ds4_gpu_tensor *residual_hc,
const ds4_gpu_tensor *split,
uint32_t n_embd,
uint32_t n_hc) {
const uint64_t mix_hc = 2ull * n_hc + (uint64_t)n_hc * n_hc;
return ds4_gpu_hc_weighted_sum_strided(out,
residual_hc,
split,
0,
mix_hc * sizeof(float),
n_embd,
n_hc,
0,
"HC weighted sum split");
}
/* Release decode fused HC pre-sublayer operation. The graph driver owns the
* optional reference fallback so this function stays a direct fused dispatch. */
int ds4_gpu_hc_split_weighted_sum_tensor(
ds4_gpu_tensor *out,
ds4_gpu_tensor *split,
const ds4_gpu_tensor *mix,
const ds4_gpu_tensor *residual_hc,
const void *model_map,
uint64_t model_size,
uint64_t scale_offset,
uint64_t base_offset,
uint32_t n_embd,
uint32_t n_hc,
uint32_t sinkhorn_iters,
float eps) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!out || !split || !mix || !residual_hc || !model_map ||
n_embd == 0 || n_hc == 0) {
return 0;
}
if (n_hc != 4) {
fprintf(stderr, "ds4: Metal fused HC split/sum is specialized for HC=4\n");
return 0;
}
const uint64_t mix_hc = 2ull * n_hc + (uint64_t)n_hc * n_hc;
const uint64_t mix_bytes = mix_hc * sizeof(float);
const uint64_t out_row_bytes = (uint64_t)n_embd * sizeof(float);
const uint64_t residual_row_bytes = (uint64_t)n_hc * n_embd * sizeof(float);
const uint64_t scale_bytes = 3ull * sizeof(float);
@autoreleasepool {
id<MTLBuffer> mixbuf = ds4_gpu_tensor_buffer(mix);
id<MTLBuffer> splitbuf = ds4_gpu_tensor_buffer(split);
id<MTLBuffer> xbuf = ds4_gpu_tensor_buffer(residual_hc);
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out);
const uint64_t out_tensor_bytes = ds4_gpu_tensor_bytes(out);
if (out_row_bytes == 0 || out_tensor_bytes < out_row_bytes ||
out_tensor_bytes % out_row_bytes != 0) {
fprintf(stderr, "ds4: Metal fused HC split/sum output size is not a whole token row\n");
return 0;
}
const uint64_t n_rows64 = out_tensor_bytes / out_row_bytes;
if (n_rows64 == 0 || n_rows64 > UINT32_MAX ||
n_rows64 > UINT64_MAX / mix_bytes ||
n_rows64 > UINT64_MAX / residual_row_bytes) {
fprintf(stderr, "ds4: Metal fused HC split/sum row count is outside supported range\n");
return 0;
}
const uint64_t mix_total_bytes = n_rows64 * mix_bytes;
const uint64_t residual_total_bytes = n_rows64 * residual_row_bytes;
if (!mixbuf || !splitbuf || !xbuf || !outbuf ||
ds4_gpu_tensor_bytes(mix) < mix_total_bytes ||
ds4_gpu_tensor_bytes(split) < mix_total_bytes ||
ds4_gpu_tensor_bytes(residual_hc) < residual_total_bytes) {
fprintf(stderr, "ds4: Metal fused HC split/sum received undersized activation buffers\n");
return 0;
}
if (scale_offset > model_size || scale_bytes > model_size - scale_offset ||
base_offset > model_size || mix_bytes > model_size - base_offset) {
fprintf(stderr, "ds4: Metal fused HC split/sum parameter range is outside the mapped model\n");
return 0;
}
uint64_t scale_inner = 0;
uint64_t base_inner = 0;
id<MTLBuffer> scalebuf = ds4_gpu_wrap_model_range(model_map, model_size, scale_offset, scale_bytes, &scale_inner);
id<MTLBuffer> basebuf = ds4_gpu_wrap_model_range(model_map, model_size, base_offset, mix_bytes, &base_inner);
if (!scalebuf || !basebuf) return 0;
ds4_gpu_hc_split_weighted_sum_args args = {
.n_embd = (int64_t)n_embd,
.n_hc = (int32_t)n_hc,
.sinkhorn_iters = (int32_t)sinkhorn_iters,
.n_rows = (int64_t)n_rows64,
.mix_hc = (int64_t)mix_hc,
.nb_mix1 = mix_bytes,
.nb_split1 = mix_bytes,
.nb_x0 = sizeof(float),
.nb_x1 = (uint64_t)n_embd * sizeof(float),
.nb_x2 = residual_row_bytes,
.nb0 = sizeof(float),
.nb1 = out_row_bytes,
.eps = eps,
};
NSUInteger nth = g_hc_split_weighted_sum_pipeline.maxTotalThreadsPerThreadgroup;
if (nth > 256u) nth = 256u;
if (nth > (NSUInteger)n_embd) nth = (NSUInteger)n_embd;
if (nth == 0) nth = 1u;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:g_hc_split_weighted_sum_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:mixbuf offset:ds4_gpu_tensor_offset(mix) atIndex:1];
[enc setBuffer:scalebuf offset:(NSUInteger)scale_inner atIndex:2];
[enc setBuffer:basebuf offset:(NSUInteger)base_inner atIndex:3];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(residual_hc) atIndex:4];
[enc setBuffer:splitbuf offset:ds4_gpu_tensor_offset(split) atIndex:5];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(out) atIndex:6];
[enc setThreadgroupMemoryLength:(NSUInteger)n_hc * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)n_rows64, 1, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "HC split/sum fused")) return 0;
}
return 1;
}
/* HC-pre plus the immediately following weighted RMSNorm, specialized for
* DS4's HC=4 shape. Both decode and batched prefill use this implementation;
* the kernel preserves their established single-row and batched scale formulas. */
/* pre_w selects the DeepSeek V4.1 variant: one row, the weighted sum's pre
* weights from that tensor, bf16 rounding. */
static int ds4_gpu_hc_split_weighted_sum_norm_impl(
ds4_gpu_tensor *out,
ds4_gpu_tensor *norm_out,
ds4_gpu_tensor *split,
const ds4_gpu_tensor *mix,
const ds4_gpu_tensor *residual_hc,
const ds4_gpu_tensor *pre_w,
const void *model_map,
uint64_t model_size,
uint64_t scale_offset,
uint64_t base_offset,
uint64_t norm_weight_offset,
uint32_t n_embd,
uint32_t n_hc,
uint32_t sinkhorn_iters,
float eps,
float norm_eps) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!out || !norm_out || !split || !mix || !residual_hc || !model_map ||
n_hc != 4 || (n_embd & 3u) != 0) {
return 0;
}
const uint64_t mix_hc = 2ull * n_hc + (uint64_t)n_hc * n_hc;
const uint64_t mix_bytes = mix_hc * sizeof(float);
const uint64_t out_row_bytes = (uint64_t)n_embd * sizeof(float);
const uint64_t residual_row_bytes = (uint64_t)n_hc * n_embd * sizeof(float);
const uint64_t scale_bytes = 3ull * sizeof(float);
@autoreleasepool {
id<MTLBuffer> mixbuf = ds4_gpu_tensor_buffer(mix);
id<MTLBuffer> splitbuf = ds4_gpu_tensor_buffer(split);
id<MTLBuffer> xbuf = ds4_gpu_tensor_buffer(residual_hc);
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out);
id<MTLBuffer> normbuf = ds4_gpu_tensor_buffer(norm_out);
const uint64_t out_tensor_bytes = ds4_gpu_tensor_bytes(out);
if (out_row_bytes == 0 || out_tensor_bytes < out_row_bytes ||
(!pre_w && out_tensor_bytes % out_row_bytes != 0)) {
fprintf(stderr, "ds4: Metal fused HC split/sum/norm output size is not a whole token row\n");
return 0;
}
const uint64_t n_rows64 = pre_w ? 1u : out_tensor_bytes / out_row_bytes;
if (n_rows64 == 0 || n_rows64 > UINT32_MAX ||
n_rows64 > UINT64_MAX / mix_bytes ||
n_rows64 > UINT64_MAX / residual_row_bytes) {
fprintf(stderr, "ds4: Metal fused HC split/sum/norm row count is outside supported range\n");
return 0;
}
const uint64_t mix_total_bytes = n_rows64 * mix_bytes;
const uint64_t residual_total_bytes = n_rows64 * residual_row_bytes;
const uint64_t out_total_bytes = n_rows64 * out_row_bytes;
if (!mixbuf || !splitbuf || !xbuf || !outbuf || !normbuf ||
ds4_gpu_tensor_bytes(mix) < mix_total_bytes ||
ds4_gpu_tensor_bytes(split) < mix_total_bytes ||
ds4_gpu_tensor_bytes(residual_hc) < residual_total_bytes ||
ds4_gpu_tensor_bytes(norm_out) < out_total_bytes) {
fprintf(stderr, "ds4: Metal fused HC split/sum/norm received undersized activation buffers\n");
return 0;
}
if (scale_offset > model_size || scale_bytes > model_size - scale_offset ||
base_offset > model_size || mix_bytes > model_size - base_offset ||
norm_weight_offset > model_size || out_row_bytes > model_size - norm_weight_offset) {
fprintf(stderr, "ds4: Metal fused HC split/sum/norm parameter range is outside the mapped model\n");
return 0;
}
uint64_t scale_inner = 0;
uint64_t base_inner = 0;
uint64_t norm_inner = 0;
id<MTLBuffer> scalebuf = ds4_gpu_wrap_model_range(model_map, model_size, scale_offset, scale_bytes, &scale_inner);
id<MTLBuffer> basebuf = ds4_gpu_wrap_model_range(model_map, model_size, base_offset, mix_bytes, &base_inner);
id<MTLBuffer> normwbuf = ds4_gpu_wrap_model_range(model_map, model_size, norm_weight_offset, out_row_bytes, &norm_inner);
if (!scalebuf || !basebuf || !normwbuf) return 0;
id<MTLComputePipelineState> pipeline = pre_w ?
ds4_gpu_get_pipeline("kernel_dsv41_hc_split_sum_norm") :
ds4_gpu_hot_pipeline(g_hc_split_weighted_sum_norm_pipeline,
"kernel_dsv4_hc_split_weighted_sum_norm4");
if (!pipeline || (pre_w && ds4_gpu_tensor_bytes(pre_w) < (uint64_t)n_hc * sizeof(float))) return 0;
ds4_gpu_hc_split_weighted_sum_norm_args args = {
.n_embd = (int64_t)n_embd,
.n_hc = (int32_t)n_hc,
.sinkhorn_iters = (int32_t)sinkhorn_iters,
.n_rows = (int64_t)n_rows64,
.mix_hc = (int64_t)mix_hc,
.nb_mix1 = mix_bytes,
.nb_split1 = mix_bytes,
.nb_x0 = sizeof(float),
.nb_x1 = (uint64_t)n_embd * sizeof(float),
.nb_x2 = residual_row_bytes,
.nb0 = sizeof(float),
.nb1 = out_row_bytes,
.nb_norm1 = out_row_bytes,
.eps = eps,
.norm_eps = norm_eps,
};
NSUInteger nth = ds4_gpu_rms_norm_threads(n_embd);
if (nth > pipeline.maxTotalThreadsPerThreadgroup) {
fprintf(stderr, "ds4: Metal fused HC split/sum/norm requires %lu threads but pipeline supports %lu\n",
(unsigned long)nth,
(unsigned long)pipeline.maxTotalThreadsPerThreadgroup);
return 0;
}
const NSUInteger shared_bytes = ((NSUInteger)n_embd + 4u + 32u) * sizeof(float);
const NSUInteger max_shared = [g_device maxThreadgroupMemoryLength];
if (max_shared != 0 && shared_bytes > max_shared) {
fprintf(stderr, "ds4: Metal fused HC split/sum/norm requires %lu bytes of threadgroup memory but device supports %lu\n",
(unsigned long)shared_bytes,
(unsigned long)max_shared);
return 0;
}
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:mixbuf offset:ds4_gpu_tensor_offset(mix) atIndex:1];
[enc setBuffer:scalebuf offset:(NSUInteger)scale_inner atIndex:2];
[enc setBuffer:basebuf offset:(NSUInteger)base_inner atIndex:3];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(residual_hc) atIndex:4];
[enc setBuffer:splitbuf offset:ds4_gpu_tensor_offset(split) atIndex:5];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(out) atIndex:6];
[enc setBuffer:normwbuf offset:(NSUInteger)norm_inner atIndex:7];
[enc setBuffer:normbuf offset:ds4_gpu_tensor_offset(norm_out) atIndex:8];
if (pre_w) [enc setBuffer:ds4_gpu_tensor_buffer(pre_w) offset:ds4_gpu_tensor_offset(pre_w) atIndex:9];
[enc setThreadgroupMemoryLength:shared_bytes atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)n_rows64, 1, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "HC split/sum/norm fused")) return 0;
}
return 1;
}
int ds4_gpu_hc_split_weighted_sum_norm_tensor(
ds4_gpu_tensor *out,
ds4_gpu_tensor *norm_out,
ds4_gpu_tensor *split,
const ds4_gpu_tensor *mix,
const ds4_gpu_tensor *residual_hc,
const void *model_map,
uint64_t model_size,
uint64_t scale_offset,
uint64_t base_offset,
uint64_t norm_weight_offset,
uint32_t n_embd,
uint32_t n_hc,
uint32_t sinkhorn_iters,
float eps,
float norm_eps) {
return ds4_gpu_hc_split_weighted_sum_norm_impl(out, norm_out, split, mix, residual_hc, NULL,
model_map, model_size, scale_offset, base_offset, norm_weight_offset,
n_embd, n_hc, sinkhorn_iters, eps, norm_eps);
}
int ds4_gpu_dsv41_hc_project(ds4_gpu_tensor *mix, const ds4_gpu_tensor *residual,
const void *model_map, uint64_t model_size, uint64_t fn_offset,
uint32_t n, uint32_t mix_dim, float eps) {
return ds4_gpu_hc_rms_norm_mix_f16_tensor(mix, residual, model_map, model_size, fn_offset,
n, mix_dim, eps);
}
int ds4_gpu_dsv41_hc_block_input_rows(ds4_gpu_tensor *mix, ds4_gpu_tensor *x, ds4_gpu_tensor *norm,
ds4_gpu_tensor *split, const ds4_gpu_tensor *stream,
const ds4_gpu_tensor *pre, const void *model_map, uint64_t model_size,
uint64_t fn_offset, uint64_t scale_offset, uint64_t base_offset,
uint64_t norm_offset, uint32_t n, uint32_t mix_dim, uint32_t n_embd,
uint32_t n_hc, uint32_t sinkhorn_iters, float hc_eps, float norm_eps,
uint32_t rows, uint32_t pre_stride) {
static id<MTLComputePipelineState> pipeline;
enum { max_rows = 8, row_words = 64 }; /* per row: the count and 32 partial sums, line-aligned */
if (!g_initialized && !ds4_gpu_init()) return 0;
/* the norm/mix half needs the 1024-thread norm and an empty matvec tail */
if (!mix || !x || !norm || !split || !stream || !pre || !model_map || n < 4096u ||
(n % 4096u) != 0u || mix_dim != 24u || n_hc != 4u || (n_embd & 3u) != 0u ||
n != n_hc * n_embd || !rows || rows > max_rows || pre_stride < n_hc) return 0;
@autoreleasepool {
const uint64_t mix_hc = 2ull * n_hc + (uint64_t)n_hc * n_hc;
const uint64_t mix_bytes = mix_hc * sizeof(float);
const uint64_t row_bytes = (uint64_t)n_embd * sizeof(float);
const uint64_t weight_bytes = (uint64_t)n * sizeof(uint16_t) * mix_dim;
if (fn_offset > model_size || weight_bytes > model_size - fn_offset ||
scale_offset > model_size || 3u * sizeof(float) > model_size - scale_offset ||
base_offset > model_size || mix_bytes > model_size - base_offset ||
norm_offset > model_size || row_bytes > model_size - norm_offset ||
ds4_gpu_tensor_bytes(stream) < (uint64_t)n * rows * sizeof(float) ||
ds4_gpu_tensor_bytes(mix) < mix_bytes * rows || ds4_gpu_tensor_bytes(split) < mix_bytes * rows ||
ds4_gpu_tensor_bytes(x) < row_bytes * rows || ds4_gpu_tensor_bytes(norm) < row_bytes * rows ||
ds4_gpu_tensor_bytes(pre) < ((uint64_t)(rows - 1u) * pre_stride + n_hc) * sizeof(float)) return 0;
uint64_t fn_inner = 0, scale_inner = 0, base_inner = 0, norm_inner = 0;
id<MTLBuffer> fnbuf = ds4_gpu_wrap_model_range(model_map, model_size, fn_offset, weight_bytes, &fn_inner);
id<MTLBuffer> scalebuf = ds4_gpu_wrap_model_range(model_map, model_size, scale_offset, 3u * sizeof(float), &scale_inner);
id<MTLBuffer> basebuf = ds4_gpu_wrap_model_range(model_map, model_size, base_offset, mix_bytes, &base_inner);
id<MTLBuffer> normwbuf = ds4_gpu_wrap_model_range(model_map, model_size, norm_offset, row_bytes, &norm_inner);
if (!fnbuf || !scalebuf || !basebuf || !normwbuf) return 0;
if (!pipeline) pipeline = ds4_gpu_get_pipeline("kernel_dsv41_hc_block_input");
/* the arrival counters live with the other completion buffers, which a
* failed command buffer drops */
if (!g_dsv4_completion_cache) return 0;
id<MTLBuffer> completion = [g_dsv4_completion_cache objectForKey:@"v41_block_input"];
if (!completion) {
completion = [g_device newBufferWithLength:(NSUInteger)max_rows * row_words * sizeof(uint32_t)
options:MTLResourceStorageModeShared];
if (!completion) return 0;
memset([completion contents], 0, (size_t)max_rows * row_words * sizeof(uint32_t));
[g_dsv4_completion_cache setObject:completion forKey:@"v41_block_input"];
}
[g_transient_buffers addObject:completion];
const NSUInteger shared_bytes = 96u * sizeof(float); /* the norm/mix reductions */
if (!pipeline || !completion || pipeline.maxTotalThreadsPerThreadgroup < 256u ||
shared_bytes > [g_device maxThreadgroupMemoryLength]) return 0;
const struct { int32_t n, out_dim; float eps; } args = {(int32_t)n, (int32_t)mix_dim, norm_eps};
const ds4_gpu_hc_split_weighted_sum_norm_args split_args = {
.n_embd = (int64_t)n_embd,
.n_hc = (int32_t)n_hc,
.sinkhorn_iters = (int32_t)sinkhorn_iters,
.n_rows = 1,
.mix_hc = (int64_t)mix_hc,
.nb_mix1 = mix_bytes,
.nb_split1 = mix_bytes,
.nb_x0 = sizeof(float),
.nb_x1 = row_bytes,
.nb_x2 = (uint64_t)n_hc * row_bytes,
.nb0 = sizeof(float),
.nb1 = row_bytes,
.nb_norm1 = row_bytes,
.eps = hc_eps,
.norm_eps = norm_eps,
};
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
id<MTLComputeCommandEncoder> enc = cb ? ds4_gpu_compute_encoder(cb) : nil;
if (!enc) return 0;
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBytes:&split_args length:sizeof(split_args) atIndex:1];
[enc setBuffer:ds4_gpu_tensor_buffer(stream) offset:ds4_gpu_tensor_offset(stream) atIndex:2];
[enc setBuffer:fnbuf offset:(NSUInteger)fn_inner atIndex:3];
[enc setBuffer:ds4_gpu_tensor_buffer(mix) offset:ds4_gpu_tensor_offset(mix) atIndex:4];
[enc setBuffer:scalebuf offset:(NSUInteger)scale_inner atIndex:5];
[enc setBuffer:basebuf offset:(NSUInteger)base_inner atIndex:6];
[enc setBuffer:ds4_gpu_tensor_buffer(split) offset:ds4_gpu_tensor_offset(split) atIndex:7];
[enc setBuffer:ds4_gpu_tensor_buffer(x) offset:ds4_gpu_tensor_offset(x) atIndex:8];
[enc setBuffer:normwbuf offset:(NSUInteger)norm_inner atIndex:9];
[enc setBuffer:ds4_gpu_tensor_buffer(norm) offset:ds4_gpu_tensor_offset(norm) atIndex:10];
[enc setBuffer:ds4_gpu_tensor_buffer(pre) offset:ds4_gpu_tensor_offset(pre) atIndex:11];
[enc setBuffer:completion offset:0 atIndex:12];
[enc setBytes:&pre_stride length:sizeof(pre_stride) atIndex:13];
[enc setThreadgroupMemoryLength:shared_bytes atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake((mix_dim + 1u) / 2u + 4u, rows, 1)
threadsPerThreadgroup:MTLSizeMake(32, 8, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_finish_command_buffer(cb, owned, "V4.1 HC block input");
}
}
int ds4_gpu_dsv41_hc_input(ds4_gpu_tensor *x, ds4_gpu_tensor *norm, ds4_gpu_tensor *split,
const ds4_gpu_tensor *mix, const ds4_gpu_tensor *residual,
const ds4_gpu_tensor *pre, const void *model_map, uint64_t model_size,
uint64_t scale_offset, uint64_t base_offset, uint64_t norm_offset,
uint32_t n_embd, uint32_t n_hc, uint32_t sinkhorn_iters,
float hc_eps, float norm_eps) {
return ds4_gpu_hc_split_weighted_sum_norm_impl(x, norm, split, mix, residual, pre,
model_map, model_size, scale_offset, base_offset, norm_offset,
n_embd, n_hc, sinkhorn_iters, hc_eps, norm_eps);
}
typedef struct {
int32_t n;
int32_t out_dim;
float eps;
} ds4_gpu_hc_norm_mix_args;
int ds4_gpu_hc_rms_norm_mix_f16_available(void) {
if (!g_initialized && !ds4_gpu_init()) return 0;
static int cached = -1;
if (cached < 0) {
cached = ds4_gpu_get_pipeline("kernel_dsv4_hc_rms_norm_mix_f16") != nil;
}
return cached;
}
/* Fused decode HC-pre: unweighted RMSNorm over the flattened HC row plus the
* F16 HC-mix matvec in one dispatch. The kernel reproduces the standalone
* kernels' reduction trees bit-exactly (see metal/dsv4_hc.metal), so this is
* purely a dispatch-count optimization. */
int ds4_gpu_hc_rms_norm_mix_f16_tensor(
ds4_gpu_tensor *out,
const ds4_gpu_tensor *x,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint32_t n,
uint32_t out_dim,
float eps) {
if (!g_initialized && !ds4_gpu_init()) return 0;
/* the norm runs 1024 threads and the matvec tail is empty at these widths */
if (!out || !x || !model_map || n < 4096u || (n % 4096u) != 0u || out_dim != 24u) return 0;
@autoreleasepool {
const uint64_t row_bytes = (uint64_t)n * sizeof(uint16_t);
const uint64_t weight_bytes = row_bytes * out_dim;
if (weight_offset > model_size || weight_bytes > model_size - weight_offset) {
fprintf(stderr, "ds4: Metal fused HC norm/mix weight range is outside the mapped model\n");
return 0;
}
id<MTLBuffer> xbuf = ds4_gpu_tensor_buffer(x);
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out);
if (!xbuf || !outbuf ||
ds4_gpu_tensor_bytes(x) < (uint64_t)n * sizeof(float) ||
ds4_gpu_tensor_bytes(out) < (uint64_t)out_dim * sizeof(float)) {
fprintf(stderr, "ds4: Metal fused HC norm/mix received undersized activation buffers\n");
return 0;
}
uint64_t weight_inner = 0;
id<MTLBuffer> wbuf = ds4_gpu_wrap_model_range(model_map, model_size,
weight_offset, weight_bytes,
&weight_inner);
if (!wbuf) return 0;
const bool use_cluster2 = n == 16384u &&
ds4_gpu_device_is_m5_apple_silicon() &&
getenv("DS4_METAL_DISABLE_M5_HC_NORM_MIX_CLUSTER2") == NULL;
id<MTLComputePipelineState> pipeline = ds4_gpu_get_pipeline(
use_cluster2 ? "kernel_dsv4_hc_rms_norm_mix_f16_cluster2" :
"kernel_dsv4_hc_rms_norm_mix_f16");
if (!pipeline ||
(use_cluster2 && pipeline.maxTotalThreadsPerThreadgroup < 512u)) {
return 0;
}
const NSUInteger shared_floats =
use_cluster2 ? 32u + 32u * 2u * 2u : 32u + 32u * 2u;
ds4_gpu_hc_norm_mix_args args = {
.n = (int32_t)n,
.out_dim = (int32_t)out_dim,
.eps = eps,
};
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:1];
[enc setBuffer:wbuf offset:(NSUInteger)weight_inner atIndex:2];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(out) atIndex:3];
[enc setThreadgroupMemoryLength:shared_floats * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:
MTLSizeMake(use_cluster2 ? (out_dim + 3u) / 4u :
(out_dim + 1u) / 2u,
1, 1)
threadsPerThreadgroup:
MTLSizeMake(32, use_cluster2 ? 16 : 8, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "fused HC norm/mix")) return 0;
}
return 1;
}
int ds4_gpu_hc_rms_norm_mix_split_norm_f16_tensor(
ds4_gpu_tensor *mix,
ds4_gpu_tensor *out,
ds4_gpu_tensor *norm_out,
ds4_gpu_tensor *split,
const ds4_gpu_tensor *residual_hc,
const void *model_map,
uint64_t model_size,
uint64_t mix_weight_offset,
uint64_t scale_offset,
uint64_t base_offset,
uint64_t norm_weight_offset,
uint32_t n,
uint32_t mix_dim,
uint32_t n_embd,
uint32_t n_hc,
uint32_t sinkhorn_iters,
float eps,
float hc_eps,
float norm_eps) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!mix || !out || !norm_out || !split || !residual_hc || !model_map ||
n != 16384u || mix_dim != 24u || n_embd != 4096u || n_hc != 4u) {
return 0;
}
@autoreleasepool {
const uint64_t mix_weight_bytes =
(uint64_t)n * (uint64_t)mix_dim * sizeof(uint16_t);
const uint64_t mix_bytes = (uint64_t)mix_dim * sizeof(float);
const uint64_t residual_bytes = (uint64_t)n * sizeof(float);
const uint64_t out_bytes = (uint64_t)n_embd * sizeof(float);
const uint64_t scale_bytes = 3u * sizeof(float);
if (mix_weight_offset > model_size ||
mix_weight_bytes > model_size - mix_weight_offset ||
scale_offset > model_size || scale_bytes > model_size - scale_offset ||
base_offset > model_size || mix_bytes > model_size - base_offset ||
norm_weight_offset > model_size ||
out_bytes > model_size - norm_weight_offset) {
fprintf(stderr,
"ds4: Metal compound HC producer parameter range is outside the mapped model\n");
return 0;
}
id<MTLBuffer> xbuf = ds4_gpu_tensor_buffer(residual_hc);
id<MTLBuffer> mixbuf = ds4_gpu_tensor_buffer(mix);
id<MTLBuffer> splitbuf = ds4_gpu_tensor_buffer(split);
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out);
id<MTLBuffer> normbuf = ds4_gpu_tensor_buffer(norm_out);
if (!xbuf || !mixbuf || !splitbuf || !outbuf || !normbuf ||
ds4_gpu_tensor_bytes(residual_hc) < residual_bytes ||
ds4_gpu_tensor_bytes(mix) < mix_bytes ||
ds4_gpu_tensor_bytes(split) < mix_bytes ||
ds4_gpu_tensor_bytes(out) < out_bytes ||
ds4_gpu_tensor_bytes(norm_out) < out_bytes) {
fprintf(stderr,
"ds4: Metal compound HC producer received undersized activation buffers\n");
return 0;
}
uint64_t mix_weight_inner = 0;
uint64_t scale_inner = 0;
uint64_t base_inner = 0;
uint64_t norm_inner = 0;
id<MTLBuffer> mix_weight = ds4_gpu_wrap_model_range(
model_map, model_size, mix_weight_offset, mix_weight_bytes,
&mix_weight_inner);
id<MTLBuffer> scalebuf = ds4_gpu_wrap_model_range(
model_map, model_size, scale_offset, scale_bytes, &scale_inner);
id<MTLBuffer> basebuf = ds4_gpu_wrap_model_range(
model_map, model_size, base_offset, mix_bytes, &base_inner);
id<MTLBuffer> norm_weight = ds4_gpu_wrap_model_range(
model_map, model_size, norm_weight_offset, out_bytes, &norm_inner);
if (!mix_weight || !scalebuf || !basebuf || !norm_weight) return 0;
if (!g_dsv4_hc_producer_pre_norm_pipeline) {
g_dsv4_hc_producer_pre_norm_pipeline = ds4_gpu_get_pipeline(
"kernel_dsv4_hc_rms_norm_mix_f16_cluster2_pre_norm");
}
id<MTLComputePipelineState> producer =
g_dsv4_hc_producer_pre_norm_pipeline;
if (!producer || producer.maxTotalThreadsPerThreadgroup < 512u) {
return 0;
}
if (!g_dsv4_completion_cache) return 0;
const NSUInteger mix_offset = ds4_gpu_tensor_offset(mix);
id<MTLBuffer> completion = nil;
if (g_dsv4_hc_producer_last_mix_buffer == mixbuf &&
g_dsv4_hc_producer_last_mix_offset == mix_offset) {
completion = g_dsv4_hc_producer_last_completion;
} else {
NSString *completion_key = [NSString stringWithFormat:@"%p:%llu",
(void *)mixbuf, (unsigned long long)mix_offset];
completion = [g_dsv4_completion_cache
objectForKey:completion_key];
if (!completion) {
completion = [g_device newBufferWithLength:sizeof(uint32_t)
options:MTLResourceStorageModeShared];
if (!completion) return 0;
*((uint32_t *)[completion contents]) = 0u;
[g_dsv4_completion_cache
setObject:completion forKey:completion_key];
}
g_dsv4_hc_producer_last_mix_buffer = mixbuf;
g_dsv4_hc_producer_last_mix_offset = mix_offset;
g_dsv4_hc_producer_last_completion = completion;
}
[g_transient_buffers addObject:completion];
ds4_gpu_hc_norm_mix_args mix_args = {
.n = (int32_t)n,
.out_dim = (int32_t)mix_dim,
.eps = eps,
};
ds4_gpu_hc_split_weighted_sum_norm_args split_args = {
.n_embd = (int64_t)n_embd,
.n_hc = (int32_t)n_hc,
.sinkhorn_iters = (int32_t)sinkhorn_iters,
.n_rows = 1,
.mix_hc = (int64_t)mix_dim,
.nb_mix1 = mix_bytes,
.nb_split1 = mix_bytes,
.nb_x0 = sizeof(float),
.nb_x1 = (uint64_t)n_embd * sizeof(float),
.nb_x2 = residual_bytes,
.nb0 = sizeof(float),
.nb1 = out_bytes,
.nb_norm1 = out_bytes,
.eps = hc_eps,
.norm_eps = norm_eps,
};
const NSUInteger shared_floats = 32u + 4u * 32u + 4u + 32u;
const NSUInteger shared_bytes = shared_floats * sizeof(float);
const NSUInteger max_shared = [g_device maxThreadgroupMemoryLength];
if (max_shared != 0 && shared_bytes > max_shared) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:producer];
[enc setBytes:&mix_args length:sizeof(mix_args) atIndex:0];
[enc setBytes:&split_args length:sizeof(split_args) atIndex:1];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(residual_hc) atIndex:2];
[enc setBuffer:mix_weight offset:(NSUInteger)mix_weight_inner atIndex:3];
[enc setBuffer:mixbuf offset:ds4_gpu_tensor_offset(mix) atIndex:4];
[enc setBuffer:scalebuf offset:(NSUInteger)scale_inner atIndex:5];
[enc setBuffer:basebuf offset:(NSUInteger)base_inner atIndex:6];
[enc setBuffer:splitbuf offset:ds4_gpu_tensor_offset(split) atIndex:7];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(out) atIndex:8];
[enc setBuffer:norm_weight offset:(NSUInteger)norm_inner atIndex:9];
[enc setBuffer:normbuf offset:ds4_gpu_tensor_offset(norm_out) atIndex:10];
[enc setBuffer:completion offset:0 atIndex:11];
[enc setThreadgroupMemoryLength:shared_bytes atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(6, 1, 1)
threadsPerThreadgroup:MTLSizeMake(32, 16, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(
cb, owned, "compound HC producer/pre-norm")) {
return -1;
}
}
return 1;
}
int ds4_gpu_hc_expand_add_rms_norm_mix_split_norm_f16_tensor(
ds4_gpu_tensor *mix,
ds4_gpu_tensor *out,
ds4_gpu_tensor *norm_out,
ds4_gpu_tensor *split,
const ds4_gpu_tensor *residual_hc,
const ds4_gpu_tensor *block_out,
const ds4_gpu_tensor *block_add,
const ds4_gpu_tensor *residual_prev,
const ds4_gpu_tensor *post,
const ds4_gpu_tensor *comb,
const void *model_map,
uint64_t model_size,
uint64_t mix_weight_offset,
uint64_t scale_offset,
uint64_t base_offset,
uint64_t norm_weight_offset,
uint32_t n,
uint32_t mix_dim,
uint32_t n_embd,
uint32_t n_hc,
uint32_t sinkhorn_iters,
float eps,
float hc_eps,
float norm_eps) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!mix || !out || !norm_out || !split || !residual_hc || !model_map ||
!block_out || !block_add || !residual_prev || !post || !comb ||
n != 16384u || mix_dim != 24u || n_embd != 4096u || n_hc != 4u) {
return 0;
}
@autoreleasepool {
const uint64_t mix_weight_bytes =
(uint64_t)n * (uint64_t)mix_dim * sizeof(uint16_t);
const uint64_t mix_bytes = (uint64_t)mix_dim * sizeof(float);
const uint64_t residual_bytes = (uint64_t)n * sizeof(float);
const uint64_t out_bytes = (uint64_t)n_embd * sizeof(float);
const uint64_t scale_bytes = 3u * sizeof(float);
if (mix_weight_offset > model_size ||
mix_weight_bytes > model_size - mix_weight_offset ||
scale_offset > model_size || scale_bytes > model_size - scale_offset ||
base_offset > model_size || mix_bytes > model_size - base_offset ||
norm_weight_offset > model_size ||
out_bytes > model_size - norm_weight_offset) {
fprintf(stderr,
"ds4: Metal compound HC producer parameter range is outside the mapped model\n");
return 0;
}
id<MTLBuffer> xbuf = ds4_gpu_tensor_buffer(residual_hc);
id<MTLBuffer> mixbuf = ds4_gpu_tensor_buffer(mix);
id<MTLBuffer> splitbuf = ds4_gpu_tensor_buffer(split);
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out);
id<MTLBuffer> normbuf = ds4_gpu_tensor_buffer(norm_out);
if (!xbuf || !mixbuf || !splitbuf || !outbuf || !normbuf ||
ds4_gpu_tensor_bytes(residual_hc) < residual_bytes ||
ds4_gpu_tensor_bytes(mix) < mix_bytes ||
ds4_gpu_tensor_bytes(split) < mix_bytes ||
ds4_gpu_tensor_bytes(out) < out_bytes ||
ds4_gpu_tensor_bytes(norm_out) < out_bytes) {
fprintf(stderr,
"ds4: Metal compound HC producer received undersized activation buffers\n");
return 0;
}
uint64_t mix_weight_inner = 0;
uint64_t scale_inner = 0;
uint64_t base_inner = 0;
uint64_t norm_inner = 0;
id<MTLBuffer> mix_weight = ds4_gpu_wrap_model_range(
model_map, model_size, mix_weight_offset, mix_weight_bytes,
&mix_weight_inner);
id<MTLBuffer> scalebuf = ds4_gpu_wrap_model_range(
model_map, model_size, scale_offset, scale_bytes, &scale_inner);
id<MTLBuffer> basebuf = ds4_gpu_wrap_model_range(
model_map, model_size, base_offset, mix_bytes, &base_inner);
id<MTLBuffer> norm_weight = ds4_gpu_wrap_model_range(
model_map, model_size, norm_weight_offset, out_bytes, &norm_inner);
if (!mix_weight || !scalebuf || !basebuf || !norm_weight) return 0;
if (!g_dsv4_hc_expand_producer_pre_norm_pipeline) {
g_dsv4_hc_expand_producer_pre_norm_pipeline = ds4_gpu_get_pipeline(
"kernel_dsv4_hc_expand4_rms_norm_mix_f16_cluster2_pre_norm");
}
id<MTLComputePipelineState> producer =
g_dsv4_hc_expand_producer_pre_norm_pipeline;
id<MTLBuffer> blockbuf = ds4_gpu_tensor_buffer(block_out);
id<MTLBuffer> addbuf = ds4_gpu_tensor_buffer(block_add);
id<MTLBuffer> prevbuf = ds4_gpu_tensor_buffer(residual_prev);
id<MTLBuffer> postbuf = ds4_gpu_tensor_buffer(post);
id<MTLBuffer> combbuf = ds4_gpu_tensor_buffer(comb);
if (!blockbuf || !addbuf || !prevbuf || !postbuf || !combbuf ||
ds4_gpu_tensor_bytes(block_out) < out_bytes ||
ds4_gpu_tensor_bytes(block_add) < out_bytes ||
ds4_gpu_tensor_bytes(residual_prev) < residual_bytes ||
ds4_gpu_tensor_bytes(post) < (uint64_t)n_hc * sizeof(float) ||
ds4_gpu_tensor_bytes(comb) < (uint64_t)n_hc * n_hc * sizeof(float)) {
fprintf(stderr,
"ds4: Metal fused HC expand/producer received undersized expand buffers\n");
return 0;
}
if (!producer || producer.maxTotalThreadsPerThreadgroup < 512u) {
return 0;
}
if (!g_dsv4_completion_cache) return 0;
const NSUInteger mix_offset = ds4_gpu_tensor_offset(mix);
id<MTLBuffer> completion = nil;
if (g_dsv4_hc_producer_last_mix_buffer == mixbuf &&
g_dsv4_hc_producer_last_mix_offset == mix_offset) {
completion = g_dsv4_hc_producer_last_completion;
} else {
NSString *completion_key = [NSString stringWithFormat:@"%p:%llu",
(void *)mixbuf, (unsigned long long)mix_offset];
completion = [g_dsv4_completion_cache
objectForKey:completion_key];
if (!completion) {
completion = [g_device newBufferWithLength:sizeof(uint32_t)
options:MTLResourceStorageModeShared];
if (!completion) return 0;
*((uint32_t *)[completion contents]) = 0u;
[g_dsv4_completion_cache
setObject:completion forKey:completion_key];
}
g_dsv4_hc_producer_last_mix_buffer = mixbuf;
g_dsv4_hc_producer_last_mix_offset = mix_offset;
g_dsv4_hc_producer_last_completion = completion;
}
[g_transient_buffers addObject:completion];
/* Monotonic cross-threadgroup barrier counter for this mix tensor,
* plus the host-side dispatch count that sets the target. */
NSString *barrier_key = [NSString stringWithFormat:@"%p:%llu",
(void *)mixbuf, (unsigned long long)mix_offset];
if (!g_dsv4_hc_barrier_cache) {
g_dsv4_hc_barrier_cache = [NSMutableDictionary dictionary];
g_dsv4_hc_barrier_gen = [NSMutableDictionary dictionary];
}
id<MTLBuffer> barrier = [g_dsv4_hc_barrier_cache objectForKey:barrier_key];
if (!barrier) {
barrier = [g_device newBufferWithLength:sizeof(uint32_t)
options:MTLResourceStorageModeShared];
if (!barrier) return 0;
*((uint32_t *)[barrier contents]) = 0u;
[g_dsv4_hc_barrier_cache setObject:barrier forKey:barrier_key];
}
const uint32_t gen =
(uint32_t)[[g_dsv4_hc_barrier_gen objectForKey:barrier_key] unsignedIntValue] + 1u;
[g_dsv4_hc_barrier_gen setObject:@(gen) forKey:barrier_key];
const uint32_t barrier_target = 6u * gen;
[g_transient_buffers addObject:barrier];
ds4_gpu_hc_expand_args ex = {
.n_embd = n_embd,
.n_hc = n_hc,
.n_tokens = 1,
.nb_block0 = sizeof(float),
.nb_block1 = (uint64_t)n_embd * sizeof(float),
.nb_add0 = sizeof(float),
.nb_add1 = (uint64_t)n_embd * sizeof(float),
.nb_res0 = sizeof(float),
.nb_res1 = (uint64_t)n_embd * sizeof(float),
.nb_res2 = (uint64_t)n_hc * n_embd * sizeof(float),
.nb_post0 = sizeof(float),
.nb_post1 = (uint64_t)n_hc * sizeof(float),
.nb_comb0 = sizeof(float),
.nb_comb1 = (uint64_t)n_hc * sizeof(float),
.nb_comb2 = (uint64_t)n_hc * n_hc * sizeof(float),
.nb0 = sizeof(float),
.nb1 = (uint64_t)n_embd * sizeof(float),
.nb2 = (uint64_t)n_hc * n_embd * sizeof(float),
.has_add = 1,
};
ds4_gpu_hc_norm_mix_args mix_args = {
.n = (int32_t)n,
.out_dim = (int32_t)mix_dim,
.eps = eps,
};
ds4_gpu_hc_split_weighted_sum_norm_args split_args = {
.n_embd = (int64_t)n_embd,
.n_hc = (int32_t)n_hc,
.sinkhorn_iters = (int32_t)sinkhorn_iters,
.n_rows = 1,
.mix_hc = (int64_t)mix_dim,
.nb_mix1 = mix_bytes,
.nb_split1 = mix_bytes,
.nb_x0 = sizeof(float),
.nb_x1 = (uint64_t)n_embd * sizeof(float),
.nb_x2 = residual_bytes,
.nb0 = sizeof(float),
.nb1 = out_bytes,
.nb_norm1 = out_bytes,
.eps = hc_eps,
.norm_eps = norm_eps,
};
const NSUInteger shared_floats = 32u + 4u * 32u + 4u + 32u;
const NSUInteger shared_bytes = shared_floats * sizeof(float);
const NSUInteger max_shared = [g_device maxThreadgroupMemoryLength];
if (max_shared != 0 && shared_bytes > max_shared) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:producer];
[enc setBytes:&mix_args length:sizeof(mix_args) atIndex:0];
[enc setBytes:&split_args length:sizeof(split_args) atIndex:1];
[enc setBytes:&ex length:sizeof(ex) atIndex:2];
[enc setBytes:&barrier_target length:sizeof(barrier_target) atIndex:3];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(residual_hc) atIndex:4];
[enc setBuffer:mix_weight offset:(NSUInteger)mix_weight_inner atIndex:5];
[enc setBuffer:mixbuf offset:ds4_gpu_tensor_offset(mix) atIndex:6];
[enc setBuffer:scalebuf offset:(NSUInteger)scale_inner atIndex:7];
[enc setBuffer:basebuf offset:(NSUInteger)base_inner atIndex:8];
[enc setBuffer:splitbuf offset:ds4_gpu_tensor_offset(split) atIndex:9];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(out) atIndex:10];
[enc setBuffer:norm_weight offset:(NSUInteger)norm_inner atIndex:11];
[enc setBuffer:normbuf offset:ds4_gpu_tensor_offset(norm_out) atIndex:12];
[enc setBuffer:completion offset:0 atIndex:13];
[enc setBuffer:barrier offset:0 atIndex:14];
[enc setBuffer:blockbuf offset:ds4_gpu_tensor_offset(block_out) atIndex:15];
[enc setBuffer:addbuf offset:ds4_gpu_tensor_offset(block_add) atIndex:16];
[enc setBuffer:prevbuf offset:ds4_gpu_tensor_offset(residual_prev) atIndex:17];
[enc setBuffer:postbuf offset:ds4_gpu_tensor_offset(post) atIndex:18];
[enc setBuffer:combbuf offset:ds4_gpu_tensor_offset(comb) atIndex:19];
[enc setThreadgroupMemoryLength:shared_bytes atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(6, 1, 1)
threadsPerThreadgroup:MTLSizeMake(32, 16, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(
cb, owned, "fused HC expand/producer/pre-norm")) {
return -1;
}
}
return 1;
}
int ds4_gpu_output_hc_weights_tensor(
ds4_gpu_tensor *out,
const ds4_gpu_tensor *pre,
const void *model_map,
uint64_t model_size,
uint64_t scale_offset,
uint64_t base_offset,
uint32_t n_hc,
float eps) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!out || !pre || !model_map || n_hc == 0) return 0;
@autoreleasepool {
if ((n_hc % 4u) != 0) {
fprintf(stderr, "ds4: Metal output HC weights requires a multiple-of-4 HC width\n");
return 0;
}
id<MTLBuffer> prebuf = ds4_gpu_tensor_buffer(pre);
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out);
const uint64_t row_bytes = (uint64_t)n_hc * sizeof(float);
const uint64_t out_tensor_bytes = ds4_gpu_tensor_bytes(out);
if (row_bytes == 0 || out_tensor_bytes < row_bytes || out_tensor_bytes % row_bytes != 0) {
fprintf(stderr, "ds4: Metal output HC weights size is not a whole token row\n");
return 0;
}
const uint64_t n_tokens64 = out_tensor_bytes / row_bytes;
if (n_tokens64 == 0 || n_tokens64 > UINT32_MAX ||
n_tokens64 > UINT64_MAX / row_bytes) {
fprintf(stderr, "ds4: Metal output HC weights token count is outside supported range\n");
return 0;
}
const uint64_t bytes = n_tokens64 * row_bytes;
if (!prebuf || !outbuf ||
ds4_gpu_tensor_bytes(pre) < bytes ||
ds4_gpu_tensor_bytes(out) < bytes) {
fprintf(stderr, "ds4: Metal output HC weights received undersized buffers\n");
return 0;
}
uint64_t scale_inner = 0;
uint64_t base_inner = 0;
id<MTLBuffer> scalebuf = ds4_gpu_wrap_model_range(model_map, model_size,
scale_offset, sizeof(float),
&scale_inner);
id<MTLBuffer> basebuf = ds4_gpu_wrap_model_range(model_map, model_size,
base_offset, row_bytes,
&base_inner);
if (!scalebuf || !basebuf) return 0;
const bool require_weights4 =
getenv("DS4_METAL_REQUIRE_OUTPUT_HC_WEIGHTS4") != NULL;
const bool weights4_shape = n_hc == 4u && n_tokens64 == 1u;
const bool use_weights4 =
weights4_shape && !g_quality_mode &&
g_output_hc_weights4_pipeline != nil &&
(ds4_gpu_device_name_contains("M3") ||
(g_test_flags & DS4_GPU_TEST_OUTPUT_HC_WEIGHTS4) != 0u) &&
g_output_hc_weights4_pipeline.maxTotalThreadsPerThreadgroup >= 2u;
if (require_weights4 && weights4_shape && !use_weights4) {
fprintf(stderr,
"ds4: required Metal output HC weights4 kernel was not selected\n");
return 0;
}
if (use_weights4) {
ds4_gpu_output_hc_weights4_args args = {
.post_scale = 1.0f,
.eps = eps,
};
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:g_output_hc_weights4_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:prebuf offset:ds4_gpu_tensor_offset(pre) atIndex:1];
[enc setBuffer:scalebuf offset:(NSUInteger)scale_inner atIndex:2];
[enc setBuffer:basebuf offset:(NSUInteger)base_inner atIndex:3];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(out) atIndex:4];
[enc dispatchThreadgroups:MTLSizeMake(1, 1, 1)
threadsPerThreadgroup:MTLSizeMake(2, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(
cb, owned, "output HC weights4")) {
return 0;
}
return 1;
}
const uint32_t n_tokens = (uint32_t)n_tokens64;
ds4_gpu_bin_args mul_args = ds4_gpu_make_bin_rows_args(n_hc, n_tokens, 1);
ds4_gpu_bin_args add_args = ds4_gpu_make_bin_rows_args(n_hc, n_tokens, n_hc);
ds4_gpu_unary_args sigmoid_args = ds4_gpu_make_unary_rows_args(n_hc, n_tokens, 1, 0.0f, 0.0f);
ds4_gpu_unary_args scale_args = ds4_gpu_make_unary_rows_args(n_hc, n_tokens, 1, 1.0f, eps);
NSUInteger mul_nth_max = g_bin_mul_scalar_pipeline.maxTotalThreadsPerThreadgroup;
if (mul_nth_max > 256u) mul_nth_max = 256u;
NSUInteger mul_nth = 1u;
while (2u * mul_nth < (NSUInteger)mul_args.ne0 && mul_nth < mul_nth_max) {
mul_nth *= 2u;
}
NSUInteger add_nth_max = g_add_pipeline.maxTotalThreadsPerThreadgroup;
if (add_nth_max > 256u) add_nth_max = 256u;
NSUInteger add_nth = 1u;
while (2u * add_nth < (NSUInteger)add_args.ne0 && add_nth < add_nth_max) {
add_nth *= 2u;
}
NSUInteger unary_nth_max = g_unary_sigmoid_pipeline.maxTotalThreadsPerThreadgroup;
if (unary_nth_max > 256u) unary_nth_max = 256u;
NSUInteger unary_nth = (NSUInteger)sigmoid_args.ne00;
if (unary_nth > unary_nth_max) unary_nth = unary_nth_max;
if (unary_nth == 0) unary_nth = 1u;
const NSUInteger unary_nk0 = ((NSUInteger)sigmoid_args.ne00 + unary_nth - 1u) / unary_nth;
const NSUInteger out_offset = ds4_gpu_tensor_offset(out);
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:g_bin_mul_scalar_pipeline];
[enc setBytes:&mul_args length:sizeof(mul_args) atIndex:0];
[enc setBuffer:prebuf offset:ds4_gpu_tensor_offset(pre) atIndex:1];
[enc setBuffer:scalebuf offset:(NSUInteger)scale_inner atIndex:2];
[enc setBuffer:outbuf offset:out_offset atIndex:3];
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)mul_args.ne01,
(NSUInteger)mul_args.ne02,
(NSUInteger)mul_args.ne03)
threadsPerThreadgroup:MTLSizeMake(mul_nth, 1, 1)];
[enc setComputePipelineState:g_add_pipeline];
[enc setBytes:&add_args length:sizeof(add_args) atIndex:0];
[enc setBuffer:outbuf offset:out_offset atIndex:1];
[enc setBuffer:basebuf offset:(NSUInteger)base_inner atIndex:2];
[enc setBuffer:outbuf offset:out_offset atIndex:3];
[enc dispatchThreadgroups:MTLSizeMake((NSUInteger)add_args.ne01,
(NSUInteger)add_args.ne02,
(NSUInteger)add_args.ne03)
threadsPerThreadgroup:MTLSizeMake(add_nth, 1, 1)];
[enc setComputePipelineState:g_unary_sigmoid_pipeline];
[enc setBytes:&sigmoid_args length:sizeof(sigmoid_args) atIndex:0];
[enc setBuffer:outbuf offset:out_offset atIndex:1];
[enc setBuffer:outbuf offset:out_offset atIndex:2];
[enc dispatchThreadgroups:MTLSizeMake(unary_nk0 * (NSUInteger)sigmoid_args.ne01,
(NSUInteger)sigmoid_args.ne02,
(NSUInteger)sigmoid_args.ne03)
threadsPerThreadgroup:MTLSizeMake(unary_nth, 1, 1)];
[enc setComputePipelineState:g_unary_scale_pipeline];
[enc setBytes:&scale_args length:sizeof(scale_args) atIndex:0];
[enc setBuffer:outbuf offset:out_offset atIndex:1];
[enc setBuffer:outbuf offset:out_offset atIndex:2];
[enc dispatchThreadgroups:MTLSizeMake(unary_nk0 * (NSUInteger)scale_args.ne01,
(NSUInteger)scale_args.ne02,
(NSUInteger)scale_args.ne03)
threadsPerThreadgroup:MTLSizeMake(unary_nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "output HC weights")) return 0;
}
return 1;
}
int ds4_gpu_hc_expand_tensor(
ds4_gpu_tensor *out_hc,
const ds4_gpu_tensor *block_out,
const ds4_gpu_tensor *residual_hc,
const ds4_gpu_tensor *post,
const ds4_gpu_tensor *comb,
uint32_t n_embd,
uint32_t n_hc) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (n_embd == 0 || n_hc == 0) return 0;
@autoreleasepool {
id<MTLBuffer> blockbuf = ds4_gpu_tensor_buffer(block_out);
id<MTLBuffer> resbuf = ds4_gpu_tensor_buffer(residual_hc);
id<MTLBuffer> postbuf = ds4_gpu_tensor_buffer(post);
id<MTLBuffer> combbuf = ds4_gpu_tensor_buffer(comb);
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out_hc);
const uint64_t hc_row_bytes = (uint64_t)n_hc * n_embd * sizeof(float);
const uint64_t out_tensor_bytes = ds4_gpu_tensor_bytes(out_hc);
if (hc_row_bytes == 0 || out_tensor_bytes < hc_row_bytes || out_tensor_bytes % hc_row_bytes != 0) {
fprintf(stderr, "ds4: Metal HC expand output size is not a whole HC token row\n");
return 0;
}
const uint64_t n_tokens64 = out_tensor_bytes / hc_row_bytes;
if (n_tokens64 == 0 || n_tokens64 > UINT32_MAX) {
fprintf(stderr, "ds4: Metal HC expand token count is outside supported range\n");
return 0;
}
const uint64_t block_values = (uint64_t)n_embd;
const uint64_t hc_values = (uint64_t)n_hc * n_embd;
const uint64_t comb_values = (uint64_t)n_hc * n_hc;
if (hc_values == 0 ||
hc_values > UINT64_MAX / sizeof(float) ||
comb_values > UINT64_MAX / sizeof(float) ||
n_tokens64 > UINT64_MAX / (block_values * sizeof(float)) ||
n_tokens64 > UINT64_MAX / (hc_values * sizeof(float)) ||
n_tokens64 > UINT64_MAX / (comb_values * sizeof(float))) {
fprintf(stderr, "ds4: Metal HC expand activation size overflow\n");
return 0;
}
const uint64_t block_bytes = n_tokens64 * block_values * sizeof(float);
const uint64_t hc_bytes = n_tokens64 * hc_values * sizeof(float);
const uint64_t post_bytes = n_tokens64 * (uint64_t)n_hc * sizeof(float);
const uint64_t comb_bytes = n_tokens64 * comb_values * sizeof(float);
if (!blockbuf || !resbuf || !postbuf || !combbuf || !outbuf ||
ds4_gpu_tensor_bytes(block_out) < block_bytes ||
ds4_gpu_tensor_bytes(residual_hc) < hc_bytes ||
ds4_gpu_tensor_bytes(post) < post_bytes ||
ds4_gpu_tensor_bytes(comb) < comb_bytes) {
fprintf(stderr, "ds4: Metal HC expand received undersized activation buffers\n");
return 0;
}
ds4_gpu_hc_expand_args args = {
.n_embd = n_embd,
.n_hc = n_hc,
.n_tokens = (int64_t)n_tokens64,
.nb_block0 = sizeof(float),
.nb_block1 = (uint64_t)n_embd * sizeof(float),
.nb_add0 = sizeof(float),
.nb_add1 = (uint64_t)n_embd * sizeof(float),
.nb_res0 = sizeof(float),
.nb_res1 = (uint64_t)n_embd * sizeof(float),
.nb_res2 = (uint64_t)n_hc * n_embd * sizeof(float),
.nb_post0 = sizeof(float),
.nb_post1 = (uint64_t)n_hc * sizeof(float),
.nb_comb0 = sizeof(float),
.nb_comb1 = (uint64_t)n_hc * sizeof(float),
.nb_comb2 = (uint64_t)n_hc * n_hc * sizeof(float),
.nb0 = sizeof(float),
.nb1 = (uint64_t)n_embd * sizeof(float),
.nb2 = (uint64_t)n_hc * n_embd * sizeof(float),
.has_add = 0,
};
id<MTLComputePipelineState> expand_pipeline = g_hc_expand_pipeline;
uint64_t n_elem = (uint64_t)n_embd * n_hc * n_tokens64;
if (n_hc == 4) {
expand_pipeline = ds4_gpu_hot_pipeline(g_dsv4_hc_expand4_pipeline,
"kernel_dsv4_hc_expand4");
n_elem = (uint64_t)n_embd * n_tokens64;
}
if (!expand_pipeline) return 0;
const NSUInteger nth = MIN((NSUInteger)256, MAX((NSUInteger)1, (NSUInteger)n_elem));
const NSUInteger n_tg = ((NSUInteger)n_elem + nth - 1u) / nth;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:expand_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:blockbuf offset:ds4_gpu_tensor_offset(block_out) atIndex:1];
[enc setBuffer:resbuf offset:ds4_gpu_tensor_offset(residual_hc) atIndex:2];
[enc setBuffer:postbuf offset:ds4_gpu_tensor_offset(post) atIndex:3];
[enc setBuffer:combbuf offset:ds4_gpu_tensor_offset(comb) atIndex:4];
[enc setBuffer:blockbuf offset:ds4_gpu_tensor_offset(block_out) atIndex:5];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(out_hc) atIndex:6];
[enc dispatchThreadgroups:MTLSizeMake(n_tg, 1, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "HC expand")) return 0;
}
return 1;
}
/* Expand of the SUM of two block vectors — the TP attention combine folded
* into the HC expand (the kernel's has_add path adds them element-wise
* before the post/comb mixing, canonical rank order preserved by argument
* position). */
int ds4_gpu_hc_expand_add_tensor(
ds4_gpu_tensor *out_hc,
const ds4_gpu_tensor *block_out,
const ds4_gpu_tensor *block_add,
const ds4_gpu_tensor *residual_hc,
const ds4_gpu_tensor *post,
const ds4_gpu_tensor *comb,
uint32_t n_embd,
uint32_t n_hc) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (n_embd == 0 || n_hc != 4) return 0;
@autoreleasepool {
id<MTLBuffer> blockbuf = ds4_gpu_tensor_buffer(block_out);
id<MTLBuffer> addbuf = ds4_gpu_tensor_buffer(block_add);
id<MTLBuffer> resbuf = ds4_gpu_tensor_buffer(residual_hc);
id<MTLBuffer> postbuf = ds4_gpu_tensor_buffer(post);
id<MTLBuffer> combbuf = ds4_gpu_tensor_buffer(comb);
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out_hc);
const uint64_t hc_row_bytes = (uint64_t)n_hc * n_embd * sizeof(float);
const uint64_t out_tensor_bytes = ds4_gpu_tensor_bytes(out_hc);
if (hc_row_bytes == 0 || out_tensor_bytes < hc_row_bytes || out_tensor_bytes % hc_row_bytes != 0) {
fprintf(stderr, "ds4: Metal HC expand output size is not a whole HC token row\n");
return 0;
}
const uint64_t n_tokens64 = out_tensor_bytes / hc_row_bytes;
if (n_tokens64 == 0 || n_tokens64 > UINT32_MAX) {
fprintf(stderr, "ds4: Metal HC expand token count is outside supported range\n");
return 0;
}
const uint64_t block_values = (uint64_t)n_embd;
const uint64_t hc_values = (uint64_t)n_hc * n_embd;
const uint64_t comb_values = (uint64_t)n_hc * n_hc;
if (hc_values == 0 ||
hc_values > UINT64_MAX / sizeof(float) ||
comb_values > UINT64_MAX / sizeof(float) ||
n_tokens64 > UINT64_MAX / (block_values * sizeof(float)) ||
n_tokens64 > UINT64_MAX / (hc_values * sizeof(float)) ||
n_tokens64 > UINT64_MAX / (comb_values * sizeof(float))) {
fprintf(stderr, "ds4: Metal HC expand activation size overflow\n");
return 0;
}
const uint64_t block_bytes = n_tokens64 * block_values * sizeof(float);
const uint64_t hc_bytes = n_tokens64 * hc_values * sizeof(float);
const uint64_t post_bytes = n_tokens64 * (uint64_t)n_hc * sizeof(float);
const uint64_t comb_bytes = n_tokens64 * comb_values * sizeof(float);
if (!blockbuf || !addbuf || !resbuf || !postbuf || !combbuf || !outbuf ||
ds4_gpu_tensor_bytes(block_out) < block_bytes ||
ds4_gpu_tensor_bytes(block_add) < block_bytes ||
ds4_gpu_tensor_bytes(residual_hc) < hc_bytes ||
ds4_gpu_tensor_bytes(post) < post_bytes ||
ds4_gpu_tensor_bytes(comb) < comb_bytes) {
fprintf(stderr, "ds4: Metal HC expand received undersized activation buffers\n");
return 0;
}
ds4_gpu_hc_expand_args args = {
.n_embd = n_embd,
.n_hc = n_hc,
.n_tokens = (int64_t)n_tokens64,
.nb_block0 = sizeof(float),
.nb_block1 = (uint64_t)n_embd * sizeof(float),
.nb_add0 = sizeof(float),
.nb_add1 = (uint64_t)n_embd * sizeof(float),
.nb_res0 = sizeof(float),
.nb_res1 = (uint64_t)n_embd * sizeof(float),
.nb_res2 = (uint64_t)n_hc * n_embd * sizeof(float),
.nb_post0 = sizeof(float),
.nb_post1 = (uint64_t)n_hc * sizeof(float),
.nb_comb0 = sizeof(float),
.nb_comb1 = (uint64_t)n_hc * sizeof(float),
.nb_comb2 = (uint64_t)n_hc * n_hc * sizeof(float),
.nb0 = sizeof(float),
.nb1 = (uint64_t)n_embd * sizeof(float),
.nb2 = (uint64_t)n_hc * n_embd * sizeof(float),
.has_add = 1,
};
id<MTLComputePipelineState> expand_pipeline = g_hc_expand_pipeline;
uint64_t n_elem = (uint64_t)n_embd * n_hc * n_tokens64;
if (n_hc == 4) {
expand_pipeline = ds4_gpu_hot_pipeline(g_dsv4_hc_expand4_pipeline,
"kernel_dsv4_hc_expand4");
n_elem = (uint64_t)n_embd * n_tokens64;
}
if (!expand_pipeline) return 0;
const NSUInteger nth = MIN((NSUInteger)256, MAX((NSUInteger)1, (NSUInteger)n_elem));
const NSUInteger n_tg = ((NSUInteger)n_elem + nth - 1u) / nth;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:expand_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:blockbuf offset:ds4_gpu_tensor_offset(block_out) atIndex:1];
[enc setBuffer:resbuf offset:ds4_gpu_tensor_offset(residual_hc) atIndex:2];
[enc setBuffer:postbuf offset:ds4_gpu_tensor_offset(post) atIndex:3];
[enc setBuffer:combbuf offset:ds4_gpu_tensor_offset(comb) atIndex:4];
[enc setBuffer:addbuf offset:ds4_gpu_tensor_offset(block_add) atIndex:5];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(out_hc) atIndex:6];
[enc dispatchThreadgroups:MTLSizeMake(n_tg, 1, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "HC expand add")) return 0;
}
return 1;
}
static int ds4_gpu_hc_expand_split_impl(
ds4_gpu_tensor *out_hc,
const ds4_gpu_tensor *block_out,
const ds4_gpu_tensor *block_add,
const ds4_gpu_tensor *residual_hc,
const ds4_gpu_tensor *split,
uint32_t n_embd,
uint32_t n_hc,
int round_bf16) {
if (!g_initialized && !ds4_gpu_init()) {
fprintf(stderr, "ds4: Metal HC expand split could not initialize the backend\n");
return 0;
}
if (!out_hc || !block_out || !residual_hc || !split || n_embd == 0 || n_hc == 0) {
fprintf(stderr, "ds4: Metal HC expand split received invalid arguments\n");
return 0;
}
@autoreleasepool {
id<MTLBuffer> blockbuf = ds4_gpu_tensor_buffer(block_out);
id<MTLBuffer> resbuf = ds4_gpu_tensor_buffer(residual_hc);
id<MTLBuffer> splitbuf = ds4_gpu_tensor_buffer(split);
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out_hc);
const uint64_t hc_row_bytes = (uint64_t)n_hc * n_embd * sizeof(float);
const uint64_t out_tensor_bytes = ds4_gpu_tensor_bytes(out_hc);
if (hc_row_bytes == 0 || out_tensor_bytes < hc_row_bytes || out_tensor_bytes % hc_row_bytes != 0) {
fprintf(stderr, "ds4: Metal HC expand split output size is not a whole HC token row\n");
return 0;
}
const uint64_t n_tokens64 = out_tensor_bytes / hc_row_bytes;
if (n_tokens64 == 0 || n_tokens64 > UINT32_MAX) {
fprintf(stderr, "ds4: Metal HC expand split token count is outside supported range\n");
return 0;
}
const uint64_t block_values = (uint64_t)n_embd;
const uint64_t hc_values = (uint64_t)n_hc * n_embd;
const uint64_t mix_hc = 2ull * n_hc + (uint64_t)n_hc * n_hc;
if (hc_values == 0 ||
hc_values > UINT64_MAX / sizeof(float) ||
mix_hc > UINT64_MAX / sizeof(float) ||
n_tokens64 > UINT64_MAX / (block_values * sizeof(float)) ||
n_tokens64 > UINT64_MAX / (hc_values * sizeof(float)) ||
n_tokens64 > UINT64_MAX / (mix_hc * sizeof(float))) {
fprintf(stderr, "ds4: Metal HC expand split activation size overflow\n");
return 0;
}
const uint64_t block_bytes = n_tokens64 * block_values * sizeof(float);
const uint64_t hc_bytes = n_tokens64 * hc_values * sizeof(float);
const uint64_t split_bytes = n_tokens64 * mix_hc * sizeof(float);
if (!blockbuf || !resbuf || !splitbuf || !outbuf ||
ds4_gpu_tensor_bytes(block_out) < block_bytes ||
ds4_gpu_tensor_bytes(residual_hc) < hc_bytes ||
ds4_gpu_tensor_bytes(split) < split_bytes) {
fprintf(stderr, "ds4: Metal HC expand split received undersized activation buffers\n");
return 0;
}
ds4_gpu_hc_expand_args args = {
.n_embd = n_embd,
.n_hc = n_hc,
.n_tokens = (int64_t)n_tokens64,
.nb_block0 = sizeof(float),
.nb_block1 = (uint64_t)n_embd * sizeof(float),
.nb_add0 = sizeof(float),
.nb_add1 = (uint64_t)n_embd * sizeof(float),
.nb_res0 = sizeof(float),
.nb_res1 = (uint64_t)n_embd * sizeof(float),
.nb_res2 = (uint64_t)n_hc * n_embd * sizeof(float),
.nb_post0 = sizeof(float),
.nb_post1 = mix_hc * sizeof(float),
.nb_comb0 = sizeof(float),
.nb_comb1 = (uint64_t)n_hc * sizeof(float),
.nb_comb2 = mix_hc * sizeof(float),
.nb0 = sizeof(float),
.nb1 = (uint64_t)n_embd * sizeof(float),
.nb2 = (uint64_t)n_hc * n_embd * sizeof(float),
.has_add = block_add != NULL,
.round_bf16 = round_bf16 != 0,
.round_block_in = block_add != NULL && round_bf16 != 0,
};
id<MTLComputePipelineState> expand_pipeline = g_hc_expand_pipeline;
uint64_t n_elem = (uint64_t)n_embd * n_hc * n_tokens64;
if (n_hc == 4) {
expand_pipeline = ds4_gpu_hot_pipeline(g_dsv4_hc_expand4_pipeline,
"kernel_dsv4_hc_expand4");
n_elem = (uint64_t)n_embd * n_tokens64;
}
if (!expand_pipeline) {
fprintf(stderr, "ds4: Metal HC expand split pipeline is unavailable\n");
return 0;
}
const NSUInteger nth = MIN((NSUInteger)256, MAX((NSUInteger)1, (NSUInteger)n_elem));
const NSUInteger n_tg = ((NSUInteger)n_elem + nth - 1u) / nth;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) {
fprintf(stderr, "ds4: Metal HC expand split could not obtain a command buffer\n");
return 0;
}
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:expand_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:blockbuf offset:ds4_gpu_tensor_offset(block_out) atIndex:1];
[enc setBuffer:resbuf offset:ds4_gpu_tensor_offset(residual_hc) atIndex:2];
[enc setBuffer:splitbuf offset:ds4_gpu_tensor_offset(split) + (NSUInteger)n_hc * sizeof(float) atIndex:3];
[enc setBuffer:splitbuf offset:ds4_gpu_tensor_offset(split) + (NSUInteger)(2u * n_hc) * sizeof(float) atIndex:4];
[enc setBuffer:block_add ? ds4_gpu_tensor_buffer(block_add) : blockbuf
offset:block_add ? ds4_gpu_tensor_offset(block_add) : ds4_gpu_tensor_offset(block_out) atIndex:5];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(out_hc) atIndex:6];
[enc dispatchThreadgroups:MTLSizeMake(n_tg, 1, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "HC expand split")) return 0;
}
return 1;
}
int ds4_gpu_hc_expand_split_tensor(
ds4_gpu_tensor *out_hc,
const ds4_gpu_tensor *block_out,
const ds4_gpu_tensor *residual_hc,
const ds4_gpu_tensor *split,
uint32_t n_embd,
uint32_t n_hc) {
return ds4_gpu_hc_expand_split_impl(out_hc, block_out, NULL, residual_hc, split, n_embd, n_hc, 0);
}
int ds4_gpu_hc_expand_split_bf16_tensor(
ds4_gpu_tensor *out_hc,
const ds4_gpu_tensor *block_out,
const ds4_gpu_tensor *residual_hc,
const ds4_gpu_tensor *split,
uint32_t n_embd,
uint32_t n_hc) {
return ds4_gpu_hc_expand_split_impl(out_hc, block_out, NULL, residual_hc, split, n_embd, n_hc, 1);
}
/* The block is block_out + block_add rounded to bf16 before the expansion. */
int ds4_gpu_hc_expand_split_add_bf16_tensor(
ds4_gpu_tensor *out_hc,
const ds4_gpu_tensor *block_out,
const ds4_gpu_tensor *block_add,
const ds4_gpu_tensor *residual_hc,
const ds4_gpu_tensor *split,
uint32_t n_embd,
uint32_t n_hc) {
if (!block_add || ds4_gpu_tensor_bytes(block_add) < ds4_gpu_tensor_bytes(block_out)) return 0;
return ds4_gpu_hc_expand_split_impl(out_hc, block_out, block_add, residual_hc, split, n_embd, n_hc, 1);
}
int ds4_gpu_hc_expand_split_half_tensor(
ds4_gpu_tensor *out_hc,
const ds4_gpu_tensor *block_out_h,
const ds4_gpu_tensor *residual_hc,
const ds4_gpu_tensor *split,
uint32_t n_embd,
uint32_t n_hc) {
(void)out_hc; (void)block_out_h; (void)residual_hc; (void)split;
(void)n_embd; (void)n_hc;
return 0;
}
int ds4_gpu_hc_expand_add_split_tensor(
ds4_gpu_tensor *out_hc,
const ds4_gpu_tensor *block_out,
const ds4_gpu_tensor *block_add,
const ds4_gpu_tensor *residual_hc,
const ds4_gpu_tensor *split,
uint32_t n_embd,
uint32_t n_hc) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!out_hc || !block_out || !block_add || !residual_hc || !split || n_embd == 0 || n_hc == 0) return 0;
@autoreleasepool {
id<MTLBuffer> blockbuf = ds4_gpu_tensor_buffer(block_out);
id<MTLBuffer> addbuf = ds4_gpu_tensor_buffer(block_add);
id<MTLBuffer> resbuf = ds4_gpu_tensor_buffer(residual_hc);
id<MTLBuffer> splitbuf = ds4_gpu_tensor_buffer(split);
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out_hc);
const uint64_t hc_row_bytes = (uint64_t)n_hc * n_embd * sizeof(float);
const uint64_t out_tensor_bytes = ds4_gpu_tensor_bytes(out_hc);
if (hc_row_bytes == 0 || out_tensor_bytes < hc_row_bytes || out_tensor_bytes % hc_row_bytes != 0) {
fprintf(stderr, "ds4: Metal HC expand add split output size is not a whole HC token row\n");
return 0;
}
const uint64_t n_tokens64 = out_tensor_bytes / hc_row_bytes;
if (n_tokens64 == 0 || n_tokens64 > UINT32_MAX) {
fprintf(stderr, "ds4: Metal HC expand add split token count is outside supported range\n");
return 0;
}
const uint64_t block_values = (uint64_t)n_embd;
const uint64_t hc_values = (uint64_t)n_hc * n_embd;
const uint64_t mix_hc = 2ull * n_hc + (uint64_t)n_hc * n_hc;
if (hc_values == 0 ||
hc_values > UINT64_MAX / sizeof(float) ||
mix_hc > UINT64_MAX / sizeof(float) ||
n_tokens64 > UINT64_MAX / (block_values * sizeof(float)) ||
n_tokens64 > UINT64_MAX / (hc_values * sizeof(float)) ||
n_tokens64 > UINT64_MAX / (mix_hc * sizeof(float))) {
fprintf(stderr, "ds4: Metal HC expand add split activation size overflow\n");
return 0;
}
const uint64_t block_bytes = n_tokens64 * block_values * sizeof(float);
const uint64_t hc_bytes = n_tokens64 * hc_values * sizeof(float);
const uint64_t split_bytes = n_tokens64 * mix_hc * sizeof(float);
if (!blockbuf || !addbuf || !resbuf || !splitbuf || !outbuf ||
ds4_gpu_tensor_bytes(block_out) < block_bytes ||
ds4_gpu_tensor_bytes(block_add) < block_bytes ||
ds4_gpu_tensor_bytes(residual_hc) < hc_bytes ||
ds4_gpu_tensor_bytes(split) < split_bytes) {
fprintf(stderr, "ds4: Metal HC expand add split received undersized activation buffers\n");
return 0;
}
ds4_gpu_hc_expand_args args = {
.n_embd = n_embd,
.n_hc = n_hc,
.n_tokens = (int64_t)n_tokens64,
.nb_block0 = sizeof(float),
.nb_block1 = (uint64_t)n_embd * sizeof(float),
.nb_add0 = sizeof(float),
.nb_add1 = (uint64_t)n_embd * sizeof(float),
.nb_res0 = sizeof(float),
.nb_res1 = (uint64_t)n_embd * sizeof(float),
.nb_res2 = (uint64_t)n_hc * n_embd * sizeof(float),
.nb_post0 = sizeof(float),
.nb_post1 = mix_hc * sizeof(float),
.nb_comb0 = sizeof(float),
.nb_comb1 = (uint64_t)n_hc * sizeof(float),
.nb_comb2 = mix_hc * sizeof(float),
.nb0 = sizeof(float),
.nb1 = (uint64_t)n_embd * sizeof(float),
.nb2 = (uint64_t)n_hc * n_embd * sizeof(float),
.has_add = 1,
};
id<MTLComputePipelineState> expand_pipeline = g_hc_expand_pipeline;
uint64_t n_elem = (uint64_t)n_embd * n_hc * n_tokens64;
if (n_hc == 4) {
expand_pipeline = ds4_gpu_hot_pipeline(g_dsv4_hc_expand4_pipeline,
"kernel_dsv4_hc_expand4");
n_elem = (uint64_t)n_embd * n_tokens64;
}
if (!expand_pipeline) return 0;
const NSUInteger nth = MIN((NSUInteger)256, MAX((NSUInteger)1, (NSUInteger)n_elem));
const NSUInteger n_tg = ((NSUInteger)n_elem + nth - 1u) / nth;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:expand_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:blockbuf offset:ds4_gpu_tensor_offset(block_out) atIndex:1];
[enc setBuffer:resbuf offset:ds4_gpu_tensor_offset(residual_hc) atIndex:2];
[enc setBuffer:splitbuf offset:ds4_gpu_tensor_offset(split) + (NSUInteger)n_hc * sizeof(float) atIndex:3];
[enc setBuffer:splitbuf offset:ds4_gpu_tensor_offset(split) + (NSUInteger)(2u * n_hc) * sizeof(float) atIndex:4];
[enc setBuffer:addbuf offset:ds4_gpu_tensor_offset(block_add) atIndex:5];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(out_hc) atIndex:6];
[enc dispatchThreadgroups:MTLSizeMake(n_tg, 1, 1)
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "HC expand add split")) return 0;
}
return 1;
}
int ds4_gpu_hc_expand_add_split_half_add_tensor(
ds4_gpu_tensor *out_hc,
const ds4_gpu_tensor *block_out,
const ds4_gpu_tensor *block_add_h,
const ds4_gpu_tensor *residual_hc,
const ds4_gpu_tensor *split,
uint32_t n_embd,
uint32_t n_hc) {
(void)out_hc; (void)block_out; (void)block_add_h; (void)residual_hc;
(void)split; (void)n_embd; (void)n_hc;
return 0;
}
int ds4_gpu_shared_down_hc_expand_q8_0_tensor(
ds4_gpu_tensor *out_hc,
ds4_gpu_tensor *shared_out,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint64_t in_dim,
uint64_t out_dim,
const ds4_gpu_tensor *shared_mid,
const ds4_gpu_tensor *routed_out,
const ds4_gpu_tensor *residual_hc,
const ds4_gpu_tensor *split,
uint32_t n_embd,
uint32_t n_hc) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!out_hc || !shared_out || !model_map || !shared_mid || !routed_out ||
!residual_hc || !split || n_embd == 0 || n_hc == 0 ||
n_hc != 4 || out_dim != n_embd || (in_dim & 31u) != 0 ||
in_dim > UINT32_MAX || out_dim > UINT32_MAX) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> midbuf = ds4_gpu_tensor_buffer(shared_mid);
id<MTLBuffer> sharedbuf = ds4_gpu_tensor_buffer(shared_out);
id<MTLBuffer> routedbuf = ds4_gpu_tensor_buffer(routed_out);
id<MTLBuffer> resbuf = ds4_gpu_tensor_buffer(residual_hc);
id<MTLBuffer> splitbuf = ds4_gpu_tensor_buffer(split);
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out_hc);
const uint64_t row_bytes = (in_dim / 32u) * 34u;
const uint64_t weight_bytes = out_dim * row_bytes;
const uint64_t shared_mid_bytes = in_dim * sizeof(float);
const uint64_t embd_bytes = out_dim * sizeof(float);
const uint64_t hc_bytes = (uint64_t)n_hc * n_embd * sizeof(float);
const uint64_t mix_hc = 2ull * n_hc + (uint64_t)n_hc * n_hc;
const uint64_t split_bytes = mix_hc * sizeof(float);
if (weight_offset > model_size || weight_bytes > model_size - weight_offset) {
fprintf(stderr, "ds4: Metal shared-down HC fusion weight range is outside the mapped model\n");
return 0;
}
if (!midbuf || !sharedbuf || !routedbuf || !resbuf || !splitbuf || !outbuf ||
ds4_gpu_tensor_bytes(shared_mid) < shared_mid_bytes ||
ds4_gpu_tensor_bytes(shared_out) < embd_bytes ||
ds4_gpu_tensor_bytes(routed_out) < embd_bytes ||
ds4_gpu_tensor_bytes(residual_hc) < hc_bytes ||
ds4_gpu_tensor_bytes(split) < split_bytes ||
ds4_gpu_tensor_bytes(out_hc) < hc_bytes) {
fprintf(stderr, "ds4: Metal shared-down HC fusion received undersized buffers\n");
return 0;
}
uint64_t inner_offset = 0;
id<MTLBuffer> wbuf = ds4_gpu_wrap_model_range(model_map, model_size,
weight_offset, weight_bytes,
&inner_offset);
if (!wbuf) return 0;
ds4_gpu_q8_0_matvec_args mv_args = ds4_gpu_make_q8_0_mv_args(in_dim, out_dim);
ds4_gpu_mv_dispatch mv_dispatch = ds4_gpu_make_q8_0_mv_dispatch();
mv_args.nr0 = mv_dispatch.nr0;
ds4_gpu_hc_expand_args hc_args = {
.n_embd = n_embd,
.n_hc = n_hc,
.n_tokens = 1,
.nb_block0 = sizeof(float),
.nb_block1 = (uint64_t)n_embd * sizeof(float),
.nb_add0 = sizeof(float),
.nb_add1 = (uint64_t)n_embd * sizeof(float),
.nb_res0 = sizeof(float),
.nb_res1 = (uint64_t)n_embd * sizeof(float),
.nb_res2 = (uint64_t)n_hc * n_embd * sizeof(float),
.nb_post0 = sizeof(float),
.nb_post1 = mix_hc * sizeof(float),
.nb_comb0 = sizeof(float),
.nb_comb1 = (uint64_t)n_hc * sizeof(float),
.nb_comb2 = mix_hc * sizeof(float),
.nb0 = sizeof(float),
.nb1 = (uint64_t)n_embd * sizeof(float),
.nb2 = (uint64_t)n_hc * n_embd * sizeof(float),
.has_add = 1,
};
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_mul_mv_pipeline("kernel_dsv4_shared_down_hc_expand4_q8_0",
mv_dispatch.nsg);
if (!pipeline) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&mv_args length:sizeof(mv_args) atIndex:0];
[enc setBytes:&hc_args length:sizeof(hc_args) atIndex:1];
[enc setBuffer:wbuf offset:(NSUInteger)inner_offset atIndex:2];
[enc setBuffer:midbuf offset:ds4_gpu_tensor_offset(shared_mid) atIndex:3];
[enc setBuffer:sharedbuf offset:ds4_gpu_tensor_offset(shared_out) atIndex:4];
[enc setBuffer:routedbuf offset:ds4_gpu_tensor_offset(routed_out) atIndex:5];
[enc setBuffer:resbuf offset:ds4_gpu_tensor_offset(residual_hc) atIndex:6];
[enc setBuffer:splitbuf offset:ds4_gpu_tensor_offset(split) + (NSUInteger)n_hc * sizeof(float) atIndex:7];
[enc setBuffer:splitbuf offset:ds4_gpu_tensor_offset(split) + (NSUInteger)(2u * n_hc) * sizeof(float) atIndex:8];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(out_hc) atIndex:9];
[enc setThreadgroupMemoryLength:mv_dispatch.smem atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(((NSUInteger)out_dim + (NSUInteger)mv_dispatch.nr0 - 1u) /
(NSUInteger)mv_dispatch.nr0,
1,
1)
threadsPerThreadgroup:MTLSizeMake(32, (NSUInteger)mv_dispatch.nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "shared-down HC expand fused")) return 0;
}
return 1;
}
int ds4_gpu_matmul_q8_0_hc_expand_tensor(
ds4_gpu_tensor *out_hc,
ds4_gpu_tensor *block_out,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint64_t in_dim,
uint64_t out_dim,
const ds4_gpu_tensor *x,
const ds4_gpu_tensor *residual_hc,
const ds4_gpu_tensor *split,
uint32_t n_embd,
uint32_t n_hc) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!out_hc || !block_out || !model_map || !x || !residual_hc || !split ||
n_embd == 0 || n_hc == 0 || n_hc != 4 || out_dim != n_embd ||
(in_dim & 31u) != 0 || in_dim > UINT32_MAX || out_dim > UINT32_MAX) {
return 0;
}
@autoreleasepool {
id<MTLBuffer> xbuf = ds4_gpu_tensor_buffer(x);
id<MTLBuffer> blockbuf = ds4_gpu_tensor_buffer(block_out);
id<MTLBuffer> resbuf = ds4_gpu_tensor_buffer(residual_hc);
id<MTLBuffer> splitbuf = ds4_gpu_tensor_buffer(split);
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out_hc);
const uint64_t row_bytes = (in_dim / 32u) * 34u;
const uint64_t weight_bytes = out_dim * row_bytes;
const uint64_t x_bytes = in_dim * sizeof(float);
const uint64_t embd_bytes = out_dim * sizeof(float);
const uint64_t hc_bytes = (uint64_t)n_hc * n_embd * sizeof(float);
const uint64_t mix_hc = 2ull * n_hc + (uint64_t)n_hc * n_hc;
const uint64_t split_bytes = mix_hc * sizeof(float);
if (weight_offset > model_size || weight_bytes > model_size - weight_offset) {
fprintf(stderr, "ds4: Metal Q8 HC fusion weight range is outside the mapped model\n");
return 0;
}
if (!xbuf || !blockbuf || !resbuf || !splitbuf || !outbuf ||
ds4_gpu_tensor_bytes(x) < x_bytes ||
ds4_gpu_tensor_bytes(block_out) < embd_bytes ||
ds4_gpu_tensor_bytes(residual_hc) < hc_bytes ||
ds4_gpu_tensor_bytes(split) < split_bytes ||
ds4_gpu_tensor_bytes(out_hc) < hc_bytes) {
fprintf(stderr, "ds4: Metal Q8 HC fusion received undersized buffers\n");
return 0;
}
uint64_t inner_offset = 0;
id<MTLBuffer> wbuf = ds4_gpu_wrap_model_range(model_map, model_size,
weight_offset, weight_bytes,
&inner_offset);
if (!wbuf) return 0;
ds4_gpu_q8_0_matvec_args mv_args = ds4_gpu_make_q8_0_mv_args(in_dim, out_dim);
ds4_gpu_mv_dispatch mv_dispatch = ds4_gpu_make_q8_0_mv_dispatch();
mv_args.nr0 = mv_dispatch.nr0;
ds4_gpu_hc_expand_args hc_args = {
.n_embd = n_embd,
.n_hc = n_hc,
.n_tokens = 1,
.nb_block0 = sizeof(float),
.nb_block1 = (uint64_t)n_embd * sizeof(float),
.nb_add0 = sizeof(float),
.nb_add1 = (uint64_t)n_embd * sizeof(float),
.nb_res0 = sizeof(float),
.nb_res1 = (uint64_t)n_embd * sizeof(float),
.nb_res2 = (uint64_t)n_hc * n_embd * sizeof(float),
.nb_post0 = sizeof(float),
.nb_post1 = mix_hc * sizeof(float),
.nb_comb0 = sizeof(float),
.nb_comb1 = (uint64_t)n_hc * sizeof(float),
.nb_comb2 = mix_hc * sizeof(float),
.nb0 = sizeof(float),
.nb1 = (uint64_t)n_embd * sizeof(float),
.nb2 = (uint64_t)n_hc * n_embd * sizeof(float),
.has_add = 0,
};
const NSUInteger split_offset = ds4_gpu_tensor_offset(split);
const bool vec_hc =
(split_offset & 15u) == 0u &&
getenv("DS4_METAL_DISABLE_M5_Q8_HC_VEC") == NULL &&
ds4_gpu_device_is_m5_apple_silicon();
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_mul_mv_pipeline(
vec_hc ? "kernel_dsv4_q8_hc_expand4_q8_0_vec_hc" :
"kernel_dsv4_q8_hc_expand4_q8_0",
mv_dispatch.nsg);
if (!pipeline && vec_hc) {
pipeline = ds4_gpu_get_mul_mv_pipeline(
"kernel_dsv4_q8_hc_expand4_q8_0", mv_dispatch.nsg);
}
if (!pipeline) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&mv_args length:sizeof(mv_args) atIndex:0];
[enc setBytes:&hc_args length:sizeof(hc_args) atIndex:1];
[enc setBuffer:wbuf offset:(NSUInteger)inner_offset atIndex:2];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:3];
[enc setBuffer:blockbuf offset:ds4_gpu_tensor_offset(block_out) atIndex:4];
[enc setBuffer:resbuf offset:ds4_gpu_tensor_offset(residual_hc) atIndex:5];
[enc setBuffer:splitbuf offset:split_offset + (NSUInteger)n_hc * sizeof(float) atIndex:6];
[enc setBuffer:splitbuf offset:split_offset + (NSUInteger)(2u * n_hc) * sizeof(float) atIndex:7];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(out_hc) atIndex:8];
[enc setThreadgroupMemoryLength:mv_dispatch.smem atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(((NSUInteger)out_dim + (NSUInteger)mv_dispatch.nr0 - 1u) /
(NSUInteger)mv_dispatch.nr0,
1,
1)
threadsPerThreadgroup:MTLSizeMake(32, (NSUInteger)mv_dispatch.nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "Q8 HC expand fused")) return 0;
}
return 1;
}
/* Kimi Delta Attention primitive shared with the GLM-5.3 graph. */
static int glm53_gpu_mul_u64(uint64_t a, uint64_t b, uint64_t *out) {
if (!out || (a != 0 && b > UINT64_MAX / a)) return 0;
*out = a * b;
return 1;
}
static int glm53_gpu_tensor_has(
const ds4_gpu_tensor *tensor,
uint64_t elements,
uint64_t element_size) {
uint64_t bytes = 0;
return tensor &&
glm53_gpu_mul_u64(elements, element_size, &bytes) &&
ds4_gpu_tensor_buffer(tensor) != nil &&
ds4_gpu_tensor_bytes(tensor) >= bytes;
}
static id<MTLBuffer> glm53_gpu_weight_buffer(
const void *model_map,
uint64_t model_size,
uint64_t offset,
uint64_t bytes,
uint64_t *inner_offset,
const char *label) {
if (!model_map || offset > model_size || bytes > model_size - offset) {
fprintf(stderr, "ds4: GLM-5.3 %s range is outside the mapped model\n",
label ? label : "weight");
return nil;
}
return ds4_gpu_wrap_model_range(model_map, model_size, offset, bytes,
inner_offset);
}
typedef struct {
uint32_t in_dim;
uint32_t out_dim;
uint32_t n_rows;
} glm53_gpu_bf16_matmul_args;
int ds4_gpu_glm53_embedding_bf16(
ds4_gpu_tensor *out,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
const ds4_gpu_tensor *token_ids,
uint32_t n_tokens,
uint32_t n_embd,
uint32_t n_vocab) {
if (!g_initialized && !ds4_gpu_init()) return 0;
uint64_t weights = 0, output = 0;
if (n_tokens == 0 || n_embd == 0 || n_vocab == 0 ||
!glm53_gpu_mul_u64(n_embd, n_vocab, &weights) ||
!glm53_gpu_mul_u64(n_embd, n_tokens, &output) ||
!glm53_gpu_tensor_has(token_ids, n_tokens, sizeof(int32_t)) ||
!glm53_gpu_tensor_has(out, output, sizeof(float))) {
fprintf(stderr, "ds4: GLM-5.3 BF16 embedding received invalid buffers\n");
return 0;
}
@autoreleasepool {
uint64_t inner = 0;
id<MTLBuffer> weightbuf = glm53_gpu_weight_buffer(
model_map, model_size, weight_offset, weights * sizeof(uint16_t),
&inner, "BF16 embedding");
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_pipeline("kernel_glm53_embedding_bf16");
if (!weightbuf || !pipeline) return 0;
glm53_gpu_bf16_matmul_args args = {
.in_dim = n_embd,
.out_dim = n_vocab,
.n_rows = n_tokens,
};
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:weightbuf offset:(NSUInteger)inner atIndex:1];
[enc setBuffer:ds4_gpu_tensor_buffer(token_ids)
offset:ds4_gpu_tensor_offset(token_ids) atIndex:2];
[enc setBuffer:ds4_gpu_tensor_buffer(out)
offset:ds4_gpu_tensor_offset(out) atIndex:3];
[enc dispatchThreads:MTLSizeMake(n_embd, n_tokens, 1)
threadsPerThreadgroup:MTLSizeMake(MIN((NSUInteger)n_embd, 256u), 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_finish_command_buffer(cb, owned, "GLM-5.3 BF16 embedding");
}
}
static uint32_t glm53_gpu_bf16_mv_nsg(void) {
return ds4_gpu_device_name_contains("M3 Ultra") &&
getenv("DS4_METAL_DISABLE_M3_ULTRA_GLM53_DECODE") == NULL &&
getenv("DS4_METAL_DISABLE_M3_ULTRA_GLM53_BF16_NSG4") == NULL
? 4u : 8u;
}
int ds4_gpu_glm53_matmul_bf16(
ds4_gpu_tensor *out,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint32_t in_dim,
uint32_t out_dim,
const ds4_gpu_tensor *x,
uint32_t n_rows) {
if (!g_initialized && !ds4_gpu_init()) return 0;
uint64_t weights = 0, input = 0, output = 0;
if (in_dim == 0 || out_dim == 0 || n_rows == 0 ||
!glm53_gpu_mul_u64(in_dim, out_dim, &weights) ||
!glm53_gpu_mul_u64(in_dim, n_rows, &input) ||
!glm53_gpu_mul_u64(out_dim, n_rows, &output) ||
!glm53_gpu_tensor_has(x, input, sizeof(float)) ||
!glm53_gpu_tensor_has(out, output, sizeof(float))) {
fprintf(stderr, "ds4: GLM-5.3 BF16 matmul received invalid buffers\n");
return 0;
}
@autoreleasepool {
uint64_t inner = 0;
id<MTLBuffer> weightbuf = glm53_gpu_weight_buffer(
model_map, model_size, weight_offset, weights * sizeof(uint16_t),
&inner, "BF16 matrix");
if (!weightbuf) return 0;
const bool use_mv = n_rows <= 8u;
const bool bc_inp = (in_dim % 32u) != 0u;
const bool bc_out = (out_dim % 64u) != 0u || (n_rows % 32u) != 0u;
id<MTLComputePipelineState> pipeline = use_mv
? ds4_gpu_get_pipeline("kernel_glm53_mul_mv_bf16_f32")
: ds4_gpu_get_mul_mm_pipeline(
"kernel_glm53_mul_mm_bf16_f32", bc_inp, bc_out);
if (!pipeline) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
if (use_mv) {
const uint32_t nsg = glm53_gpu_bf16_mv_nsg();
glm53_gpu_bf16_matmul_args args = {
.in_dim = in_dim,
.out_dim = out_dim,
.n_rows = n_rows,
};
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:weightbuf offset:(NSUInteger)inner atIndex:1];
[enc setBuffer:ds4_gpu_tensor_buffer(x)
offset:ds4_gpu_tensor_offset(x) atIndex:2];
[enc setBuffer:ds4_gpu_tensor_buffer(out)
offset:ds4_gpu_tensor_offset(out) atIndex:3];
[enc dispatchThreadgroups:MTLSizeMake((out_dim + nsg - 1u) / nsg,
n_rows, 1)
threadsPerThreadgroup:MTLSizeMake(32u * nsg, 1, 1)];
} else {
ds4_gpu_mul_mm_args args = ds4_gpu_make_mm_args(
in_dim, out_dim, n_rows, (uint64_t)in_dim * sizeof(uint16_t));
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:weightbuf offset:(NSUInteger)inner atIndex:1];
[enc setBuffer:ds4_gpu_tensor_buffer(x)
offset:ds4_gpu_tensor_offset(x) atIndex:2];
[enc setBuffer:ds4_gpu_tensor_buffer(out)
offset:ds4_gpu_tensor_offset(out) atIndex:3];
[enc setThreadgroupMemoryLength:(bc_out ? 8192u : 6144u) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake((n_rows + 31u) / 32u,
(out_dim + 63u) / 64u, 1)
threadsPerThreadgroup:MTLSizeMake(128, 1, 1)];
}
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_finish_command_buffer(cb, owned, "GLM-5.3 BF16 matmul");
}
}
int ds4_gpu_glm53_matmul_bf16_qkv(
ds4_gpu_tensor *out_q,
ds4_gpu_tensor *out_k,
ds4_gpu_tensor *out_v,
const void *model_map,
uint64_t model_size,
uint64_t weight_q_offset,
uint64_t weight_k_offset,
uint64_t weight_v_offset,
uint32_t in_dim,
uint32_t out_dim,
const ds4_gpu_tensor *x) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!ds4_gpu_device_name_contains("M3 Ultra")) return 0;
uint64_t weights = 0;
if (in_dim == 0 || out_dim == 0 ||
!glm53_gpu_mul_u64(in_dim, out_dim, &weights) ||
!glm53_gpu_tensor_has(x, in_dim, sizeof(float)) ||
!glm53_gpu_tensor_has(out_q, out_dim, sizeof(float)) ||
!glm53_gpu_tensor_has(out_k, out_dim, sizeof(float)) ||
!glm53_gpu_tensor_has(out_v, out_dim, sizeof(float))) {
return 0;
}
@autoreleasepool {
const uint64_t weight_bytes = weights * sizeof(uint16_t);
uint64_t inner_q = 0, inner_k = 0, inner_v = 0;
id<MTLBuffer> weight_q = glm53_gpu_weight_buffer(
model_map, model_size, weight_q_offset, weight_bytes,
&inner_q, "BF16 Q matrix");
id<MTLBuffer> weight_k = glm53_gpu_weight_buffer(
model_map, model_size, weight_k_offset, weight_bytes,
&inner_k, "BF16 K matrix");
id<MTLBuffer> weight_v = glm53_gpu_weight_buffer(
model_map, model_size, weight_v_offset, weight_bytes,
&inner_v, "BF16 V matrix");
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_pipeline("kernel_glm53_mul_mv_bf16_f32_qkv");
if (!weight_q || !weight_k || !weight_v || !pipeline) return 0;
const uint32_t nsg = glm53_gpu_bf16_mv_nsg();
glm53_gpu_bf16_matmul_args args = {
.in_dim = in_dim,
.out_dim = out_dim,
.n_rows = 1u,
};
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:weight_q offset:(NSUInteger)inner_q atIndex:1];
[enc setBuffer:weight_k offset:(NSUInteger)inner_k atIndex:2];
[enc setBuffer:weight_v offset:(NSUInteger)inner_v atIndex:3];
[enc setBuffer:ds4_gpu_tensor_buffer(x)
offset:ds4_gpu_tensor_offset(x) atIndex:4];
[enc setBuffer:ds4_gpu_tensor_buffer(out_q)
offset:ds4_gpu_tensor_offset(out_q) atIndex:5];
[enc setBuffer:ds4_gpu_tensor_buffer(out_k)
offset:ds4_gpu_tensor_offset(out_k) atIndex:6];
[enc setBuffer:ds4_gpu_tensor_buffer(out_v)
offset:ds4_gpu_tensor_offset(out_v) atIndex:7];
[enc dispatchThreadgroups:MTLSizeMake((out_dim + nsg - 1u) / nsg,
1u, 3u)
threadsPerThreadgroup:MTLSizeMake(32u * nsg, 1u, 1u)];
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_finish_command_buffer(cb, owned,
"GLM-5.3 BF16 QKV matmul");
}
}
typedef struct {
uint32_t width;
uint32_t rows;
float eps;
} glm53_vision_rows_args;
typedef struct {
uint32_t rows;
uint32_t grid_h;
uint32_t grid_w;
float eps;
} glm53_vision_qkv_args;
typedef struct {
uint32_t rows;
float scale;
} glm53_vision_attention_args;
typedef struct {
uint32_t dst_row;
uint32_t image_row;
uint32_t rows;
uint32_t total_rows;
uint32_t width;
uint32_t hc;
} glm53_vision_scatter_args;
static int glm53_vision_dispatch_rows(
const char *kernel,
ds4_gpu_tensor *out,
const ds4_gpu_tensor *x,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint32_t width,
uint32_t rows,
float eps,
const char *label) {
uint64_t inner = 0;
id<MTLBuffer> weight = glm53_gpu_weight_buffer(
model_map, model_size, weight_offset,
(uint64_t)width * sizeof(uint16_t), &inner, label);
id<MTLComputePipelineState> pipeline = ds4_gpu_get_pipeline(kernel);
if (!weight || !pipeline) return 0;
glm53_vision_rows_args args = { width, rows, eps };
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
id<MTLComputeCommandEncoder> enc = cb ? ds4_gpu_compute_encoder(cb) : nil;
if (!enc) return 0;
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:ds4_gpu_tensor_buffer(x)
offset:ds4_gpu_tensor_offset(x) atIndex:1];
[enc setBuffer:weight offset:(NSUInteger)inner atIndex:2];
[enc setBuffer:ds4_gpu_tensor_buffer(out)
offset:ds4_gpu_tensor_offset(out) atIndex:3];
[enc setThreadgroupMemoryLength:8u * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(rows, 1, 1)
threadsPerThreadgroup:MTLSizeMake(256u, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_finish_command_buffer(cb, owned, label);
}
static int glm53_vision_dispatch_bias(
const char *kernel,
ds4_gpu_tensor *x,
const ds4_gpu_tensor *residual,
const void *model_map,
uint64_t model_size,
uint64_t bias_offset,
uint32_t width,
uint32_t rows,
const char *label) {
uint64_t inner = 0;
id<MTLBuffer> bias = glm53_gpu_weight_buffer(
model_map, model_size, bias_offset,
(uint64_t)width * sizeof(uint16_t), &inner, label);
id<MTLComputePipelineState> pipeline = ds4_gpu_get_pipeline(kernel);
if (!bias || !pipeline) return 0;
glm53_vision_rows_args args = { width, rows, 0.0f };
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
id<MTLComputeCommandEncoder> enc = cb ? ds4_gpu_compute_encoder(cb) : nil;
if (!enc) return 0;
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:ds4_gpu_tensor_buffer(x)
offset:ds4_gpu_tensor_offset(x) atIndex:1];
[enc setBuffer:bias offset:(NSUInteger)inner atIndex:2];
if (residual) {
[enc setBuffer:ds4_gpu_tensor_buffer(residual)
offset:ds4_gpu_tensor_offset(residual) atIndex:3];
}
[enc dispatchThreads:MTLSizeMake(width, rows, 1)
threadsPerThreadgroup:MTLSizeMake(MIN((NSUInteger)width, 256u), 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_finish_command_buffer(cb, owned, label);
}
static int glm53_vision_dispatch_qkv(
ds4_gpu_tensor *q,
ds4_gpu_tensor *k,
ds4_gpu_tensor *v,
const ds4_gpu_tensor *qkv,
const void *model_map,
uint64_t model_size,
const ds4_glm53_vision_layer_weights *w,
uint32_t rows,
uint32_t grid_h,
uint32_t grid_w) {
uint64_t bias_inner = 0, q_inner = 0, k_inner = 0;
id<MTLBuffer> bias = glm53_gpu_weight_buffer(
model_map, model_size, w->qkv_bias, 3072u * sizeof(uint16_t),
&bias_inner, "vision QKV bias");
id<MTLBuffer> qw = glm53_gpu_weight_buffer(
model_map, model_size, w->q_norm, 64u * sizeof(uint16_t),
&q_inner, "vision Q norm");
id<MTLBuffer> kw = glm53_gpu_weight_buffer(
model_map, model_size, w->k_norm, 64u * sizeof(uint16_t),
&k_inner, "vision K norm");
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_pipeline("kernel_glm53_vision_qkv_rope");
if (!bias || !qw || !kw || !pipeline) return 0;
glm53_vision_qkv_args args = { rows, grid_h, grid_w, 1.0e-5f };
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
id<MTLComputeCommandEncoder> enc = cb ? ds4_gpu_compute_encoder(cb) : nil;
if (!enc) return 0;
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:ds4_gpu_tensor_buffer(qkv)
offset:ds4_gpu_tensor_offset(qkv) atIndex:1];
[enc setBuffer:bias offset:(NSUInteger)bias_inner atIndex:2];
[enc setBuffer:qw offset:(NSUInteger)q_inner atIndex:3];
[enc setBuffer:kw offset:(NSUInteger)k_inner atIndex:4];
[enc setBuffer:ds4_gpu_tensor_buffer(q)
offset:ds4_gpu_tensor_offset(q) atIndex:5];
[enc setBuffer:ds4_gpu_tensor_buffer(k)
offset:ds4_gpu_tensor_offset(k) atIndex:6];
[enc setBuffer:ds4_gpu_tensor_buffer(v)
offset:ds4_gpu_tensor_offset(v) atIndex:7];
[enc dispatchThreadgroups:MTLSizeMake(rows, 16u, 1)
threadsPerThreadgroup:MTLSizeMake(32u, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_finish_command_buffer(cb, owned, "GLM-5.3 vision QKV");
}
static int glm53_vision_dispatch_attention(
ds4_gpu_tensor *out,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *k,
const ds4_gpu_tensor *v,
uint32_t rows) {
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_pipeline("kernel_glm53_vision_attention");
if (!pipeline) return 0;
glm53_vision_attention_args args = { rows, 0.125f };
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
id<MTLComputeCommandEncoder> enc = cb ? ds4_gpu_compute_encoder(cb) : nil;
if (!enc) return 0;
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:ds4_gpu_tensor_buffer(q)
offset:ds4_gpu_tensor_offset(q) atIndex:1];
[enc setBuffer:ds4_gpu_tensor_buffer(k)
offset:ds4_gpu_tensor_offset(k) atIndex:2];
[enc setBuffer:ds4_gpu_tensor_buffer(v)
offset:ds4_gpu_tensor_offset(v) atIndex:3];
[enc setBuffer:ds4_gpu_tensor_buffer(out)
offset:ds4_gpu_tensor_offset(out) atIndex:4];
[enc dispatchThreadgroups:MTLSizeMake(rows, 16u, 1)
threadsPerThreadgroup:MTLSizeMake(32u, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_finish_command_buffer(cb, owned, "GLM-5.3 vision attention");
}
static int glm53_vision_dispatch_swiglu_bias(
ds4_gpu_tensor *out,
const ds4_gpu_tensor *gate,
const ds4_gpu_tensor *up,
const void *model_map,
uint64_t model_size,
uint64_t gate_bias_offset,
uint64_t up_bias_offset,
uint32_t width,
uint32_t rows) {
uint64_t gate_inner = 0, up_inner = 0;
id<MTLBuffer> gate_bias = glm53_gpu_weight_buffer(
model_map, model_size, gate_bias_offset,
(uint64_t)width * sizeof(uint16_t), &gate_inner,
"vision gate bias");
id<MTLBuffer> up_bias = glm53_gpu_weight_buffer(
model_map, model_size, up_bias_offset,
(uint64_t)width * sizeof(uint16_t), &up_inner,
"vision up bias");
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_pipeline("kernel_glm53_vision_swiglu_bias");
if (!gate_bias || !up_bias || !pipeline) return 0;
glm53_vision_rows_args args = { width, rows, 0.0f };
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
id<MTLComputeCommandEncoder> enc = cb ? ds4_gpu_compute_encoder(cb) : nil;
if (!enc) return 0;
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:ds4_gpu_tensor_buffer(gate)
offset:ds4_gpu_tensor_offset(gate) atIndex:1];
[enc setBuffer:gate_bias offset:(NSUInteger)gate_inner atIndex:2];
[enc setBuffer:ds4_gpu_tensor_buffer(up)
offset:ds4_gpu_tensor_offset(up) atIndex:3];
[enc setBuffer:up_bias offset:(NSUInteger)up_inner atIndex:4];
[enc setBuffer:ds4_gpu_tensor_buffer(out)
offset:ds4_gpu_tensor_offset(out) atIndex:5];
[enc dispatchThreads:MTLSizeMake(width, rows, 1)
threadsPerThreadgroup:MTLSizeMake(MIN((NSUInteger)width, 256u), 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_finish_command_buffer(cb, owned, "GLM-5.3 vision SwiGLU");
}
static int glm53_vision_dispatch_reorder(
ds4_gpu_tensor *out,
const ds4_gpu_tensor *x,
uint32_t rows) {
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_pipeline("kernel_glm53_vision_downsample_reorder");
if (!pipeline) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
id<MTLComputeCommandEncoder> enc = cb ? ds4_gpu_compute_encoder(cb) : nil;
if (!enc) return 0;
[enc setComputePipelineState:pipeline];
[enc setBuffer:ds4_gpu_tensor_buffer(x)
offset:ds4_gpu_tensor_offset(x) atIndex:0];
[enc setBuffer:ds4_gpu_tensor_buffer(out)
offset:ds4_gpu_tensor_offset(out) atIndex:1];
[enc dispatchThreads:MTLSizeMake(4096u, rows, 1)
threadsPerThreadgroup:MTLSizeMake(256u, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_finish_command_buffer(cb, owned, "GLM-5.3 vision downsample reorder");
}
static int glm53_vision_dispatch_layernorm_gelu(
ds4_gpu_tensor *out,
const ds4_gpu_tensor *x,
const void *model_map,
uint64_t model_size,
uint64_t weight_offset,
uint64_t bias_offset,
uint32_t rows) {
uint64_t weight_inner = 0, bias_inner = 0;
id<MTLBuffer> weight = glm53_gpu_weight_buffer(
model_map, model_size, weight_offset, 4096u * sizeof(uint16_t),
&weight_inner, "vision merger norm");
id<MTLBuffer> bias = glm53_gpu_weight_buffer(
model_map, model_size, bias_offset, 4096u * sizeof(uint16_t),
&bias_inner, "vision merger norm bias");
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_pipeline("kernel_glm53_vision_layernorm_gelu");
if (!weight || !bias || !pipeline) return 0;
glm53_vision_rows_args args = { 4096u, rows, 1.0e-5f };
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
id<MTLComputeCommandEncoder> enc = cb ? ds4_gpu_compute_encoder(cb) : nil;
if (!enc) return 0;
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:ds4_gpu_tensor_buffer(x)
offset:ds4_gpu_tensor_offset(x) atIndex:1];
[enc setBuffer:weight offset:(NSUInteger)weight_inner atIndex:2];
[enc setBuffer:bias offset:(NSUInteger)bias_inner atIndex:3];
[enc setBuffer:ds4_gpu_tensor_buffer(out)
offset:ds4_gpu_tensor_offset(out) atIndex:4];
[enc setThreadgroupMemoryLength:8u * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(rows, 1, 1)
threadsPerThreadgroup:MTLSizeMake(256u, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_finish_command_buffer(cb, owned, "GLM-5.3 vision merger norm");
}
int ds4_gpu_glm53_vision_encode(
float *out,
const float *patches,
uint32_t grid_h,
uint32_t grid_w,
const void *model_map,
uint64_t model_size,
const ds4_glm53_vision_weights *weights) {
if (!out || !patches || !model_map || !weights || grid_h == 0 || grid_w == 0 ||
(grid_h & 1u) != 0 || (grid_w & 1u) != 0 ||
grid_h > UINT32_MAX / grid_w || ds4_gpu_commands_active()) {
fprintf(stderr, "ds4: invalid GLM-5.3 vision encoder input\n");
return 0;
}
if (!g_initialized && !ds4_gpu_init()) return 0;
const uint32_t rows = grid_h * grid_w;
const uint32_t merged_rows = rows / 4u;
const uint64_t row1024 = (uint64_t)rows * 1024u;
const uint64_t row3072 = (uint64_t)rows * 3072u;
const uint64_t row4096 = (uint64_t)rows * 4096u;
const uint64_t merged10240 = (uint64_t)merged_rows * 10240u;
if (row4096 > SIZE_MAX / sizeof(float) ||
merged10240 > SIZE_MAX / sizeof(float)) return 0;
ds4_gpu_tensor *patch = NULL, *a = NULL, *b = NULL, *qkv = NULL;
ds4_gpu_tensor *q = NULL, *k = NULL, *v = NULL, *attn = NULL;
ds4_gpu_tensor *gate = NULL, *up = NULL, *mid = NULL;
int ok = 0;
#define VISION_ALLOC(name_, count_) do { \
name_ = ds4_gpu_tensor_alloc((count_) * sizeof(float)); \
if (!(name_)) goto cleanup; \
} while (0)
VISION_ALLOC(patch, (uint64_t)rows * 1176u);
VISION_ALLOC(a, row1024);
VISION_ALLOC(b, row1024);
VISION_ALLOC(qkv, row3072);
VISION_ALLOC(q, row1024);
VISION_ALLOC(k, row1024);
VISION_ALLOC(v, row1024);
VISION_ALLOC(attn, row1024);
VISION_ALLOC(gate, row4096);
VISION_ALLOC(up, row4096);
VISION_ALLOC(mid, row4096);
#undef VISION_ALLOC
if (!ds4_gpu_tensor_write(patch, 0, patches,
(uint64_t)rows * 1176u * sizeof(float))) goto cleanup;
if (!ds4_gpu_begin_commands()) goto cleanup;
ok = ds4_gpu_glm53_matmul_bf16(a, model_map, model_size,
weights->patch_weight,
1176u, 1024u, patch, rows);
if (ok) ok = glm53_vision_dispatch_bias(
"kernel_glm53_vision_add_bias", a, NULL, model_map, model_size,
weights->patch_bias, 1024u, rows, "vision patch bias");
ds4_gpu_tensor *cur = a;
ds4_gpu_tensor *tmp = b;
for (uint32_t il = 0; ok && il < DS4_GLM53_VISION_LAYERS; il++) {
const ds4_glm53_vision_layer_weights *w = &weights->layer[il];
ok = glm53_vision_dispatch_rows(
"kernel_glm53_vision_rms_bf16", tmp, cur,
model_map, model_size, w->norm1, 1024u, rows, 1.0e-5f,
"vision norm1");
if (ok) ok = ds4_gpu_glm53_matmul_bf16(
qkv, model_map, model_size, w->qkv_weight,
1024u, 3072u, tmp, rows);
if (ok) ok = glm53_vision_dispatch_qkv(
q, k, v, qkv, model_map, model_size, w, rows, grid_h, grid_w);
if (ok) ok = glm53_vision_dispatch_attention(attn, q, k, v, rows);
if (ok) ok = ds4_gpu_glm53_matmul_bf16(
tmp, model_map, model_size, w->attn_proj_weight,
1024u, 1024u, attn, rows);
if (ok) ok = glm53_vision_dispatch_bias(
"kernel_glm53_vision_bias_residual", tmp, cur,
model_map, model_size, w->attn_proj_bias,
1024u, rows, "vision attention bias");
ds4_gpu_tensor *swap = cur; cur = tmp; tmp = swap;
if (ok) ok = glm53_vision_dispatch_rows(
"kernel_glm53_vision_rms_bf16", tmp, cur,
model_map, model_size, w->norm2, 1024u, rows, 1.0e-5f,
"vision norm2");
if (ok) ok = ds4_gpu_glm53_matmul_bf16(
gate, model_map, model_size, w->gate_weight,
1024u, 4096u, tmp, rows);
if (ok) ok = ds4_gpu_glm53_matmul_bf16(
up, model_map, model_size, w->up_weight,
1024u, 4096u, tmp, rows);
if (ok) ok = glm53_vision_dispatch_swiglu_bias(
mid, gate, up, model_map, model_size,
w->gate_bias, w->up_bias, 4096u, rows);
if (ok) ok = ds4_gpu_glm53_matmul_bf16(
tmp, model_map, model_size, w->down_weight,
4096u, 1024u, mid, rows);
if (ok) ok = glm53_vision_dispatch_bias(
"kernel_glm53_vision_bias_residual", tmp, cur,
model_map, model_size, w->down_bias,
1024u, rows, "vision down bias");
swap = cur; cur = tmp; tmp = swap;
}
if (ok) ok = glm53_vision_dispatch_rows(
"kernel_glm53_vision_rms_bf16", tmp, cur,
model_map, model_size, weights->post_norm,
1024u, rows, 1.0e-5f, "vision post norm");
if (ok) ok = glm53_vision_dispatch_reorder(cur, tmp, merged_rows);
if (ok) ok = ds4_gpu_glm53_matmul_bf16(
tmp, model_map, model_size, weights->downsample_weight,
4096u, 4096u, cur, merged_rows);
if (ok) ok = glm53_vision_dispatch_bias(
"kernel_glm53_vision_add_bias", tmp, NULL,
model_map, model_size, weights->downsample_bias,
4096u, merged_rows, "vision downsample bias");
if (ok) ok = ds4_gpu_glm53_matmul_bf16(
cur, model_map, model_size, weights->merger_proj,
4096u, 4096u, tmp, merged_rows);
if (ok) ok = glm53_vision_dispatch_layernorm_gelu(
tmp, cur, model_map, model_size,
weights->merger_norm, weights->merger_norm_bias, merged_rows);
if (ok) ok = ds4_gpu_glm53_matmul_bf16(
gate, model_map, model_size, weights->merger_gate,
4096u, 10240u, tmp, merged_rows);
if (ok) ok = ds4_gpu_glm53_matmul_bf16(
up, model_map, model_size, weights->merger_up,
4096u, 10240u, tmp, merged_rows);
if (ok) ok = ds4_gpu_swiglu_tensor(
mid, gate, up, merged_rows * 10240u, 10.0f, 1.0f);
if (ok) ok = ds4_gpu_glm53_matmul_bf16(
cur, model_map, model_size, weights->merger_down,
10240u, 4096u, mid, merged_rows);
if (ok) ok = ds4_gpu_end_commands();
else (void)ds4_gpu_end_commands();
if (ok) ok = ds4_gpu_tensor_read(
cur, 0, out, (uint64_t)merged_rows * 4096u * sizeof(float));
cleanup:
ds4_gpu_tensor_free(mid);
ds4_gpu_tensor_free(up);
ds4_gpu_tensor_free(gate);
ds4_gpu_tensor_free(attn);
ds4_gpu_tensor_free(v);
ds4_gpu_tensor_free(k);
ds4_gpu_tensor_free(q);
ds4_gpu_tensor_free(qkv);
ds4_gpu_tensor_free(b);
ds4_gpu_tensor_free(a);
ds4_gpu_tensor_free(patch);
return ok;
}
typedef struct {
uint32_t width;
uint32_t rows;
} deepseek4_vision_rows_args;
typedef struct {
uint32_t rows;
uint32_t grid_width;
} deepseek4_vision_qkv_args;
typedef struct {
uint32_t grid_height;
uint32_t grid_width;
uint32_t output_rows;
} deepseek4_vision_align_args;
static int deepseek4_vision_dispatch_qkv(
ds4_gpu_tensor *q,
ds4_gpu_tensor *k,
ds4_gpu_tensor *v,
const ds4_gpu_tensor *qkv,
const void *model_map,
uint64_t model_size,
uint64_t bias_offset,
uint32_t rows,
uint32_t grid_width) {
uint64_t bias_inner = 0;
id<MTLBuffer> bias = glm53_gpu_weight_buffer(
model_map, model_size, bias_offset, 3072u * sizeof(uint16_t),
&bias_inner, "DeepSeek vision QKV bias");
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_pipeline("kernel_deepseek4_vision_qkv_rope");
if (!bias || !pipeline) return 0;
deepseek4_vision_qkv_args args = { rows, grid_width };
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
id<MTLComputeCommandEncoder> enc = cb ? ds4_gpu_compute_encoder(cb) : nil;
if (!enc) return 0;
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:ds4_gpu_tensor_buffer(qkv)
offset:ds4_gpu_tensor_offset(qkv) atIndex:1];
[enc setBuffer:bias offset:(NSUInteger)bias_inner atIndex:2];
[enc setBuffer:ds4_gpu_tensor_buffer(q)
offset:ds4_gpu_tensor_offset(q) atIndex:3];
[enc setBuffer:ds4_gpu_tensor_buffer(k)
offset:ds4_gpu_tensor_offset(k) atIndex:4];
[enc setBuffer:ds4_gpu_tensor_buffer(v)
offset:ds4_gpu_tensor_offset(v) atIndex:5];
[enc dispatchThreadgroups:MTLSizeMake(rows, 16u, 1)
threadsPerThreadgroup:MTLSizeMake(32u, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_finish_command_buffer(cb, owned,
"DeepSeek vision QKV");
}
static int deepseek4_vision_dispatch_two_rows(
const char *kernel,
ds4_gpu_tensor *out,
const ds4_gpu_tensor *x,
uint32_t width,
uint32_t rows,
const char *label) {
id<MTLComputePipelineState> pipeline = ds4_gpu_get_pipeline(kernel);
if (!pipeline) return 0;
deepseek4_vision_rows_args args = { width, rows };
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
id<MTLComputeCommandEncoder> enc = cb ? ds4_gpu_compute_encoder(cb) : nil;
if (!enc) return 0;
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:ds4_gpu_tensor_buffer(out)
offset:ds4_gpu_tensor_offset(out) atIndex:1];
[enc setBuffer:ds4_gpu_tensor_buffer(x)
offset:ds4_gpu_tensor_offset(x) atIndex:2];
[enc dispatchThreads:MTLSizeMake(width, rows, 1)
threadsPerThreadgroup:MTLSizeMake(MIN((NSUInteger)width, 256u), 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_finish_command_buffer(cb, owned, label);
}
static int deepseek4_vision_dispatch_round(
ds4_gpu_tensor *x,
uint32_t width,
uint32_t rows) {
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_pipeline("kernel_deepseek4_vision_round_bf16");
if (!pipeline) return 0;
deepseek4_vision_rows_args args = { width, rows };
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
id<MTLComputeCommandEncoder> enc = cb ? ds4_gpu_compute_encoder(cb) : nil;
if (!enc) return 0;
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:ds4_gpu_tensor_buffer(x)
offset:ds4_gpu_tensor_offset(x) atIndex:1];
[enc dispatchThreads:MTLSizeMake(width, rows, 1)
threadsPerThreadgroup:MTLSizeMake(MIN((NSUInteger)width, 256u), 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_finish_command_buffer(cb, owned,
"DeepSeek vision BF16 round");
}
static int deepseek4_vision_dispatch_aligner_reorder(
ds4_gpu_tensor *out,
const ds4_gpu_tensor *x,
uint32_t grid_height,
uint32_t grid_width,
uint32_t output_rows) {
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_pipeline("kernel_deepseek4_vision_aligner_reorder");
if (!pipeline) return 0;
deepseek4_vision_align_args args = {
grid_height, grid_width, output_rows
};
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
id<MTLComputeCommandEncoder> enc = cb ? ds4_gpu_compute_encoder(cb) : nil;
if (!enc) return 0;
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:ds4_gpu_tensor_buffer(x)
offset:ds4_gpu_tensor_offset(x) atIndex:1];
[enc setBuffer:ds4_gpu_tensor_buffer(out)
offset:ds4_gpu_tensor_offset(out) atIndex:2];
[enc dispatchThreads:MTLSizeMake(9216u, output_rows, 1)
threadsPerThreadgroup:MTLSizeMake(256u, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_finish_command_buffer(cb, owned,
"DeepSeek vision aligner reorder");
}
static int deepseek4_vision_dispatch_gelu_bias(
ds4_gpu_tensor *out,
const ds4_gpu_tensor *x,
const void *model_map,
uint64_t model_size,
uint64_t bias_offset,
uint32_t rows,
uint32_t width) {
uint64_t bias_inner = 0;
id<MTLBuffer> bias = glm53_gpu_weight_buffer(
model_map, model_size, bias_offset, (uint64_t)width * sizeof(uint16_t),
&bias_inner, "DeepSeek vision aligner bias");
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_pipeline("kernel_deepseek4_vision_gelu_bias");
if (!bias || !pipeline) return 0;
deepseek4_vision_rows_args args = { width, rows };
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
id<MTLComputeCommandEncoder> enc = cb ? ds4_gpu_compute_encoder(cb) : nil;
if (!enc) return 0;
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:ds4_gpu_tensor_buffer(x)
offset:ds4_gpu_tensor_offset(x) atIndex:1];
[enc setBuffer:bias offset:(NSUInteger)bias_inner atIndex:2];
[enc setBuffer:ds4_gpu_tensor_buffer(out)
offset:ds4_gpu_tensor_offset(out) atIndex:3];
[enc dispatchThreads:MTLSizeMake(width, rows, 1)
threadsPerThreadgroup:MTLSizeMake(256u, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_finish_command_buffer(cb, owned,
"DeepSeek vision aligner GELU");
}
static int deepseek4_vision_debug_dump(
const char *stage,
const ds4_gpu_tensor *tensor,
uint64_t values) {
const char *prefix = getenv("DS4_DEEPSEEK4_VISION_DEBUG_PREFIX");
if (!prefix || !prefix[0]) return 1;
if (!stage || !tensor || values > SIZE_MAX / sizeof(float)) return 0;
const bool resume = ds4_gpu_commands_active();
if (resume && !ds4_gpu_end_commands()) return 0;
float *data = malloc((size_t)values * sizeof(float));
if (!data) return 0;
int ok = ds4_gpu_tensor_read(
tensor, 0, data, values * sizeof(float)) != 0;
char path[PATH_MAX];
int length = snprintf(path, sizeof(path), "%s.%s.f32", prefix, stage);
if (ok && (length < 0 || (size_t)length >= sizeof(path))) ok = 0;
FILE *fp = ok ? fopen(path, "wb") : NULL;
if (ok && (!fp || fwrite(data, sizeof(float), (size_t)values, fp) != values))
ok = 0;
if (fp && fclose(fp) != 0) ok = 0;
free(data);
if (resume && !ds4_gpu_begin_commands()) ok = 0;
return ok;
}
int ds4_gpu_deepseek4_vision_encode(
float *out,
const float *patches,
uint32_t grid_h,
uint32_t grid_w,
const void *model_map,
uint64_t model_size,
const ds4_deepseek4_vision_weights *weights) {
if (!out || !patches || !model_map || !weights || grid_h == 0u ||
grid_w == 0u || grid_h > UINT32_MAX / grid_w ||
ds4_gpu_commands_active()) {
fprintf(stderr, "ds4: invalid DeepSeek vision encoder input\n");
return 0;
}
if (!g_initialized && !ds4_gpu_init()) return 0;
const uint32_t projection = weights->projection_dim ? weights->projection_dim : 4096u;
if (projection != 4096u && projection != 5120u) return 0;
const uint32_t rows = grid_h * grid_w;
const uint32_t aligned_rows =
((grid_h + 2u) / 3u) * ((grid_w + 2u) / 3u);
const uint64_t row1024 = (uint64_t)rows * 1024u;
const uint64_t row2816 = (uint64_t)rows * 2816u;
const uint64_t row3072 = (uint64_t)rows * 3072u;
const uint64_t row5632 = (uint64_t)rows * 5632u;
const uint64_t aligned_projection = (uint64_t)aligned_rows * projection;
const uint64_t aligned9216 = (uint64_t)aligned_rows * 9216u;
if (row5632 > SIZE_MAX / sizeof(float) ||
aligned9216 > SIZE_MAX / sizeof(float)) return 0;
ds4_gpu_tensor *patch = NULL, *a = NULL, *b = NULL, *qkv = NULL;
ds4_gpu_tensor *q = NULL, *k = NULL, *v = NULL, *attn = NULL;
ds4_gpu_tensor *mlp_w1 = NULL, *mlp_mid = NULL;
ds4_gpu_tensor *align_in = NULL, *align_a = NULL, *align_b = NULL;
int ok = 0;
#define DEEPSEEK4_VISION_ALLOC(name_, count_) do { \
name_ = ds4_gpu_tensor_alloc((count_) * sizeof(float)); \
if (!(name_)) goto cleanup; \
} while (0)
DEEPSEEK4_VISION_ALLOC(patch, (uint64_t)rows * 588u);
DEEPSEEK4_VISION_ALLOC(a, row1024);
DEEPSEEK4_VISION_ALLOC(b, row1024);
DEEPSEEK4_VISION_ALLOC(qkv, row3072);
DEEPSEEK4_VISION_ALLOC(q, row1024);
DEEPSEEK4_VISION_ALLOC(k, row1024);
DEEPSEEK4_VISION_ALLOC(v, row1024);
DEEPSEEK4_VISION_ALLOC(attn, row1024);
DEEPSEEK4_VISION_ALLOC(mlp_w1, row5632);
DEEPSEEK4_VISION_ALLOC(mlp_mid, row2816);
DEEPSEEK4_VISION_ALLOC(align_in, aligned9216);
DEEPSEEK4_VISION_ALLOC(align_a, aligned_projection);
DEEPSEEK4_VISION_ALLOC(align_b, aligned_projection);
#undef DEEPSEEK4_VISION_ALLOC
if (!ds4_gpu_tensor_write(patch, 0, patches,
(uint64_t)rows * 588u * sizeof(float)) ||
!ds4_gpu_begin_commands()) goto cleanup;
ok = projection != 5120u || deepseek4_vision_dispatch_round(patch, 588u, rows);
if (ok) ok = ds4_gpu_glm53_matmul_bf16(
a, model_map, model_size, weights->patch_weight,
588u, 1024u, patch, rows);
if (ok) ok = glm53_vision_dispatch_bias(
"kernel_glm53_vision_add_bias", a, NULL,
model_map, model_size, weights->patch_bias,
1024u, rows, "DeepSeek vision patch bias");
if (ok) ok = deepseek4_vision_dispatch_round(a, 1024u, rows);
if (ok) ok = deepseek4_vision_debug_dump(
"patch_embed", a, row1024);
ds4_gpu_tensor *cur = a;
ds4_gpu_tensor *tmp = b;
for (uint32_t il = 0; ok && il < DS4_DEEPSEEK4_VISION_LAYERS; il++) {
const ds4_deepseek4_vision_layer_weights *w = &weights->layer[il];
ok = glm53_vision_dispatch_rows(
"kernel_glm53_vision_rms_bf16", tmp, cur,
model_map, model_size, w->norm1,
1024u, rows, 1.0e-6f, "DeepSeek vision norm1");
if (ok) ok = deepseek4_vision_dispatch_round(tmp, 1024u, rows);
if (ok && il == 0u) ok = deepseek4_vision_debug_dump(
"block0_norm1", tmp, row1024);
if (ok) ok = ds4_gpu_glm53_matmul_bf16(
qkv, model_map, model_size, w->qkv_weight,
1024u, 3072u, tmp, rows);
if (ok && il == 0u) ok = deepseek4_vision_debug_dump(
"block0_qkv", qkv, row3072);
if (ok) ok = deepseek4_vision_dispatch_qkv(
q, k, v, qkv, model_map, model_size,
w->qkv_bias, rows, grid_w);
if (ok && il == 0u) ok = deepseek4_vision_debug_dump(
"block0_q", q, row1024);
if (ok && il == 0u) ok = deepseek4_vision_debug_dump(
"block0_k", k, row1024);
if (ok && il == 0u) ok = deepseek4_vision_debug_dump(
"block0_v", v, row1024);
if (ok) ok = glm53_vision_dispatch_attention(attn, q, k, v, rows);
if (ok) ok = deepseek4_vision_dispatch_round(attn, 1024u, rows);
if (ok && il == 0u) ok = deepseek4_vision_debug_dump(
"block0_attention", attn, row1024);
if (ok) ok = ds4_gpu_glm53_matmul_bf16(
tmp, model_map, model_size, w->attn_proj_weight,
1024u, 1024u, attn, rows);
if (ok) ok = glm53_vision_dispatch_bias(
projection == 5120u ? "kernel_deepseek41_vision_bias_residual" :
"kernel_glm53_vision_bias_residual", tmp, cur,
model_map, model_size, w->attn_proj_bias,
1024u, rows, "DeepSeek vision attention bias");
if (ok) ok = deepseek4_vision_dispatch_round(tmp, 1024u, rows);
if (ok && il == 0u) ok = deepseek4_vision_debug_dump(
"block0_attn_residual", tmp, row1024);
ds4_gpu_tensor *swap = cur; cur = tmp; tmp = swap;
if (ok) ok = glm53_vision_dispatch_rows(
"kernel_glm53_vision_rms_bf16", tmp, cur,
model_map, model_size, w->norm2,
1024u, rows, 1.0e-6f, "DeepSeek vision norm2");
if (ok) ok = deepseek4_vision_dispatch_round(tmp, 1024u, rows);
if (ok && il == 0u) ok = deepseek4_vision_debug_dump(
"block0_norm2", tmp, row1024);
if (ok) ok = ds4_gpu_glm53_matmul_bf16(
mlp_w1, model_map, model_size, w->mlp_w1,
1024u, 5632u, tmp, rows);
if (ok) ok = deepseek4_vision_dispatch_round(mlp_w1, 5632u, rows);
if (ok && il == 0u) ok = deepseek4_vision_debug_dump(
"block0_mlp_w1", mlp_w1, row5632);
if (ok) ok = deepseek4_vision_dispatch_two_rows(
projection == 5120u ? "kernel_deepseek41_vision_swiglu_split" :
"kernel_deepseek4_vision_swiglu_split",
mlp_w1, mlp_mid, 2816u, rows,
"DeepSeek vision SwiGLU");
if (ok && il == 0u) ok = deepseek4_vision_debug_dump(
"block0_mlp_mid", mlp_mid, row2816);
if (ok) ok = ds4_gpu_glm53_matmul_bf16(
tmp, model_map, model_size, w->mlp_w2,
2816u, 1024u, mlp_mid, rows);
if (ok) ok = deepseek4_vision_dispatch_round(tmp, 1024u, rows);
if (ok) ok = deepseek4_vision_dispatch_two_rows(
"kernel_deepseek4_vision_add_residual",
tmp, cur, 1024u, rows,
"DeepSeek vision MLP residual");
swap = cur; cur = tmp; tmp = swap;
if (ok && (il == 0u || il == 15u || il == 31u)) {
char stage[24];
snprintf(stage, sizeof(stage), "block%u", il);
ok = deepseek4_vision_debug_dump(stage, cur, row1024);
}
}
if (ok) ok = glm53_vision_dispatch_rows(
"kernel_glm53_vision_rms_bf16", tmp, cur,
model_map, model_size, weights->post_norm,
1024u, rows, 1.0e-6f, "DeepSeek vision final norm");
if (ok) ok = deepseek4_vision_dispatch_round(tmp, 1024u, rows);
if (ok) ok = deepseek4_vision_debug_dump(
"vision_norm", tmp, row1024);
if (ok) ok = deepseek4_vision_dispatch_aligner_reorder(
align_in, tmp, grid_h, grid_w, aligned_rows);
if (ok) ok = ds4_gpu_glm53_matmul_bf16(
align_a, model_map, model_size, weights->aligner_w1,
9216u, projection, align_in, aligned_rows);
if (ok) ok = deepseek4_vision_dispatch_gelu_bias(
align_b, align_a, model_map, model_size,
weights->aligner_w1_bias, aligned_rows, projection);
if (ok) ok = ds4_gpu_glm53_matmul_bf16(
align_a, model_map, model_size, weights->aligner_w2,
projection, projection, align_b, aligned_rows);
if (ok) ok = glm53_vision_dispatch_bias(
"kernel_glm53_vision_add_bias", align_a, NULL,
model_map, model_size, weights->aligner_w2_bias,
projection, aligned_rows, "DeepSeek vision aligner output bias");
if (ok) ok = deepseek4_vision_dispatch_round(
align_a, projection, aligned_rows);
if (ok) ok = deepseek4_vision_debug_dump(
"aligned", align_a, aligned_projection);
if (ok) ok = ds4_gpu_end_commands();
else (void)ds4_gpu_end_commands();
if (ok) ok = ds4_gpu_tensor_read(
align_a, 0, out, aligned_projection * sizeof(float));
cleanup:
ds4_gpu_tensor_free(align_b);
ds4_gpu_tensor_free(align_a);
ds4_gpu_tensor_free(align_in);
ds4_gpu_tensor_free(mlp_mid);
ds4_gpu_tensor_free(mlp_w1);
ds4_gpu_tensor_free(attn);
ds4_gpu_tensor_free(v);
ds4_gpu_tensor_free(k);
ds4_gpu_tensor_free(q);
ds4_gpu_tensor_free(qkv);
ds4_gpu_tensor_free(b);
ds4_gpu_tensor_free(a);
ds4_gpu_tensor_free(patch);
return ok;
}
int ds4_gpu_glm53_scatter_image_hc(
ds4_gpu_tensor *hc,
const ds4_gpu_tensor *image,
uint32_t dst_row,
uint32_t image_row,
uint32_t rows,
uint32_t total_rows,
uint32_t n_embd,
uint32_t n_hc) {
if (!hc || !image || rows == 0 || n_embd == 0 || n_hc == 0 ||
dst_row > total_rows || rows > total_rows - dst_row ||
!ds4_gpu_commands_active()) return 0;
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_pipeline("kernel_glm53_vision_scatter_hc");
if (!pipeline) return 0;
glm53_vision_scatter_args args = {
dst_row, image_row, rows, total_rows, n_embd, n_hc
};
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
id<MTLComputeCommandEncoder> enc = cb ? ds4_gpu_compute_encoder(cb) : nil;
if (!enc) return 0;
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:ds4_gpu_tensor_buffer(hc)
offset:ds4_gpu_tensor_offset(hc) atIndex:1];
[enc setBuffer:ds4_gpu_tensor_buffer(image)
offset:ds4_gpu_tensor_offset(image) atIndex:2];
[enc dispatchThreads:MTLSizeMake(n_embd, rows * n_hc, 1)
threadsPerThreadgroup:MTLSizeMake(MIN((NSUInteger)n_embd, 256u), 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_finish_command_buffer(cb, owned, "GLM-5.3 vision scatter");
}
typedef struct {
uint32_t n_heads;
uint32_t n_rows;
float lower_bound;
float norm_eps;
} glm53_gpu_kda_args;
int ds4_gpu_glm53_kda_decode(
ds4_gpu_tensor *out,
ds4_gpu_tensor *conv_state,
ds4_gpu_tensor *recurrent_state,
const ds4_gpu_tensor *q,
const ds4_gpu_tensor *k,
const ds4_gpu_tensor *v,
const ds4_gpu_tensor *raw_gate,
const ds4_gpu_tensor *raw_beta,
const ds4_gpu_tensor *output_gate,
const void *model_map,
uint64_t model_size,
uint64_t q_conv_offset,
uint64_t k_conv_offset,
uint64_t v_conv_offset,
uint64_t a_log_offset,
uint64_t dt_bias_offset,
uint64_t output_norm_offset,
uint32_t n_heads,
uint32_t n_rows,
float gate_lower_bound,
float norm_eps) {
enum { GLM53_KDA_DIM = 128, GLM53_KDA_HISTORY = 3 };
if (!g_initialized && !ds4_gpu_init()) return 0;
uint64_t projection = 0, activation_elements = 0;
uint64_t conv_elements = 0, state_elements = 0;
if (n_heads == 0 || n_rows == 0 || gate_lower_bound >= 0.0f ||
!glm53_gpu_mul_u64(n_heads, GLM53_KDA_DIM, &projection) ||
!glm53_gpu_mul_u64(projection, n_rows, &activation_elements) ||
!glm53_gpu_mul_u64(activation_elements,
3u * GLM53_KDA_HISTORY, &conv_elements) ||
!glm53_gpu_mul_u64(activation_elements, GLM53_KDA_DIM, &state_elements) ||
!glm53_gpu_tensor_has(q, activation_elements, sizeof(float)) ||
!glm53_gpu_tensor_has(k, activation_elements, sizeof(float)) ||
!glm53_gpu_tensor_has(v, activation_elements, sizeof(float)) ||
!glm53_gpu_tensor_has(raw_gate, activation_elements, sizeof(float)) ||
!glm53_gpu_tensor_has(output_gate, activation_elements, sizeof(float)) ||
!glm53_gpu_tensor_has(raw_beta,
(uint64_t)n_rows * n_heads, sizeof(float)) ||
!glm53_gpu_tensor_has(out, activation_elements, sizeof(float)) ||
!glm53_gpu_tensor_has(conv_state, conv_elements, sizeof(float)) ||
!glm53_gpu_tensor_has(recurrent_state, state_elements, sizeof(float))) {
fprintf(stderr, "ds4: GLM-5.3 KDA decode received invalid buffers\n");
return 0;
}
uint64_t conv_bytes = 0, dt_bytes = 0;
if (!glm53_gpu_mul_u64(projection, 4u * sizeof(float), &conv_bytes) ||
!glm53_gpu_mul_u64(projection, sizeof(float), &dt_bytes)) {
return 0;
}
const uint64_t a_log_bytes = (uint64_t)n_heads * sizeof(float);
const uint64_t norm_bytes = GLM53_KDA_DIM * sizeof(float);
@autoreleasepool {
uint64_t qw_inner = 0, kw_inner = 0, vw_inner = 0;
uint64_t a_inner = 0, dt_inner = 0, norm_inner = 0;
id<MTLBuffer> qw = glm53_gpu_weight_buffer(
model_map, model_size, q_conv_offset, conv_bytes,
&qw_inner, "KDA Q convolution");
id<MTLBuffer> kw = glm53_gpu_weight_buffer(
model_map, model_size, k_conv_offset, conv_bytes,
&kw_inner, "KDA K convolution");
id<MTLBuffer> vw = glm53_gpu_weight_buffer(
model_map, model_size, v_conv_offset, conv_bytes,
&vw_inner, "KDA V convolution");
id<MTLBuffer> a_log = glm53_gpu_weight_buffer(
model_map, model_size, a_log_offset, a_log_bytes,
&a_inner, "KDA A_log");
id<MTLBuffer> dt_bias = glm53_gpu_weight_buffer(
model_map, model_size, dt_bias_offset, dt_bytes,
&dt_inner, "KDA dt bias");
id<MTLBuffer> output_norm = glm53_gpu_weight_buffer(
model_map, model_size, output_norm_offset, norm_bytes,
&norm_inner, "KDA output norm");
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_pipeline("kernel_glm53_kda_decode");
if (!qw || !kw || !vw || !a_log || !dt_bias || !output_norm ||
!pipeline) {
return 0;
}
glm53_gpu_kda_args args = {
.n_heads = n_heads,
.n_rows = n_rows,
.lower_bound = gate_lower_bound,
.norm_eps = norm_eps,
};
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:ds4_gpu_tensor_buffer(q)
offset:ds4_gpu_tensor_offset(q) atIndex:1];
[enc setBuffer:ds4_gpu_tensor_buffer(k)
offset:ds4_gpu_tensor_offset(k) atIndex:2];
[enc setBuffer:ds4_gpu_tensor_buffer(v)
offset:ds4_gpu_tensor_offset(v) atIndex:3];
[enc setBuffer:ds4_gpu_tensor_buffer(raw_gate)
offset:ds4_gpu_tensor_offset(raw_gate) atIndex:4];
[enc setBuffer:ds4_gpu_tensor_buffer(raw_beta)
offset:ds4_gpu_tensor_offset(raw_beta) atIndex:5];
[enc setBuffer:ds4_gpu_tensor_buffer(output_gate)
offset:ds4_gpu_tensor_offset(output_gate) atIndex:6];
[enc setBuffer:qw offset:(NSUInteger)qw_inner atIndex:7];
[enc setBuffer:kw offset:(NSUInteger)kw_inner atIndex:8];
[enc setBuffer:vw offset:(NSUInteger)vw_inner atIndex:9];
[enc setBuffer:a_log offset:(NSUInteger)a_inner atIndex:10];
[enc setBuffer:dt_bias offset:(NSUInteger)dt_inner atIndex:11];
[enc setBuffer:output_norm offset:(NSUInteger)norm_inner atIndex:12];
[enc setBuffer:ds4_gpu_tensor_buffer(conv_state)
offset:ds4_gpu_tensor_offset(conv_state) atIndex:13];
[enc setBuffer:ds4_gpu_tensor_buffer(recurrent_state)
offset:ds4_gpu_tensor_offset(recurrent_state) atIndex:14];
[enc setBuffer:ds4_gpu_tensor_buffer(out)
offset:ds4_gpu_tensor_offset(out) atIndex:15];
[enc setThreadgroupMemoryLength:656u * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(n_rows, n_heads, 1)
threadsPerThreadgroup:MTLSizeMake(128, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_finish_command_buffer(
cb, owned, "GLM-5.3 fused KDA decode");
}
}
int ds4_gpu_glm53_kda_prefill(
ds4_gpu_tensor *out,
ds4_gpu_tensor *conv_state,
ds4_gpu_tensor *recurrent_state,
ds4_gpu_tensor *q,
ds4_gpu_tensor *k,
ds4_gpu_tensor *v,
ds4_gpu_tensor *raw_gate,
const ds4_gpu_tensor *raw_beta,
const ds4_gpu_tensor *output_gate,
const void *model_map,
uint64_t model_size,
uint64_t q_conv_offset,
uint64_t k_conv_offset,
uint64_t v_conv_offset,
uint64_t a_log_offset,
uint64_t dt_bias_offset,
uint64_t output_norm_offset,
uint32_t n_heads,
uint32_t n_tokens,
float gate_lower_bound,
float norm_eps) {
enum { GLM53_KDA_DIM = 128, GLM53_KDA_HISTORY = 3 };
if (!g_initialized && !ds4_gpu_init()) return 0;
uint64_t projection = 0, activation_elements = 0;
uint64_t conv_elements = 0, state_elements = 0;
if (n_heads == 0 || n_tokens == 0 || gate_lower_bound >= 0.0f ||
!glm53_gpu_mul_u64(n_heads, GLM53_KDA_DIM, &projection) ||
!glm53_gpu_mul_u64(projection, n_tokens, &activation_elements) ||
!glm53_gpu_mul_u64(projection,
3u * GLM53_KDA_HISTORY, &conv_elements) ||
!glm53_gpu_mul_u64(projection, GLM53_KDA_DIM, &state_elements) ||
!glm53_gpu_tensor_has(q, activation_elements, sizeof(float)) ||
!glm53_gpu_tensor_has(k, activation_elements, sizeof(float)) ||
!glm53_gpu_tensor_has(v, activation_elements, sizeof(float)) ||
!glm53_gpu_tensor_has(raw_gate, activation_elements, sizeof(float)) ||
!glm53_gpu_tensor_has(output_gate, activation_elements, sizeof(float)) ||
!glm53_gpu_tensor_has(raw_beta,
(uint64_t)n_tokens * n_heads, sizeof(float)) ||
!glm53_gpu_tensor_has(out, activation_elements, sizeof(float)) ||
!glm53_gpu_tensor_has(conv_state, conv_elements, sizeof(float)) ||
!glm53_gpu_tensor_has(recurrent_state, state_elements, sizeof(float))) {
fprintf(stderr, "ds4: GLM-5.3 KDA prefill received invalid buffers\n");
return 0;
}
uint64_t conv_bytes = 0, dt_bytes = 0;
if (!glm53_gpu_mul_u64(projection, 4u * sizeof(float), &conv_bytes) ||
!glm53_gpu_mul_u64(projection, sizeof(float), &dt_bytes)) {
return 0;
}
const uint64_t a_log_bytes = (uint64_t)n_heads * sizeof(float);
const uint64_t norm_bytes = GLM53_KDA_DIM * sizeof(float);
@autoreleasepool {
uint64_t qw_inner = 0, kw_inner = 0, vw_inner = 0;
uint64_t a_inner = 0, dt_inner = 0, norm_inner = 0;
id<MTLBuffer> qw = glm53_gpu_weight_buffer(
model_map, model_size, q_conv_offset, conv_bytes,
&qw_inner, "KDA Q convolution");
id<MTLBuffer> kw = glm53_gpu_weight_buffer(
model_map, model_size, k_conv_offset, conv_bytes,
&kw_inner, "KDA K convolution");
id<MTLBuffer> vw = glm53_gpu_weight_buffer(
model_map, model_size, v_conv_offset, conv_bytes,
&vw_inner, "KDA V convolution");
id<MTLBuffer> a_log = glm53_gpu_weight_buffer(
model_map, model_size, a_log_offset, a_log_bytes,
&a_inner, "KDA A_log");
id<MTLBuffer> dt_bias = glm53_gpu_weight_buffer(
model_map, model_size, dt_bias_offset, dt_bytes,
&dt_inner, "KDA dt bias");
id<MTLBuffer> output_norm = glm53_gpu_weight_buffer(
model_map, model_size, output_norm_offset, norm_bytes,
&norm_inner, "KDA output norm");
id<MTLComputePipelineState> prep_pipeline =
ds4_gpu_get_pipeline("kernel_glm53_kda_prefill_prepare");
id<MTLComputePipelineState> recurrence_pipeline =
ds4_gpu_get_pipeline("kernel_glm53_kda_prefill_recurrence");
id<MTLComputePipelineState> output_pipeline =
ds4_gpu_get_pipeline("kernel_glm53_kda_prefill_output");
if (!qw || !kw || !vw || !a_log || !dt_bias || !output_norm ||
!prep_pipeline || !recurrence_pipeline || !output_pipeline) {
return 0;
}
glm53_gpu_kda_args args = {
.n_heads = n_heads,
.n_rows = n_tokens,
.lower_bound = gate_lower_bound,
.norm_eps = norm_eps,
};
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:prep_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:ds4_gpu_tensor_buffer(q)
offset:ds4_gpu_tensor_offset(q) atIndex:1];
[enc setBuffer:ds4_gpu_tensor_buffer(k)
offset:ds4_gpu_tensor_offset(k) atIndex:2];
[enc setBuffer:ds4_gpu_tensor_buffer(v)
offset:ds4_gpu_tensor_offset(v) atIndex:3];
[enc setBuffer:ds4_gpu_tensor_buffer(raw_gate)
offset:ds4_gpu_tensor_offset(raw_gate) atIndex:4];
[enc setBuffer:qw offset:(NSUInteger)qw_inner atIndex:5];
[enc setBuffer:kw offset:(NSUInteger)kw_inner atIndex:6];
[enc setBuffer:vw offset:(NSUInteger)vw_inner atIndex:7];
[enc setBuffer:a_log offset:(NSUInteger)a_inner atIndex:8];
[enc setBuffer:dt_bias offset:(NSUInteger)dt_inner atIndex:9];
[enc setBuffer:ds4_gpu_tensor_buffer(conv_state)
offset:ds4_gpu_tensor_offset(conv_state) atIndex:10];
[enc setThreadgroupMemoryLength:264u * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(n_heads, 1, 1)
threadsPerThreadgroup:MTLSizeMake(128, 1, 1)];
[enc setComputePipelineState:recurrence_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:ds4_gpu_tensor_buffer(q)
offset:ds4_gpu_tensor_offset(q) atIndex:1];
[enc setBuffer:ds4_gpu_tensor_buffer(k)
offset:ds4_gpu_tensor_offset(k) atIndex:2];
[enc setBuffer:ds4_gpu_tensor_buffer(v)
offset:ds4_gpu_tensor_offset(v) atIndex:3];
[enc setBuffer:ds4_gpu_tensor_buffer(raw_gate)
offset:ds4_gpu_tensor_offset(raw_gate) atIndex:4];
[enc setBuffer:ds4_gpu_tensor_buffer(raw_beta)
offset:ds4_gpu_tensor_offset(raw_beta) atIndex:5];
[enc setBuffer:ds4_gpu_tensor_buffer(recurrent_state)
offset:ds4_gpu_tensor_offset(recurrent_state) atIndex:6];
[enc setBuffer:ds4_gpu_tensor_buffer(out)
offset:ds4_gpu_tensor_offset(out) atIndex:7];
[enc dispatchThreadgroups:MTLSizeMake(n_heads, 32, 1)
threadsPerThreadgroup:MTLSizeMake(128, 1, 1)];
[enc setComputePipelineState:output_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:ds4_gpu_tensor_buffer(out)
offset:ds4_gpu_tensor_offset(out) atIndex:1];
[enc setBuffer:ds4_gpu_tensor_buffer(output_gate)
offset:ds4_gpu_tensor_offset(output_gate) atIndex:2];
[enc setBuffer:output_norm offset:(NSUInteger)norm_inner atIndex:3];
[enc setThreadgroupMemoryLength:4u * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(n_tokens, n_heads, 1)
threadsPerThreadgroup:MTLSizeMake(128, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_finish_command_buffer(
cb, owned, "GLM-5.3 KDA layer-major prefill");
}
}
static bool dsv41_tensor_has_floats(const ds4_gpu_tensor *tensor, uint64_t count) {
return tensor && count <= UINT64_MAX / sizeof(float) &&
ds4_gpu_tensor_bytes(tensor) >= count * sizeof(float);
}
/* Frequency rounding errors accumulate into phase errors at long contexts.
* Preserve the reference's pow/reciprocal and YaRN operation order here. */
#pragma float_control(precise, on, push)
static int ds4_gpu_dsv41_rope_impl(ds4_gpu_tensor *x, uint32_t width, uint32_t heads,
uint32_t rows, uint32_t start, uint32_t stride,
bool compressed, bool inverse, uint32_t round_all);
int ds4_gpu_dsv41_rope_stride(ds4_gpu_tensor *x, uint32_t width, uint32_t heads,
uint32_t rows, uint32_t start, uint32_t stride,
bool compressed, bool inverse) {
return ds4_gpu_dsv41_rope_impl(x, width, heads, rows, start, stride, compressed, inverse, 0);
}
/* Every element is rounded to bf16 before the rotation, as a separate rounding
* pass followed by the rope would do. */
int ds4_gpu_dsv41_rope_bf16(ds4_gpu_tensor *x, uint32_t width, uint32_t heads,
uint32_t rows, uint32_t start, bool compressed, bool inverse) {
return ds4_gpu_dsv41_rope_impl(x, width, heads, rows, start, 1, compressed, inverse, 1);
}
/* The rope frequencies of the raw (0) and compressed (1) positions. */
static const float *ds4_gpu_dsv41_rope_frequencies(bool compressed) {
static float frequencies[2][32];
static dispatch_once_t once;
dispatch_once(&once, ^{
for (int kind = 0; kind < 2; kind++) {
const float base = kind ? 160000.0f : 10000.0f;
const float low = (float)floor(64.0 * log(65536.0 / (32.0 * 2.0 * M_PI)) / (2.0 * log(base)));
const float high = (float)ceil(64.0 * log(65536.0 / (2.0 * M_PI)) / (2.0 * log(base)));
for (int i = 0; i < 32; i++) {
const float denominator = powf(base, (float)i / 32.0f);
float f = 1.0f / denominator;
if (kind) {
const float ramp = fminf(1.0f, fmaxf(0.0f, (i - low) / (high - low)));
const float smooth = 1.0f - ramp;
const float interpolate = (f / 16.0f) * (1.0f - smooth);
const float extrapolate = f * smooth;
f = interpolate + extrapolate;
}
frequencies[kind][i] = f;
}
}
});
return frequencies[compressed ? 1 : 0];
}
static int ds4_gpu_dsv41_rope_impl(ds4_gpu_tensor *x, uint32_t width, uint32_t heads,
uint32_t rows, uint32_t start, uint32_t stride,
bool compressed, bool inverse, uint32_t round_all) {
if (width < 64 || !heads || !rows || rows > 1048576 || !stride ||
(uint64_t)start + (uint64_t)(rows - 1u) * stride >= 1048576u ||
(uint64_t)heads * rows > UINT64_MAX / width ||
!dsv41_tensor_has_floats(x, (uint64_t)width * heads * rows)) return 0;
if (!g_initialized && !ds4_gpu_init()) return 0;
@autoreleasepool {
id<MTLComputePipelineState> pipeline = ds4_gpu_get_pipeline("kernel_dsv41_rope");
if (!pipeline) return 0;
struct {
uint32_t width, heads, rows, start, inverse, stride;
float frequencies[32];
uint32_t round_all;
} args = {width, heads, rows, start, inverse, stride, {0}, round_all};
memcpy(args.frequencies, ds4_gpu_dsv41_rope_frequencies(compressed), sizeof(args.frequencies));
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
id<MTLComputeCommandEncoder> enc = cb ? ds4_gpu_compute_encoder(cb) : nil;
if (!enc) return 0;
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:ds4_gpu_tensor_buffer(x) offset:ds4_gpu_tensor_offset(x) atIndex:1];
[enc dispatchThreadgroups:MTLSizeMake(heads, rows, 1)
threadsPerThreadgroup:MTLSizeMake(32, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_finish_command_buffer(cb, owned, "V4.1 unit-magnitude RoPE");
}
}
#pragma float_control(pop)
int ds4_gpu_dsv41_rope(ds4_gpu_tensor *x, uint32_t width, uint32_t heads,
uint32_t rows, uint32_t start, bool compressed, bool inverse) {
return ds4_gpu_dsv41_rope_stride(x, width, heads, rows, start, 1, compressed, inverse);
}
int ds4_gpu_dsv41_router_rows(ds4_gpu_tensor *selected, ds4_gpu_tensor *weights,
ds4_gpu_tensor *probs, const ds4_gpu_tensor *logits,
const void *model_map, uint64_t model_size, uint64_t bias_offset,
uint32_t n_expert, uint32_t top_k, float scale, uint32_t rows) {
if (!selected || !weights || !probs || !logits || !n_expert || !top_k || top_k > n_expert ||
top_k > 32u || !rows || rows > 65535u) return 0;
if (!g_initialized && !ds4_gpu_init()) return 0;
@autoreleasepool {
id<MTLComputePipelineState> pipeline = ds4_gpu_get_pipeline("kernel_dsv41_router_one");
if (!pipeline) return 0;
NSUInteger nth = 1;
while (nth < n_expert) nth *= 2u;
if (nth > pipeline.maxTotalThreadsPerThreadgroup) return 0;
const uint64_t bias_bytes = (uint64_t)n_expert * sizeof(float);
if (bias_offset > model_size || bias_bytes > model_size - bias_offset) return 0;
uint64_t bias_inner = 0;
id<MTLBuffer> biasbuf = ds4_gpu_wrap_model_range(model_map, model_size, bias_offset,
bias_bytes, &bias_inner);
if (!biasbuf || ds4_gpu_tensor_bytes(logits) < bias_bytes * rows ||
ds4_gpu_tensor_bytes(probs) < bias_bytes * rows ||
ds4_gpu_tensor_bytes(selected) < (uint64_t)top_k * rows * sizeof(int32_t) ||
ds4_gpu_tensor_bytes(weights) < (uint64_t)top_k * rows * sizeof(float)) return 0;
const struct { uint32_t n_expert, top_k, has_bias; float scale; } args =
{n_expert, top_k, 1u, scale};
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
id<MTLComputeCommandEncoder> enc = cb ? ds4_gpu_compute_encoder(cb) : nil;
if (!enc) return 0;
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:ds4_gpu_tensor_buffer(logits) offset:ds4_gpu_tensor_offset(logits) atIndex:1];
[enc setBuffer:biasbuf offset:(NSUInteger)bias_inner atIndex:2];
[enc setBuffer:ds4_gpu_tensor_buffer(probs) offset:ds4_gpu_tensor_offset(probs) atIndex:3];
[enc setBuffer:ds4_gpu_tensor_buffer(selected) offset:ds4_gpu_tensor_offset(selected) atIndex:4];
[enc setBuffer:ds4_gpu_tensor_buffer(weights) offset:ds4_gpu_tensor_offset(weights) atIndex:5];
[enc setThreadgroupMemoryLength:((nth * 12u) + 15u + 128u) & ~(NSUInteger)15u atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(1, rows, 1) threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_finish_command_buffer(cb, owned, "V4.1 router");
}
}
int ds4_gpu_dsv41_router_one(ds4_gpu_tensor *selected, ds4_gpu_tensor *weights,
ds4_gpu_tensor *probs, const ds4_gpu_tensor *logits,
const void *model_map, uint64_t model_size, uint64_t bias_offset,
uint32_t n_expert, uint32_t top_k, float scale) {
return ds4_gpu_dsv41_router_rows(selected, weights, probs, logits, model_map, model_size,
bias_offset, n_expert, top_k, scale, 1u);
}
/* A few rows through the single-row F32 matvec kernel (one grid row each), so
* every row reduces exactly as a decode step does. */
int ds4_gpu_dsv41_matmul_f32_rows(ds4_gpu_tensor *out, const void *model_map, uint64_t model_size,
uint64_t weight_offset, uint32_t in_dim, uint32_t out_dim,
const ds4_gpu_tensor *x, uint32_t rows) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!out || !x || !model_map || !in_dim || !out_dim || !rows || rows > 65535u ||
ds4_gpu_tensor_bytes(x) < (uint64_t)rows * in_dim * sizeof(float) ||
ds4_gpu_tensor_bytes(out) < (uint64_t)rows * out_dim * sizeof(float)) return 0;
@autoreleasepool {
const uint64_t row_bytes = (uint64_t)in_dim * sizeof(float);
const uint64_t weight_bytes = row_bytes * out_dim;
if (weight_offset > model_size || weight_bytes > model_size - weight_offset) return 0;
uint64_t inner = 0;
id<MTLBuffer> wbuf = ds4_gpu_wrap_model_range(model_map, model_size, weight_offset, weight_bytes, &inner);
if (!wbuf) return 0;
ds4_gpu_q8_0_matvec_args mv_args = ds4_gpu_make_f32_mv_args(in_dim, out_dim, rows);
ds4_gpu_mv_dispatch mv_dispatch = ds4_gpu_make_plain_mv_dispatch(in_dim, 1);
if (ds4_gpu_plain_mv_single_row(out_dim)) {
mv_dispatch.nr0 = 1;
mv_dispatch.smem = 32u * sizeof(float);
}
mv_args.nr0 = mv_dispatch.nr0;
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_mul_mv_pipeline(mv_dispatch.function_name, mv_dispatch.nsg);
if (!pipeline) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
id<MTLComputeCommandEncoder> enc = cb ? ds4_gpu_compute_encoder(cb) : nil;
if (!enc) return 0;
[enc setComputePipelineState:pipeline];
[enc setBytes:&mv_args length:sizeof(mv_args) atIndex:0];
[enc setBuffer:wbuf offset:(NSUInteger)inner atIndex:1];
[enc setBuffer:ds4_gpu_tensor_buffer(x) offset:ds4_gpu_tensor_offset(x) atIndex:2];
[enc setBuffer:ds4_gpu_tensor_buffer(out) offset:ds4_gpu_tensor_offset(out) atIndex:3];
if (mv_dispatch.smem) [enc setThreadgroupMemoryLength:mv_dispatch.smem atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(((NSUInteger)out_dim + (NSUInteger)mv_dispatch.nr0 - 1u) / (NSUInteger)mv_dispatch.nr0,
rows, 1)
threadsPerThreadgroup:MTLSizeMake(32, (NSUInteger)mv_dispatch.nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_finish_command_buffer(cb, owned, "V4.1 F32 matvec rows");
}
}
int ds4_gpu_dsv41_quantize(ds4_gpu_tensor *x, uint32_t width, uint32_t rows,
ds4_v41_activation_format format) {
const uint32_t block = format == DS4_V41_FP4_E4M3 ? 16u : 32u;
if (!width || !rows || format < DS4_V41_BF16 || format > DS4_V41_FP4_E4M3 ||
(format != DS4_V41_BF16 && width % block) ||
!dsv41_tensor_has_floats(x, (uint64_t)width * rows)) return 0;
if (!g_initialized && !ds4_gpu_init()) return 0;
@autoreleasepool {
const uint64_t count = (uint64_t)width * rows;
const bool linear = format == DS4_V41_BF16 && count <= UINT32_MAX &&
ds4_gpu_tensor_offset(x) % 16u == 0 &&
!getenv("DS4_METAL_DISABLE_V41_LINEAR_BF16");
id<MTLComputePipelineState> pipeline = ds4_gpu_get_pipeline(linear ?
"kernel_dsv41_bf16_linear" : "kernel_dsv41_quantize");
if (!pipeline) return 0;
const uint32_t args[] = {width, rows, (uint32_t)format};
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
id<MTLComputeCommandEncoder> enc = cb ? ds4_gpu_compute_encoder(cb) : nil;
if (!enc) return 0;
[enc setComputePipelineState:pipeline];
if (linear) [enc setBytes:&count length:sizeof(count) atIndex:0];
else [enc setBytes:args length:sizeof(args) atIndex:0];
[enc setBuffer:ds4_gpu_tensor_buffer(x) offset:ds4_gpu_tensor_offset(x) atIndex:1];
if (linear) {
[enc dispatchThreadgroups:MTLSizeMake((count + 1023u) / 1024u, 1, 1)
threadsPerThreadgroup:MTLSizeMake(256, 1, 1)];
} else {
[enc dispatchThreadgroups:MTLSizeMake(((uint64_t)width + block - 1u) / block, rows, 1)
threadsPerThreadgroup:MTLSizeMake(32, 1, 1)];
}
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_finish_command_buffer(cb, owned, "V4.1 activation quantization");
}
}
typedef struct {
uint32_t width, start, inverse;
float frequencies[32];
} ds4_gpu_mv_rope_args;
static ds4_gpu_mv_rope_args ds4_gpu_dsv41_mv_rope_args(uint32_t width, uint32_t start,
bool compressed, bool inverse) {
ds4_gpu_mv_rope_args rope = {width, start, inverse ? 1u : 0u, {0}};
memcpy(rope.frequencies, ds4_gpu_dsv41_rope_frequencies(compressed), sizeof(rope.frequencies));
return rope;
}
int ds4_gpu_dsv41_project_q(ds4_gpu_tensor *out, const void *model_map, uint64_t model_size,
uint64_t weight_offset, uint32_t in_dim, uint32_t out_dim,
const ds4_gpu_tensor *x, uint32_t pos, bool compressed) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!out || !x || !model_map || !in_dim || (in_dim & 31u) || !out_dim || out_dim % 512u ||
out_dim > 65536u || pos >= 1048576u ||
ds4_gpu_tensor_bytes(x) < (uint64_t)in_dim * sizeof(float) ||
ds4_gpu_tensor_bytes(out) < (uint64_t)out_dim * sizeof(float)) return 0;
@autoreleasepool {
const uint64_t row_bytes = (uint64_t)(in_dim / 32u) * 34u;
const uint64_t weight_bytes = row_bytes * out_dim;
if (weight_offset > model_size || weight_bytes > model_size - weight_offset) return 0;
uint64_t inner = 0;
id<MTLBuffer> wbuf = ds4_gpu_wrap_model_range(model_map, model_size, weight_offset, weight_bytes, &inner);
if (!wbuf) return 0;
ds4_gpu_q8_0_matvec_args args = ds4_gpu_make_q8_0_mv_args(in_dim, out_dim);
ds4_gpu_mv_dispatch dispatch = ds4_gpu_make_q8_0_mv_dispatch();
if (strcmp(dispatch.function_name, "kernel_mul_mv_q8_0_f32") != 0 || dispatch.nr0 != 2) return 0;
args.nr0 = dispatch.nr0;
const ds4_gpu_mv_rope_args rope = ds4_gpu_dsv41_mv_rope_args(512u, pos, compressed, false);
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_mul_mv_pipeline("kernel_mul_mv_q8_0_f32_bf16_rope", dispatch.nsg);
if (!pipeline) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
id<MTLComputeCommandEncoder> enc = cb ? ds4_gpu_compute_encoder(cb) : nil;
if (!enc) return 0;
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:wbuf offset:(NSUInteger)inner atIndex:1];
[enc setBuffer:ds4_gpu_tensor_buffer(x) offset:ds4_gpu_tensor_offset(x) atIndex:2];
[enc setBuffer:ds4_gpu_tensor_buffer(out) offset:ds4_gpu_tensor_offset(out) atIndex:3];
[enc setBytes:&rope length:sizeof(rope) atIndex:4];
[enc setThreadgroupMemoryLength:dispatch.smem atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(((NSUInteger)out_dim + 1u) / 2u, 1, 1)
threadsPerThreadgroup:MTLSizeMake(32, (NSUInteger)dispatch.nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_finish_command_buffer(cb, owned, "V4.1 q projection with rope");
}
}
int ds4_gpu_dsv41_matmul_expand_rows(ds4_gpu_tensor *out_hc, const void *model_map, uint64_t model_size,
uint64_t weight_offset, uint32_t in_dim, uint32_t out_dim,
const ds4_gpu_tensor *x, const ds4_gpu_tensor *add,
const ds4_gpu_tensor *residual_hc, const ds4_gpu_tensor *split,
uint32_t n_hc, uint32_t rows) {
if (!g_initialized && !ds4_gpu_init()) return 0;
const uint64_t mix_hc = 2ull * n_hc + (uint64_t)n_hc * n_hc;
if (!out_hc || !x || !residual_hc || !split || !model_map || n_hc != 4 || !in_dim ||
(in_dim & 31u) || !out_dim || out_dim > 65536u || !rows || rows > 8u ||
ds4_gpu_tensor_bytes(x) < (uint64_t)rows * in_dim * sizeof(float) ||
(add && ds4_gpu_tensor_bytes(add) < (uint64_t)rows * out_dim * sizeof(float)) ||
ds4_gpu_tensor_bytes(residual_hc) < (uint64_t)rows * n_hc * out_dim * sizeof(float) ||
ds4_gpu_tensor_bytes(out_hc) < (uint64_t)rows * n_hc * out_dim * sizeof(float) ||
ds4_gpu_tensor_bytes(split) < (uint64_t)rows * mix_hc * sizeof(float)) return 0;
@autoreleasepool {
const uint64_t row_bytes = (uint64_t)(in_dim / 32u) * 34u;
const uint64_t weight_bytes = row_bytes * out_dim;
if (weight_offset > model_size || weight_bytes > model_size - weight_offset) return 0;
uint64_t inner = 0;
id<MTLBuffer> wbuf = ds4_gpu_wrap_model_range(model_map, model_size, weight_offset, weight_bytes, &inner);
if (!wbuf) return 0;
ds4_gpu_q8_0_matvec_args args = ds4_gpu_make_q8_0_mv_args(in_dim, out_dim);
ds4_gpu_mv_dispatch dispatch = ds4_gpu_make_q8_0_mv_dispatch();
if (strcmp(dispatch.function_name, "kernel_mul_mv_q8_0_f32") != 0 || dispatch.nr0 != 2) return 0;
args.nr0 = dispatch.nr0;
args.ne11 = (int32_t)rows;
args.nb12 = (uint64_t)rows * in_dim * sizeof(float);
args.ne1 = (int32_t)rows;
const struct { uint32_t n_embd, has_add, split_stride; } hc = { out_dim, add != NULL, (uint32_t)mix_hc };
const ds4_gpu_tensor *add_or_x = add ? add : x;
const NSUInteger split_offset = ds4_gpu_tensor_offset(split);
if (rows > 1u) { dispatch.nr0 = 4; args.nr0 = 4; }
const int mma_off = ds4_gpu_v41_rows_mma_off();
const int mma = in_dim >= 4096u ? 8 : 4;
const bool mma_kernel = !mma_off && rows >= (in_dim >= 8192u ? 4u : 3u) &&
(in_dim % (mma == 8 ? 256u : 128u)) == 0 && (out_dim % 8u) == 0;
id<MTLComputePipelineState> pipeline = ds4_gpu_get_mul_mv_pipeline(
mma_kernel ? (mma == 8 ? "kernel_mul_mv_q8_0_f32_bf16io_hc_expand4_mma_rows8" :
"kernel_mul_mv_q8_0_f32_bf16io_hc_expand4_mma_rows") :
rows > 1u ? "kernel_mul_mv_q8_0_f32_bf16io_hc_expand4_rows_seq4" : "kernel_mul_mv_q8_0_f32_bf16io_hc_expand4",
dispatch.nsg);
if (!pipeline) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
id<MTLComputeCommandEncoder> enc = cb ? ds4_gpu_compute_encoder(cb) : nil;
if (!enc) return 0;
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:wbuf offset:(NSUInteger)inner atIndex:1];
[enc setBuffer:ds4_gpu_tensor_buffer(x) offset:ds4_gpu_tensor_offset(x) atIndex:2];
[enc setBuffer:ds4_gpu_tensor_buffer(out_hc) offset:ds4_gpu_tensor_offset(out_hc) atIndex:3];
[enc setBytes:&hc length:sizeof(hc) atIndex:4];
[enc setBuffer:ds4_gpu_tensor_buffer(add_or_x) offset:ds4_gpu_tensor_offset(add_or_x) atIndex:5];
[enc setBuffer:ds4_gpu_tensor_buffer(residual_hc) offset:ds4_gpu_tensor_offset(residual_hc) atIndex:6];
[enc setBuffer:ds4_gpu_tensor_buffer(split) offset:split_offset + n_hc * sizeof(float) atIndex:7];
[enc setBuffer:ds4_gpu_tensor_buffer(split) offset:split_offset + 2u * n_hc * sizeof(float) atIndex:8];
[enc setThreadgroupMemoryLength:(mma_kernel ? (NSUInteger)mma * (2u * 8u * 32u + 64u) * sizeof(float) :
rows > 1u ? 32u * sizeof(float) * 4u : dispatch.smem) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(mma_kernel ? (NSUInteger)out_dim / 8u :
((NSUInteger)out_dim + (NSUInteger)dispatch.nr0 - 1u) / (NSUInteger)dispatch.nr0, 1, 1)
threadsPerThreadgroup:MTLSizeMake(32, mma_kernel ? (NSUInteger)mma : (NSUInteger)dispatch.nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_finish_command_buffer(cb, owned, "V4.1 projection with HC expand");
}
}
int ds4_gpu_dsv41_attention_low(ds4_gpu_tensor *low, const void *model_map, uint64_t model_size,
uint64_t out_a_offset, uint32_t group_dim, uint32_t rank,
uint32_t n_groups, ds4_gpu_tensor *heads, uint32_t pos, bool compressed) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!low || !heads || !model_map || !group_dim || group_dim % 512u || !rank || !n_groups ||
pos >= 1048576u || (uint64_t)n_groups * rank > UINT32_MAX) return 0;
@autoreleasepool {
const uint64_t row_a_bytes = (uint64_t)(group_dim / 32u) * 34u;
const uint64_t out_a_bytes = (uint64_t)n_groups * rank * row_a_bytes;
if (out_a_offset > model_size || out_a_bytes > model_size - out_a_offset ||
ds4_gpu_tensor_bytes(heads) < (uint64_t)n_groups * group_dim * sizeof(float) ||
ds4_gpu_tensor_bytes(low) < (uint64_t)n_groups * rank * sizeof(float)) return 0;
uint64_t inner = 0;
id<MTLBuffer> wbuf = ds4_gpu_wrap_model_range(model_map, model_size, out_a_offset, out_a_bytes, &inner);
if (!wbuf) return 0;
ds4_gpu_mul_mv_id_args args = {
.nei0 = (int32_t)n_groups,
.nei1 = 1,
.nbi1 = 0,
.ne00 = (int32_t)group_dim,
.ne01 = (int32_t)rank,
.ne02 = (int32_t)n_groups,
.nb00 = 34,
.nb01 = row_a_bytes,
.nb02 = (uint64_t)rank * row_a_bytes,
.ne10 = (int32_t)group_dim,
.ne11 = (int32_t)n_groups,
.ne12 = 1,
.ne13 = 1,
.nb10 = sizeof(float),
.nb11 = (uint64_t)group_dim * sizeof(float),
.nb12 = (uint64_t)n_groups * group_dim * sizeof(float),
.ne0 = (int32_t)rank,
.ne1 = (int32_t)n_groups,
.nb1 = (uint64_t)rank * sizeof(float),
.nr0 = 2,
};
const ds4_gpu_mv_rope_args rope = ds4_gpu_dsv41_mv_rope_args(512u, pos, compressed, true);
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_mul_mv_pipeline("kernel_dsv4_attn_out_low_q8_0_f32_rope", 4);
if (!pipeline) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
id<MTLComputeCommandEncoder> enc = cb ? ds4_gpu_compute_encoder(cb) : nil;
if (!enc) return 0;
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:wbuf offset:(NSUInteger)inner atIndex:1];
[enc setBuffer:ds4_gpu_tensor_buffer(heads) offset:ds4_gpu_tensor_offset(heads) atIndex:2];
[enc setBuffer:ds4_gpu_tensor_buffer(low) offset:ds4_gpu_tensor_offset(low) atIndex:3];
[enc setBytes:&rope length:sizeof(rope) atIndex:4];
[enc setThreadgroupMemoryLength:32u * 2u * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(((NSUInteger)rank + 1u) / 2u, 1, n_groups)
threadsPerThreadgroup:MTLSizeMake(32, 4, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_finish_command_buffer(cb, owned, "V4.1 attention low projection with rope");
}
}
int ds4_gpu_dsv41_norm_pair_rows(ds4_gpu_tensor *out0, const ds4_gpu_tensor *x0, uint64_t weight0_offset, uint32_t n0,
ds4_gpu_tensor *out1, const ds4_gpu_tensor *x1, uint64_t weight1_offset, uint32_t n1,
const void *model_map, uint64_t model_size, float eps, uint32_t rows) {
if (!rows || rows > 65535u) return 0;
if (!g_initialized && !ds4_gpu_init()) return 0;
const NSUInteger ntg0 = ds4_gpu_rms_norm_threads(n0), ntg1 = ds4_gpu_rms_norm_threads(n1);
if (!out0 || !x0 || !out1 || !x1 || !model_map || !n0 || !n1 || (n0 & 3u) || (n1 & 3u) ||
ntg0 % 32u || ntg1 % 32u || ntg0 + ntg1 > 1024u ||
!dsv41_tensor_has_floats(x0, (uint64_t)rows * n0) || !dsv41_tensor_has_floats(out0, (uint64_t)rows * n0) ||
!dsv41_tensor_has_floats(x1, (uint64_t)rows * n1) || !dsv41_tensor_has_floats(out1, (uint64_t)rows * n1) ||
weight0_offset > model_size || (uint64_t)n0 * sizeof(float) > model_size - weight0_offset ||
weight1_offset > model_size || (uint64_t)n1 * sizeof(float) > model_size - weight1_offset) return 0;
@autoreleasepool {
uint64_t inner0 = 0, inner1 = 0;
id<MTLBuffer> w0 = ds4_gpu_wrap_model_range(model_map, model_size, weight0_offset, (uint64_t)n0 * sizeof(float), &inner0);
id<MTLBuffer> w1 = ds4_gpu_wrap_model_range(model_map, model_size, weight1_offset, (uint64_t)n1 * sizeof(float), &inner1);
id<MTLComputePipelineState> pipeline = ds4_gpu_get_pipeline("kernel_dsv41_norm_pair");
if (!w0 || !w1 || !pipeline || ntg0 + ntg1 > pipeline.maxTotalThreadsPerThreadgroup) return 0;
const struct { uint32_t n0, n1, ntg0, ntg1; float eps; } args = {n0, n1, (uint32_t)ntg0, (uint32_t)ntg1, eps};
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
id<MTLComputeCommandEncoder> enc = cb ? ds4_gpu_compute_encoder(cb) : nil;
if (!enc) return 0;
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:ds4_gpu_tensor_buffer(x0) offset:ds4_gpu_tensor_offset(x0) atIndex:1];
[enc setBuffer:w0 offset:(NSUInteger)inner0 atIndex:2];
[enc setBuffer:ds4_gpu_tensor_buffer(out0) offset:ds4_gpu_tensor_offset(out0) atIndex:3];
[enc setBuffer:ds4_gpu_tensor_buffer(x1) offset:ds4_gpu_tensor_offset(x1) atIndex:4];
[enc setBuffer:w1 offset:(NSUInteger)inner1 atIndex:5];
[enc setBuffer:ds4_gpu_tensor_buffer(out1) offset:ds4_gpu_tensor_offset(out1) atIndex:6];
[enc setThreadgroupMemoryLength:64u * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(1, rows, 1)
threadsPerThreadgroup:MTLSizeMake(ntg0 + ntg1, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_finish_command_buffer(cb, owned, "V4.1 paired norm");
}
}
int ds4_gpu_dsv41_rope_quantize(const ds4_gpu_tensor *x, ds4_gpu_tensor *dst, uint64_t dst_offset,
uint32_t width, uint32_t start, bool compressed,
ds4_v41_activation_format format) {
const uint32_t block = format == DS4_V41_FP4_E4M3 ? 16u : 32u;
static int off = -1;
if (off < 0) off = getenv("DS4_METAL_DISABLE_V41_ROPE_QUANTIZE") != NULL;
if (off || width < 64u || width % block || format < DS4_V41_FP8_E8M0 || format > DS4_V41_FP4_E4M3 ||
start >= 1048576u || dst_offset % 4u || !dsv41_tensor_has_floats(x, width) ||
!dst || ds4_gpu_tensor_bytes(dst) < dst_offset + (uint64_t)width * sizeof(float)) return 0;
if (!g_initialized && !ds4_gpu_init()) return 0;
@autoreleasepool {
id<MTLComputePipelineState> pipeline = ds4_gpu_get_pipeline("kernel_dsv41_rope_quantize");
if (!pipeline) return 0;
struct {
uint32_t width, start, inverse, mode;
float frequencies[32];
} args = {width, start, 0u, (uint32_t)format, {0}};
memcpy(args.frequencies, ds4_gpu_dsv41_rope_frequencies(compressed), sizeof(args.frequencies));
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
id<MTLComputeCommandEncoder> enc = cb ? ds4_gpu_compute_encoder(cb) : nil;
if (!enc) return 0;
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:ds4_gpu_tensor_buffer(x) offset:ds4_gpu_tensor_offset(x) atIndex:1];
[enc setBuffer:ds4_gpu_tensor_buffer(dst) offset:ds4_gpu_tensor_offset(dst) + (NSUInteger)dst_offset atIndex:2];
[enc dispatchThreadgroups:MTLSizeMake(width / block, 1, 1)
threadsPerThreadgroup:MTLSizeMake(32, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_finish_command_buffer(cb, owned, "V4.1 rope, quantization and store");
}
}
int ds4_gpu_dsv41_engram_add(ds4_gpu_tensor *residual,
const ds4_gpu_tensor *kv,
const ds4_gpu_tensor *q_weight,
const ds4_gpu_tensor *k_weight,
const ds4_gpu_tensor *mask,
uint32_t width, uint32_t rows, float eps) {
const uint64_t count = (uint64_t)width * rows;
if (!width || !rows || !isfinite(eps) || eps <= 0 || count > UINT64_MAX / 5u ||
!dsv41_tensor_has_floats(residual, count * 4u) ||
!dsv41_tensor_has_floats(kv, count * 5u) ||
!dsv41_tensor_has_floats(q_weight, (uint64_t)width * 4u) ||
!dsv41_tensor_has_floats(k_weight, (uint64_t)width * 4u) ||
(mask && ds4_gpu_tensor_bytes(mask) < rows)) return 0;
if (!g_initialized && !ds4_gpu_init()) return 0;
@autoreleasepool {
id<MTLComputePipelineState> pipeline = ds4_gpu_get_pipeline("kernel_dsv41_engram_add");
if (!pipeline) return 0;
const struct { uint32_t width, rows; float eps; uint32_t masked; } args =
{width, rows, eps, mask != NULL};
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
id<MTLComputeCommandEncoder> enc = cb ? ds4_gpu_compute_encoder(cb) : nil;
if (!enc) return 0;
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
const ds4_gpu_tensor *inputs[] = {residual, kv, q_weight, k_weight, mask ? mask : residual};
for (NSUInteger i = 0; i < 5; i++)
[enc setBuffer:ds4_gpu_tensor_buffer(inputs[i])
offset:ds4_gpu_tensor_offset(inputs[i]) atIndex:i + 1];
[enc dispatchThreadgroups:MTLSizeMake(rows, 4, 1)
threadsPerThreadgroup:MTLSizeMake(32, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_finish_command_buffer(cb, owned, "V4.1 Engram gate");
}
}
int ds4_gpu_dsv41_carry_copy(ds4_gpu_tensor *packed, uint32_t row_offset,
ds4_gpu_tensor *plain, uint32_t width, uint32_t rows,
uint32_t format, bool pack) {
if (!width || !rows || rows > UINT32_MAX - row_offset ||
format > DS4_V41_CARRY_MASK || packed == plain) return 0;
const uint32_t words = format == DS4_V41_CARRY_BF16 ?
((uint64_t)width + 1u) / 2u : ((uint64_t)width + 31u) / 32u;
if (!dsv41_tensor_has_floats(packed, (uint64_t)(row_offset + rows) * words) ||
!dsv41_tensor_has_floats(plain, (uint64_t)rows * width)) return 0;
if (!g_initialized && !ds4_gpu_init()) return 0;
@autoreleasepool {
id<MTLComputePipelineState> pipeline = ds4_gpu_get_pipeline("kernel_dsv41_carry_copy");
if (!pipeline) return 0;
const uint32_t args[] = {width, rows, words, format, pack};
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
id<MTLComputeCommandEncoder> enc = cb ? ds4_gpu_compute_encoder(cb) : nil;
if (!enc) return 0;
[enc setComputePipelineState:pipeline];
[enc setBytes:args length:sizeof(args) atIndex:0];
[enc setBuffer:ds4_gpu_tensor_buffer(packed) offset:ds4_gpu_tensor_offset(packed) +
(NSUInteger)row_offset * words * sizeof(uint32_t) atIndex:1];
[enc setBuffer:ds4_gpu_tensor_buffer(plain) offset:ds4_gpu_tensor_offset(plain) atIndex:2];
[enc dispatchThreadgroups:MTLSizeMake(((uint64_t)width + 127u) / 128u, rows, 1)
threadsPerThreadgroup:MTLSizeMake(128, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_finish_command_buffer(cb, owned, "V4.1 compact prefill carry");
}
}
int ds4_gpu_dsv41_pool2(ds4_gpu_tensor *out,
const ds4_gpu_tensor *kv, const ds4_gpu_tensor *scores,
ds4_gpu_tensor *previous_kv, ds4_gpu_tensor *previous_scores,
uint32_t width, uint32_t rows, uint32_t start) {
const uint64_t count = (uint64_t)width * rows;
const uint32_t pairs = (uint32_t)(((uint64_t)rows + (start & 1u)) / 2u);
if (!width || !rows || rows > UINT32_MAX - start ||
!dsv41_tensor_has_floats(kv, count) || !dsv41_tensor_has_floats(scores, count) ||
!dsv41_tensor_has_floats(previous_kv, width) ||
!dsv41_tensor_has_floats(previous_scores, width) ||
(pairs && !dsv41_tensor_has_floats(out, (uint64_t)width * pairs))) return 0;
if (!g_initialized && !ds4_gpu_init()) return 0;
@autoreleasepool {
if (pairs) {
id<MTLComputePipelineState> pipeline = ds4_gpu_get_pipeline("kernel_dsv41_pool2");
if (!pipeline) return 0;
const uint32_t args[] = {width, pairs, start & 1u};
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
id<MTLComputeCommandEncoder> enc = cb ? ds4_gpu_compute_encoder(cb) : nil;
if (!enc) return 0;
[enc setComputePipelineState:pipeline];
[enc setBytes:args length:sizeof(args) atIndex:0];
const ds4_gpu_tensor *inputs[] = {out, kv, scores, previous_kv, previous_scores};
for (NSUInteger i = 0; i < 5; i++)
[enc setBuffer:ds4_gpu_tensor_buffer(inputs[i])
offset:ds4_gpu_tensor_offset(inputs[i]) atIndex:i + 1];
[enc dispatchThreads:MTLSizeMake(width, pairs, 1)
threadsPerThreadgroup:MTLSizeMake(MIN(width, 256u), 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, "V4.1 KV pair pooling")) return 0;
}
/* Keep the last even-position input, as the row-at-a-time path does.
* It is live only for an odd frontier, but matching it also makes
* snapshots independent of how the prefix was partitioned. */
const uint32_t last_even = (start + rows - 1u) & ~1u;
if (last_even >= start) {
const uint64_t bytes = (uint64_t)width * sizeof(float);
const uint64_t offset = (last_even - start) * bytes;
if (!ds4_gpu_tensor_copy(previous_kv, 0, kv, offset, bytes) ||
!ds4_gpu_tensor_copy(previous_scores, 0, scores, offset, bytes)) return 0;
}
}
return 1;
}
static int dsv41_candidates(ds4_gpu_tensor *out, const ds4_gpu_tensor *scores,
const ds4_gpu_tensor *mask, uint32_t width,
uint32_t rows, uint32_t start, uint32_t ratio,
uint32_t mask_stride) {
if (!width || width > UINT32_MAX - 7u || !rows || !ratio ||
rows > UINT32_MAX - start) return 0;
const uint32_t blocks = (width + 7u) / 8u;
const uint32_t output_width = mask ? width : blocks;
if (!mask_stride) mask_stride = blocks;
if (mask_stride < blocks ||
!dsv41_tensor_has_floats(scores, (uint64_t)width * rows) ||
!dsv41_tensor_has_floats(out, (uint64_t)output_width * rows) ||
(mask && !dsv41_tensor_has_floats(mask, (uint64_t)mask_stride * (rows - 1u) + blocks)))
return 0;
if (!g_initialized && !ds4_gpu_init()) return 0;
@autoreleasepool {
id<MTLComputePipelineState> pipeline = ds4_gpu_get_pipeline(mask ?
"kernel_dsv41_candidate_filter" : "kernel_dsv41_candidate_blocks");
if (!pipeline) return 0;
const uint32_t args[] = {width, rows, start, ratio, mask_stride};
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
id<MTLComputeCommandEncoder> enc = cb ? ds4_gpu_compute_encoder(cb) : nil;
if (!enc) return 0;
[enc setComputePipelineState:pipeline];
[enc setBytes:args length:sizeof(args) atIndex:0];
const ds4_gpu_tensor *inputs[] = {scores, out, mask ? mask : scores};
for (NSUInteger i = 0; i < 3; i++)
[enc setBuffer:ds4_gpu_tensor_buffer(inputs[i])
offset:ds4_gpu_tensor_offset(inputs[i]) atIndex:i + 1];
[enc dispatchThreads:MTLSizeMake(output_width, rows, 1)
threadsPerThreadgroup:MTLSizeMake(MIN(output_width, 256u), 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_finish_command_buffer(cb, owned, "V4.1 candidate selection");
}
}
int ds4_gpu_dsv41_candidate_blocks(ds4_gpu_tensor *blocks,
const ds4_gpu_tensor *scores,
uint32_t width, uint32_t rows,
uint32_t start, uint32_t ratio) {
return dsv41_candidates(blocks, scores, NULL, width, rows, start, ratio, 0);
}
int ds4_gpu_dsv41_candidate_filter(ds4_gpu_tensor *scores,
const ds4_gpu_tensor *block_mask,
uint32_t width, uint32_t rows,
uint32_t start, uint32_t ratio,
uint32_t mask_stride) {
if (!block_mask) return 0;
return dsv41_candidates(scores, scores, block_mask, width, rows, start, ratio, mask_stride);
}
/* Row t sees (start + t + 8) / 8 blocks: the causal sort with start + 7 and
* ratio 8 keeps each row's own width, padding and tie order. */
int ds4_gpu_dsv41_candidate_topk_batch(ds4_gpu_tensor *selected,
const ds4_gpu_tensor *blocks,
uint32_t width, uint32_t rows, uint32_t start) {
if (!rows || start > UINT32_MAX - 8u || rows > UINT32_MAX - 8u - start ||
width > INT32_MAX || rows > INT32_MAX ||
(start + rows + 7u) / 8u > width || (start + 8u) / 8u <= 2048u) return 0;
return ds4_gpu_indexer_topk_tensor_impl(selected, blocks, width, rows, 2048u, start + 7u, 8u);
}
int ds4_gpu_dsv41_candidate_mask_batch(ds4_gpu_tensor *mask,
const ds4_gpu_tensor *selected,
uint32_t width, uint32_t rows,
uint32_t mask_stride) {
return ds4_gpu_dsv4_topk_mask_tensor_impl(mask, selected, width, rows, 2048u, mask_stride);
}
int ds4_gpu_dsv41_hc_mean(uint32_t rows, uint32_t dim, uint32_t hc,
const ds4_gpu_tensor *stream, ds4_gpu_tensor *out,
uint32_t out_stride, uint32_t out_off) {
if (!rows || !dim || !hc || out_stride < dim || out_off > out_stride - dim ||
!dsv41_tensor_has_floats(stream, (uint64_t)rows * hc * dim) ||
!dsv41_tensor_has_floats(out, (uint64_t)rows * out_stride)) return 0;
if (!g_initialized && !ds4_gpu_init()) return 0;
@autoreleasepool {
id<MTLComputePipelineState> pipeline = ds4_gpu_get_pipeline("kernel_dsv41_hc_mean");
if (!pipeline) return 0;
const uint32_t args[] = {dim, hc, out_stride, out_off};
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
id<MTLComputeCommandEncoder> enc = cb ? ds4_gpu_compute_encoder(cb) : nil;
if (!enc) return 0;
[enc setComputePipelineState:pipeline];
[enc setBytes:args length:sizeof(args) atIndex:0];
[enc setBuffer:ds4_gpu_tensor_buffer(stream) offset:ds4_gpu_tensor_offset(stream) atIndex:1];
[enc setBuffer:ds4_gpu_tensor_buffer(out) offset:ds4_gpu_tensor_offset(out) atIndex:2];
[enc dispatchThreads:MTLSizeMake(dim, rows, 1)
threadsPerThreadgroup:MTLSizeMake(MIN(dim, 256u), 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_finish_command_buffer(cb, owned, "V4.1 stream mean");
}
}
int ds4_gpu_dsv41_markov_chain(uint32_t block, uint32_t vocab, uint32_t rank, uint32_t dim,
const ds4_gpu_tensor *logits, const ds4_gpu_tensor *x,
const void *model_map, uint64_t model_size,
uint64_t embed_offset, uint64_t head_offset, int f16,
const ds4_gpu_tensor *conf_proj, ds4_gpu_tensor *tokens,
ds4_gpu_tensor *conf, ds4_gpu_tensor *parts, uint32_t n_parts) {
if (!block || !vocab || !rank || (rank % 32u) || rank > 512u || !dim || !n_parts || n_parts > 4096u ||
!dsv41_tensor_has_floats(logits, (uint64_t)block * vocab) ||
!dsv41_tensor_has_floats(x, (uint64_t)block * dim) ||
!dsv41_tensor_has_floats(conf_proj, (uint64_t)dim + rank) ||
!dsv41_tensor_has_floats(tokens, (uint64_t)block + 1u) ||
!dsv41_tensor_has_floats(conf, block) ||
!dsv41_tensor_has_floats(parts, (uint64_t)n_parts * 2u)) return 0;
if (!g_initialized && !ds4_gpu_init()) return 0;
@autoreleasepool {
const uint64_t bytes = (uint64_t)vocab * rank * (f16 ? 2u : 4u);
uint64_t e_inner = 0, h_inner = 0;
id<MTLBuffer> ebuf = ds4_gpu_wrap_model_range(model_map, model_size, embed_offset, bytes, &e_inner);
id<MTLBuffer> hbuf = ds4_gpu_wrap_model_range(model_map, model_size, head_offset, bytes, &h_inner);
id<MTLComputePipelineState> part = ds4_gpu_get_pipeline("kernel_dsv41_markov_part");
id<MTLComputePipelineState> final = ds4_gpu_get_pipeline("kernel_dsv41_markov_final");
if (!ebuf || !hbuf || !part || !final) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLBuffer> pbuf = ds4_gpu_tensor_buffer(parts);
const NSUInteger p_val = ds4_gpu_tensor_offset(parts);
const NSUInteger p_idx = p_val + (NSUInteger)n_parts * sizeof(float);
for (uint32_t step = 0; step < block; step++) {
const uint32_t args[] = {vocab, rank, dim, step, n_parts, f16 ? 1u : 0u};
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:part];
[enc setBytes:args length:sizeof(args) atIndex:0];
[enc setBuffer:ds4_gpu_tensor_buffer(logits) offset:ds4_gpu_tensor_offset(logits) atIndex:1];
[enc setBuffer:ebuf offset:(NSUInteger)e_inner atIndex:2];
[enc setBuffer:hbuf offset:(NSUInteger)h_inner atIndex:3];
[enc setBuffer:ds4_gpu_tensor_buffer(tokens) offset:ds4_gpu_tensor_offset(tokens) atIndex:4];
[enc setBuffer:pbuf offset:p_val atIndex:5];
[enc setBuffer:pbuf offset:p_idx atIndex:6];
[enc setThreadgroupMemoryLength:(NSUInteger)rank * sizeof(float) atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(n_parts, 1, 1) threadsPerThreadgroup:MTLSizeMake(256, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:final];
[enc setBytes:args length:sizeof(args) atIndex:0];
[enc setBuffer:pbuf offset:p_val atIndex:1];
[enc setBuffer:pbuf offset:p_idx atIndex:2];
[enc setBuffer:ds4_gpu_tensor_buffer(tokens) offset:ds4_gpu_tensor_offset(tokens) atIndex:3];
[enc setBuffer:ds4_gpu_tensor_buffer(x) offset:ds4_gpu_tensor_offset(x) atIndex:4];
[enc setBuffer:ebuf offset:(NSUInteger)e_inner atIndex:5];
[enc setBuffer:ds4_gpu_tensor_buffer(conf_proj) offset:ds4_gpu_tensor_offset(conf_proj) atIndex:6];
[enc setBuffer:ds4_gpu_tensor_buffer(conf) offset:ds4_gpu_tensor_offset(conf) atIndex:7];
[enc dispatchThreadgroups:MTLSizeMake(1, 1, 1) threadsPerThreadgroup:MTLSizeMake(256, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
}
return ds4_gpu_finish_command_buffer(cb, owned, "V4.1 Markov chain");
}
}
int ds4_gpu_dsv41_gather_kv(ds4_gpu_tensor *out, const ds4_gpu_tensor *source,
const ds4_gpu_tensor *ids, uint32_t source_rows,
uint32_t selected_rows) {
if (!source_rows || !selected_rows || selected_rows > 512 ||
selected_rows > source_rows ||
!dsv41_tensor_has_floats(source, (uint64_t)source_rows * 512u) ||
!dsv41_tensor_has_floats(out, (uint64_t)selected_rows * 512u) ||
!dsv41_tensor_has_floats(ids, selected_rows)) return 0;
if (!g_initialized && !ds4_gpu_init()) return 0;
@autoreleasepool {
const uint64_t row_bytes = 512u * sizeof(float);
const ds4_gpu_get_rows_args args = {
.ne00t = 512, .ne00 = 512, .nb01 = row_bytes,
.nb02 = (uint64_t)source_rows * row_bytes,
.nb03 = (uint64_t)source_rows * row_bytes,
.ne10 = (int32_t)selected_rows, .nb10 = sizeof(int32_t),
.nb11 = (uint64_t)selected_rows * sizeof(int32_t),
.nb12 = (uint64_t)selected_rows * sizeof(int32_t),
.nb1 = row_bytes, .nb2 = (uint64_t)selected_rows * row_bytes,
.nb3 = (uint64_t)selected_rows * row_bytes,
};
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
id<MTLComputeCommandEncoder> enc = cb ? ds4_gpu_compute_encoder(cb) : nil;
if (!enc) return 0;
[enc setComputePipelineState:g_get_rows_f32_pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
const ds4_gpu_tensor *inputs[] = {source, ids, out};
for (NSUInteger i = 0; i < 3; i++)
[enc setBuffer:ds4_gpu_tensor_buffer(inputs[i])
offset:ds4_gpu_tensor_offset(inputs[i]) atIndex:i + 1];
[enc dispatchThreadgroups:MTLSizeMake(2u * selected_rows, 1, 1)
threadsPerThreadgroup:MTLSizeMake(256, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_finish_command_buffer(cb, owned, "V4.1 sparse KV gather");
}
}
void ds4_gpu_set_glm_mtp_verify_mode(bool enabled) {
(void)enabled;
}
/* Qwen3.8-Flash-Next kernels (metal/qwen4.metal): one dispatch per wrapper,
* weights bound from the model map by offset, transients as ds4_gpu_tensors.
* Every wrapper joins the active command batch. */
typedef struct {
id<MTLBuffer> buf;
NSUInteger off;
} qwen4_bind;
static bool qwen4_bind_tensor(qwen4_bind *b, const ds4_gpu_tensor *t, uint64_t min_bytes, const char *what) {
if (!t || !ds4_gpu_tensor_buffer(t) || ds4_gpu_tensor_bytes(t) < min_bytes) {
fprintf(stderr, "ds4: Qwen3.8 %s buffer is missing or undersized (%" PRIu64 " < %" PRIu64 ")\n",
what, t ? ds4_gpu_tensor_bytes(t) : 0, min_bytes);
return false;
}
b->buf = ds4_gpu_tensor_buffer(t);
b->off = ds4_gpu_tensor_offset(t);
return true;
}
static bool qwen4_bind_weight(qwen4_bind *b, const void *map, uint64_t size,
uint64_t offset, uint64_t bytes, const char *what) {
uint64_t inner = 0;
if (!map || offset > size || bytes > size - offset) {
fprintf(stderr, "ds4: Qwen3.8 %s range is outside the mapped model\n", what);
return false;
}
b->buf = ds4_gpu_wrap_model_range(map, size, offset, bytes, &inner);
b->off = (NSUInteger)inner;
return b->buf != nil;
}
enum {
QWEN4_K_HC_NORM_F16 = 0,
QWEN4_K_HC_NORM_F32,
QWEN4_K_HC_NORM_Q8,
QWEN4_K_HC_NORM_REUSE_F16,
QWEN4_K_HC_NORM_REUSE_F32,
QWEN4_K_HC_NORM_REUSE_Q8,
QWEN4_K_HC_GATE_MIX_F16,
QWEN4_K_HC_GATE_MIX_F16_PF,
QWEN4_K_HC_GATE_MIX_F32,
QWEN4_K_HC_GATE_MIX_Q8,
QWEN4_K_HC_GATE_MIX_PAIR_F16,
QWEN4_K_HC_GATE_MIX_PAIR_F16_PF,
QWEN4_K_HC_GATE_MIX_PAIR_F32,
QWEN4_K_HC_GATE_MIX_PAIR_Q8,
QWEN4_K_MULTI_GEMV,
QWEN4_K_HC_COMBINE,
QWEN4_K_CONV_STREAM,
QWEN4_K_GDN_PREP,
QWEN4_K_GDN_SCAN,
QWEN4_K_GDN_SCAN_R4,
QWEN4_K_GDN_OUT,
QWEN4_K_PLE_GATE,
QWEN4_K_PLE_CONV,
QWEN4_K_ROUTER,
QWEN4_K_ATTN_PREP,
QWEN4_K_IDX_BLOCK_KEY,
QWEN4_K_IDX_SCORE,
QWEN4_K_IDX_SCORE_VEC,
QWEN4_K_IDX_SELECT,
QWEN4_K_IDX_SELECT_PRE,
QWEN4_K_IDX_SCORE_MM,
QWEN4_K_IDX_EXPAND,
QWEN4_K_ATTN_DECODE_NPT8,
QWEN4_K_ATTN_DECODE_NPT4,
QWEN4_K_ATTN_DECODE_NPT1,
QWEN4_K_ATTN_MERGE_NPT8,
QWEN4_K_ATTN_MERGE_NPT4,
QWEN4_K_ATTN_MERGE_NPT1,
QWEN4_K_ATTN_MERGE_WIDE_NPT8,
QWEN4_K_ATTN_MERGE_WIDE_NPT4,
QWEN4_K_ATTN_MM,
QWEN4_K_MOE_MID,
QWEN4_K_MOE_MID_Q4K,
QWEN4_K_MOE_MID_Q4K_NR1,
QWEN4_K_MOE_DOWN,
QWEN4_K_MOE_DOWN_MXFP4_PF,
QWEN4_K_MOE_MID_Q4K_GROUPED,
QWEN4_K_MOE_DOWN_MXFP4_GROUPED,
QWEN4_K_MOE_REDUCE,
QWEN4_K_HC_COMBINE_NORM,
QWEN4_K_ARGMAX,
QWEN4_K_MTP_STAGE,
QWEN4_K_MTP_COMBINE,
QWEN4_K_GDN_FRONT,
QWEN4_K_MOE_BUILD_LISTS,
QWEN4_K_MOE_MM_MID,
QWEN4_K_MOE_MM_MID_NT1,
QWEN4_K_MOE_MM_MID_NT2,
QWEN4_K_MOE_MM_DOWN,
QWEN4_K_MOE_MM_DOWN_NT1,
QWEN4_K_MOE_MM_DOWN_NT2,
QWEN4_K_MOE_MM_DOWN_NT8,
QWEN4_K_MOE_MM_MID_NT8,
QWEN4_K_MOE_MM_MID_NAX,
QWEN4_K_MOE_MM_DOWN_NAX,
QWEN4_K_MOE_MM_MID_NAX64,
QWEN4_K_MOE_MM_DOWN_NAX64,
QWEN4_K_MOE_MM_MID_NAXF,
QWEN4_K_MOE_MM_DOWN_NAXF,
QWEN4_K_MOE_MM_MID_NAXF64,
QWEN4_K_MOE_MM_DOWN_NAXF64,
QWEN4_K_MOE_MM_MID_NAXC,
QWEN4_K_MOE_MM_DOWN_NAXC,
QWEN4_K_MOE_MM_MID_NAXC64,
QWEN4_K_MOE_MM_DOWN_NAXC64,
QWEN4_K_ROWS_F32_TO_F16,
QWEN4_K_DENSE_MM,
QWEN4_K_DENSE_MM_REDUCE,
QWEN4_K_BATCH_MM_Q8_T1,
QWEN4_K_BATCH_MM_Q8_T2,
QWEN4_K_GDN_SCAN_ROWS,
QWEN4_K_CONV_STREAM_ROWS,
QWEN4_K_GDN_SCAN_ROWS2,
QWEN4_K_CONV_STREAM_ROWS2,
QWEN4_K_ATTN_PREP_ROWS,
QWEN4_K_IDX_BLOCK_KEY_ROWS,
QWEN4_K_IDX_SCORE_ROWS,
QWEN4_K_IDX_SELECT_ROWS,
QWEN4_K_IDX_EXPAND_ROWS,
QWEN4_K_ATTN_DECODE_ROWS_NPT8,
QWEN4_K_ATTN_DECODE_ROWS_NPT4,
QWEN4_K_ATTN_MERGE_ROWS_NPT8,
QWEN4_K_ATTN_MERGE_ROWS_NPT4,
QWEN4_K_HC_LO_ACT,
QWEN4_K_HC_MIX_ROWS,
QWEN4_K_VIS_PATCH_FINISH,
QWEN4_K_VIS_LAYERNORM,
QWEN4_K_VIS_QKV_ROPE,
QWEN4_K_VIS_ATTENTION,
QWEN4_K_VIS_BIAS_RESIDUAL,
QWEN4_K_VIS_BIAS_ACT,
QWEN4_K_COUNT,
};
static const char *const qwen4_kernel_names[QWEN4_K_COUNT] = {
"kernel_qwen4_hc_norm_f16",
"kernel_qwen4_hc_norm_f32",
"kernel_qwen4_hc_norm_q8",
"kernel_qwen4_hc_norm_reuse_f16",
"kernel_qwen4_hc_norm_reuse_f32",
"kernel_qwen4_hc_norm_reuse_q8",
"kernel_qwen4_hc_gate_mix_f16",
"kernel_qwen4_hc_gate_mix_f16_pf",
"kernel_qwen4_hc_gate_mix_f32",
"kernel_qwen4_hc_gate_mix_q8",
"kernel_qwen4_hc_gate_mix_pair_f16",
"kernel_qwen4_hc_gate_mix_pair_f16_pf",
"kernel_qwen4_hc_gate_mix_pair_f32",
"kernel_qwen4_hc_gate_mix_pair_q8",
"kernel_qwen4_multi_gemv",
"kernel_qwen4_hc_combine",
"kernel_qwen4_conv_stream",
"kernel_qwen4_gdn_prep",
"kernel_qwen4_gdn_scan",
"kernel_qwen4_gdn_scan_r4",
"kernel_qwen4_gdn_out",
"kernel_qwen4_ple_gate",
"kernel_qwen4_ple_conv",
"kernel_qwen4_router_topk",
"kernel_qwen4_attn_prep",
"kernel_qwen4_idx_block_key",
"kernel_qwen4_idx_score",
"kernel_qwen4_idx_score_vec",
"kernel_qwen4_idx_select",
"kernel_qwen4_idx_select_pre",
"kernel_qwen4_idx_score_mm",
"kernel_qwen4_idx_expand",
"kernel_qwen4_attn_decode_npt8",
"kernel_qwen4_attn_decode_npt4",
"kernel_qwen4_attn_decode_npt1",
"kernel_qwen4_attn_merge_npt8",
"kernel_qwen4_attn_merge_npt4",
"kernel_qwen4_attn_merge_npt1",
"kernel_qwen4_attn_merge_wide_npt8",
"kernel_qwen4_attn_merge_wide_npt4",
"kernel_qwen4_attn_mm",
"kernel_qwen4_moe_mid",
"kernel_qwen4_moe_mid_q4k",
"kernel_qwen4_moe_mid_q4k_nr1",
"kernel_qwen4_moe_down",
"kernel_qwen4_moe_down_mxfp4_pf",
"kernel_qwen4_moe_mid_q4k_grouped",
"kernel_qwen4_moe_down_mxfp4_grouped",
"kernel_qwen4_moe_reduce",
"kernel_qwen4_hc_combine_norm_f16",
"kernel_qwen4_argmax",
"kernel_qwen4_mtp_stage",
"kernel_qwen4_mtp_combine",
"kernel_qwen4_gdn_front",
"kernel_qwen4_moe_build_lists",
"kernel_qwen4_moe_mm_mid",
"kernel_qwen4_moe_mm_mid_nt1",
"kernel_qwen4_moe_mm_mid_nt2",
"kernel_qwen4_moe_mm_down",
"kernel_qwen4_moe_mm_down_nt1",
"kernel_qwen4_moe_mm_down_nt2",
"kernel_qwen4_moe_mm_down_nt8",
"kernel_qwen4_moe_mm_mid_nt8",
"kernel_qwen4_moe_mm_mid_nax",
"kernel_qwen4_moe_mm_down_nax",
"kernel_qwen4_moe_mm_mid_nax64",
"kernel_qwen4_moe_mm_down_nax64",
"kernel_qwen4_moe_mm_mid_naxf",
"kernel_qwen4_moe_mm_down_naxf",
"kernel_qwen4_moe_mm_mid_naxf64",
"kernel_qwen4_moe_mm_down_naxf64",
"kernel_qwen4_moe_mm_mid_naxc",
"kernel_qwen4_moe_mm_down_naxc",
"kernel_qwen4_moe_mm_mid_naxc64",
"kernel_qwen4_moe_mm_down_naxc64",
"kernel_qwen4_rows_f32_to_f16",
"kernel_qwen4_dense_mm",
"kernel_qwen4_dense_mm_reduce",
"kernel_qwen4_batch_mm_q8_t1",
"kernel_qwen4_batch_mm_q8_t2",
"kernel_qwen4_gdn_scan_rows",
"kernel_qwen4_conv_stream_rows",
"kernel_qwen4_gdn_scan_rows2",
"kernel_qwen4_conv_stream_rows2",
"kernel_qwen4_attn_prep_rows",
"kernel_qwen4_idx_block_key_rows",
"kernel_qwen4_idx_score_rows",
"kernel_qwen4_idx_select_rows",
"kernel_qwen4_idx_expand_rows",
"kernel_qwen4_attn_decode_rows_npt8",
"kernel_qwen4_attn_decode_rows_npt4",
"kernel_qwen4_attn_merge_rows_npt8",
"kernel_qwen4_attn_merge_rows_npt4",
"kernel_qwen4_hc_lo_act",
"kernel_qwen4_hc_mix_rows",
"kernel_qwen4_vis_patch_finish",
"kernel_qwen4_vis_layernorm",
"kernel_qwen4_vis_qkv_rope",
"kernel_qwen4_vis_attention",
"kernel_qwen4_vis_bias_residual",
"kernel_qwen4_vis_bias_act",
};
typedef struct {
uint32_t n_tokens, n_slots, n_out, in_dim, out_rows, weight_type, row_bytes, list_cap;
uint64_t expert_bytes;
uint32_t n_expert, tiles_per_launch, tail_base, expert_major;
} qwen4_moe_mm_args;
/* Expert-major tile order (DS4_QWEN4_MOE_MM_ORDER=0 restores the tile-major
* grid): consecutive threadgroups share a token tile and one expert's rows
* stay cache-resident across its tiles. */
static uint32_t qwen4_moe_mm_expert_major(void) {
const int override = ds4_gpu_env_bool("DS4_QWEN4_MOE_MM_ORDER");
return override >= 0 ? (uint32_t)override : ds4_gpu_device_is_m5_apple_silicon() ? 1u : 0u;
}
static MTLSize qwen4_moe_mm_grid(uint32_t row_blocks, uint32_t n_expert, uint32_t tiles, uint32_t expert_major) {
return expert_major ? MTLSizeMake((NSUInteger)row_blocks * tiles, n_expert, 1)
: MTLSizeMake(row_blocks, n_expert, tiles);
}
static id<MTLComputePipelineState> g_qwen4_pipelines[QWEN4_K_COUNT];
#define QWEN4_ATTN_NSG 4
#define QWEN4_ATTN_MAX_SPLITS 64
#define QWEN4_ATTN_ROWS_MAX 64 /* decode batch rows the attention rows kernels take */
typedef struct {
uint32_t n_tokens, n_slots, in_dim, out_rows, weight_type, row_bytes;
uint64_t expert_bytes;
uint32_t has_shared, shared_type, shared_row_bytes, n_total_expert;
uint32_t list_cap, pad0;
} qwen4_moe_args;
static bool qwen4_moe_mv_specialize(uint32_t type) {
/* Constant quantization and logical width remove the generic decode
* branches. Keep the original per-lane reduction order and padded stride.
* M3 Ultra uses low-bit and MXFP4 down rows; M5 uses MXFP4 down rows. */
const int override = ds4_gpu_env_bool("DS4_QWEN4_MOE_MV_SPECIALIZE");
return override >= 0 ? override != 0 :
((type == 16u || type == 10u || type == 39u) && ds4_gpu_device_name_contains("M3 Ultra")) ||
(type == 39u && ds4_gpu_device_is_m5_apple_silicon());
}
static uint32_t qwen4_moe_mv_rows(void) {
return (uint32_t)ds4_gpu_env_u64("DS4_QWEN4_MOE_MV_NR", 1u, 1u, 4u);
}
static uint32_t qwen4_moe_mv_groups(uint32_t type) {
/* Q2_K and MXFP4 use sixteen groups on M3 Ultra; MXFP4 also does on M5.
* Each group keeps its own rows and unchanged per-lane reduction order. */
const uint32_t default_nsg =
((type == 10u || type == 39u) && ds4_gpu_device_name_contains("M3 Ultra")) ||
(type == 39u && ds4_gpu_device_is_m5_apple_silicon()) ? 16u : 8u;
return (uint32_t)ds4_gpu_env_u64("DS4_QWEN4_MOE_MV_NSG", default_nsg, 1u, 16u);
}
/* resident: buffers the kernel reaches through GPU addresses rather than
* bindings (the decode batch's per-session caches); they are made resident
* for this dispatch. */
static int qwen4_dispatch_resident(int kernel, const void *args, size_t args_len,
const qwen4_bind *binds, int n_binds,
MTLSize grid, MTLSize tg, NSUInteger tg_mem,
const qwen4_bind *resident, uint32_t n_resident) {
if (!g_initialized && !ds4_gpu_init()) return 0;
@autoreleasepool {
id<MTLComputePipelineState> pipeline = nil;
if (kernel == QWEN4_K_MOE_MID || kernel == QWEN4_K_MOE_DOWN || kernel == QWEN4_K_MOE_DOWN_MXFP4_PF) {
const qwen4_moe_args *a = args;
const bool specialize = qwen4_moe_mv_specialize(a->weight_type);
const uint32_t values[] = {a->weight_type, a->shared_type, specialize ? a->in_dim : 0u, specialize ? qwen4_moe_mv_rows() : 0u};
NSString *key = [NSString stringWithFormat:@"%s_type=%u_shared=%u_dim=%u_rows=%u",
qwen4_kernel_names[kernel], values[0], values[1], values[2], values[3]];
pipeline = [g_pipeline_cache objectForKey:key];
if (!pipeline) {
MTLFunctionConstantValues *constants = [[MTLFunctionConstantValues alloc] init];
for (uint32_t i = 0; specialize && i < 4u; i++)
[constants setConstantValue:&values[i] type:MTLDataTypeUInt atIndex:901u+i];
NSError *error = nil;
id<MTLFunction> fn = [g_library newFunctionWithName:
[NSString stringWithUTF8String:qwen4_kernel_names[kernel]]
constantValues:constants error:&error];
if (fn) pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!pipeline) {
fprintf(stderr, "ds4: Qwen MoE decode pipeline failed: %s\n", [[error localizedDescription] UTF8String]);
return 0;
}
[g_pipeline_cache setObject:pipeline forKey:key];
}
} else if (kernel >= QWEN4_K_MOE_MM_MID && kernel <= QWEN4_K_MOE_MM_DOWN_NAXC64) {
/* Keep each quantization's dequantizer constant through the K
* loop. M3 Ultra and M5 have balanced full-model measurements;
* other devices can opt in, and zero restores the generic kernel. */
const int override = ds4_gpu_env_bool("DS4_QWEN4_MOE_MM_SPECIALIZE");
const bool specialize = override >= 0 ? override != 0 :
ds4_gpu_device_name_contains("M3 Ultra") ||
ds4_gpu_device_is_m5_apple_silicon();
const uint32_t type = specialize ?
((const qwen4_moe_mm_args *)args)->weight_type : 0u;
if (type >= 40u) return 0;
const uint32_t tail_base = ((const qwen4_moe_mm_args *)args)->tail_base;
NSString *key = [NSString stringWithFormat:@"%s_type=%u_tail=%u",
qwen4_kernel_names[kernel], type, tail_base];
pipeline = [g_pipeline_cache objectForKey:key];
if (!pipeline) {
MTLFunctionConstantValues *constants = [[MTLFunctionConstantValues alloc] init];
[constants setConstantValue:&type type:MTLDataTypeUInt atIndex:900];
[constants setConstantValue:&tail_base type:MTLDataTypeUInt atIndex:905];
NSError *error = nil;
id<MTLFunction> fn = [g_library newFunctionWithName:
[NSString stringWithUTF8String:qwen4_kernel_names[kernel]]
constantValues:constants error:&error];
if (fn) pipeline = [g_device newComputePipelineStateWithFunction:fn error:&error];
if (!pipeline) {
fprintf(stderr, "ds4: Qwen MoE pipeline failed: %s\n",
[[error localizedDescription] UTF8String]);
return 0;
}
[g_pipeline_cache setObject:pipeline forKey:key];
}
} else {
if (!g_qwen4_pipelines[kernel]) {
g_qwen4_pipelines[kernel] = ds4_gpu_get_pipeline(qwen4_kernel_names[kernel]);
if (!g_qwen4_pipelines[kernel]) {
static int source_skew_note;
if (!source_skew_note) {
source_skew_note = 1;
fprintf(stderr,
"ds4: Qwen3.8 kernel '%s' is absent from the compiled Metal sources.\n"
"ds4: Metal sources are loaded from ./metal at startup, so a binary newer\n"
"ds4: than the working directory's metal/ files (e.g. run from an older\n"
"ds4: checkout) produces this error. Run from the matching checkout, update\n"
"ds4: metal/, or point DS4_METAL_QWEN4_SOURCE at a current qwen4.metal.\n",
qwen4_kernel_names[kernel]);
}
return 0;
}
}
pipeline = g_qwen4_pipelines[kernel];
}
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:args length:args_len atIndex:0];
for (int i = 0; i < n_binds; i++) {
[enc setBuffer:binds[i].buf offset:binds[i].off atIndex:(NSUInteger)(i + 1)];
}
if (tg_mem) [enc setThreadgroupMemoryLength:tg_mem atIndex:0];
if (n_resident) {
id<MTLResource> res[QWEN4_ATTN_ROWS_MAX * 5u];
if (n_resident > sizeof(res) / sizeof(res[0])) return 0;
for (uint32_t i = 0; i < n_resident; i++) res[i] = resident[i].buf;
[enc useResources:res count:n_resident usage:MTLResourceUsageRead | MTLResourceUsageWrite];
}
[enc dispatchThreadgroups:grid threadsPerThreadgroup:tg];
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_finish_command_buffer(cb, owned, qwen4_kernel_names[kernel]);
}
}
static int qwen4_dispatch(int kernel, const void *args, size_t args_len,
const qwen4_bind *binds, int n_binds,
MTLSize grid, MTLSize tg, NSUInteger tg_mem) {
return qwen4_dispatch_resident(kernel, args, args_len, binds, n_binds, grid, tg, tg_mem, NULL, 0u);
}
/* bytes of one hc mixer row of n elements: f16, f32 or q8_0 */
static uint64_t qwen4_hc_row_bytes(uint32_t weight_type, uint64_t n) {
return weight_type == 1u ? n * 2u : weight_type == 0u ? n * 4u : weight_type == 8u ? (n / 32u) * 34u : 0u;
}
static int qwen4_hc_kernel(uint32_t weight_type, int k16, int k32, int kq8) {
return weight_type == 1u ? k16 : weight_type == 0u ? k32 : kq8;
}
static uint32_t qwen4_expert_row_bytes(uint32_t weight_type, uint32_t in_dim) {
if (in_dim == 0 || (in_dim % 32u) != 0) return 0;
switch (weight_type) {
case 8u: return (in_dim / 32u) * 34u; /* q8_0 */
case 12u: return (in_dim % 256u) ? 0u : (in_dim / 256u) * 144u; /* q4_K */
case 10u: return (in_dim % 256u) ? 0u : (in_dim / 256u) * 84u; /* q2_K */
case 16u: return (in_dim % 256u) ? 0u : (in_dim / 256u) * 66u; /* iq2_xxs */
case 39u: return (in_dim / 32u) * 17u; /* mxfp4 */
case 2u: return (in_dim / 32u) * 18u; /* q4_0 */
case 1u: return in_dim * 2u; /* f16 */
case 30u: return in_dim * 2u; /* bf16 */
case 0u: return in_dim * 4u; /* f32 */
default: return 0;
}
}
int ds4_gpu_qwen4_decode_fusions_enabled(void) {
const int override = ds4_gpu_env_bool("DS4_QWEN4_DECODE_FUSIONS");
return override >= 0 ? override : ds4_gpu_device_name_contains("M3 Ultra");
}
int ds4_gpu_qwen4_hc_combine_norm_tensor(
ds4_gpu_tensor *next_R, const ds4_gpu_tensor *blk, const ds4_gpu_tensor *old_inj,
ds4_gpu_tensor *xn, ds4_gpu_tensor *inj_part, const ds4_gpu_tensor *R,
const void *model_map, uint64_t model_size, uint64_t gamma_offset, uint64_t inject_offset,
uint32_t weight_type, uint32_t n_tokens, uint32_t n_embd, uint32_t n_hc, uint32_t n_inject, float eps) {
const uint64_t dim = (uint64_t)n_embd * n_hc;
const uint64_t wrow = qwen4_hc_row_bytes(weight_type, dim);
struct { uint32_t n_tokens, n_embd, n_hc, n_inject; float eps; uint32_t pad0, pad1, pad2; } args =
{ n_tokens, n_embd, n_hc, n_inject, eps, 0, 0, 0 };
qwen4_bind b[8];
if (n_tokens != 1u || weight_type != 1u || n_inject != n_hc) return 0;
if (ds4_gpu_tensor_buffer(next_R) == ds4_gpu_tensor_buffer(R) ||
ds4_gpu_tensor_buffer(inj_part) == ds4_gpu_tensor_buffer(old_inj)) return 0;
if (n_tokens == 0 || n_embd == 0 || n_hc == 0 || n_hc > 8 || n_inject > 4 || wrow == 0 || (dim % 32) != 0 ||
!qwen4_bind_tensor(&b[0], R, n_tokens * dim * sizeof(float), "hc norm input") ||
!qwen4_bind_weight(&b[1], model_map, model_size, gamma_offset, dim * sizeof(float), "hc norm gamma") ||
!qwen4_bind_tensor(&b[3], xn, n_tokens * dim * sizeof(float), "hc norm output")) {
return 0;
}
if (n_inject) {
if (!qwen4_bind_weight(&b[2], model_map, model_size, inject_offset, (uint64_t)n_inject * wrow, "hc inject") ||
!qwen4_bind_tensor(&b[4], inj_part,
(uint64_t)n_tokens * n_hc * DS4_QWEN4_HC_CHUNKS * n_inject * sizeof(float),
"hc inject partials")) {
return 0;
}
} else {
b[2] = b[1];
b[4] = b[3];
}
if (!qwen4_bind_tensor(&b[5], next_R, dim * sizeof(float), "combine norm next R") ||
!qwen4_bind_tensor(&b[6], blk, n_embd * sizeof(float), "combine norm block") ||
!qwen4_bind_tensor(&b[7], old_inj, n_hc * DS4_QWEN4_HC_CHUNKS * n_hc * sizeof(float), "combine norm old inject")) return 0;
return qwen4_dispatch(QWEN4_K_HC_COMBINE_NORM, &args, sizeof(args), b, 8,
MTLSizeMake(n_hc * DS4_QWEN4_HC_CHUNKS, 1, 1), MTLSizeMake(128, 1, 1), 0);
}
int ds4_gpu_qwen4_hc_norm_tensor(
ds4_gpu_tensor *xn, ds4_gpu_tensor *inj_part, const ds4_gpu_tensor *R,
const void *model_map, uint64_t model_size, uint64_t gamma_offset, uint64_t inject_offset,
uint32_t weight_type, uint32_t n_tokens, uint32_t n_embd, uint32_t n_hc, uint32_t n_inject, float eps) {
const uint64_t dim = (uint64_t)n_embd * n_hc;
const uint64_t wrow = qwen4_hc_row_bytes(weight_type, dim);
struct { uint32_t n_tokens, n_embd, n_hc, n_inject; float eps; uint32_t pad0, pad1, pad2; } args =
{ n_tokens, n_embd, n_hc, n_inject, eps, 0, 0, 0 };
qwen4_bind b[5];
if (n_tokens == 0 || n_embd == 0 || n_hc == 0 || n_hc > 8 || n_inject > 4 || wrow == 0 || (dim % 32) != 0 ||
!qwen4_bind_tensor(&b[0], R, n_tokens * dim * sizeof(float), "hc norm input") ||
!qwen4_bind_weight(&b[1], model_map, model_size, gamma_offset, dim * sizeof(float), "hc norm gamma") ||
!qwen4_bind_tensor(&b[3], xn, n_tokens * dim * sizeof(float), "hc norm output")) {
return 0;
}
if (n_inject) {
if (!qwen4_bind_weight(&b[2], model_map, model_size, inject_offset, (uint64_t)n_inject * wrow, "hc inject") ||
!qwen4_bind_tensor(&b[4], inj_part,
(uint64_t)n_tokens * n_hc * DS4_QWEN4_HC_CHUNKS * n_inject * sizeof(float),
"hc inject partials")) {
return 0;
}
} else {
b[2] = b[1];
b[4] = b[3];
}
/* Reuse wins at the measured large prefill shape. Decode and MTP keep
* the original chunk parallelism, including with an explicit override. */
bool reuse = false;
if (n_tokens > 2u) {
const char *reuse_env = getenv("DS4_QWEN4_HC_NORM_REUSE");
if (reuse_env != NULL && strcmp(reuse_env, "1") == 0) {
reuse = true;
} else if (reuse_env == NULL || strcmp(reuse_env, "0") != 0) {
reuse = n_tokens >= 8192u && n_embd == 2560u && n_hc == 4u && n_inject == 4u &&
(ds4_gpu_device_name_contains("M3 Ultra") || ds4_gpu_device_is_m5_apple_silicon());
}
}
const int kernel = reuse
? qwen4_hc_kernel(weight_type, QWEN4_K_HC_NORM_REUSE_F16, QWEN4_K_HC_NORM_REUSE_F32, QWEN4_K_HC_NORM_REUSE_Q8)
: qwen4_hc_kernel(weight_type, QWEN4_K_HC_NORM_F16, QWEN4_K_HC_NORM_F32, QWEN4_K_HC_NORM_Q8);
return qwen4_dispatch(kernel, &args, sizeof(args), b, 5,
MTLSizeMake(reuse ? n_hc : n_hc * DS4_QWEN4_HC_CHUNKS, n_tokens, 1), MTLSizeMake(128, 1, 1), 0);
}
int ds4_gpu_qwen4_hc_gate_mix_tensor(
ds4_gpu_tensor *mixed, const ds4_gpu_tensor *xn, const ds4_gpu_tensor *lo,
const void *model_map, uint64_t model_size, uint64_t up_offset,
uint32_t weight_type, uint32_t n_tokens, uint32_t n_embd, uint32_t n_hc, uint32_t n_rank) {
const uint64_t dim = (uint64_t)n_embd * n_hc;
const uint64_t wrow = qwen4_hc_row_bytes(weight_type, n_rank);
struct { uint32_t n_tokens, n_embd, n_hc, n_rank; } args = { n_tokens, n_embd, n_hc, n_rank };
qwen4_bind b[4];
if (n_tokens == 0 || n_hc != 4 || (n_rank % 4) != 0 || wrow == 0 || (weight_type == 8u && (n_rank % 32) != 0) ||
!qwen4_bind_tensor(&b[0], xn, n_tokens * dim * sizeof(float), "hc gate input") ||
!qwen4_bind_tensor(&b[1], lo, (uint64_t)n_tokens * n_rank * sizeof(float), "hc gate lowrank") ||
!qwen4_bind_weight(&b[2], model_map, model_size, up_offset, dim * wrow, "hc up") ||
!qwen4_bind_tensor(&b[3], mixed, (uint64_t)n_tokens * n_embd * sizeof(float), "hc mixed")) {
return 0;
}
const bool pair = n_tokens == 2u && getenv("DS4_QWEN4_NO_HC_PAIR") == NULL;
/* Register-prefetched F16 rows (same lane order and rounding, pinned
* against the plain kernel by tests/test_qwen4_kernels.c); M5 default. */
const int prefetch_override = ds4_gpu_env_bool("DS4_QWEN4_HC_MIX_PREFETCH");
const bool prefetch = weight_type == 1u &&
(prefetch_override >= 0 ? prefetch_override > 0 : ds4_gpu_device_is_m5_apple_silicon());
const int kernel = pair ? (prefetch ? QWEN4_K_HC_GATE_MIX_PAIR_F16_PF
: qwen4_hc_kernel(weight_type, QWEN4_K_HC_GATE_MIX_PAIR_F16,
QWEN4_K_HC_GATE_MIX_PAIR_F32, QWEN4_K_HC_GATE_MIX_PAIR_Q8))
: prefetch ? QWEN4_K_HC_GATE_MIX_F16_PF
: qwen4_hc_kernel(weight_type, QWEN4_K_HC_GATE_MIX_F16, QWEN4_K_HC_GATE_MIX_F32,
QWEN4_K_HC_GATE_MIX_Q8);
/* More independent output rows share the activated inputs in MTP.
* Keep the per-row lane mapping and reduction order unchanged. */
const uint32_t default_nsg = n_embd == 2560u && n_rank == 320u &&
ds4_gpu_device_name_contains("M3 Ultra") ? 16u : 4u;
const uint32_t nsg = pair ?
(uint32_t)ds4_gpu_env_u64("DS4_QWEN4_HC_PAIR_NSG", default_nsg, 1u, 16u) : 4u;
return qwen4_dispatch(kernel, &args, sizeof(args), b, 4,
MTLSizeMake((n_embd + nsg - 1u) / nsg, pair ? 1u : n_tokens, 1), MTLSizeMake(nsg * 32u, 1, 1),
pair ? (NSUInteger)n_rank * 2u * sizeof(float) : 0u);
}
int ds4_gpu_qwen4_hc_combine_tensor(
ds4_gpu_tensor *R, const ds4_gpu_tensor *out, const ds4_gpu_tensor *inj,
uint32_t n_tokens, uint32_t n_embd, uint32_t n_hc) {
struct { uint32_t n_tokens, n_embd, n_hc, pad0; } args = { n_tokens, n_embd, n_hc, 0 };
qwen4_bind b[3];
if (n_tokens == 0 || n_hc == 0 || n_hc > 8 ||
!qwen4_bind_tensor(&b[0], R, (uint64_t)n_tokens * n_embd * n_hc * sizeof(float), "hc combine residual") ||
!qwen4_bind_tensor(&b[1], out, (uint64_t)n_tokens * n_embd * sizeof(float), "hc combine block output") ||
!qwen4_bind_tensor(&b[2], inj, (uint64_t)n_tokens * n_hc * DS4_QWEN4_HC_CHUNKS * n_hc * sizeof(float),
"hc combine inject")) {
return 0;
}
return qwen4_dispatch(QWEN4_K_HC_COMBINE, &args, sizeof(args), b, 3,
MTLSizeMake((n_embd + 255) / 256, n_tokens, 1), MTLSizeMake(256, 1, 1), 0);
}
/* Diagnostic rollback is read per dispatch, including within one loaded engine. */
static bool qwen4_prefill_reuse(uint32_t n_tokens) {
if (n_tokens <= 8u) return false;
const int override = ds4_gpu_env_bool("DS4_QWEN4_PREFILL_REUSE");
return override >= 0 ? override > 0 :
n_tokens >= 1024u && ds4_gpu_device_name_contains("M3 Ultra");
}
int ds4_gpu_qwen4_conv_stream_tensor(
ds4_gpu_tensor *x, ds4_gpu_tensor *state,
const void *model_map, uint64_t model_size, uint64_t weight_offset,
uint32_t n_tokens, uint32_t n_channels, uint32_t conv_kernel, bool apply_silu) {
struct { uint32_t n_tokens, n_channels, conv_kernel, apply_silu; } args =
{ n_tokens, n_channels, conv_kernel, apply_silu ? 1u : 0u };
qwen4_bind b[3];
if (n_tokens == 0 || n_channels == 0 || conv_kernel < 2 || conv_kernel > 4 ||
!qwen4_bind_tensor(&b[0], x, (uint64_t)n_tokens * n_channels * sizeof(float), "conv input") ||
!qwen4_bind_tensor(&b[1], state, (uint64_t)(conv_kernel - 1) * n_channels * sizeof(float), "conv state") ||
!qwen4_bind_weight(&b[2], model_map, model_size, weight_offset,
(uint64_t)n_channels * conv_kernel * sizeof(float), "conv weight")) {
return 0;
}
const uint64_t blocks = ((uint64_t)n_tokens + 63u) / 64u;
const uint64_t total = blocks * (conv_kernel - 1u) * n_channels;
if (total <= (1u << 28) && qwen4_prefill_reuse(n_tokens)) {
if (!ds4_gpu_ensure_scratch_buffer(&g_qwen4_conv_halo_buffer, &g_qwen4_conv_halo_bytes,
(NSUInteger)(total * 4u), "convolution block history")) return 0;
const char *names[] = {"kernel_qwen4_conv_halo", "kernel_qwen4_conv_blocked"};
for (int stage = 0; stage < 2; stage++) {
id<MTLComputePipelineState> pipeline = ds4_gpu_get_pipeline(names[stage]);
if (!pipeline) return 0;
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&args length:sizeof(args) atIndex:0];
[enc setBuffer:b[0].buf offset:b[0].off atIndex:1];
[enc setBuffer:b[1].buf offset:b[1].off atIndex:2];
if (stage) [enc setBuffer:b[2].buf offset:b[2].off atIndex:3];
[enc setBuffer:g_qwen4_conv_halo_buffer offset:0 atIndex:stage ? 4 : 3];
const uint64_t work = stage ? blocks * n_channels : total;
[enc dispatchThreadgroups:MTLSizeMake((work + 255u) / 256u, 1, 1) threadsPerThreadgroup:MTLSizeMake(256, 1, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
if (!ds4_gpu_finish_command_buffer(cb, owned, names[stage])) return 0;
}
return 1;
}
return qwen4_dispatch(QWEN4_K_CONV_STREAM, &args, sizeof(args), b, 3,
MTLSizeMake((n_channels + 255) / 256, 1, 1), MTLSizeMake(256, 1, 1), 0);
}
int ds4_gpu_qwen4_gdn_prep_tensor(
ds4_gpu_tensor *qkv, ds4_gpu_tensor *a, ds4_gpu_tensor *b,
const void *model_map, uint64_t model_size, uint64_t ssm_a_offset, uint64_t dt_bias_offset,
uint32_t n_tokens, uint32_t n_k_head, uint32_t n_v_head, uint32_t head_dim) {
const uint64_t conv_dim = (uint64_t)(2u * n_k_head + n_v_head) * head_dim;
struct { uint32_t n_tokens, n_k_head, n_v_head, head_dim; } args = { n_tokens, n_k_head, n_v_head, head_dim };
qwen4_bind bd[5];
if (n_tokens == 0 || head_dim < 32 || head_dim > 128 || (head_dim % 32) != 0 ||
!qwen4_bind_tensor(&bd[0], qkv, n_tokens * conv_dim * sizeof(float), "gdn qkv") ||
!qwen4_bind_tensor(&bd[1], a, (uint64_t)n_tokens * n_v_head * sizeof(float), "gdn alpha") ||
!qwen4_bind_tensor(&bd[2], b, (uint64_t)n_tokens * n_v_head * sizeof(float), "gdn beta") ||
!qwen4_bind_weight(&bd[3], model_map, model_size, ssm_a_offset, (uint64_t)n_v_head * sizeof(float), "ssm_a") ||
!qwen4_bind_weight(&bd[4], model_map, model_size, dt_bias_offset, (uint64_t)n_v_head * sizeof(float),
"dt_bias")) {
return 0;
}
return qwen4_dispatch(QWEN4_K_GDN_PREP, &args, sizeof(args), bd, 5,
MTLSizeMake(n_k_head, n_tokens, 1), MTLSizeMake(32, 1, 1), 0);
}
int ds4_gpu_qwen4_gdn_scan_tensor(
ds4_gpu_tensor *out, ds4_gpu_tensor *state, const ds4_gpu_tensor *qkv,
const ds4_gpu_tensor *a, const ds4_gpu_tensor *b,
uint32_t n_tokens, uint32_t n_k_head, uint32_t n_v_head, uint32_t head_dim,
ds4_gpu_tensor *snap_state, uint32_t snap_tok,
ds4_gpu_tensor *snap2_state, uint32_t snap2_tok) {
const uint64_t conv_dim = (uint64_t)(2u * n_k_head + n_v_head) * head_dim;
struct { uint32_t n_tokens, n_k_head, n_v_head, head_dim, snap_tok, snap2_tok, pad1, pad2; } args =
{ n_tokens, n_k_head, n_v_head, head_dim,
snap_state ? snap_tok : UINT32_MAX, snap2_state ? snap2_tok : UINT32_MAX, 0, 0 };
const uint64_t state_bytes = (uint64_t)n_v_head * head_dim * head_dim * sizeof(float);
qwen4_bind bd[7];
if (n_tokens == 0 || head_dim < 32 || head_dim > 128 || (head_dim % 32) != 0 || n_k_head == 0 ||
!qwen4_bind_tensor(&bd[0], qkv, n_tokens * conv_dim * sizeof(float), "gdn scan qkv") ||
!qwen4_bind_tensor(&bd[1], a, (uint64_t)n_tokens * n_v_head * sizeof(float), "gdn scan g") ||
!qwen4_bind_tensor(&bd[2], b, (uint64_t)n_tokens * n_v_head * sizeof(float), "gdn scan beta") ||
!qwen4_bind_tensor(&bd[3], state, state_bytes, "gdn state") ||
!qwen4_bind_tensor(&bd[4], out, (uint64_t)n_tokens * n_v_head * head_dim * sizeof(float), "gdn scan output")) {
return 0;
}
if (snap_state) {
if (!qwen4_bind_tensor(&bd[5], snap_state, state_bytes, "gdn snapshot")) return 0;
} else {
bd[5] = bd[3];
}
if (snap2_state) {
if (!qwen4_bind_tensor(&bd[6], snap2_state, state_bytes, "gdn snapshot 2")) return 0;
} else {
bd[6] = bd[3];
}
const bool decode_r4 = (n_tokens <= 2u || (n_tokens == 3u && g_qwen4_verify_rows_exact)) &&
getenv("DS4_QWEN4_NO_GDN_R4") == NULL;
if (head_dim == 128u && (n_tokens > 8u || decode_r4)) {
/* SIMD groups own independent value rows, so changing their grouping
* preserves the recurrent arithmetic. M3 Ultra prefers eight groups
* for verification and four at the measured large-prefill shape. */
const uint32_t default_nsg = n_tokens == 2u && ds4_gpu_device_name_contains("M3 Ultra") ? 8u : 1u;
const uint32_t prefill_default_nsg =
n_tokens >= 8192u && n_k_head == 16u && n_v_head == 48u &&
ds4_gpu_device_name_contains("M3 Ultra") ? 4u : 1u;
const uint32_t nsg = n_tokens <= 2u ?
(uint32_t)ds4_gpu_env_u64("DS4_QWEN4_GDN_NSG", default_nsg, 1u, 8u) :
(uint32_t)ds4_gpu_env_u64("DS4_QWEN4_GDN_PREFILL_NSG", prefill_default_nsg, 1u, 8u);
return qwen4_dispatch(QWEN4_K_GDN_SCAN_R4, &args, sizeof(args), bd, 7,
MTLSizeMake((head_dim + 4u * nsg - 1u) / (4u * nsg), n_v_head, 1),
MTLSizeMake(32u * nsg, 1, 1), 0);
}
return qwen4_dispatch(QWEN4_K_GDN_SCAN, &args, sizeof(args), bd, 7,
MTLSizeMake(head_dim, n_v_head, 1), MTLSizeMake(32, 1, 1), 0);
}
static ds4_gpu_tensor *g_qwen4_gdn_slots;
/* One decode step for every row of a batch against its own slot of the state
* pool. The grid keeps the per-row kernel's shape and simd-group grouping, so
* each row's arithmetic is unchanged and the result is bit-identical; the rows
* merely share a dispatch long enough to reach steady state. */
int ds4_gpu_qwen4_gdn_scan_rows_tensor(
ds4_gpu_tensor *out, ds4_gpu_tensor *state_pool,
const ds4_gpu_tensor *qkv, const ds4_gpu_tensor *ga, const ds4_gpu_tensor *gb,
const uint32_t *slots, uint32_t n_rows,
uint32_t n_k_head, uint32_t n_v_head, uint32_t head_dim,
uint32_t state_stride, uint32_t qkv_stride, uint32_t out_stride) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (head_dim != 128u || n_rows == 0u || n_rows > 64u) return 0;
struct { uint32_t n_rows, n_k_head, n_v_head, head_dim, state_stride, qkv_stride, out_stride, pad0; } args =
{ n_rows, n_k_head, n_v_head, head_dim, state_stride, qkv_stride, out_stride, 0 };
const uint64_t slot_bytes = (uint64_t)n_rows * sizeof(uint32_t);
if (!g_qwen4_gdn_slots) {
g_qwen4_gdn_slots = ds4_gpu_tensor_alloc(64u * sizeof(uint32_t));
if (!g_qwen4_gdn_slots) return 0;
}
if (ds4_gpu_tensor_write(g_qwen4_gdn_slots, 0, slots, slot_bytes) == 0) return 0;
qwen4_bind b[6];
if (!qwen4_bind_tensor(&b[0], qkv, (uint64_t)n_rows * qkv_stride * sizeof(float), "gdn rows qkv") ||
!qwen4_bind_tensor(&b[1], ga, (uint64_t)n_rows * n_v_head * sizeof(float), "gdn rows decay") ||
!qwen4_bind_tensor(&b[2], gb, (uint64_t)n_rows * n_v_head * sizeof(float), "gdn rows beta") ||
!qwen4_bind_tensor(&b[3], state_pool, 0, "gdn rows state pool") ||
!qwen4_bind_tensor(&b[4], out, (uint64_t)n_rows * out_stride * sizeof(float), "gdn rows output") ||
!qwen4_bind_tensor(&b[5], g_qwen4_gdn_slots, slot_bytes, "gdn rows slots")) {
return 0;
}
/* Simd groups own independent value rows, so their grouping changes the
* dispatch shape without touching a row's recurrent arithmetic. */
const uint32_t nsg = 1u;
return qwen4_dispatch(QWEN4_K_GDN_SCAN_ROWS, &args, sizeof(args), b, 6,
MTLSizeMake((head_dim + 4u * nsg - 1u) / (4u * nsg), n_v_head, n_rows),
MTLSizeMake(32u * nsg, 1, 1), 0);
}
/* One decode token per batch row, each against its own slot of the history
* pool; see kernel_qwen4_conv_stream_rows. */
int ds4_gpu_qwen4_conv_stream_rows_tensor(
ds4_gpu_tensor *x, ds4_gpu_tensor *hist_pool,
const void *model_map, uint64_t model_size, uint64_t weight_offset,
const uint32_t *slots, uint32_t n_rows, uint32_t n_channels,
uint32_t conv_kernel, uint32_t state_stride, uint32_t x_stride, int apply_silu) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (n_rows == 0u || n_rows > 64u || conv_kernel < 2u || conv_kernel > 4u) return 0;
struct { uint32_t n_rows, n_channels, conv_kernel, apply_silu, state_stride, x_stride, pad0, pad1; } args =
{ n_rows, n_channels, conv_kernel, apply_silu ? 1u : 0u, state_stride, x_stride, 0, 0 };
const uint64_t slot_bytes = (uint64_t)n_rows * sizeof(uint32_t);
if (!g_qwen4_gdn_slots) {
g_qwen4_gdn_slots = ds4_gpu_tensor_alloc(64u * sizeof(uint32_t));
if (!g_qwen4_gdn_slots) return 0;
}
if (ds4_gpu_tensor_write(g_qwen4_gdn_slots, 0, slots, slot_bytes) == 0) return 0;
qwen4_bind b[4];
if (!qwen4_bind_tensor(&b[0], x, (uint64_t)n_rows * x_stride * sizeof(float), "conv rows x") ||
!qwen4_bind_tensor(&b[1], hist_pool, 0, "conv rows history pool") ||
!qwen4_bind_weight(&b[2], model_map, model_size, weight_offset,
(uint64_t)n_channels * conv_kernel * sizeof(float), "conv rows weight") ||
!qwen4_bind_tensor(&b[3], g_qwen4_gdn_slots, slot_bytes, "conv rows slots")) {
return 0;
}
return qwen4_dispatch(QWEN4_K_CONV_STREAM_ROWS, &args, sizeof(args), b, 4,
MTLSizeMake((n_channels + 255u) / 256u, n_rows, 1), MTLSizeMake(256, 1, 1), 0);
}
/* ---- GDN rows by address: one or two tokens per session ---------------- */
static uint64_t qwen4_tensor_address(const ds4_gpu_tensor *t);
typedef struct {
uint64_t state, hist, snap_state, snap_hist;
uint32_t row0, n_tok, pad0, pad1;
} qwen4_gdn_row_entry;
int ds4_gpu_qwen4_gdn_rows_stage(ds4_gpu_tensor *table, uint64_t entry0,
const ds4_gpu_qwen4_gdn_row *rows, uint32_t n_rows) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!rows || n_rows == 0u || n_rows > QWEN4_ATTN_ROWS_MAX) return 0;
qwen4_gdn_row_entry e[QWEN4_ATTN_ROWS_MAX];
for (uint32_t i = 0; i < n_rows; i++) {
e[i].state = qwen4_tensor_address(rows[i].state);
e[i].hist = qwen4_tensor_address(rows[i].hist);
e[i].snap_state = qwen4_tensor_address(rows[i].snap_state);
e[i].snap_hist = qwen4_tensor_address(rows[i].snap_hist);
if (!e[i].state || !e[i].hist || (rows[i].snap_state && !e[i].snap_state) ||
(rows[i].snap_hist && !e[i].snap_hist) || rows[i].n_tok == 0u || rows[i].n_tok > 2u) {
return 0;
}
e[i].row0 = rows[i].row0;
e[i].n_tok = rows[i].n_tok;
e[i].pad0 = e[i].pad1 = 0u;
}
return ds4_gpu_tensor_write(table, entry0 * sizeof(e[0]), e, (uint64_t)n_rows * sizeof(e[0]));
}
static uint32_t qwen4_gdn_rows_resident(qwen4_bind *out, const ds4_gpu_qwen4_gdn_row *rows, uint32_t n_rows) {
uint32_t n = 0;
for (uint32_t i = 0; i < n_rows; i++) {
const ds4_gpu_tensor *ts[4] = { rows[i].state, rows[i].hist, rows[i].snap_state, rows[i].snap_hist };
for (int j = 0; j < 4; j++) {
if (!ts[j]) continue;
out[n].buf = ds4_gpu_tensor_buffer(ts[j]);
out[n].off = 0;
n++;
}
}
return n;
}
static bool qwen4_bind_gdn_rows(qwen4_bind *b, const ds4_gpu_tensor *table, uint64_t entry0, uint32_t n_rows) {
const uint64_t off = entry0 * sizeof(qwen4_gdn_row_entry);
if (!table || !ds4_gpu_tensor_buffer(table) ||
ds4_gpu_tensor_bytes(table) < off + (uint64_t)n_rows * sizeof(qwen4_gdn_row_entry)) {
fprintf(stderr, "ds4: Qwen3.8 GDN row table is missing or undersized\n");
return false;
}
b->buf = ds4_gpu_tensor_buffer(table);
b->off = ds4_gpu_tensor_offset(table) + (NSUInteger)off;
return true;
}
int ds4_gpu_qwen4_gdn_scan_rows2_tensor(
ds4_gpu_tensor *out, const ds4_gpu_tensor *qkv, const ds4_gpu_tensor *ga, const ds4_gpu_tensor *gb,
const ds4_gpu_tensor *table, uint64_t entry0, const ds4_gpu_qwen4_gdn_row *rows, uint32_t n_rows,
uint32_t n_batch_rows, uint32_t n_k_head, uint32_t n_v_head, uint32_t head_dim,
uint32_t qkv_stride, uint32_t out_stride) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (head_dim != 128u || n_rows == 0u || n_rows > QWEN4_ATTN_ROWS_MAX) return 0;
struct { uint32_t n_rows, n_k_head, n_v_head, head_dim, state_stride, qkv_stride, out_stride, pad0; } args =
{ n_rows, n_k_head, n_v_head, head_dim, 0u, qkv_stride, out_stride, 0 };
qwen4_bind b[5], res[QWEN4_ATTN_ROWS_MAX * 4u];
if (!qwen4_bind_tensor(&b[0], qkv, (uint64_t)n_batch_rows * qkv_stride * sizeof(float), "gdn rows qkv") ||
!qwen4_bind_tensor(&b[1], ga, (uint64_t)n_batch_rows * n_v_head * sizeof(float), "gdn rows decay") ||
!qwen4_bind_tensor(&b[2], gb, (uint64_t)n_batch_rows * n_v_head * sizeof(float), "gdn rows beta") ||
!qwen4_bind_tensor(&b[3], out, (uint64_t)n_batch_rows * out_stride * sizeof(float), "gdn rows output") ||
!qwen4_bind_gdn_rows(&b[4], table, entry0, n_rows)) {
return 0;
}
const uint32_t nsg = 1u;
return qwen4_dispatch_resident(QWEN4_K_GDN_SCAN_ROWS2, &args, sizeof(args), b, 5,
MTLSizeMake((head_dim + 4u * nsg - 1u) / (4u * nsg), n_v_head, n_rows),
MTLSizeMake(32u * nsg, 1, 1), 0, res, qwen4_gdn_rows_resident(res, rows, n_rows));
}
int ds4_gpu_qwen4_conv_stream_rows2_tensor(
ds4_gpu_tensor *x, const void *model_map, uint64_t model_size, uint64_t weight_offset,
const ds4_gpu_tensor *table, uint64_t entry0, const ds4_gpu_qwen4_gdn_row *rows, uint32_t n_rows,
uint32_t n_batch_rows, uint32_t n_channels, uint32_t conv_kernel, uint32_t x_stride, int apply_silu) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (n_rows == 0u || n_rows > QWEN4_ATTN_ROWS_MAX || conv_kernel < 2u || conv_kernel > 4u) return 0;
struct { uint32_t n_rows, n_channels, conv_kernel, apply_silu, state_stride, x_stride, pad0, pad1; } args =
{ n_rows, n_channels, conv_kernel, apply_silu ? 1u : 0u, 0u, x_stride, 0, 0 };
qwen4_bind b[3], res[QWEN4_ATTN_ROWS_MAX * 4u];
if (!qwen4_bind_tensor(&b[0], x, (uint64_t)n_batch_rows * x_stride * sizeof(float), "conv rows x") ||
!qwen4_bind_weight(&b[1], model_map, model_size, weight_offset,
(uint64_t)n_channels * conv_kernel * sizeof(float), "conv rows weight") ||
!qwen4_bind_gdn_rows(&b[2], table, entry0, n_rows)) {
return 0;
}
return qwen4_dispatch_resident(QWEN4_K_CONV_STREAM_ROWS2, &args, sizeof(args), b, 3,
MTLSizeMake((n_channels + 255u) / 256u, n_rows, 1), MTLSizeMake(256, 1, 1), 0,
res, qwen4_gdn_rows_resident(res, rows, n_rows));
}
int ds4_gpu_qwen4_gdn_out_tensor(
ds4_gpu_tensor *o, const ds4_gpu_tensor *z,
const void *model_map, uint64_t model_size, uint64_t weight_offset,
uint32_t n_tokens, uint32_t n_head, uint32_t head_dim, float eps) {
struct { uint32_t n_tokens, n_head, head_dim; float eps; } args = { n_tokens, n_head, head_dim, eps };
qwen4_bind b[3];
const uint64_t bytes = (uint64_t)n_tokens * n_head * head_dim * sizeof(float);
if (n_tokens == 0 || head_dim < 32 || (head_dim % 32) != 0 ||
!qwen4_bind_tensor(&b[0], o, bytes, "gdn out") ||
!qwen4_bind_tensor(&b[1], z, bytes, "gdn gate") ||
!qwen4_bind_weight(&b[2], model_map, model_size, weight_offset, (uint64_t)head_dim * sizeof(float),
"ssm_norm")) {
return 0;
}
return qwen4_dispatch(QWEN4_K_GDN_OUT, &args, sizeof(args), b, 3,
MTLSizeMake(n_head, n_tokens, 1), MTLSizeMake(32, 1, 1), 0);
}
int ds4_gpu_qwen4_ple_gate_tensor(
ds4_gpu_tensor *gated, ds4_gpu_tensor *normed, const ds4_gpu_tensor *R,
const ds4_gpu_tensor *key, const ds4_gpu_tensor *value,
const void *model_map, uint64_t model_size,
uint64_t g_key_offset, uint64_t g_query_offset, uint64_t g_conv_offset,
uint32_t n_tokens, uint32_t n_embd, uint32_t n_hc, float eps) {
const uint64_t dim = (uint64_t)n_embd * n_hc;
struct { uint32_t n_tokens, n_embd, n_hc; float eps; } args = { n_tokens, n_embd, n_hc, eps };
qwen4_bind b[8];
if (n_tokens == 0 ||
!qwen4_bind_tensor(&b[0], R, n_tokens * dim * sizeof(float), "ple residual") ||
!qwen4_bind_tensor(&b[1], key, n_tokens * dim * sizeof(float), "ple key") ||
!qwen4_bind_tensor(&b[2], value, (uint64_t)n_tokens * n_embd * sizeof(float), "ple value") ||
!qwen4_bind_weight(&b[3], model_map, model_size, g_key_offset, dim * sizeof(float), "ple norm_key") ||
!qwen4_bind_weight(&b[4], model_map, model_size, g_query_offset, dim * sizeof(float), "ple norm_query") ||
!qwen4_bind_weight(&b[5], model_map, model_size, g_conv_offset, dim * sizeof(float), "ple norm_conv") ||
!qwen4_bind_tensor(&b[6], gated, n_tokens * dim * sizeof(float), "ple gated") ||
!qwen4_bind_tensor(&b[7], normed, n_tokens * dim * sizeof(float), "ple normed")) {
return 0;
}
return qwen4_dispatch(QWEN4_K_PLE_GATE, &args, sizeof(args), b, 8,
MTLSizeMake(n_tokens, 1, 1), MTLSizeMake(256, 1, 1), 0);
}
int ds4_gpu_qwen4_ple_conv_tensor(
ds4_gpu_tensor *R, const ds4_gpu_tensor *gated, const ds4_gpu_tensor *normed,
ds4_gpu_tensor *history, const void *model_map, uint64_t model_size, uint64_t weight_offset,
uint32_t weight_type, uint32_t n_tokens, uint32_t n_channels, uint32_t conv_kernel, uint32_t dilation,
ds4_gpu_tensor *snap_history, uint32_t snap_tok,
ds4_gpu_tensor *snap2_history, uint32_t snap2_tok) {
const uint32_t wsize = weight_type == 1u ? 2u : weight_type == 0u ? 4u : 0u;
struct { uint32_t n_tokens, n_channels, conv_kernel, dilation, weight_f16, snap_tok, snap2_tok, pad2; } args =
{ n_tokens, n_channels, conv_kernel, dilation, weight_type == 1u ? 1u : 0u,
snap_history ? snap_tok : UINT32_MAX, snap2_history ? snap2_tok : UINT32_MAX, 0 };
if (wsize == 0) return 0;
const uint64_t rows = (uint64_t)n_tokens * n_channels * sizeof(float);
const uint32_t hist = (conv_kernel - 1u) * dilation;
qwen4_bind b[7];
if (n_tokens == 0 || conv_kernel < 2 || conv_kernel > 4 || dilation == 0 || hist > 9 ||
!qwen4_bind_tensor(&b[0], R, rows, "ple residual") ||
!qwen4_bind_tensor(&b[1], gated, rows, "ple gated") ||
!qwen4_bind_tensor(&b[2], normed, rows, "ple normed") ||
!qwen4_bind_tensor(&b[3], history, (uint64_t)hist * n_channels * sizeof(float), "ple history") ||
!qwen4_bind_weight(&b[4], model_map, model_size, weight_offset,
(uint64_t)n_channels * conv_kernel * wsize, "ple conv weight")) {
return 0;
}
if (snap_history) {
if (!qwen4_bind_tensor(&b[5], snap_history,
(uint64_t)(conv_kernel - 1) * dilation * n_channels * sizeof(float), "ple snapshot")) {
return 0;
}
} else {
b[5] = b[3];
}
if (snap2_history) {
if (!qwen4_bind_tensor(&b[6], snap2_history,
(uint64_t)(conv_kernel - 1) * dilation * n_channels * sizeof(float), "ple snapshot 2")) {
return 0;
}
} else {
b[6] = b[3];
}
return qwen4_dispatch(QWEN4_K_PLE_CONV, &args, sizeof(args), b, 7,
MTLSizeMake((n_channels + 255) / 256, 1, 1), MTLSizeMake(256, 1, 1), 0);
}
int ds4_gpu_qwen4_router_topk_tensor(
ds4_gpu_tensor *selected, ds4_gpu_tensor *weights, const ds4_gpu_tensor *logits,
const ds4_gpu_tensor *x, const void *model_map, uint64_t model_size, uint64_t gate_offset,
uint32_t gate_type, uint32_t in_dim, ds4_gpu_tensor *shared_gate,
uint32_t n_tokens, uint32_t n_expert, uint32_t n_used) {
const uint32_t gate_bytes = in_dim ? qwen4_expert_row_bytes(gate_type, in_dim) : 0u;
struct { uint32_t n_tokens, n_expert, n_used, gate_type, in_dim, pad0, pad1, pad2; } args =
{ n_tokens, n_expert, n_used, gate_type, in_dim, 0, 0, 0 };
qwen4_bind b[6];
if (n_tokens == 0 || n_expert == 0 || n_expert > 512 || n_used == 0 || n_used > n_expert || n_used > 16 ||
(in_dim && gate_bytes == 0) ||
!qwen4_bind_tensor(&b[0], logits, (uint64_t)n_tokens * n_expert * sizeof(float), "router logits") ||
!qwen4_bind_tensor(&b[1], selected, (uint64_t)n_tokens * n_used * sizeof(int32_t), "router selected") ||
!qwen4_bind_tensor(&b[2], weights, (uint64_t)n_tokens * n_used * sizeof(float), "router weights")) {
return 0;
}
if (in_dim) {
if (!qwen4_bind_tensor(&b[3], x, (uint64_t)n_tokens * in_dim * sizeof(float), "router input") ||
!qwen4_bind_weight(&b[4], model_map, model_size, gate_offset, gate_bytes, "shared gate row") ||
!qwen4_bind_tensor(&b[5], shared_gate, (uint64_t)n_tokens * sizeof(float), "shared gate logit")) {
return 0;
}
} else {
b[3] = b[0];
b[4] = b[0];
b[5] = b[2];
}
return qwen4_dispatch(QWEN4_K_ROUTER, &args, sizeof(args), b, 6,
MTLSizeMake(n_tokens, 1, 1), MTLSizeMake(256, 1, 1), 0);
}
static float g_qwen4_rope_freq[32];
static float g_qwen4_rope_mscale = 1.0f;
static bool g_qwen4_rope_set = false;
void ds4_gpu_qwen4_set_rope(const float *freq, uint32_t n_pairs, float mscale) {
memset(g_qwen4_rope_freq, 0, sizeof(g_qwen4_rope_freq));
for (uint32_t i = 0; freq && i < n_pairs && i < 32u; i++) g_qwen4_rope_freq[i] = freq[i];
g_qwen4_rope_mscale = freq ? mscale : 1.0f;
g_qwen4_rope_set = freq != NULL;
}
static void qwen4_rope_fill(float *freq, float *mscale, uint32_t n_rot, float base) {
for (uint32_t i = 0; i < 32u; i++) {
freq[i] = g_qwen4_rope_set ? g_qwen4_rope_freq[i] :
i < n_rot / 2u ? powf(base, -2.0f * (float)i / (float)n_rot) : 0.0f;
}
*mscale = g_qwen4_rope_set ? g_qwen4_rope_mscale : 1.0f;
}
int ds4_gpu_qwen4_attn_prep_tensor(
ds4_gpu_tensor *q_out, ds4_gpu_tensor *gate_out, ds4_gpu_tensor *k_cache, ds4_gpu_tensor *v_cache,
ds4_gpu_tensor *iq_out, ds4_gpu_tensor *ik_cache,
const ds4_gpu_tensor *qg, const ds4_gpu_tensor *kproj, const ds4_gpu_tensor *vproj,
const ds4_gpu_tensor *iq, const ds4_gpu_tensor *ik, const ds4_gpu_tensor *pos3,
const void *model_map, uint64_t model_size,
uint64_t g_q_offset, uint64_t g_k_offset, uint64_t g_iq_offset,
uint32_t n_tokens, uint32_t n_head, uint32_t n_head_kv, uint32_t head_dim, uint32_t n_rot,
uint32_t n_idx_head, uint32_t idx_dim, uint32_t pos0, uint32_t cache_cap,
float rope_base, float eps) {
struct {
uint32_t n_tokens, n_head, n_head_kv, head_dim, n_rot, n_idx_head, idx_dim, pos0, cache_cap;
float rope_base, eps; uint32_t pad0; float rope_mscale; float rope_freq[32];
} args = { n_tokens, n_head, n_head_kv, head_dim, n_rot, n_idx_head, idx_dim, pos0, cache_cap,
rope_base, eps, 0, 1.0f, { 0 } };
qwen4_rope_fill(args.rope_freq, &args.rope_mscale, n_rot, rope_base);
const uint64_t q_bytes = (uint64_t)n_tokens * n_head * head_dim * sizeof(float);
const uint64_t kv_bytes = (uint64_t)n_tokens * n_head_kv * head_dim * sizeof(float);
const uint64_t iq_bytes = (uint64_t)n_tokens * n_idx_head * idx_dim * sizeof(float);
const uint64_t cache_bytes = (uint64_t)cache_cap * n_head_kv * head_dim * 2u;
qwen4_bind b[15];
if (n_tokens == 0 || head_dim < 32 || head_dim > 256 || (head_dim % 32) != 0 ||
idx_dim < 32 || idx_dim > 128 || (idx_dim % 32) != 0 || n_rot > 64 || (n_rot % 2) != 0 ||
n_rot > idx_dim || (uint64_t)pos0 + n_tokens > cache_cap || n_head_kv == 0 || (n_head % n_head_kv) != 0 ||
!qwen4_bind_tensor(&b[0], qg, 2u * q_bytes, "attn q/gate projection") ||
!qwen4_bind_tensor(&b[1], kproj, kv_bytes, "attn k projection") ||
!qwen4_bind_tensor(&b[2], vproj, kv_bytes, "attn v projection") ||
!qwen4_bind_tensor(&b[3], iq, iq_bytes, "indexer q projection") ||
!qwen4_bind_tensor(&b[4], ik, (uint64_t)n_tokens * idx_dim * sizeof(float), "indexer k projection") ||
!qwen4_bind_weight(&b[5], model_map, model_size, g_q_offset, (uint64_t)head_dim * sizeof(float),
"attn q_norm") ||
!qwen4_bind_weight(&b[6], model_map, model_size, g_k_offset, (uint64_t)head_dim * sizeof(float),
"attn k_norm") ||
!qwen4_bind_weight(&b[7], model_map, model_size, g_iq_offset, (uint64_t)idx_dim * sizeof(float),
"indexer q_norm") ||
!qwen4_bind_tensor(&b[8], q_out, q_bytes, "attn q") ||
!qwen4_bind_tensor(&b[9], gate_out, q_bytes, "attn gate") ||
!qwen4_bind_tensor(&b[10], k_cache, cache_bytes, "k cache") ||
!qwen4_bind_tensor(&b[11], v_cache, cache_bytes, "v cache") ||
!qwen4_bind_tensor(&b[12], iq_out, iq_bytes, "indexer q") ||
!qwen4_bind_tensor(&b[13], ik_cache, (uint64_t)cache_cap * idx_dim * sizeof(float), "indexer k cache") ||
!qwen4_bind_tensor(&b[14], pos3, (uint64_t)cache_cap * 16u, "rope positions")) {
return 0;
}
return qwen4_dispatch(QWEN4_K_ATTN_PREP, &args, sizeof(args), b, 15,
MTLSizeMake(n_head + n_head_kv + n_idx_head + 1u, n_tokens, 1), MTLSizeMake(32, 1, 1), 0);
}
int ds4_gpu_qwen4_idx_block_key_tensor(
ds4_gpu_tensor *block_key, const ds4_gpu_tensor *ik_cache, const ds4_gpu_tensor *pos3,
const void *model_map, uint64_t model_size, uint64_t g_ik_offset,
uint32_t block0, uint32_t n_blocks, uint32_t ratio, uint32_t idx_dim, uint32_t n_rot,
float rope_base, float eps) {
struct { uint32_t block0, n_blocks, ratio, idx_dim, n_rot; float rope_base, eps; uint32_t pad0;
float rope_mscale; float rope_freq[32]; } args =
{ block0, n_blocks, ratio, idx_dim, n_rot, rope_base, eps, 0, 1.0f, { 0 } };
qwen4_rope_fill(args.rope_freq, &args.rope_mscale, n_rot, rope_base);
const uint64_t n_keys = ((uint64_t)block0 + n_blocks) * ratio;
qwen4_bind b[4];
if (n_blocks == 0 || ratio == 0 || idx_dim < 32 || idx_dim > 128 || (idx_dim % 32) != 0 || n_rot > idx_dim ||
!qwen4_bind_tensor(&b[0], ik_cache, n_keys * idx_dim * sizeof(float), "indexer k cache") ||
!qwen4_bind_weight(&b[1], model_map, model_size, g_ik_offset, (uint64_t)idx_dim * sizeof(float),
"indexer k_norm") ||
!qwen4_bind_tensor(&b[2], pos3, n_keys * 16u, "rope positions") ||
!qwen4_bind_tensor(&b[3], block_key, ((uint64_t)block0 + n_blocks) * idx_dim * 2u, "block keys")) {
return 0;
}
return qwen4_dispatch(QWEN4_K_IDX_BLOCK_KEY, &args, sizeof(args), b, 4,
MTLSizeMake(n_blocks, 1, 1), MTLSizeMake(32, 1, 1), 0);
}
int ds4_gpu_qwen4_idx_score_tensor(
ds4_gpu_tensor *score, ds4_gpu_tensor *tile_max, const ds4_gpu_tensor *iq, const ds4_gpu_tensor *block_key,
uint32_t n_tokens, uint32_t n_blocks, uint32_t n_idx_head, uint32_t idx_dim,
uint32_t pos0, uint32_t ratio) {
struct { uint32_t n_tokens, n_blocks, n_idx_head, idx_dim, pos0, ratio, pad0, pad1; } args =
{ n_tokens, n_blocks, n_idx_head, idx_dim, pos0, ratio, 0, 0 };
qwen4_bind b[4];
if (n_tokens == 0 || n_blocks == 0 || ratio == 0 ||
!qwen4_bind_tensor(&b[0], iq, (uint64_t)n_tokens * n_idx_head * idx_dim * sizeof(float), "indexer q") ||
!qwen4_bind_tensor(&b[1], block_key, (uint64_t)n_blocks * idx_dim * 2u, "block keys") ||
!qwen4_bind_tensor(&b[2], score, (uint64_t)n_tokens * n_blocks * sizeof(float), "indexer scores")) {
return 0;
}
if (n_tokens > 2u && n_idx_head == 4u && idx_dim == 128u) {
return qwen4_dispatch(QWEN4_K_IDX_SCORE_MM, &args, sizeof(args), b, 3,
MTLSizeMake((n_blocks + 63) / 64, (n_tokens + 15) / 16, 1), MTLSizeMake(128, 1, 1), 0);
}
/* staged queries and vector key loads, plus the tile maxima the
* prefiltered selector needs; measured on M5, other devices keep the
* scalar scorer */
const int vec_override = ds4_gpu_env_bool("DS4_QWEN4_IDX_SCORE_VEC");
const bool vec = tile_max && n_idx_head * idx_dim <= 512u && (idx_dim & 3u) == 0u &&
(vec_override >= 0 ? vec_override != 0 : ds4_gpu_device_is_m5_apple_silicon());
if (vec) {
if (!qwen4_bind_tensor(&b[3], tile_max, (uint64_t)n_tokens * ((n_blocks + 7u) / 8u) * sizeof(uint32_t),
"indexer tile maxima")) return 0;
return qwen4_dispatch(QWEN4_K_IDX_SCORE_VEC, &args, sizeof(args), b, 4,
MTLSizeMake((n_blocks + 127) / 128, n_tokens, 1), MTLSizeMake(128, 1, 1), 0);
}
return qwen4_dispatch(QWEN4_K_IDX_SCORE, &args, sizeof(args), b, 3,
MTLSizeMake((n_blocks + 127) / 128, n_tokens, 1), MTLSizeMake(128, 1, 1), 0);
}
int ds4_gpu_qwen4_idx_select_tensor(
ds4_gpu_tensor *sel, const ds4_gpu_tensor *score, const ds4_gpu_tensor *tile_max,
uint32_t n_blocks, uint32_t n_tokens, uint32_t top_k) {
struct { uint32_t n_tokens, n_blocks, top_k, pad0; } args = { n_tokens, n_blocks, top_k, 0 };
qwen4_bind b[3];
if (n_tokens == 0 || top_k == 0 || top_k > n_blocks ||
!qwen4_bind_tensor(&b[0], score, (uint64_t)n_tokens * n_blocks * sizeof(float), "indexer scores") ||
!qwen4_bind_tensor(&b[2], sel, (uint64_t)n_tokens * top_k * sizeof(int32_t), "selected blocks")) {
return 0;
}
/* Long rows: bound the k-th score by the k-th tile maximum and select
* over the surviving tiles in threadgroup memory (exact; see the kernel).
* The scorer that emits tile maxima only runs for decode batches. */
const int pre_override = ds4_gpu_env_bool("DS4_QWEN4_IDX_PREFILTER");
/* Scalar/MM scorers do not populate tile maxima. An independent
* scorer override must also disable their consumer. */
const int vec_override = ds4_gpu_env_bool("DS4_QWEN4_IDX_SCORE_VEC");
const bool vec = vec_override >= 0 ? vec_override != 0 : ds4_gpu_device_is_m5_apple_silicon();
const bool pre = tile_max && vec && n_tokens <= 2u && n_blocks > 8u * top_k &&
(pre_override >= 0 ? pre_override != 0 : ds4_gpu_device_is_m5_apple_silicon());
if (pre) {
if (!qwen4_bind_tensor(&b[1], tile_max, (uint64_t)n_tokens * ((n_blocks + 7u) / 8u) * sizeof(uint32_t),
"indexer tile maxima")) return 0;
return qwen4_dispatch(QWEN4_K_IDX_SELECT_PRE, &args, sizeof(args), b, 3,
MTLSizeMake(n_tokens, 1, 1), MTLSizeMake(1024, 1, 1), 0);
}
b[1] = b[2];
return qwen4_dispatch(QWEN4_K_IDX_SELECT, &args, sizeof(args), b, 2,
MTLSizeMake(n_tokens, 1, 1), MTLSizeMake(1024, 1, 1), 0);
}
int ds4_gpu_qwen4_idx_expand_tensor(
ds4_gpu_tensor *sel_tokens, ds4_gpu_tensor *n_sel, const ds4_gpu_tensor *sel_blocks,
uint32_t n_tokens, uint32_t n_sel_blocks, uint32_t ratio, uint32_t pos0, uint32_t sel_stride) {
struct { uint32_t n_tokens, n_sel_blocks, ratio, pos0, sel_stride, pad0, pad1, pad2; } args =
{ n_tokens, n_sel_blocks, ratio, pos0, sel_stride, 0, 0, 0 };
qwen4_bind b[3];
if (n_tokens == 0 || ratio == 0 || sel_stride < n_sel_blocks * ratio + ratio - 1u ||
!qwen4_bind_tensor(&b[0], sel_blocks, (uint64_t)n_tokens * n_sel_blocks * sizeof(int32_t), "selected blocks") ||
!qwen4_bind_tensor(&b[1], sel_tokens, (uint64_t)n_tokens * sel_stride * sizeof(int32_t), "selected tokens") ||
!qwen4_bind_tensor(&b[2], n_sel, (uint64_t)n_tokens * sizeof(uint32_t), "selected counts")) {
return 0;
}
return qwen4_dispatch(QWEN4_K_IDX_EXPAND, &args, sizeof(args), b, 3,
MTLSizeMake(n_tokens, 1, 1), MTLSizeMake(256, 1, 1), 0);
}
/* keys per split of the partial-softmax decode path (DS4_QWEN4_ATTN_SPLIT_KEYS overrides) */
/* Read per call, not cached, so an A/B harness can switch it per step. */
static uint32_t qwen4_attn_split_keys(void) {
const char *env = getenv("DS4_QWEN4_ATTN_SPLIT_KEYS");
const int v = env ? atoi(env) : 0;
return v > 0 ? (uint32_t)v : 32u;
}
uint64_t ds4_gpu_qwen4_attn_part_floats(uint32_t n_tokens, uint32_t n_head, uint32_t head_dim) {
return (uint64_t)n_tokens * n_head * QWEN4_ATTN_MAX_SPLITS * (2u + head_dim);
}
int ds4_gpu_qwen4_attn_decode_tensor(
ds4_gpu_tensor *out, const ds4_gpu_tensor *q, const ds4_gpu_tensor *gate,
const ds4_gpu_tensor *k_cache, const ds4_gpu_tensor *v_cache,
const ds4_gpu_tensor *sel_tokens, const ds4_gpu_tensor *n_sel, ds4_gpu_tensor *part,
uint32_t n_tokens, uint32_t n_head, uint32_t n_head_kv, uint32_t head_dim,
uint32_t pos0, bool use_sel, uint32_t sel_stride, float scale) {
const uint32_t n_keys = use_sel ? sel_stride : pos0 + n_tokens;
uint32_t n_splits = 1;
if (part) {
n_splits = (n_keys + qwen4_attn_split_keys() - 1) / qwen4_attn_split_keys();
if (n_splits < 1) n_splits = 1;
if (n_splits > QWEN4_ATTN_MAX_SPLITS) n_splits = QWEN4_ATTN_MAX_SPLITS;
}
const uint32_t keys_per_split = (n_keys + n_splits - 1) / n_splits;
struct { uint32_t n_tokens, n_head, n_head_kv, head_dim, pos0, use_sel, sel_stride; float scale;
uint32_t n_splits, keys_per_split, pad0, pad1; } args =
{ n_tokens, n_head, n_head_kv, head_dim, pos0, use_sel ? 1u : 0u, sel_stride, scale,
n_splits, keys_per_split, 0, 0 };
const uint64_t q_bytes = (uint64_t)n_tokens * n_head * head_dim * sizeof(float);
const uint64_t cache_bytes = (uint64_t)(pos0 + n_tokens) * n_head_kv * head_dim * 2u;
const uint64_t part_bytes = (uint64_t)n_tokens * n_head * n_splits * (2u + head_dim) * sizeof(float);
qwen4_bind b[8];
if (n_tokens == 0 || n_head_kv == 0 || (n_head % n_head_kv) != 0 || n_head / n_head_kv > 12 ||
(head_dim != 32 && head_dim != 128 && head_dim != 256) ||
!qwen4_bind_tensor(&b[0], q, q_bytes, "attn q") ||
!qwen4_bind_tensor(&b[1], gate, q_bytes, "attn gate") ||
!qwen4_bind_tensor(&b[2], k_cache, cache_bytes, "k cache") ||
!qwen4_bind_tensor(&b[3], v_cache, cache_bytes, "v cache") ||
!qwen4_bind_tensor(&b[6], out, q_bytes, "attn out")) {
return 0;
}
if (use_sel) {
if (!qwen4_bind_tensor(&b[4], sel_tokens, (uint64_t)n_tokens * sel_stride * sizeof(int32_t),
"selected tokens") ||
!qwen4_bind_tensor(&b[5], n_sel, (uint64_t)n_tokens * sizeof(uint32_t), "selected counts")) {
return 0;
}
} else {
b[4] = b[0];
b[5] = b[0];
}
if (n_splits > 1) {
if (!qwen4_bind_tensor(&b[7], part, part_bytes, "attn partials")) return 0;
} else {
b[7] = b[6];
}
/* Keep short prefill tails on the decode arithmetic. Rounding queries
* to half for just a few rows can flip nearly tied expert selections
* relative to one-token continuation; large prefills retain matrix tiles. */
if (n_splits == 1 && n_tokens > 8u && head_dim == 256u && n_head / n_head_kv <= 16u &&
getenv("DS4_QWEN4_NO_ATTN_MM") == NULL) {
return qwen4_dispatch(QWEN4_K_ATTN_MM, &args, sizeof(args), b, 7,
MTLSizeMake(n_head_kv, n_tokens, 1), MTLSizeMake(128, 1, 1), 0);
}
const int kd = head_dim == 256u ? QWEN4_K_ATTN_DECODE_NPT8 :
head_dim == 128u ? QWEN4_K_ATTN_DECODE_NPT4 : QWEN4_K_ATTN_DECODE_NPT1;
const int km = head_dim == 256u ? QWEN4_K_ATTN_MERGE_NPT8 :
head_dim == 128u ? QWEN4_K_ATTN_MERGE_NPT4 : QWEN4_K_ATTN_MERGE_NPT1;
if (!qwen4_dispatch(kd, &args, sizeof(args), b, 8,
MTLSizeMake(n_splits, n_head_kv, n_tokens), MTLSizeMake(32 * QWEN4_ATTN_NSG, 1, 1), 0)) {
return 0;
}
if (n_splits == 1) return 1;
qwen4_bind mb[3] = { b[7], b[1], b[6] };
/* One thread per dim merges the same split chain with eight times the
* threads; measured on M5, other devices keep the simdgroup merge. */
const int wide_override = ds4_gpu_env_bool("DS4_QWEN4_ATTN_MERGE_WIDE");
const bool wide = head_dim >= 128u &&
(wide_override >= 0 ? wide_override != 0 : ds4_gpu_device_is_m5_apple_silicon());
if (wide) {
return qwen4_dispatch(head_dim == 256u ? QWEN4_K_ATTN_MERGE_WIDE_NPT8 : QWEN4_K_ATTN_MERGE_WIDE_NPT4,
&args, sizeof(args), mb, 3,
MTLSizeMake(n_head, n_tokens, 1), MTLSizeMake(head_dim, 1, 1), 0);
}
return qwen4_dispatch(km, &args, sizeof(args), mb, 3,
MTLSizeMake(n_head, n_tokens, 1), MTLSizeMake(32, 1, 1), 0);
}
/* ---- decode batch: attention over rows ----
*
* The caches stay per session, so the rows kernels find each row's through
* a table of GPU addresses (one entry per row, staged per layer) and the
* dispatch makes those buffers resident. Every kernel keeps the single-row
* arithmetic, so a row's outputs match the per-row dispatches bit for bit. */
typedef struct {
uint64_t k_cache, v_cache, ik_cache, block_key, pos3;
uint32_t pos, n_blocks, use_sel, pad0;
} qwen4_attn_row_entry;
static uint64_t qwen4_tensor_address(const ds4_gpu_tensor *t) {
return t ? ds4_gpu_buffer_address(ds4_gpu_tensor_buffer(t), ds4_gpu_tensor_offset(t)) : 0u;
}
int ds4_gpu_qwen4_attn_rows_stage(ds4_gpu_tensor *table, uint64_t entry0,
const ds4_gpu_qwen4_attn_row *rows, uint32_t n_rows, uint32_t ratio) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if (!rows || n_rows == 0u || n_rows > QWEN4_ATTN_ROWS_MAX || ratio == 0u) return 0;
qwen4_attn_row_entry e[QWEN4_ATTN_ROWS_MAX];
for (uint32_t i = 0; i < n_rows; i++) {
e[i].k_cache = qwen4_tensor_address(rows[i].k_cache);
e[i].v_cache = qwen4_tensor_address(rows[i].v_cache);
e[i].ik_cache = qwen4_tensor_address(rows[i].ik_cache);
e[i].block_key = qwen4_tensor_address(rows[i].block_key);
e[i].pos3 = qwen4_tensor_address(rows[i].pos3);
if (!e[i].k_cache || !e[i].v_cache || !e[i].ik_cache || !e[i].block_key || !e[i].pos3) {
fprintf(stderr, "ds4: Qwen3.8 attention rows need GPU buffer addresses (macOS 13+)\n");
return 0;
}
e[i].pos = rows[i].pos;
e[i].n_blocks = (rows[i].pos + 1u) / ratio;
e[i].use_sel = rows[i].use_sel ? 1u : 0u;
e[i].pad0 = 0u;
}
return ds4_gpu_tensor_write(table, entry0 * sizeof(e[0]), e, (uint64_t)n_rows * sizeof(e[0]));
}
static bool qwen4_bind_rows(qwen4_bind *b, const ds4_gpu_tensor *table, uint64_t entry0, uint32_t n_rows) {
const uint64_t off = entry0 * sizeof(qwen4_attn_row_entry);
if (!table || !ds4_gpu_tensor_buffer(table) ||
ds4_gpu_tensor_bytes(table) < off + (uint64_t)n_rows * sizeof(qwen4_attn_row_entry)) {
fprintf(stderr, "ds4: Qwen3.8 attention row table is missing or undersized\n");
return false;
}
b->buf = ds4_gpu_tensor_buffer(table);
b->off = ds4_gpu_tensor_offset(table) + (NSUInteger)off;
return true;
}
static uint32_t qwen4_rows_resident(qwen4_bind *out, const ds4_gpu_qwen4_attn_row *rows, uint32_t n_rows) {
uint32_t n = 0;
for (uint32_t i = 0; i < n_rows; i++) {
const ds4_gpu_tensor *ts[5] = { rows[i].k_cache, rows[i].v_cache, rows[i].ik_cache,
rows[i].block_key, rows[i].pos3 };
for (int j = 0; j < 5; j++) { out[n].buf = ds4_gpu_tensor_buffer(ts[j]); out[n].off = 0; n++; }
}
return n;
}
int ds4_gpu_qwen4_attn_prep_rows_tensor(
ds4_gpu_tensor *q_out, ds4_gpu_tensor *gate_out, ds4_gpu_tensor *iq_out,
const ds4_gpu_tensor *qg, const ds4_gpu_tensor *kproj, const ds4_gpu_tensor *vproj,
const ds4_gpu_tensor *iq, const ds4_gpu_tensor *ik,
const ds4_gpu_tensor *table, uint64_t entry0, const ds4_gpu_qwen4_attn_row *rows, uint32_t n_rows,
const void *model_map, uint64_t model_size,
uint64_t g_q_offset, uint64_t g_k_offset, uint64_t g_iq_offset,
uint32_t n_head, uint32_t n_head_kv, uint32_t head_dim, uint32_t n_rot,
uint32_t n_idx_head, uint32_t idx_dim, float rope_base, float eps) {
/* The caller checks each row's position against its own cache; the
* kernel does not read cache_cap. */
struct {
uint32_t n_tokens, n_head, n_head_kv, head_dim, n_rot, n_idx_head, idx_dim, pos0, cache_cap;
float rope_base, eps; uint32_t pad0; float rope_mscale; float rope_freq[32];
} args = { n_rows, n_head, n_head_kv, head_dim, n_rot, n_idx_head, idx_dim, 0u, 0u,
rope_base, eps, 0, 1.0f, { 0 } };
qwen4_rope_fill(args.rope_freq, &args.rope_mscale, n_rot, rope_base);
const uint64_t q_bytes = (uint64_t)n_rows * n_head * head_dim * sizeof(float);
const uint64_t kv_bytes = (uint64_t)n_rows * n_head_kv * head_dim * sizeof(float);
const uint64_t iq_bytes = (uint64_t)n_rows * n_idx_head * idx_dim * sizeof(float);
qwen4_bind b[12], res[QWEN4_ATTN_ROWS_MAX * 5u];
if (n_rows == 0 || n_rows > QWEN4_ATTN_ROWS_MAX || head_dim < 32 || head_dim > 256 || (head_dim % 32) != 0 ||
idx_dim < 32 || idx_dim > 128 || (idx_dim % 32) != 0 || n_rot > 64 || (n_rot % 2) != 0 ||
n_rot > idx_dim || n_head_kv == 0 || (n_head % n_head_kv) != 0) {
return 0;
}
if (!qwen4_bind_tensor(&b[0], qg, 2u * q_bytes, "attn rows q/gate projection") ||
!qwen4_bind_tensor(&b[1], kproj, kv_bytes, "attn rows k projection") ||
!qwen4_bind_tensor(&b[2], vproj, kv_bytes, "attn rows v projection") ||
!qwen4_bind_tensor(&b[3], iq, iq_bytes, "indexer rows q projection") ||
!qwen4_bind_tensor(&b[4], ik, (uint64_t)n_rows * idx_dim * sizeof(float), "indexer rows k projection") ||
!qwen4_bind_weight(&b[5], model_map, model_size, g_q_offset, (uint64_t)head_dim * sizeof(float), "attn q_norm") ||
!qwen4_bind_weight(&b[6], model_map, model_size, g_k_offset, (uint64_t)head_dim * sizeof(float), "attn k_norm") ||
!qwen4_bind_weight(&b[7], model_map, model_size, g_iq_offset, (uint64_t)idx_dim * sizeof(float), "indexer q_norm") ||
!qwen4_bind_tensor(&b[8], q_out, q_bytes, "attn rows q") ||
!qwen4_bind_tensor(&b[9], gate_out, q_bytes, "attn rows gate") ||
!qwen4_bind_tensor(&b[10], iq_out, iq_bytes, "indexer rows q") ||
!qwen4_bind_rows(&b[11], table, entry0, n_rows)) {
return 0;
}
return qwen4_dispatch_resident(QWEN4_K_ATTN_PREP_ROWS, &args, sizeof(args), b, 12,
MTLSizeMake(n_head + n_head_kv + n_idx_head + 1u, n_rows, 1), MTLSizeMake(32, 1, 1), 0,
res, qwen4_rows_resident(res, rows, n_rows));
}
int ds4_gpu_qwen4_idx_block_key_rows_tensor(
const ds4_gpu_tensor *table, uint64_t entry0, const ds4_gpu_qwen4_attn_row *rows, uint32_t n_rows,
const void *model_map, uint64_t model_size, uint64_t g_ik_offset,
uint32_t ratio, uint32_t idx_dim, uint32_t n_rot, float rope_base, float eps) {
struct { uint32_t block0, n_blocks, ratio, idx_dim, n_rot; float rope_base, eps; uint32_t pad0;
float rope_mscale; float rope_freq[32]; } args =
{ 0u, n_rows, ratio, idx_dim, n_rot, rope_base, eps, 0, 1.0f, { 0 } };
qwen4_rope_fill(args.rope_freq, &args.rope_mscale, n_rot, rope_base);
qwen4_bind b[2], res[QWEN4_ATTN_ROWS_MAX * 5u];
if (n_rows == 0 || n_rows > QWEN4_ATTN_ROWS_MAX || ratio == 0 || idx_dim < 32 || idx_dim > 128 ||
(idx_dim % 32) != 0 || n_rot > idx_dim ||
!qwen4_bind_weight(&b[0], model_map, model_size, g_ik_offset, (uint64_t)idx_dim * sizeof(float), "indexer k_norm") ||
!qwen4_bind_rows(&b[1], table, entry0, n_rows)) {
return 0;
}
return qwen4_dispatch_resident(QWEN4_K_IDX_BLOCK_KEY_ROWS, &args, sizeof(args), b, 2,
MTLSizeMake(n_rows, 1, 1), MTLSizeMake(32, 1, 1), 0,
res, qwen4_rows_resident(res, rows, n_rows));
}
int ds4_gpu_qwen4_idx_score_rows_tensor(
ds4_gpu_tensor *score, ds4_gpu_tensor *tile_max, const ds4_gpu_tensor *iq,
const ds4_gpu_tensor *table, uint64_t entry0, const ds4_gpu_qwen4_attn_row *rows, uint32_t n_rows,
uint32_t n_block_stride, uint32_t n_idx_head, uint32_t idx_dim, uint32_t ratio) {
struct { uint32_t n_tokens, n_blocks, n_idx_head, idx_dim, pos0, ratio, pad0, pad1; } args =
{ n_rows, n_block_stride, n_idx_head, idx_dim, 0u, ratio, 0, 0 };
const uint32_t n_tiles = (n_block_stride + 7u) / 8u;
qwen4_bind b[4], res[QWEN4_ATTN_ROWS_MAX * 5u];
if (n_rows == 0 || n_rows > QWEN4_ATTN_ROWS_MAX || n_block_stride == 0 || ratio == 0 ||
n_idx_head * idx_dim > 512u || (idx_dim & 3u) != 0u ||
!qwen4_bind_tensor(&b[0], iq, (uint64_t)n_rows * n_idx_head * idx_dim * sizeof(float), "indexer rows q") ||
!qwen4_bind_tensor(&b[1], score, (uint64_t)n_rows * n_block_stride * sizeof(float), "indexer rows scores") ||
!qwen4_bind_tensor(&b[2], tile_max, (uint64_t)n_rows * n_tiles * sizeof(uint32_t), "indexer rows tile maxima") ||
!qwen4_bind_rows(&b[3], table, entry0, n_rows)) {
return 0;
}
return qwen4_dispatch_resident(QWEN4_K_IDX_SCORE_ROWS, &args, sizeof(args), b, 4,
MTLSizeMake((n_block_stride + 127) / 128, n_rows, 1), MTLSizeMake(128, 1, 1), 0,
res, qwen4_rows_resident(res, rows, n_rows));
}
int ds4_gpu_qwen4_idx_select_rows_tensor(
ds4_gpu_tensor *sel, const ds4_gpu_tensor *score, const ds4_gpu_tensor *tile_max,
const ds4_gpu_tensor *table, uint64_t entry0, const ds4_gpu_qwen4_attn_row *rows, uint32_t n_rows,
uint32_t n_block_stride, uint32_t top_k) {
struct { uint32_t n_tokens, n_blocks, top_k, pad0; } args = { n_rows, n_block_stride, top_k, 0 };
const uint32_t n_tiles = (n_block_stride + 7u) / 8u;
qwen4_bind b[4];
if (n_rows == 0 || n_rows > QWEN4_ATTN_ROWS_MAX || top_k == 0 || top_k > n_block_stride ||
!qwen4_bind_tensor(&b[0], score, (uint64_t)n_rows * n_block_stride * sizeof(float), "indexer rows scores") ||
!qwen4_bind_tensor(&b[1], tile_max, (uint64_t)n_rows * n_tiles * sizeof(uint32_t), "indexer rows tile maxima") ||
!qwen4_bind_tensor(&b[2], sel, (uint64_t)n_rows * top_k * sizeof(int32_t), "rows selected blocks") ||
!qwen4_bind_rows(&b[3], table, entry0, n_rows)) {
return 0;
}
(void)rows;
return qwen4_dispatch(QWEN4_K_IDX_SELECT_ROWS, &args, sizeof(args), b, 4,
MTLSizeMake(n_rows, 1, 1), MTLSizeMake(1024, 1, 1), 0);
}
int ds4_gpu_qwen4_idx_expand_rows_tensor(
ds4_gpu_tensor *sel_tokens, ds4_gpu_tensor *n_sel, const ds4_gpu_tensor *sel_blocks,
const ds4_gpu_tensor *table, uint64_t entry0, uint32_t n_rows,
uint32_t n_sel_blocks, uint32_t ratio, uint32_t sel_stride) {
struct { uint32_t n_tokens, n_sel_blocks, ratio, pos0, sel_stride, pad0, pad1, pad2; } args =
{ n_rows, n_sel_blocks, ratio, 0u, sel_stride, 0, 0, 0 };
qwen4_bind b[4];
if (n_rows == 0 || n_rows > QWEN4_ATTN_ROWS_MAX || ratio == 0 || sel_stride < n_sel_blocks * ratio + ratio - 1u ||
!qwen4_bind_tensor(&b[0], sel_blocks, (uint64_t)n_rows * n_sel_blocks * sizeof(int32_t), "rows selected blocks") ||
!qwen4_bind_tensor(&b[1], sel_tokens, (uint64_t)n_rows * sel_stride * sizeof(int32_t), "rows selected tokens") ||
!qwen4_bind_tensor(&b[2], n_sel, (uint64_t)n_rows * sizeof(uint32_t), "rows selected counts") ||
!qwen4_bind_rows(&b[3], table, entry0, n_rows)) {
return 0;
}
return qwen4_dispatch(QWEN4_K_IDX_EXPAND_ROWS, &args, sizeof(args), b, 4,
MTLSizeMake(n_rows, 1, 1), MTLSizeMake(256, 1, 1), 0);
}
int ds4_gpu_qwen4_attn_decode_rows_tensor(
ds4_gpu_tensor *out, const ds4_gpu_tensor *q, const ds4_gpu_tensor *gate,
const ds4_gpu_tensor *sel_tokens, const ds4_gpu_tensor *n_sel, ds4_gpu_tensor *part,
const ds4_gpu_tensor *table, uint64_t entry0, const ds4_gpu_qwen4_attn_row *rows, uint32_t n_rows,
uint32_t n_head, uint32_t n_head_kv, uint32_t head_dim, uint32_t sel_stride, float scale) {
/* Each row takes the splits the single-row dispatch gives its key count
* (see the kernel); the partials use the split cap as their stride. */
struct { uint32_t n_tokens, n_head, n_head_kv, head_dim, pos0, use_sel, sel_stride; float scale;
uint32_t n_splits, keys_per_split, pad0, pad1; } args =
{ n_rows, n_head, n_head_kv, head_dim, 0u, 0u, sel_stride, scale,
QWEN4_ATTN_MAX_SPLITS, qwen4_attn_split_keys(), 0, 0 };
const uint64_t q_bytes = (uint64_t)n_rows * n_head * head_dim * sizeof(float);
const uint64_t part_bytes = ds4_gpu_qwen4_attn_part_floats(n_rows, n_head, head_dim) * sizeof(float);
qwen4_bind b[7], res[QWEN4_ATTN_ROWS_MAX * 5u];
if (n_rows == 0 || n_rows > QWEN4_ATTN_ROWS_MAX || n_head_kv == 0 || (n_head % n_head_kv) != 0 ||
n_head / n_head_kv > 12 || (head_dim != 128 && head_dim != 256) ||
!qwen4_bind_tensor(&b[0], q, q_bytes, "attn rows q") ||
!qwen4_bind_tensor(&b[1], gate, q_bytes, "attn rows gate") ||
!qwen4_bind_tensor(&b[2], sel_tokens, (uint64_t)n_rows * sel_stride * sizeof(int32_t), "rows selected tokens") ||
!qwen4_bind_tensor(&b[3], n_sel, (uint64_t)n_rows * sizeof(uint32_t), "rows selected counts") ||
!qwen4_bind_tensor(&b[4], out, q_bytes, "attn rows out") ||
!qwen4_bind_tensor(&b[5], part, part_bytes, "attn rows partials") ||
!qwen4_bind_rows(&b[6], table, entry0, n_rows)) {
return 0;
}
const bool npt8 = head_dim == 256u;
if (!qwen4_dispatch_resident(npt8 ? QWEN4_K_ATTN_DECODE_ROWS_NPT8 : QWEN4_K_ATTN_DECODE_ROWS_NPT4,
&args, sizeof(args), b, 7,
MTLSizeMake(QWEN4_ATTN_MAX_SPLITS, n_head_kv, n_rows),
MTLSizeMake(32 * QWEN4_ATTN_NSG, 1, 1), 0,
res, qwen4_rows_resident(res, rows, n_rows))) {
return 0;
}
qwen4_bind mb[4] = { b[5], b[1], b[4], b[6] };
return qwen4_dispatch(npt8 ? QWEN4_K_ATTN_MERGE_ROWS_NPT8 : QWEN4_K_ATTN_MERGE_ROWS_NPT4,
&args, sizeof(args), mb, 4,
MTLSizeMake(n_head, n_rows, 1), MTLSizeMake(head_dim, 1, 1), 0);
}
int ds4_gpu_qwen4_moe_mid_tensor(
ds4_gpu_tensor *mid, const ds4_gpu_tensor *x, const ds4_gpu_tensor *selected,
const void *model_map, uint64_t model_size, uint64_t gate_offset, uint64_t up_offset,
uint32_t weight_type, uint32_t n_total_expert, uint32_t n_tokens, uint32_t n_slots,
uint32_t in_dim, uint32_t ff_dim,
uint64_t shared_gate_offset, uint64_t shared_up_offset, uint32_t shared_type) {
const uint32_t row_bytes = qwen4_expert_row_bytes(weight_type, in_dim);
const uint64_t expert_bytes = (uint64_t)row_bytes * ff_dim;
const bool has_shared = shared_type != UINT32_MAX;
const uint32_t sh_row_bytes = has_shared ? qwen4_expert_row_bytes(shared_type, in_dim) : 0u;
const uint32_t n_out = n_slots + (has_shared ? 1u : 0u);
const uint64_t experts_bytes = expert_bytes * n_total_expert;
const uint64_t shared_bytes = (uint64_t)sh_row_bytes * ff_dim;
qwen4_moe_args args = { n_tokens, n_slots, in_dim, ff_dim, weight_type, row_bytes, expert_bytes,
has_shared ? 1u : 0u, has_shared ? shared_type : 0u, sh_row_bytes, n_total_expert, 0u, 0u };
qwen4_bind b[7];
if (n_tokens == 0 || n_slots == 0 || row_bytes == 0 || ff_dim == 0 || (has_shared && sh_row_bytes == 0) ||
!qwen4_bind_weight(&b[0], model_map, model_size, gate_offset, experts_bytes, "moe gate experts") ||
!qwen4_bind_weight(&b[1], model_map, model_size, up_offset, experts_bytes, "moe up experts") ||
!qwen4_bind_tensor(&b[2], selected, (uint64_t)n_tokens * n_slots * sizeof(int32_t), "moe selected") ||
!qwen4_bind_tensor(&b[3], x, (uint64_t)n_tokens * in_dim * sizeof(float), "moe input") ||
!qwen4_bind_tensor(&b[4], mid, (uint64_t)n_tokens * n_out * ff_dim * sizeof(float), "moe mid")) {
return 0;
}
if (has_shared) {
if (!qwen4_bind_weight(&b[5], model_map, model_size, shared_gate_offset, shared_bytes, "shared gate") ||
!qwen4_bind_weight(&b[6], model_map, model_size, shared_up_offset, shared_bytes, "shared up")) {
return 0;
}
} else {
b[5] = b[0];
b[6] = b[1];
}
const bool q4k = weight_type == 12u && getenv("DS4_QWEN4_NO_Q4K_MID") == NULL;
const bool m3_ultra = q4k && ds4_gpu_device_name_contains("M3 Ultra");
/* One row per SIMD group improves both single-token decode and the
* two-token MTP verifier on M3 Ultra without changing dot-product order.
* M5 measured the single-token case with four groups per threadgroup and
* the two-row MTP passes with four. */
const bool m5_single = q4k && (n_tokens <= 2u || (n_tokens == 3u && g_qwen4_verify_rows_exact)) &&
ds4_gpu_device_is_m5_apple_silicon();
const uint32_t default_nr = (m3_ultra && n_tokens <= 2u) || m5_single ? 1u : 2u;
const bool specialize = !q4k && qwen4_moe_mv_specialize(weight_type);
const uint64_t nr_env = q4k ?
ds4_gpu_env_u64("DS4_QWEN4_Q4K_MID_NR", default_nr, 1u, UINT64_MAX) :
(specialize ? qwen4_moe_mv_rows() : 2u);
const uint32_t nr = nr_env >= 1u && nr_env <= (q4k ? 2u : 4u) ? (uint32_t)nr_env : default_nr;
/* NR2 without an NSG override restores the former ordered dispatch. */
const uint32_t default_nsg = nr != 1u ? 2u : m3_ultra ? 8u : m5_single ? 4u : 2u;
const uint32_t nsg = q4k ?
(uint32_t)ds4_gpu_env_u64("DS4_QWEN4_Q4K_MID_NSG", default_nsg, 1u, 8u) :
(specialize ? qwen4_moe_mv_groups(weight_type) : 4u);
const uint32_t rows_per_tg = nr * nsg;
const int kernel = !q4k ? QWEN4_K_MOE_MID : nr == 1u ? QWEN4_K_MOE_MID_Q4K_NR1 : QWEN4_K_MOE_MID_Q4K;
return qwen4_dispatch(kernel, &args, sizeof(args), b, 7,
MTLSizeMake((ff_dim + rows_per_tg - 1) / rows_per_tg, n_out, n_tokens),
MTLSizeMake(32u * nsg, 1, 1), 0);
}
int ds4_gpu_qwen4_moe_down_tensor(
ds4_gpu_tensor *part, const ds4_gpu_tensor *mid, const ds4_gpu_tensor *selected,
const void *model_map, uint64_t model_size, uint64_t down_offset,
uint32_t weight_type, uint32_t n_total_expert, uint32_t n_tokens, uint32_t n_slots,
uint32_t ff_dim, uint32_t out_dim,
uint64_t shared_down_offset, uint32_t shared_type) {
const uint32_t weight_dim = weight_type == 10u ? (ff_dim + 255u) / 256u * 256u : ff_dim;
const uint32_t row_bytes = qwen4_expert_row_bytes(weight_type, weight_dim);
const uint64_t expert_bytes = (uint64_t)row_bytes * out_dim;
const bool has_shared = shared_type != UINT32_MAX;
const uint32_t sh_row_bytes = has_shared ? qwen4_expert_row_bytes(shared_type, ff_dim) : 0u;
const uint32_t n_out = n_slots + (has_shared ? 1u : 0u);
const uint64_t experts_bytes = expert_bytes * n_total_expert;
const uint64_t shared_bytes = (uint64_t)sh_row_bytes * out_dim;
qwen4_moe_args args = { n_tokens, n_slots, ff_dim, out_dim, weight_type, row_bytes, expert_bytes,
has_shared ? 1u : 0u, has_shared ? shared_type : 0u, sh_row_bytes, 0u, 0u, 0u };
qwen4_bind b[5];
if (n_tokens == 0 || n_slots == 0 || row_bytes == 0 || (ff_dim % 32u) != 0 ||
out_dim == 0 || (has_shared && sh_row_bytes == 0) ||
!qwen4_bind_weight(&b[0], model_map, model_size, down_offset, experts_bytes, "moe down experts") ||
!qwen4_bind_tensor(&b[1], selected, (uint64_t)n_tokens * n_slots * sizeof(int32_t), "moe selected") ||
!qwen4_bind_tensor(&b[2], mid, (uint64_t)n_tokens * n_out * ff_dim * sizeof(float), "moe mid") ||
!qwen4_bind_tensor(&b[3], part, (uint64_t)n_tokens * n_out * out_dim * sizeof(float), "moe partial")) {
return 0;
}
if (has_shared) {
if (!qwen4_bind_weight(&b[4], model_map, model_size, shared_down_offset, shared_bytes, "shared down")) return 0;
} else {
b[4] = b[0];
}
const uint32_t nsg = qwen4_moe_mv_specialize(weight_type) ? qwen4_moe_mv_groups(weight_type) : 4u;
const uint32_t rows_per_tg = nsg * (qwen4_moe_mv_specialize(weight_type) ? qwen4_moe_mv_rows() : 2u);
/* MXFP4 rows with four blocks per lane requested ahead (same lane map and
* chain order, byte-identical); M5 default, DS4_QWEN4_MOE_DOWN_PREFETCH=0/1
* overrides on any device. */
const int prefetch_override = ds4_gpu_env_bool("DS4_QWEN4_MOE_DOWN_PREFETCH");
const bool prefetch = weight_type == 39u && (ff_dim % 32u) == 0 &&
(prefetch_override >= 0 ? prefetch_override > 0 : ds4_gpu_device_is_m5_apple_silicon());
return qwen4_dispatch(prefetch ? QWEN4_K_MOE_DOWN_MXFP4_PF : QWEN4_K_MOE_DOWN, &args, sizeof(args), b, 5,
MTLSizeMake((out_dim + rows_per_tg - 1) / rows_per_tg, n_out, n_tokens),
MTLSizeMake(32u * nsg, 1, 1), 0);
}
/* Decode batch over its distinct experts (kernel_qwen4_moe_mid_q4k_grouped):
* Q4_K gate/up, no shared slot, per-expert pair lists from
* ds4_gpu_qwen4_moe_build_lists_tensor. Same grid as the per-token kernel;
* the pairs that do not own their expert return. */
int ds4_gpu_qwen4_moe_mid_grouped_tensor(
ds4_gpu_tensor *mid, const ds4_gpu_tensor *x, const ds4_gpu_tensor *selected,
const ds4_gpu_tensor *lists, const ds4_gpu_tensor *counts, uint32_t list_cap,
const void *model_map, uint64_t model_size, uint64_t gate_offset, uint64_t up_offset,
uint32_t weight_type, uint32_t n_total_expert, uint32_t n_tokens, uint32_t n_slots,
uint32_t in_dim, uint32_t ff_dim) {
if (weight_type != 12u || (in_dim % 256u) != 0) return 0;
const uint32_t row_bytes = qwen4_expert_row_bytes(weight_type, in_dim);
const uint64_t expert_bytes = (uint64_t)row_bytes * ff_dim;
qwen4_moe_args args = { n_tokens, n_slots, in_dim, ff_dim, weight_type, row_bytes, expert_bytes,
0u, 0u, 0u, n_total_expert, list_cap, 0u };
qwen4_bind b[7];
if (n_tokens == 0 || n_slots == 0 || row_bytes == 0 || ff_dim == 0 || list_cap == 0 ||
!qwen4_bind_weight(&b[0], model_map, model_size, gate_offset, expert_bytes * n_total_expert, "moe gate experts") ||
!qwen4_bind_weight(&b[1], model_map, model_size, up_offset, expert_bytes * n_total_expert, "moe up experts") ||
!qwen4_bind_tensor(&b[2], selected, (uint64_t)n_tokens * n_slots * sizeof(int32_t), "moe selected") ||
!qwen4_bind_tensor(&b[3], x, (uint64_t)n_tokens * in_dim * sizeof(float), "moe input") ||
!qwen4_bind_tensor(&b[4], mid, (uint64_t)n_tokens * n_slots * ff_dim * sizeof(float), "moe mid") ||
!qwen4_bind_tensor(&b[5], lists, (uint64_t)n_total_expert * list_cap * sizeof(int32_t), "moe lists") ||
!qwen4_bind_tensor(&b[6], counts, (uint64_t)n_total_expert * sizeof(int32_t), "moe counts")) {
return 0;
}
const uint32_t nsg = 2u, rows_per_tg = 2u * nsg;
return qwen4_dispatch(QWEN4_K_MOE_MID_Q4K_GROUPED, &args, sizeof(args), b, 7,
MTLSizeMake((ff_dim + rows_per_tg - 1) / rows_per_tg, n_slots, n_tokens),
MTLSizeMake(32u * nsg, 1, 1), 0);
}
/* The MXFP4 down rows of the same batch, on the same lists. */
int ds4_gpu_qwen4_moe_down_grouped_tensor(
ds4_gpu_tensor *part, const ds4_gpu_tensor *mid, const ds4_gpu_tensor *selected,
const ds4_gpu_tensor *lists, const ds4_gpu_tensor *counts, uint32_t list_cap,
const void *model_map, uint64_t model_size, uint64_t down_offset,
uint32_t weight_type, uint32_t n_total_expert, uint32_t n_tokens, uint32_t n_slots,
uint32_t ff_dim, uint32_t out_dim) {
if (weight_type != 39u || (ff_dim % 32u) != 0) return 0;
const uint32_t row_bytes = qwen4_expert_row_bytes(weight_type, ff_dim);
const uint64_t expert_bytes = (uint64_t)row_bytes * out_dim;
qwen4_moe_args args = { n_tokens, n_slots, ff_dim, out_dim, weight_type, row_bytes, expert_bytes,
0u, 0u, 0u, n_total_expert, list_cap, 0u };
qwen4_bind b[6];
if (n_tokens == 0 || n_slots == 0 || row_bytes == 0 || out_dim == 0 || list_cap == 0 ||
!qwen4_bind_weight(&b[0], model_map, model_size, down_offset, expert_bytes * n_total_expert, "moe down experts") ||
!qwen4_bind_tensor(&b[1], selected, (uint64_t)n_tokens * n_slots * sizeof(int32_t), "moe selected") ||
!qwen4_bind_tensor(&b[2], mid, (uint64_t)n_tokens * n_slots * ff_dim * sizeof(float), "moe mid") ||
!qwen4_bind_tensor(&b[3], part, (uint64_t)n_tokens * n_slots * out_dim * sizeof(float), "moe partial") ||
!qwen4_bind_tensor(&b[4], lists, (uint64_t)n_total_expert * list_cap * sizeof(int32_t), "moe lists") ||
!qwen4_bind_tensor(&b[5], counts, (uint64_t)n_total_expert * sizeof(int32_t), "moe counts")) {
return 0;
}
const uint32_t nsg = 4u, rows_per_tg = 2u * nsg;
return qwen4_dispatch(QWEN4_K_MOE_DOWN_MXFP4_GROUPED, &args, sizeof(args), b, 6,
MTLSizeMake((out_dim + rows_per_tg - 1) / rows_per_tg, n_slots, n_tokens),
MTLSizeMake(32u * nsg, 1, 1), 0);
}
int ds4_gpu_qwen4_moe_reduce_tensor(
ds4_gpu_tensor *out, const ds4_gpu_tensor *part, const ds4_gpu_tensor *weights,
const ds4_gpu_tensor *shared_gate, const ds4_gpu_tensor *shared, ds4_gpu_tensor *R, const ds4_gpu_tensor *inj,
uint32_t n_tokens, uint32_t n_slots, uint32_t part_stride, uint32_t dim, uint32_t n_hc) {
const uint32_t shared_src = !shared_gate ? 0u : shared ? 2u : 1u;
struct { uint32_t n_tokens, n_slots, dim, shared_src, n_hc, part_stride, pad1, pad2; } args =
{ n_tokens, n_slots, dim, shared_src, R ? n_hc : 0u, part_stride, 0, 0 };
qwen4_bind b[7];
if (n_tokens == 0 || n_slots == 0 || dim == 0 || (R && n_hc == 0) || n_hc > 8 || part_stride < n_slots ||
(shared_src == 1u && part_stride < n_slots + 1u) ||
!qwen4_bind_tensor(&b[0], part, (uint64_t)n_tokens * part_stride * dim * sizeof(float), "moe partial") ||
!qwen4_bind_tensor(&b[1], weights, (uint64_t)n_tokens * n_slots * sizeof(float), "moe weights") ||
!qwen4_bind_tensor(&b[3], out, (uint64_t)n_tokens * dim * sizeof(float), "moe out")) {
return 0;
}
if (shared_gate) {
if (!qwen4_bind_tensor(&b[2], shared_gate, (uint64_t)n_tokens * sizeof(float), "shared gate")) return 0;
} else {
b[2] = b[1];
}
if (R) {
if (!qwen4_bind_tensor(&b[4], R, (uint64_t)n_tokens * dim * n_hc * sizeof(float), "moe residual") ||
!qwen4_bind_tensor(&b[5], inj, (uint64_t)n_tokens * n_hc * DS4_QWEN4_HC_CHUNKS * n_hc * sizeof(float),
"moe inject")) {
return 0;
}
} else {
b[4] = b[3];
b[5] = b[1];
}
if (shared_src == 2u) {
if (!qwen4_bind_tensor(&b[6], shared, (uint64_t)n_tokens * dim * sizeof(float), "shared expert output")) {
return 0;
}
} else {
b[6] = b[0];
}
return qwen4_dispatch(QWEN4_K_MOE_REDUCE, &args, sizeof(args), b, 7,
MTLSizeMake((dim + 255) / 256, n_tokens, 1), MTLSizeMake(256, 1, 1), 0);
}
int ds4_gpu_qwen4_moe_build_lists_tensor(
ds4_gpu_tensor *lists, ds4_gpu_tensor *counts, const ds4_gpu_tensor *selected,
uint32_t n_tokens, uint32_t n_slots, uint32_t n_expert, uint32_t list_cap) {
qwen4_moe_mm_args args = { n_tokens, n_slots, 0, 0, 0, 0, 0, list_cap, 0, n_expert, 0, 0, 0 };
qwen4_bind b[3];
if (n_tokens == 0 || n_slots == 0 || n_expert == 0 || n_expert > 512 || list_cap == 0 ||
!qwen4_bind_tensor(&b[0], selected, (uint64_t)n_tokens * n_slots * sizeof(int32_t), "moe selected") ||
!qwen4_bind_tensor(&b[1], lists, (uint64_t)n_expert * list_cap * sizeof(int32_t), "moe lists") ||
!qwen4_bind_tensor(&b[2], counts, (uint64_t)n_expert * sizeof(int32_t), "moe counts")) {
return 0;
}
return qwen4_dispatch(QWEN4_K_MOE_BUILD_LISTS, &args, sizeof(args), b, 3,
MTLSizeMake(1, 1, 1), MTLSizeMake(512, 1, 1), 0);
}
static uint32_t qwen4_moe_mm_tiles(uint32_t n_tokens, bool mid) {
uint32_t tiles = (n_tokens + 31u) / 32u;
/* Spread large routed batches over more independent tiles on M3 Ultra
* (gate/up and down) and on M5 (gate/up; the down tiles measured flat).
* Each tile keeps the same K loop and accumulation order. */
const bool spread = ds4_gpu_device_name_contains("M3 Ultra") ||
(mid && ds4_gpu_device_is_m5_apple_silicon());
const uint32_t default_cap = spread ?
(n_tokens >= 8192u ? 32u : n_tokens >= 4096u ? 16u : 8u) : 8u;
const uint32_t cap = (uint32_t)ds4_gpu_env_u64(
mid ? "DS4_QWEN4_MOE_MID_TILES" : "DS4_QWEN4_MOE_DOWN_TILES", default_cap, 1u, 32u);
return tiles > cap ? cap : tiles;
}
static uint32_t qwen4_moe_mm_nt(uint32_t n_tokens, uint32_t type, const char *env_name) {
/* Low-bit experts receive few tokens in short prefills. Smaller token
* tiles avoid unused matrix products while retaining the same K order.
* Keep 32-token tiles for larger batches and unmeasured devices/types. */
uint32_t default_nt = 4u;
if ((type == 16u || type == 10u) && ds4_gpu_device_name_contains("M3 Ultra")) {
if (n_tokens <= 512u) default_nt = 1u;
else if (n_tokens <= 1024u) default_nt = 2u;
}
const bool down = strstr(env_name, "DOWN") != NULL;
/* Reuse each decoded down-weight tile across 64 tokens. The K loop and
* partial-tail accumulation stay unchanged; short batches keep small tiles. */
if (down && type == 10u && n_tokens >= 8192u && qwen4_prefill_reuse(n_tokens)) default_nt = 8u;
/* M5: 64-token gate/up and down tiles for the Q4_K / MXFP4 pack at
* chunk-sized batches (same tile arithmetic; remainders take the
* 8/16/32-token kernels). Measured on M5 Max, see
* speed-bench/qwen38-m5-round4.md. */
if ((type == 12u || type == 39u) && n_tokens >= 4096u && ds4_gpu_device_is_m5_apple_silicon()) default_nt = 8u;
const uint32_t nt = (uint32_t)ds4_gpu_env_u64(env_name, default_nt, 1u, 8u);
return nt == 1u || nt == 2u || nt == 4u || nt == 8u ? nt : default_nt;
}
/* Routed tiles on the Metal 4 tensor ops (drift class: the cooperative
* matmul's accumulation order differs from the simdgroup tiles; needs the
* tensor API). DS4_QWEN4_MOE_MM_NAX: 0 simdgroup tiles; 1 tensor-op
* tiles of 32 tokens; 2 tensor-op tiles of 64 tokens; 3/4 the same widths
* with the activation operand kept at full float precision (x gathered from
* the float tensor, no half rounding of x or of the mid hand-off, no
* conversion pass); 5/6 compensated tiles (64/32 tokens), which stage the
* half rounding residual of the operand beside it and run the tensor op on
* both, reaching the 3/4 accuracy while keeping the fp16 tensor-op rate.
* Unset selects 2 (the 64-token half tiles) for every tensor tier on
* devices with the tensor API: its operand rounding matches the simdgroup
* tiles and its fixture NLL sits within noise of them, so the compensated
* level stays an env-selectable accuracy option. 0 forces the simdgroup
* tiles. Returns the token tile width, 0 for the simdgroup path. */
static long qwen4_moe_mm_nax_level(uint32_t type) {
const char *v = getenv("DS4_QWEN4_MOE_MM_NAX");
/* default 2 for every tensor tier: level 2 matches the simdgroup tiles'
* operand rounding and differs only by accumulation order, and its
* fixture NLL is within noise of them on both packs (Q4 0.20503 vs
* 0.20505, Q2 0.30418 vs 0.30346), while it carries the full prefill
* gain (+48/+51% Q4, +29/+36% Q2). The compensated level 5 keeps the
* best absolute NLL and stays one env variable away. */
if (!v || !v[0]) return (type == 12u || type == 39u || type == 16u || type == 10u) ? 2 : 0;
return strtol(v, NULL, 10);
}
static uint32_t qwen4_moe_mm_nax(uint32_t type) {
if (!ds4_gpu_mpp_available()) return 0;
if (!(type == 12u || type == 39u || type == 16u || type == 10u)) return 0;
const long n = qwen4_moe_mm_nax_level(type);
if (n <= 0) return 0;
return (n == 1 || n == 4 || n == 6) ? 32u : 64u;
}
/* float-activation tensor tiles (levels 3 and 4) */
static bool qwen4_moe_mm_nax_fx(uint32_t type) {
const long n = qwen4_moe_mm_nax_level(type);
return n == 3 || n == 4;
}
/* compensated tensor tiles (levels 5 and 6): the fast fp16 tensor kernels run
* twice per K step, on the half-rounded operand and on its half residual, so
* the activation operand enters at ~2^-22 relative instead of 2^-11 */
static bool qwen4_moe_mm_nax_comp(uint32_t type) {
const long n = qwen4_moe_mm_nax_level(type);
return n == 5 || n == 6;
}
static ds4_gpu_tensor *g_qwen4_nax_half_x, *g_qwen4_nax_half_mid, *g_qwen4_nax_half_midr;
static uint64_t g_qwen4_nax_half_x_bytes, g_qwen4_nax_half_mid_bytes, g_qwen4_nax_half_midr_bytes;
static const ds4_gpu_tensor *g_qwen4_nax_half_mid_for;
static uint64_t g_qwen4_nax_half_mid_count;
static void qwen4_nax_release_scratch(void) {
if (g_qwen4_nax_half_x) ds4_gpu_tensor_free(g_qwen4_nax_half_x);
if (g_qwen4_nax_half_mid) ds4_gpu_tensor_free(g_qwen4_nax_half_mid);
if (g_qwen4_nax_half_midr) ds4_gpu_tensor_free(g_qwen4_nax_half_midr);
g_qwen4_nax_half_x = g_qwen4_nax_half_mid = g_qwen4_nax_half_midr = NULL;
g_qwen4_nax_half_x_bytes = g_qwen4_nax_half_mid_bytes = g_qwen4_nax_half_midr_bytes = 0;
g_qwen4_nax_half_mid_for = NULL;
g_qwen4_nax_half_mid_count = 0;
}
static ds4_gpu_tensor *qwen4_nax_scratch(ds4_gpu_tensor **slot, uint64_t *slot_bytes, uint64_t bytes) {
if (!*slot || *slot_bytes < bytes) {
if (*slot) ds4_gpu_tensor_free(*slot);
*slot = ds4_gpu_tensor_alloc(bytes);
*slot_bytes = *slot ? bytes : 0;
}
return *slot;
}
static ds4_gpu_tensor *qwen4_nax_half_operand(ds4_gpu_tensor **slot, uint64_t *slot_bytes,
const ds4_gpu_tensor *src, uint64_t count) {
const uint64_t bytes = count * sizeof(uint16_t);
if (count == 0 || (count % 4u) != 0) return NULL;
ds4_gpu_tensor *dst = qwen4_nax_scratch(slot, slot_bytes, bytes);
if (!dst) return NULL;
struct { uint32_t n4; } args = { (uint32_t)(count / 4u) };
qwen4_bind b[2];
if (!qwen4_bind_tensor(&b[0], src, count * sizeof(float), "nax half operand source") ||
!qwen4_bind_tensor(&b[1], dst, bytes, "nax half operand")) return NULL;
const uint32_t n_threads = (args.n4 + 3u) / 4u; /* 16 values per thread */
if (!qwen4_dispatch(QWEN4_K_ROWS_F32_TO_F16, &args, sizeof(args), b, 2,
MTLSizeMake((n_threads + 255u) / 256u, 1, 1), MTLSizeMake(256, 1, 1), 0)) return NULL;
return dst;
}
static bool qwen4_moe_mm_tails(uint32_t type, uint32_t nt) {
/* Remainder tiles measured on M3 Ultra for the low-bit experts and on M5
* for Q4_K gate/up with MXFP4 down. */
const int override = ds4_gpu_env_bool("DS4_QWEN4_MOE_TAILS");
return nt > 1u && (override >= 0 ? override != 0 :
((type == 16u || type == 10u) && ds4_gpu_device_name_contains("M3 Ultra")) ||
((type == 12u || type == 39u) && ds4_gpu_device_is_m5_apple_silicon()));
}
int ds4_gpu_qwen4_moe_mm_mid_tensor(
ds4_gpu_tensor *mid, const ds4_gpu_tensor *x, const ds4_gpu_tensor *lists, const ds4_gpu_tensor *counts,
const void *model_map, uint64_t model_size, uint64_t gate_offset, uint64_t up_offset,
uint32_t weight_type, uint32_t n_expert, uint32_t n_tokens, uint32_t n_slots, uint32_t n_out,
uint32_t in_dim, uint32_t ff_dim, uint32_t list_cap) {
const uint32_t row_bytes = qwen4_expert_row_bytes(weight_type, in_dim);
const uint64_t expert_bytes = (uint64_t)row_bytes * ff_dim;
const uint32_t tiles = qwen4_moe_mm_tiles(n_tokens, true);
const uint32_t nt = qwen4_moe_mm_nt(n_tokens, weight_type, "DS4_QWEN4_MOE_MID_NT");
const int kernel = nt == 1u ? QWEN4_K_MOE_MM_MID_NT1 :
nt == 2u ? QWEN4_K_MOE_MM_MID_NT2 :
nt == 8u ? QWEN4_K_MOE_MM_MID_NT8 : QWEN4_K_MOE_MM_MID;
qwen4_moe_mm_args args = { n_tokens, n_slots, n_out, in_dim, ff_dim, weight_type, row_bytes, list_cap,
expert_bytes, n_expert, tiles, 0, qwen4_moe_mm_expert_major() };
const bool tails = qwen4_moe_mm_tails(weight_type, nt);
if (tails) args.tail_base = nt * 8u;
qwen4_bind b[8];
if (n_tokens == 0 || n_slots == 0 || n_out < n_slots || row_bytes == 0 ||
(weight_type != 8u && weight_type != 39u && weight_type != 12u && weight_type != 10u && weight_type != 16u && weight_type != 2u) ||
(in_dim % 64) != 0 || ff_dim == 0 || n_expert == 0 || n_expert > 512 ||
!qwen4_bind_weight(&b[0], model_map, model_size, gate_offset, expert_bytes * n_expert, "moe gate experts") ||
!qwen4_bind_weight(&b[1], model_map, model_size, up_offset, expert_bytes * n_expert, "moe up experts") ||
!qwen4_bind_tensor(&b[2], lists, (uint64_t)n_expert * list_cap * sizeof(int32_t), "moe lists") ||
!qwen4_bind_tensor(&b[3], counts, (uint64_t)n_expert * sizeof(int32_t), "moe counts") ||
!qwen4_bind_tensor(&b[4], x, (uint64_t)n_tokens * in_dim * sizeof(float), "moe input") ||
!qwen4_bind_tensor(&b[5], mid, (uint64_t)n_tokens * n_out * ff_dim * sizeof(float), "moe mid")) {
return 0;
}
const uint32_t nax = qwen4_moe_mm_nax(weight_type);
const bool nax_fx = qwen4_moe_mm_nax_fx(weight_type);
const bool nax_comp = qwen4_moe_mm_nax_comp(weight_type);
if (nax) {
args.tail_base = 0;
const uint64_t mid_count = (uint64_t)n_tokens * n_out * ff_dim;
if (nax_comp) {
/* compensated tiles: x stays bound at b[4] (float) and the tiles
* write both the half copy of mid and its half residual */
ds4_gpu_tensor *mh = qwen4_nax_scratch(&g_qwen4_nax_half_mid, &g_qwen4_nax_half_mid_bytes, mid_count * sizeof(uint16_t));
ds4_gpu_tensor *mr = qwen4_nax_scratch(&g_qwen4_nax_half_midr, &g_qwen4_nax_half_midr_bytes, mid_count * sizeof(uint16_t));
if (!mh || !mr || !qwen4_bind_tensor(&b[6], mh, mid_count * sizeof(uint16_t), "moe mid half") ||
!qwen4_bind_tensor(&b[7], mr, mid_count * sizeof(uint16_t), "moe mid residual")) return 0;
g_qwen4_nax_half_mid_for = mid;
g_qwen4_nax_half_mid_count = mid_count;
} else if (nax_fx) {
/* float-activation tiles: x stays bound at b[4]; they write no
* half copy of mid, so b[6] is a never-read placeholder */
if (!qwen4_bind_tensor(&b[6], mid, mid_count * sizeof(uint16_t), "moe mid")) return 0;
} else {
ds4_gpu_tensor *xh = qwen4_nax_half_operand(&g_qwen4_nax_half_x, &g_qwen4_nax_half_x_bytes, x, (uint64_t)n_tokens * in_dim);
ds4_gpu_tensor *mh = qwen4_nax_scratch(&g_qwen4_nax_half_mid, &g_qwen4_nax_half_mid_bytes, mid_count * sizeof(uint16_t));
if (!xh || !mh || !qwen4_bind_tensor(&b[4], xh, (uint64_t)n_tokens * in_dim * sizeof(uint16_t), "moe input half") ||
!qwen4_bind_tensor(&b[6], mh, mid_count * sizeof(uint16_t), "moe mid half")) return 0;
g_qwen4_nax_half_mid_for = mid;
g_qwen4_nax_half_mid_count = mid_count;
}
const bool nax_tails = nax == 64u && qwen4_moe_mm_tails(weight_type, 8u);
if (nax_tails) args.tail_base = 64u;
const int nax_mid = nax_comp ? (nax == 64u ? QWEN4_K_MOE_MM_MID_NAXC64 : QWEN4_K_MOE_MM_MID_NAXC)
: nax_fx ? (nax == 64u ? QWEN4_K_MOE_MM_MID_NAXF64 : QWEN4_K_MOE_MM_MID_NAXF)
: (nax == 64u ? QWEN4_K_MOE_MM_MID_NAX64 : QWEN4_K_MOE_MM_MID_NAX);
const int nax_mid_tail = nax_comp ? QWEN4_K_MOE_MM_MID_NAXC : nax_fx ? QWEN4_K_MOE_MM_MID_NAXF : QWEN4_K_MOE_MM_MID_NAX;
if (!qwen4_dispatch(nax_mid, &args, sizeof(args), b, nax_comp ? 8 : 7,
qwen4_moe_mm_grid((ff_dim + 63u) / 64u, n_expert, tiles, args.expert_major),
MTLSizeMake(128, 1, 1), nax == 64u ? 16384u : nax_fx || nax_comp ? 12288u : 10240u)) return 0;
if (!nax_tails) return 1;
args.tiles_per_launch = 1;
return qwen4_dispatch(nax_mid_tail, &args, sizeof(args), b, nax_comp ? 8 : 7,
qwen4_moe_mm_grid((ff_dim + 63u) / 64u, n_expert, 1, args.expert_major),
MTLSizeMake(128, 1, 1), nax_fx || nax_comp ? 12288u : 10240u);
}
if (!qwen4_dispatch(kernel, &args, sizeof(args), b, 6,
qwen4_moe_mm_grid((ff_dim + 31u) / 32u, n_expert, tiles, args.expert_major),
MTLSizeMake(128, 1, 1), 0)) return 0;
if (tails) {
const MTLSize tail_grid = MTLSizeMake((ff_dim + 31u) / 32u, n_expert, 1);
if (!qwen4_dispatch(QWEN4_K_MOE_MM_MID_NT1, &args, sizeof(args), b, 6, tail_grid, MTLSizeMake(128, 1, 1), 0)) return 0;
if (nt > 2u && !qwen4_dispatch(QWEN4_K_MOE_MM_MID_NT2, &args, sizeof(args), b, 6, tail_grid, MTLSizeMake(128, 1, 1), 0)) return 0;
if (nt > 4u && !qwen4_dispatch(QWEN4_K_MOE_MM_MID, &args, sizeof(args), b, 6, tail_grid, MTLSizeMake(128, 1, 1), 0)) return 0;
}
return 1;
}
int ds4_gpu_qwen4_moe_mm_down_tensor(
ds4_gpu_tensor *part, const ds4_gpu_tensor *mid, const ds4_gpu_tensor *lists, const ds4_gpu_tensor *counts,
const void *model_map, uint64_t model_size, uint64_t down_offset,
uint32_t weight_type, uint32_t n_expert, uint32_t n_tokens, uint32_t n_slots, uint32_t n_out,
uint32_t ff_dim, uint32_t out_dim, uint32_t list_cap) {
const uint32_t weight_dim = weight_type == 10u ? (ff_dim + 255u) / 256u * 256u : ff_dim;
const uint32_t row_bytes = qwen4_expert_row_bytes(weight_type, weight_dim);
const uint64_t expert_bytes = (uint64_t)row_bytes * out_dim;
const uint32_t tiles = qwen4_moe_mm_tiles(n_tokens, false);
const uint32_t nt = qwen4_moe_mm_nt(n_tokens, weight_type, "DS4_QWEN4_MOE_DOWN_NT");
const int kernel = nt == 1u ? QWEN4_K_MOE_MM_DOWN_NT1 :
nt == 2u ? QWEN4_K_MOE_MM_DOWN_NT2 :
nt == 8u ? QWEN4_K_MOE_MM_DOWN_NT8 : QWEN4_K_MOE_MM_DOWN;
qwen4_moe_mm_args args = { n_tokens, n_slots, n_out, ff_dim, out_dim, weight_type, row_bytes, list_cap,
expert_bytes, n_expert, tiles, 0, qwen4_moe_mm_expert_major() };
const bool tails = qwen4_moe_mm_tails(weight_type, nt);
if (tails) args.tail_base = nt * 8u;
qwen4_bind b[6];
if (n_tokens == 0 || n_slots == 0 || n_out < n_slots || row_bytes == 0 ||
(weight_type != 8u && weight_type != 39u && weight_type != 12u && weight_type != 10u && weight_type != 16u && weight_type != 2u) ||
(ff_dim % 64) != 0 || out_dim == 0 || n_expert == 0 || n_expert > 512 ||
!qwen4_bind_weight(&b[0], model_map, model_size, down_offset, expert_bytes * n_expert, "moe down experts") ||
!qwen4_bind_tensor(&b[1], lists, (uint64_t)n_expert * list_cap * sizeof(int32_t), "moe lists") ||
!qwen4_bind_tensor(&b[2], counts, (uint64_t)n_expert * sizeof(int32_t), "moe counts") ||
!qwen4_bind_tensor(&b[3], mid, (uint64_t)n_tokens * n_out * ff_dim * sizeof(float), "moe mid") ||
!qwen4_bind_tensor(&b[4], part, (uint64_t)n_tokens * n_out * out_dim * sizeof(float), "moe partial")) {
return 0;
}
const uint32_t nax = qwen4_moe_mm_nax(weight_type);
const bool nax_fx = qwen4_moe_mm_nax_fx(weight_type);
bool nax_comp = qwen4_moe_mm_nax_comp(weight_type);
if (nax) {
args.tail_base = 0;
const uint64_t mid_count = (uint64_t)n_tokens * n_out * ff_dim;
if (nax_comp) {
/* the compensated tiles read the half copy of mid and its
* residual; both must come from the mid tiles' matching launch,
* otherwise fall back to the uncompensated tiles */
if (g_qwen4_nax_half_mid_for == mid && g_qwen4_nax_half_mid_count == mid_count &&
g_qwen4_nax_half_mid && g_qwen4_nax_half_midr) {
if (!qwen4_bind_tensor(&b[3], g_qwen4_nax_half_mid, mid_count * sizeof(uint16_t), "moe mid half") ||
!qwen4_bind_tensor(&b[5], g_qwen4_nax_half_midr, mid_count * sizeof(uint16_t), "moe mid residual")) return 0;
g_qwen4_nax_half_mid_for = NULL;
g_qwen4_nax_half_mid_count = 0;
} else {
nax_comp = false;
}
}
if (!nax_comp && !nax_fx) {
/* the half tiles read the mid tiles' half copy of mid */
ds4_gpu_tensor *mh = g_qwen4_nax_half_mid_for == mid && g_qwen4_nax_half_mid_count == mid_count && g_qwen4_nax_half_mid
? g_qwen4_nax_half_mid
: qwen4_nax_half_operand(&g_qwen4_nax_half_mid, &g_qwen4_nax_half_mid_bytes, mid, mid_count);
g_qwen4_nax_half_mid_for = NULL;
g_qwen4_nax_half_mid_count = 0;
if (!mh || !qwen4_bind_tensor(&b[3], mh, mid_count * sizeof(uint16_t), "moe mid half")) return 0;
}
const bool nax_tails = nax == 64u && qwen4_moe_mm_tails(weight_type, 8u);
if (nax_tails) args.tail_base = 64u;
const int nax_down = nax_comp ? (nax == 64u ? QWEN4_K_MOE_MM_DOWN_NAXC64 : QWEN4_K_MOE_MM_DOWN_NAXC)
: nax_fx ? (nax == 64u ? QWEN4_K_MOE_MM_DOWN_NAXF64 : QWEN4_K_MOE_MM_DOWN_NAXF)
: (nax == 64u ? QWEN4_K_MOE_MM_DOWN_NAX64 : QWEN4_K_MOE_MM_DOWN_NAX);
const int nax_down_tail = nax_comp ? QWEN4_K_MOE_MM_DOWN_NAXC : nax_fx ? QWEN4_K_MOE_MM_DOWN_NAXF : QWEN4_K_MOE_MM_DOWN_NAX;
if (!qwen4_dispatch(nax_down, &args, sizeof(args), b, nax_comp ? 6 : 5,
qwen4_moe_mm_grid((out_dim + 63u) / 64u, n_expert, tiles, args.expert_major),
MTLSizeMake(128, 1, 1), nax == 64u ? 16384u : 8192u)) return 0;
if (!nax_tails) return 1;
args.tiles_per_launch = 1;
return qwen4_dispatch(nax_down_tail, &args, sizeof(args), b, nax_comp ? 6 : 5,
qwen4_moe_mm_grid((out_dim + 63u) / 64u, n_expert, 1, args.expert_major),
MTLSizeMake(128, 1, 1), 8192u);
}
if (!qwen4_dispatch(kernel, &args, sizeof(args), b, 5,
qwen4_moe_mm_grid((out_dim + 31u) / 32u, n_expert, tiles, args.expert_major),
MTLSizeMake(128, 1, 1), 0)) return 0;
if (tails) {
const MTLSize tail_grid = MTLSizeMake((out_dim + 31u) / 32u, n_expert, 1);
if (!qwen4_dispatch(QWEN4_K_MOE_MM_DOWN_NT1, &args, sizeof(args), b, 5, tail_grid, MTLSizeMake(128, 1, 1), 0)) return 0;
if (nt > 2u && !qwen4_dispatch(QWEN4_K_MOE_MM_DOWN_NT2, &args, sizeof(args), b, 5, tail_grid, MTLSizeMake(128, 1, 1), 0)) return 0;
if (nt > 4u && !qwen4_dispatch(QWEN4_K_MOE_MM_DOWN, &args, sizeof(args), b, 5, tail_grid, MTLSizeMake(128, 1, 1), 0)) return 0;
}
return 1;
}
int ds4_gpu_qwen4_matmul_q8_0_weights_tensor(ds4_gpu_tensor *out, const ds4_gpu_tensor *w,
uint32_t in_dim, uint32_t out_dim, const ds4_gpu_tensor *x) {
if (!g_initialized && !ds4_gpu_init()) return 0;
if ((in_dim & 31u) != 0 || !out_dim) return 0;
const uint64_t row_bytes = (uint64_t)(in_dim / 32u) * 34u;
@autoreleasepool {
id<MTLBuffer> xbuf = ds4_gpu_tensor_buffer(x);
id<MTLBuffer> outbuf = ds4_gpu_tensor_buffer(out);
id<MTLBuffer> wbuf = ds4_gpu_tensor_buffer(w);
if (!xbuf || !outbuf || !wbuf ||
ds4_gpu_tensor_bytes(x) < (uint64_t)in_dim * sizeof(float) ||
ds4_gpu_tensor_bytes(out) < (uint64_t)out_dim * sizeof(float) ||
ds4_gpu_tensor_bytes(w) < (uint64_t)out_dim * row_bytes) {
fprintf(stderr, "ds4: Metal Q8_0 tensor-weight matvec received undersized buffers\n");
return 0;
}
int owned = 0;
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
if (!cb) return 0;
ds4_gpu_q8_0_matvec_args mv_args = ds4_gpu_make_q8_0_mv_args(in_dim, out_dim);
ds4_gpu_mv_dispatch mv_dispatch = ds4_gpu_make_q8_0_mv_dispatch();
if (out_dim > 65536u) mv_dispatch.nsg = 8;
mv_args.nr0 = mv_dispatch.nr0;
id<MTLComputePipelineState> pipeline =
ds4_gpu_get_mul_mv_pipeline(mv_dispatch.function_name, mv_dispatch.nsg);
if (!pipeline) return 0;
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
[enc setComputePipelineState:pipeline];
[enc setBytes:&mv_args length:sizeof(mv_args) atIndex:0];
[enc setBuffer:wbuf offset:ds4_gpu_tensor_offset(w) atIndex:1];
[enc setBuffer:xbuf offset:ds4_gpu_tensor_offset(x) atIndex:2];
[enc setBuffer:outbuf offset:ds4_gpu_tensor_offset(out) atIndex:3];
[enc setThreadgroupMemoryLength:mv_dispatch.smem atIndex:0];
[enc dispatchThreadgroups:MTLSizeMake(((NSUInteger)out_dim + (NSUInteger)mv_dispatch.nr0 - 1u) / (NSUInteger)mv_dispatch.nr0, 1, 1)
threadsPerThreadgroup:MTLSizeMake(32, (NSUInteger)mv_dispatch.nsg, 1)];
ds4_gpu_end_compute_encoder(cb, enc);
return ds4_gpu_finish_command_buffer(cb, owned, "Q8_0 tensor-weight matvec") ? 1 : 0;
}
}
int ds4_gpu_qwen4_argmax_tensor(ds4_gpu_tensor *out_idx, ds4_gpu_tensor *scratch,
const ds4_gpu_tensor *logits, uint32_t n_vocab) {
if (!n_vocab || n_vocab > INT32_MAX) return 0;
const uint32_t chunks = (n_vocab + 4095u) / 4096u;
struct { uint32_t n, finish; } args = {n_vocab, 0};
qwen4_bind b[3];
if (!qwen4_bind_tensor(&b[0], logits, (uint64_t)n_vocab * sizeof(float), "argmax logits") ||
!qwen4_bind_tensor(&b[1], scratch, (uint64_t)chunks * 8u, "argmax partials") ||
!qwen4_bind_tensor(&b[2], out_idx, sizeof(int32_t), "argmax index")) return 0;
if (!qwen4_dispatch(QWEN4_K_ARGMAX, &args, sizeof(args), b, 3,
MTLSizeMake(chunks, 1, 1), MTLSizeMake(256, 1, 1), 0)) return 0;
args.n = chunks;
args.finish = 1;
return qwen4_dispatch(QWEN4_K_ARGMAX, &args, sizeof(args), b, 3,
MTLSizeMake(1, 1, 1), MTLSizeMake(256, 1, 1), 0);
}
int ds4_gpu_qwen4_mtp_stage_tensor(
ds4_gpu_tensor *cat, const ds4_gpu_tensor *e, const ds4_gpu_tensor *R,
const void *model_map, uint64_t model_size, uint64_t g_e_offset, uint64_t g_h_offset,
uint32_t n_embd, uint32_t n_hc, float eps) {
struct { uint32_t n_embd, n_hc, pad0; float eps; } args = { n_embd, n_hc, 0u, eps };
qwen4_bind b[5];
if (n_embd == 0 || n_hc == 0 ||
!qwen4_bind_tensor(&b[0], e, (uint64_t)n_embd * sizeof(float), "mtp embedding") ||
!qwen4_bind_tensor(&b[1], R, (uint64_t)n_embd * n_hc * sizeof(float), "mtp streams") ||
!qwen4_bind_weight(&b[2], model_map, model_size, g_e_offset, (uint64_t)n_embd * sizeof(float), "mtp enorm") ||
!qwen4_bind_weight(&b[3], model_map, model_size, g_h_offset, (uint64_t)n_embd * n_hc * sizeof(float),
"mtp hnorm") ||
!qwen4_bind_tensor(&b[4], cat, (uint64_t)(n_hc + 1u) * 2u * n_embd * sizeof(float), "mtp concat")) {
return 0;
}
return qwen4_dispatch(QWEN4_K_MTP_STAGE, &args, sizeof(args), b, 5,
MTLSizeMake(n_hc + 1u, 1, 1), MTLSizeMake(256, 1, 1), 0);
}
int ds4_gpu_qwen4_mtp_combine_tensor(
ds4_gpu_tensor *R_out, const ds4_gpu_tensor *proj, uint32_t n_embd, uint32_t n_hc) {
struct { uint32_t n_embd, n_hc; } args = { n_embd, n_hc };
qwen4_bind b[2];
if (n_embd == 0 || n_hc == 0 ||
!qwen4_bind_tensor(&b[0], proj, (uint64_t)(n_hc + 1u) * n_embd * sizeof(float), "mtp projection") ||
!qwen4_bind_tensor(&b[1], R_out, (uint64_t)n_embd * n_hc * sizeof(float), "mtp residual")) {
return 0;
}
const uint32_t n = n_embd * n_hc;
return qwen4_dispatch(QWEN4_K_MTP_COMBINE, &args, sizeof(args), b, 2,
MTLSizeMake((n + 255u) / 256u, 1, 1), MTLSizeMake(256, 1, 1), 0);
}
int ds4_gpu_qwen4_gdn_front_tensor(
ds4_gpu_tensor *qkv, ds4_gpu_tensor *state, const ds4_gpu_tensor *mixed,
ds4_gpu_tensor *ga, ds4_gpu_tensor *gb,
const void *model_map, uint64_t model_size, uint64_t conv_offset,
uint64_t alpha_offset, uint64_t beta_offset, uint64_t ssm_a_offset, uint64_t dt_bias_offset,
uint32_t weight_type, uint32_t n_tokens, uint32_t n_k_head, uint32_t n_v_head, uint32_t head_dim,
uint32_t conv_kernel, uint32_t in_dim, ds4_gpu_tensor *snap_state, uint32_t snap_tok,
ds4_gpu_tensor *snap2_state, uint32_t snap2_tok) {
const uint32_t row_bytes = qwen4_expert_row_bytes(weight_type, in_dim);
struct { uint32_t n_tokens, n_k_head, n_v_head, head_dim, conv_kernel, weight_type, in_dim, row_bytes,
snap_tok, snap2_tok, pad1, pad2; } args =
{ n_tokens, n_k_head, n_v_head, head_dim, conv_kernel, weight_type, in_dim, row_bytes,
snap_state ? snap_tok : UINT32_MAX, snap2_state ? snap2_tok : UINT32_MAX, 0, 0 };
const uint64_t conv_dim = 2ull * n_k_head * head_dim + (uint64_t)n_v_head * head_dim;
const uint64_t state_bytes = (uint64_t)(conv_kernel - 1) * conv_dim * sizeof(float);
const uint64_t proj_bytes = (uint64_t)n_v_head * row_bytes;
const uint64_t vhead_bytes = (uint64_t)n_v_head * sizeof(float);
qwen4_bind b[12];
if (n_tokens == 0 || n_k_head == 0 || head_dim % 32 != 0 || n_v_head % n_k_head != 0 ||
conv_kernel < 2 || conv_kernel > 4 || row_bytes == 0 ||
!qwen4_bind_tensor(&b[0], qkv, (uint64_t)n_tokens * conv_dim * sizeof(float), "gdn front qkv") ||
!qwen4_bind_tensor(&b[1], state, state_bytes, "gdn front conv state") ||
!qwen4_bind_weight(&b[2], model_map, model_size, conv_offset, conv_dim * conv_kernel * sizeof(float),
"gdn front conv weight") ||
!qwen4_bind_tensor(&b[3], mixed, (uint64_t)n_tokens * in_dim * sizeof(float), "gdn front input") ||
!qwen4_bind_weight(&b[4], model_map, model_size, alpha_offset, proj_bytes, "gdn front alpha") ||
!qwen4_bind_weight(&b[5], model_map, model_size, beta_offset, proj_bytes, "gdn front beta") ||
!qwen4_bind_weight(&b[6], model_map, model_size, ssm_a_offset, vhead_bytes, "gdn front ssm_a") ||
!qwen4_bind_weight(&b[7], model_map, model_size, dt_bias_offset, vhead_bytes, "gdn front dt_bias") ||
!qwen4_bind_tensor(&b[8], ga, (uint64_t)n_tokens * n_v_head * sizeof(float), "gdn front decay") ||
!qwen4_bind_tensor(&b[9], gb, (uint64_t)n_tokens * n_v_head * sizeof(float), "gdn front beta out")) {
return 0;
}
if (snap_state) {
if (!qwen4_bind_tensor(&b[10], snap_state, state_bytes, "gdn front snapshot")) return 0;
} else {
b[10] = b[1];
}
if (snap2_state) {
if (!qwen4_bind_tensor(&b[11], snap2_state, state_bytes, "gdn front snapshot 2")) return 0;
} else {
b[11] = b[1];
}
/* One threadgroup per key head: simdgroups 0/1 normalize, the rest take
* the alpha/beta rows, and every channel's conv stays on one thread, so
* the thread count only spreads the conv wider. M5 measured 1024. */
const uint64_t nth = ds4_gpu_env_u64("DS4_QWEN4_GDN_FRONT_THREADS",
ds4_gpu_device_is_m5_apple_silicon() ? 1024u : 256u, 96u, 1024u);
return qwen4_dispatch(QWEN4_K_GDN_FRONT, &args, sizeof(args), b, 12,
MTLSizeMake(n_k_head, 1, 1), MTLSizeMake((NSUInteger)(nth / 32u * 32u), 1, 1), 0);
}
int ds4_gpu_qwen4_multi_gemv_tensor(
const ds4_gpu_tensor *x, uint32_t n_tokens, uint32_t in_dim, uint32_t n_out,
ds4_gpu_tensor *const *outs, const void *model_map, uint64_t model_size,
const uint64_t *offsets, const uint32_t *types, const uint32_t *out_rows) {
struct { uint32_t n_tokens, in_dim, n_out, pad0, rows[4], types[4], row_bytes[4]; } args = {0};
qwen4_bind b[9];
if (!x || n_tokens == 0 || in_dim == 0 || (in_dim % 32) != 0 || n_out == 0 || n_out > 4 ||
!qwen4_bind_tensor(&b[0], x, (uint64_t)n_tokens * in_dim * sizeof(float), "multi gemv input")) {
return 0;
}
args.n_tokens = n_tokens;
args.in_dim = in_dim;
args.n_out = n_out;
uint32_t total = 0;
for (uint32_t i = 0; i < 4; i++) {
if (i < n_out) {
const uint32_t rb = qwen4_expert_row_bytes(types[i], in_dim);
if (rb == 0 || out_rows[i] == 0 ||
!qwen4_bind_weight(&b[1 + i], model_map, model_size, offsets[i], (uint64_t)rb * out_rows[i],
"multi gemv weight") ||
!qwen4_bind_tensor(&b[5 + i], outs[i], (uint64_t)n_tokens * out_rows[i] * sizeof(float),
"multi gemv output")) {
return 0;
}
args.rows[i] = out_rows[i];
args.types[i] = types[i];
args.row_bytes[i] = rb;
total += out_rows[i];
} else {
b[1 + i] = b[1];
b[5 + i] = b[5];
}
}
return qwen4_dispatch(QWEN4_K_MULTI_GEMV, &args, sizeof(args), b, 9,
MTLSizeMake((total + 7) / 8, n_tokens, 1), MTLSizeMake(128, 1, 1), 0);
}
static ds4_gpu_tensor *g_qwen4_dense_mm_partials;
static uint64_t g_qwen4_dense_mm_partials_bytes;
static void qwen4_batch_release_scratch(void) {
ds4_gpu_tensor_free(g_qwen4_dense_mm_partials);
ds4_gpu_tensor_free(g_qwen4_gdn_slots);
g_qwen4_dense_mm_partials = g_qwen4_gdn_slots = NULL;
g_qwen4_dense_mm_partials_bytes = 0;
for (unsigned i = 0; i < QWEN4_K_COUNT; i++) g_qwen4_pipelines[i] = nil;
}
/* Scratch for the k-split planes, allocated once and generously: the
* command buffer holds unretained references, so a scratch replaced while
* earlier dispatches of the same buffer still point at it is read after its
* release. Growing it per request was what made wide splits look wrong. */
static bool qwen4_dense_mm_partials_ensure(uint64_t bytes) {
if (g_qwen4_dense_mm_partials && g_qwen4_dense_mm_partials_bytes >= bytes) return true;
if (g_qwen4_dense_mm_partials) {
fprintf(stderr, "ds4: Qwen3.8 k-split scratch of %.1f MiB cannot grow to %.1f MiB mid-batch\n",
ds4_gpu_mib(g_qwen4_dense_mm_partials_bytes), ds4_gpu_mib(bytes));
return false;
}
const uint64_t grow = bytes > (64u << 20) ? bytes : (64u << 20);
g_qwen4_dense_mm_partials = ds4_gpu_tensor_alloc(grow);
if (!g_qwen4_dense_mm_partials) return false;
g_qwen4_dense_mm_partials_bytes = grow;
return true;
}
int ds4_gpu_qwen4_dense_mm_tensor(
ds4_gpu_tensor *out, const ds4_gpu_tensor *x,
const void *model_map, uint64_t model_size, uint64_t weight_offset, uint32_t weight_type,
uint32_t n_tokens, uint32_t in_dim, uint32_t out_rows) {
const uint32_t row_bytes = weight_type == 0u ? in_dim * 4u : weight_type == 1u ? in_dim * 2u :
qwen4_expert_row_bytes(weight_type, in_dim);
struct { uint32_t n_tokens, in_dim, out_rows, weight_type, row_bytes, n_split, pad1, pad2; } args =
{ n_tokens, in_dim, out_rows, weight_type, row_bytes, 1, 0, 0 };
qwen4_bind b[3];
if (n_tokens == 0 || row_bytes == 0 || (weight_type != 0u && weight_type != 1u && weight_type != 8u) ||
(weight_type == 8u && (in_dim % 32) != 0) || (in_dim % 8) != 0 || out_rows == 0) {
fprintf(stderr, "ds4: Qwen3.8 dense mm rejected type %u in %u out %u tokens %u\n",
weight_type, in_dim, out_rows, n_tokens);
return 0;
}
if (!qwen4_bind_weight(&b[0], model_map, model_size, weight_offset, (uint64_t)row_bytes * out_rows,
"dense mm weight") ||
!qwen4_bind_tensor(&b[1], x, (uint64_t)n_tokens * in_dim * sizeof(float), "dense mm input") ||
!qwen4_bind_tensor(&b[2], out, (uint64_t)n_tokens * out_rows * sizeof(float), "dense mm output")) {
return 0;
}
/* A projection whose output is narrow gives this grid only a few
* threadgroups when the batch is a decode step; split k until the
* machine has work, then sum the planes. A prefill chunk's token tiles
* already fill it, and its planes would not fit the scratch anyway.
* The split is chosen so the partials stay small and every threadgroup
* still walks enough k to amortize its staging. */
const uint32_t tiles = (out_rows + 31u) / 32u;
const uint32_t threadgroups = tiles * ((n_tokens + 31u) / 32u);
uint32_t n_split = 1u;
if (!ds4_gpu_env_u64("DS4_QWEN4_NO_DENSE_MM_KSPLIT", 0u, 0u, 1u)) {
const uint32_t nk = (in_dim + 31u) / 32u;
const uint32_t target = 128u;
const uint32_t want = threadgroups >= target ? 1u : (target + threadgroups - 1u) / threadgroups;
n_split = want > nk / 4u ? (nk / 4u ? nk / 4u : 1u) : want;
if (n_split > 64u) n_split = 64u;
}
args.n_split = n_split;
if (n_split <= 1u) {
return qwen4_dispatch(QWEN4_K_DENSE_MM, &args, sizeof(args), b, 3,
MTLSizeMake(tiles, (n_tokens + 31) / 32, 1), MTLSizeMake(128, 1, 1), 0);
}
const uint64_t plane = (uint64_t)n_tokens * out_rows * sizeof(float);
if (!qwen4_dense_mm_partials_ensure(plane * n_split)) return 0;
qwen4_bind bp[3];
bp[0] = b[0];
bp[1] = b[1];
if (!qwen4_bind_tensor(&bp[2], g_qwen4_dense_mm_partials, plane * n_split, "dense mm partials")) {
return 0;
}
if (!qwen4_dispatch(QWEN4_K_DENSE_MM, &args, sizeof(args), bp, 3,
MTLSizeMake(tiles, (n_tokens + 31) / 32, n_split), MTLSizeMake(128, 1, 1), 0)) {
return 0;
}
qwen4_bind br[2];
br[0] = bp[2];
br[1] = b[2];
const uint64_t n = (uint64_t)n_tokens * out_rows;
return qwen4_dispatch(QWEN4_K_DENSE_MM_REDUCE, &args, sizeof(args), br, 2,
MTLSizeMake((NSUInteger)((n + 255) / 256), 1, 1), MTLSizeMake(256, 1, 1), 0);
}
/* Decode batch of 8 or 16 rows against a Q8 matrix (kernel_qwen4_batch_mm_q8):
* the k-split gives narrow projections enough simdgroups, the partial planes
* are summed by kernel_qwen4_dense_mm_reduce. */
int ds4_gpu_qwen4_batch_mm_q8_tensor(
ds4_gpu_tensor *out, const ds4_gpu_tensor *x,
const void *model_map, uint64_t model_size, uint64_t weight_offset,
uint32_t n_tokens, uint32_t in_dim, uint32_t out_rows) {
if ((n_tokens != 8u && n_tokens != 16u) || (in_dim % 32u) != 0 || (out_rows % 32u) != 0 || in_dim == 0) return 0;
const uint32_t row_bytes = (in_dim / 32u) * 34u;
const uint32_t nk = in_dim / 32u;
const uint32_t simdgroups = out_rows / 32u;
/* enough simdgroups to cover the machine several times over, no split
* shorter than four blocks */
const uint32_t target = 640u;
uint32_t n_split = simdgroups >= target ? 1u : (target + simdgroups - 1u) / simdgroups;
if (n_split > nk / 4u) n_split = nk / 4u ? nk / 4u : 1u;
if (n_split > 16u) n_split = 16u;
struct { uint32_t n_tokens, in_dim, out_rows, weight_type, row_bytes, n_split, pad1, pad2; } args =
{ n_tokens, in_dim, out_rows, 8u, row_bytes, n_split, 0, 0 };
qwen4_bind b[3];
if (!qwen4_bind_weight(&b[0], model_map, model_size, weight_offset, (uint64_t)row_bytes * out_rows, "batch mm weight") ||
!qwen4_bind_tensor(&b[1], x, (uint64_t)n_tokens * in_dim * sizeof(float), "batch mm input") ||
!qwen4_bind_tensor(&b[2], out, (uint64_t)n_tokens * out_rows * sizeof(float), "batch mm output")) {
return 0;
}
const MTLSize grid = MTLSizeMake((out_rows + 127u) / 128u, 1, n_split);
const int kernel = n_tokens == 16u ? QWEN4_K_BATCH_MM_Q8_T2 : QWEN4_K_BATCH_MM_Q8_T1;
if (n_split <= 1u) {
return qwen4_dispatch(kernel, &args, sizeof(args), b, 3, grid, MTLSizeMake(128, 1, 1), 0);
}
const uint64_t plane = (uint64_t)n_tokens * out_rows * sizeof(float);
if (!qwen4_dense_mm_partials_ensure(plane * n_split)) return 0;
qwen4_bind bp[3] = { b[0], b[1] };
if (!qwen4_bind_tensor(&bp[2], g_qwen4_dense_mm_partials, plane * n_split, "batch mm partials")) return 0;
if (!qwen4_dispatch(kernel, &args, sizeof(args), bp, 3, grid, MTLSizeMake(128, 1, 1), 0)) return 0;
qwen4_bind br[2] = { bp[2], b[2] };
const uint64_t n = (uint64_t)n_tokens * out_rows;
return qwen4_dispatch(QWEN4_K_DENSE_MM_REDUCE, &args, sizeof(args), br, 2,
MTLSizeMake((NSUInteger)((n + 255) / 256), 1, 1), MTLSizeMake(256, 1, 1), 0);
}
/* Vision tower (metal/qwen4_vision.metal). */
typedef struct { uint32_t rows, width, grid_w, n_head, head_dim, mode; float eps, scale; } qwen4_vis_args;
static int qwen4_vis_rows(int kernel, const qwen4_vis_args *a, qwen4_bind *b, int n_bind, uint32_t rows, uint32_t y) {
const int ok = qwen4_dispatch(kernel, a, sizeof(*a), b, n_bind, MTLSizeMake(rows, y, 1),
MTLSizeMake(kernel == QWEN4_K_VIS_ATTENTION ? 32 : 256, 1, 1), 0);
if (!ok) fprintf(stderr, "ds4: Qwen3.8 vision kernel %s failed\n", qwen4_kernel_names[kernel]);
return ok;
}
static int qwen4_vis_elems(int kernel, const qwen4_vis_args *a, qwen4_bind *b, int n_bind) {
const uint64_t n = (uint64_t)a->rows * a->width;
const int ok = qwen4_dispatch(kernel, a, sizeof(*a), b, n_bind, MTLSizeMake((NSUInteger)((n + 255) / 256), 1, 1),
MTLSizeMake(256, 1, 1), 0);
if (!ok) fprintf(stderr, "ds4: Qwen3.8 vision kernel %s failed\n", qwen4_kernel_names[kernel]);
return ok;
}
static bool qwen4_vis_w(qwen4_bind *b, const void *map, uint64_t size, uint64_t off, uint64_t n, const char *what) {
return qwen4_bind_weight(b, map, size, off, n * sizeof(float), what);
}
static bool qwen4_vis_t(qwen4_bind *b, const ds4_gpu_tensor *t, uint64_t n, const char *what) {
return qwen4_bind_tensor(b, t, n * sizeof(float), what);
}
int ds4_gpu_qwen4_vision_encode(float *out, const float *patches, const float *pos_embed, uint32_t n_patches,
uint32_t grid_w, const void *map, uint64_t size, const ds4_qwen4_vision_weights *w) {
if (!out || !patches || !pos_embed || !map || !w) return 0;
const uint32_t E = w->n_embd, FF = w->n_ff, H = w->n_head, D = H ? E / H : 0, P = w->n_patch, M = w->n_merge;
const uint32_t in_patch = 3u * P * P, mm = M * M, ME = E * mm, O = w->n_out;
if (n_patches == 0 || mm == 0 || (n_patches % mm) != 0 || grid_w == 0 || (grid_w % M) != 0 ||
(n_patches % grid_w) != 0 || ((n_patches / grid_w) % M) != 0 ||
(D != 64u && D != 72u) || H * D != E || FF == 0 || O == 0 || (in_patch % 32) != 0 || (E % 32) != 0 ||
n_patches > 65535u || ds4_gpu_commands_active()) {
fprintf(stderr, "ds4: invalid Qwen3.8 vision encoder input\n");
return 0;
}
if (!g_initialized && !ds4_gpu_init()) return 0;
const uint32_t merged = n_patches / mm;
const uint64_t N = n_patches;
ds4_gpu_tensor *patch = NULL, *a0 = NULL, *a1 = NULL, *pos = NULL, *x = NULL, *tmp = NULL, *qkv = NULL;
ds4_gpu_tensor *q = NULL, *k = NULL, *v = NULL, *attn = NULL, *ffn = NULL, *m0 = NULL, *res = NULL;
qwen4_vis_args a = { n_patches, E, grid_w, H, D, 0, w->eps, 1.0f / sqrtf((float)D) };
qwen4_bind b[5];
int ok = 0;
#define QWEN4_VISION_ALLOC(name_, count_) do { \
name_ = ds4_gpu_tensor_alloc((uint64_t)(count_) * sizeof(float)); \
if (!(name_)) goto cleanup; \
} while (0)
QWEN4_VISION_ALLOC(patch, N * in_patch);
QWEN4_VISION_ALLOC(a0, N * E);
QWEN4_VISION_ALLOC(a1, N * E);
QWEN4_VISION_ALLOC(pos, N * E);
QWEN4_VISION_ALLOC(x, N * E);
QWEN4_VISION_ALLOC(tmp, N * E);
QWEN4_VISION_ALLOC(qkv, N * 3u * E);
QWEN4_VISION_ALLOC(q, N * E);
QWEN4_VISION_ALLOC(k, N * E);
QWEN4_VISION_ALLOC(v, N * E);
QWEN4_VISION_ALLOC(attn, N * E);
QWEN4_VISION_ALLOC(ffn, N * FF);
QWEN4_VISION_ALLOC(m0, (uint64_t)merged * ME);
QWEN4_VISION_ALLOC(res, (uint64_t)merged * O);
#undef QWEN4_VISION_ALLOC
if (!ds4_gpu_tensor_write(patch, 0, patches, N * in_patch * sizeof(float)) ||
!ds4_gpu_tensor_write(pos, 0, pos_embed, N * E * sizeof(float))) goto cleanup;
if (!ds4_gpu_begin_commands()) goto cleanup;
ok = ds4_gpu_qwen4_dense_mm_tensor(a0, patch, map, size, w->patch_w0, w->patch_type, n_patches, in_patch, E) &&
ds4_gpu_qwen4_dense_mm_tensor(a1, patch, map, size, w->patch_w1, w->patch_type, n_patches, in_patch, E) &&
qwen4_vis_t(&b[0], a0, N * E, "vision patch a0") &&
qwen4_vis_t(&b[1], a1, N * E, "vision patch a1") &&
qwen4_vis_w(&b[2], map, size, w->patch_b, E, "vision patch bias") &&
qwen4_vis_t(&b[3], pos, N * E, "vision pos") &&
qwen4_vis_t(&b[4], x, N * E, "vision x") &&
qwen4_vis_elems(QWEN4_K_VIS_PATCH_FINISH, &a, b, 5);
for (uint32_t il = 0; ok && il < DS4_QWEN4_VISION_LAYERS; il++) {
const ds4_qwen4_vision_layer_weights *l = &w->layer[il];
a.rows = n_patches; a.width = E;
ok = qwen4_vis_t(&b[0], x, N * E, "vision x") &&
qwen4_vis_w(&b[1], map, size, l->ln1_w, E, "vision ln1") &&
qwen4_vis_w(&b[2], map, size, l->ln1_b, E, "vision ln1 bias") &&
qwen4_vis_t(&b[3], tmp, N * E, "vision tmp") &&
qwen4_vis_rows(QWEN4_K_VIS_LAYERNORM, &a, b, 4, n_patches, 1) &&
ds4_gpu_qwen4_dense_mm_tensor(qkv, tmp, map, size, l->qkv_w, l->qkv_type, n_patches, E, 3u * E) &&
qwen4_vis_t(&b[0], qkv, N * 3u * E, "vision qkv") &&
qwen4_vis_w(&b[1], map, size, l->qkv_b, 3u * E, "vision qkv bias") &&
qwen4_vis_t(&b[2], q, N * E, "vision q") &&
qwen4_vis_t(&b[3], k, N * E, "vision k") &&
qwen4_vis_t(&b[4], v, N * E, "vision v") &&
qwen4_vis_rows(QWEN4_K_VIS_QKV_ROPE, &a, b, 5, n_patches, 1) &&
qwen4_vis_t(&b[0], q, N * E, "vision q") &&
qwen4_vis_t(&b[1], k, N * E, "vision k") &&
qwen4_vis_t(&b[2], v, N * E, "vision v") &&
qwen4_vis_t(&b[3], attn, N * E, "vision attn") &&
qwen4_vis_rows(QWEN4_K_VIS_ATTENTION, &a, b, 4, n_patches, H) &&
ds4_gpu_qwen4_dense_mm_tensor(tmp, attn, map, size, l->out_w, l->out_type, n_patches, E, E) &&
qwen4_vis_t(&b[0], x, N * E, "vision x") &&
qwen4_vis_t(&b[1], tmp, N * E, "vision tmp") &&
qwen4_vis_w(&b[2], map, size, l->out_b, E, "vision out bias") &&
qwen4_vis_elems(QWEN4_K_VIS_BIAS_RESIDUAL, &a, b, 3) &&
qwen4_vis_t(&b[0], x, N * E, "vision x") &&
qwen4_vis_w(&b[1], map, size, l->ln2_w, E, "vision ln2") &&
qwen4_vis_w(&b[2], map, size, l->ln2_b, E, "vision ln2 bias") &&
qwen4_vis_t(&b[3], tmp, N * E, "vision tmp") &&
qwen4_vis_rows(QWEN4_K_VIS_LAYERNORM, &a, b, 4, n_patches, 1) &&
ds4_gpu_qwen4_dense_mm_tensor(ffn, tmp, map, size, l->up_w, l->up_type, n_patches, E, FF);
if (!ok) break;
a.width = FF; a.mode = 0;
ok = qwen4_vis_t(&b[0], ffn, N * FF, "vision ffn") &&
qwen4_vis_w(&b[1], map, size, l->up_b, FF, "vision up bias") &&
qwen4_vis_elems(QWEN4_K_VIS_BIAS_ACT, &a, b, 2) &&
ds4_gpu_qwen4_dense_mm_tensor(tmp, ffn, map, size, l->down_w, l->down_type, n_patches, FF, E);
a.width = E;
ok = ok && qwen4_vis_t(&b[0], x, N * E, "vision x") &&
qwen4_vis_t(&b[1], tmp, N * E, "vision tmp") &&
qwen4_vis_w(&b[2], map, size, l->down_b, E, "vision down bias") &&
qwen4_vis_elems(QWEN4_K_VIS_BIAS_RESIDUAL, &a, b, 3);
}
if (ok) {
a.rows = n_patches; a.width = E;
ok = qwen4_vis_t(&b[0], x, N * E, "vision x") &&
qwen4_vis_w(&b[1], map, size, w->post_ln_w, E, "vision post ln") &&
qwen4_vis_w(&b[2], map, size, w->post_ln_b, E, "vision post ln bias") &&
qwen4_vis_t(&b[3], tmp, N * E, "vision tmp") &&
qwen4_vis_rows(QWEN4_K_VIS_LAYERNORM, &a, b, 4, n_patches, 1) &&
ds4_gpu_qwen4_dense_mm_tensor(m0, tmp, map, size, w->mm0_w, w->mm0_type, merged, ME, ME);
a.rows = merged; a.width = ME; a.mode = 1;
ok = ok && qwen4_vis_t(&b[0], m0, (uint64_t)merged * ME, "vision merger") &&
qwen4_vis_w(&b[1], map, size, w->mm0_b, ME, "vision merger bias") &&
qwen4_vis_elems(QWEN4_K_VIS_BIAS_ACT, &a, b, 2) &&
ds4_gpu_qwen4_dense_mm_tensor(res, m0, map, size, w->mm2_w, w->mm2_type, merged, ME, O);
a.width = O; a.mode = 2;
ok = ok && qwen4_vis_t(&b[0], res, (uint64_t)merged * O, "vision out") &&
qwen4_vis_w(&b[1], map, size, w->mm2_b, O, "vision out bias") &&
qwen4_vis_elems(QWEN4_K_VIS_BIAS_ACT, &a, b, 2);
}
if (!ds4_gpu_end_commands()) ok = 0;
if (ok) ok = ds4_gpu_tensor_read(res, 0, out, (uint64_t)merged * O * sizeof(float));
cleanup:
ds4_gpu_tensor_free(res);
ds4_gpu_tensor_free(m0);
ds4_gpu_tensor_free(ffn);
ds4_gpu_tensor_free(attn);
ds4_gpu_tensor_free(v);
ds4_gpu_tensor_free(k);
ds4_gpu_tensor_free(q);
ds4_gpu_tensor_free(qkv);
ds4_gpu_tensor_free(tmp);
ds4_gpu_tensor_free(x);
ds4_gpu_tensor_free(pos);
ds4_gpu_tensor_free(a1);
ds4_gpu_tensor_free(a0);
ds4_gpu_tensor_free(patch);
return ok;
}
int ds4_gpu_qwen4_hc_lo_act_tensor(ds4_gpu_tensor *lo_act, const ds4_gpu_tensor *lo,
uint32_t n_tokens, uint32_t n_hc, uint32_t n_rank) {
struct { uint32_t n_tokens, n_embd, n_hc, n_rank; } args = { n_tokens, 0, n_hc, n_rank };
qwen4_bind b[2];
const uint64_t n = (uint64_t)n_tokens * n_rank;
if (n == 0 || n_hc == 0 ||
!qwen4_bind_tensor(&b[0], lo, n * sizeof(float), "hc lo") ||
!qwen4_bind_tensor(&b[1], lo_act, n * sizeof(float), "hc lo act")) {
return 0;
}
return qwen4_dispatch(QWEN4_K_HC_LO_ACT, &args, sizeof(args), b, 2,
MTLSizeMake((n + 255) / 256, 1, 1), MTLSizeMake(256, 1, 1), 0);
}
int ds4_gpu_qwen4_hc_mix_rows_tensor(ds4_gpu_tensor *mixed, const ds4_gpu_tensor *u, const ds4_gpu_tensor *xn,
uint32_t n_tokens, uint32_t n_embd, uint32_t n_hc) {
struct { uint32_t n_tokens, n_embd, n_hc, n_rank; } args = { n_tokens, n_embd, n_hc, 0 };
qwen4_bind b[3];
const uint64_t dim = (uint64_t)n_embd * n_hc;
if (n_tokens == 0 || n_embd == 0 || n_hc == 0 ||
!qwen4_bind_tensor(&b[0], u, (uint64_t)n_tokens * dim * sizeof(float), "hc u") ||
!qwen4_bind_tensor(&b[1], xn, (uint64_t)n_tokens * dim * sizeof(float), "hc xn") ||
!qwen4_bind_tensor(&b[2], mixed, (uint64_t)n_tokens * n_embd * sizeof(float), "hc mixed")) {
return 0;
}
return qwen4_dispatch(QWEN4_K_HC_MIX_ROWS, &args, sizeof(args), b, 3,
MTLSizeMake((n_embd + 255) / 256, n_tokens, 1), MTLSizeMake(256, 1, 1), 0);
}