/* SPDX-License-Identifier: AGPL-3.0-or-later */ /* Copyright (C) 2026 David (Corey) Herrera */ /* * fullmodel.cu — FULL RWKV-x training step on the K80 (sm_37), device-resident. * * The Phase-2 completion piece: embedding gather (fwd + scatter-add bwd), LM head * (fwd + bwd, cuBLAS), fused log-softmax cross-entropy (fwd + bwd, double- * accumulated LSE/normalizer), and the WHOLE-model assembly: * * x0 = emb[token] * for each interleaved block (RWKV-7 time-mix OR MoBA sparse-attention): * x = block(x) (ln1 -> core -> +x -> ln2 -> cm -> +x1) * h = ln_out(x) ; logits = h @ head ; loss = mean_t CE(logits[t], target[t]) * * GPU counterpart of the CPU oracle rx_pretrain_forward / rx_pretrain_backward * (rwkvx/rx_pretrain.c), B=1 single sequence. Head is NOT weight-tied to the * embedding (foundry stores rwkv.head.weight separately) — each gets its own grad. * * REUSE: this is ONE translation unit that includes every already-merged block * kernel via a rename/undef shim, so all their device helpers (tm_fwd_device, * k_ln_fwd/bwd, k_cm_*, moba_k_*, the rm_gemm cuBLAS helper, the WKV7 recurrence) * are called directly. Weights upload ONCE into resident device structs; the * backward recomputes each block's sub-forward from the cached residual stream, * exactly as the CPU oracle does. No edits to the merged component files. * * Build (K80 host, GPU 0, standalone — NOT the fc_cuda_ptx.h embed pipeline): * gcc-11 ... -c rwkvx/rx_pretrain.c -o rx_pretrain_fm.o * nvcc -arch=sm_37 -ccbin gcc-11 -isystem cuda_kernels/nvcc_shim -I. \ * test_fullmodel_cuda.cu rx_pretrain_fm.o -lcublas -lm */ #include #include #include #include #include #include #include /* ============================ include shim ============================ * Pull all merged block kernels into this single TU. Only nblk / rm_gemm * (static) and TPB / TCK / TBK (macros) collide across files; every __global__ * kernel name is already unique. Rename the two statics per file and undef the * macros between includes. wkv7.cu is pulled in by timemix.cu (only). */ #define nblk nblk_tm #define rm_gemm rm_gemm_tm #include "cuda_kernels/timemix.cu" #undef nblk #undef rm_gemm #undef TPB #undef TCK #undef TBK #undef RWDECAY_COEFF #define nblk nblk_cm #define rm_gemm rm_gemm_cm #include "cuda_kernels/channelmix.cu" #undef nblk #undef rm_gemm #undef TPB #undef TCK #undef TBK #define nblk nblk_ln #include "cuda_kernels/layernorm.cu" #undef nblk #undef TPB #define rm_gemm rm_gemm_moba #include "cuda_kernels/moba_attn.cu" #undef rm_gemm #include "rwkvx/rx_pretrain.h" /* guarded; brings the oracle decls + structs */ /* ============================ fullmodel-local kernels ============================ */ #define FM_TPB 256 static inline int fm_nblk(size_t n){ return (int)((n+FM_TPB-1)/FM_TPB); } #define FCK(call) do{ cudaError_t e_=(call); if(e_!=cudaSuccess){ \ fprintf(stderr,"CUDA %s:%d %s\n",__FILE__,__LINE__,cudaGetErrorString(e_)); exit(1);} }while(0) #define FBK(call) do{ cublasStatus_t s_=(call); if(s_!=CUBLAS_STATUS_SUCCESS){ \ fprintf(stderr,"cuBLAS %s:%d status %d\n",__FILE__,__LINE__,(int)s_); exit(1);} }while(0) __global__ void fm_k_emb_fwd(const float*emb,const int*tok,float*out,int T,int C){ size_t g=(size_t)blockIdx.x*blockDim.x+threadIdx.x; size_t tot=(size_t)T*C; if(g>=tot)return; int i=(int)(g%C); int t=(int)(g/C); out[g]=emb[(size_t)tok[t]*C+i]; } /* scatter-add: one thread per (t,i) atomicAdds into the token's embedding row. * Row collisions (a token repeated in the sequence) are resolved by atomicAdd. */ __global__ void fm_k_emb_bwd(const int*tok,const float*dout,float*demb,int T,int C){ size_t g=(size_t)blockIdx.x*blockDim.x+threadIdx.x; size_t tot=(size_t)T*C; if(g>=tot)return; int i=(int)(g%C); int t=(int)(g/C); atomicAdd(&demb[(size_t)tok[t]*C+i], dout[g]); } __global__ void fm_k_add(const float*a,const float*b,float*o,size_t n){ size_t g=(size_t)blockIdx.x*blockDim.x+threadIdx.x; if(g>=n)return; o[g]=a[g]+b[g]; } __global__ void fm_k_addi(float*a,const float*b,size_t n){ size_t g=(size_t)blockIdx.x*blockDim.x+threadIdx.x; if(g>=n)return; a[g]+=b[g]; } /* fused log-softmax cross-entropy, one thread per token (double LSE + normalizer). * Writes dlogits=(softmax-onehot)/nv (ignored rows zeroed) and per-token loss * (lse - logit[target]) into loss_t[T] (double, host reduces -> /nv). sm_37 has * no double atomicAdd, so we avoid a global reduction entirely. */ __global__ void fm_k_ce_bwd(const float*logits,const int*targets,float*dlogits, double*loss_t,int T,int V,int ignore_index,int nv){ int t=blockIdx.x*blockDim.x+threadIdx.x; if(t>=T)return; int tg=targets[t]; const float*lt=logits+(size_t)t*V; float*dl=dlogits+(size_t)t*V; if(ignore_index>=0 && tg==ignore_index){ for(int j=0;jmx)mx=d; } double se=0.0; for(int j=0;j0 ? 1.0/(double)nv : 0.0; for(int j=0;jgname[m->ng],48,"%s",name); m->gptr[m->ng]=d; m->gn[m->ng]=n; m->ng++; return d; } static float* fm_gfind(fm_model*m,const char*name,int*n){ for(int i=0;ing;i++) if(!strcmp(m->gname[i],name)){ if(n)*n=m->gn[i]; return m->gptr[i]; } return NULL; } /* upload a whole model from the CPU oracle's rx_ptmodel (same names/shapes). */ static fm_model* fm_build(rx_ptmodel *cm, int T){ rx_pt_config pc=rx_ptmodel_config(cm); fm_model*m=(fm_model*)calloc(1,sizeof(fm_model)); m->C=pc.n_embd; m->V=pc.vocab_size; m->nH=pc.n_head; m->N=pc.head_size; m->F=pc.ffn_dim; m->Dw=pc.Dw; m->Da=pc.Da; m->Dg=pc.Dg; m->Dv=pc.Dv; m->n_rwkv=pc.n_rwkv_layer; m->n_attn=pc.n_attn_layer; m->n_blocks=m->n_rwkv+m->n_attn; m->T=T; int C=m->C,V=m->V,F=m->F; FBK(cublasCreate(&m->H)); /* opt #2: one resident device scratch slab, sized generously for this config's * per-step forward+backward peak (B=1 in fm_forward/fm_backward). Transient * buffers bump from here and are reclaimed en-masse each fm_forward/backward. */ { size_t TC=(size_t)T*C; size_t scsz=(size_t)T*(size_t)m->nH*m->N*m->N; size_t cap=(size_t)384*TC*sizeof(float)+(size_t)48*scsz*sizeof(float)+(32u<<20); fmA_init(cap); } /* rebuild the interleave (mirror rx_model_new_random) */ m->btype=(int*)calloc(m->n_blocks,sizeof(int)); m->bidx=(int*)calloc(m->n_blocks,sizeof(int)); if(m->n_attn==0){ for(int i=0;in_rwkv;i++){m->btype[i]=0;m->bidx[i]=i;} } else{ int interval=m->n_rwkv/m->n_attn,pos=0; for(int a=0;an_attn;a++){ int s=a*interval,e=(a+1)*interval; for(int j=s;jbtype[pos]=0;m->bidx[pos]=j;pos++;} m->btype[pos]=1;m->bidx[pos]=a;pos++; } for(int j=m->n_attn*interval;jn_rwkv;j++){m->btype[pos]=0;m->bidx[pos]=j;pos++;} } /* globals */ { int n; float*d,*g; const char*nm; /* find emb/head/ln_out by name in the registry to get their host data */ m->emb=NULL; m->head=NULL; m->lnout_w=NULL; m->lnout_b=NULL; for(int i=0;iemb=fm_up(d,(size_t)V*C); else if(!strcmp(nm,"head")) m->head=fm_up(d,(size_t)C*V); else if(!strcmp(nm,"ln_out_w")) m->lnout_w=fm_up(d,C); else if(!strcmp(nm,"ln_out_b")) m->lnout_b=fm_up(d,C); } m->g_emb=fm_upg(m,"emb",V*C); m->g_head=fm_upg(m,"head",C*V); m->g_lnout_w=fm_upg(m,"ln_out_w",C); m->g_lnout_b=fm_upg(m,"ln_out_b",C); } /* helper to fetch a host param pointer by name */ #define HOSTP(dst,nm_) do{ const char*_n; float*_d,*_g; int _k; dst=NULL; \ for(int _i=0;_irw=(fm_rwkv_blk*)calloc(m->n_rwkv>0?m->n_rwkv:1,sizeof(fm_rwkv_blk)); for(int i=0;in_rwkv;i++){ fm_rwkv_blk*B=&m->rw[i]; const rx_block_params*bp=rx_ptmodel_rwkv(cm,i); B->tmP=bp->tm; B->cmP=bp->cm; /* host mirror (dims/layer_id + host ptrs) */ /* upload time-mix weights via the merged uploader */ if(tm_upload_weights(&bp->tm,&B->D)){ fprintf(stderr,"tm upload fail\n"); exit(1); } /* time-mix grads: register each by the oracle name */ char nm[64]; #define TMG(field,cnt) do{ snprintf(nm,64,"rwkv%d." #field,i); B->Gd.field=fm_upg(m,nm,cnt); }while(0) TMG(x_r,C);TMG(x_k,C);TMG(x_v,C);TMG(x_w,C);TMG(x_a,C);TMG(x_g,C); TMG(receptance_w,C*C);TMG(key_w,C*C);TMG(value_w,C*C);TMG(output_w,C*C); TMG(w0,C);TMG(w1,C*m->Dw);TMG(w2,m->Dw*C); TMG(a0,C);TMG(a1,C*m->Da);TMG(a2,m->Da*C); TMG(g1,C*m->Dg);TMG(g2,m->Dg*C); if(i>0){ TMG(v0,C);TMG(v1,C*m->Dv);TMG(v2,m->Dv*C); } else { B->Gd.v0=B->Gd.v1=B->Gd.v2=NULL; } TMG(k_k,C);TMG(k_a,C);TMG(r_k,C);TMG(ln_x_w,C);TMG(ln_x_b,C); #undef TMG /* channel-mix weights + grads */ float*hp; HOSTP(hp,(snprintf(nm,64,"rwkv%d.ffn_x_k",i),nm)); B->cm.x_k=fm_up(hp,C); HOSTP(hp,(snprintf(nm,64,"rwkv%d.ffn_key_w",i),nm)); B->cm.key_w=fm_up(hp,(size_t)C*F); HOSTP(hp,(snprintf(nm,64,"rwkv%d.ffn_value_w",i),nm)); B->cm.value_w=fm_up(hp,(size_t)F*C); snprintf(nm,64,"rwkv%d.ffn_x_k",i); B->cmG.x_k=fm_upg(m,nm,C); snprintf(nm,64,"rwkv%d.ffn_key_w",i); B->cmG.key_w=fm_upg(m,nm,C*F); snprintf(nm,64,"rwkv%d.ffn_value_w",i); B->cmG.value_w=fm_upg(m,nm,F*C); /* ln1/ln2 */ HOSTP(hp,(snprintf(nm,64,"rwkv%d.ln1_w",i),nm)); B->ln1_w=fm_up(hp,C); HOSTP(hp,(snprintf(nm,64,"rwkv%d.ln1_b",i),nm)); B->ln1_b=fm_up(hp,C); HOSTP(hp,(snprintf(nm,64,"rwkv%d.ln2_w",i),nm)); B->ln2_w=fm_up(hp,C); HOSTP(hp,(snprintf(nm,64,"rwkv%d.ln2_b",i),nm)); B->ln2_b=fm_up(hp,C); snprintf(nm,64,"rwkv%d.ln1_w",i); B->g_ln1_w=fm_upg(m,nm,C); snprintf(nm,64,"rwkv%d.ln1_b",i); B->g_ln1_b=fm_upg(m,nm,C); snprintf(nm,64,"rwkv%d.ln2_w",i); B->g_ln2_w=fm_upg(m,nm,C); snprintf(nm,64,"rwkv%d.ln2_b",i); B->g_ln2_b=fm_upg(m,nm,C); /* point the host cm mirror at oracle host pointers (dims only used) */ B->cmP=bp->cm; } /* attn blocks */ m->at=(fm_attn_blk*)calloc(m->n_attn>0?m->n_attn:1,sizeof(fm_attn_blk)); for(int i=0;in_attn;i++){ fm_attn_blk*A=&m->at[i]; const rx_attn_params*ap=rx_ptmodel_attn(cm,i); A->P=*ap; char nm[64]; float*hp; HOSTP(hp,(snprintf(nm,64,"attn%d.ln1_w",i),nm)); A->ln1_w=fm_up(hp,C); HOSTP(hp,(snprintf(nm,64,"attn%d.ln1_b",i),nm)); A->ln1_b=fm_up(hp,C); HOSTP(hp,(snprintf(nm,64,"attn%d.receptance_w",i),nm)); A->Wq=fm_up(hp,(size_t)C*C); HOSTP(hp,(snprintf(nm,64,"attn%d.key_w",i),nm)); A->Wk=fm_up(hp,(size_t)C*C); HOSTP(hp,(snprintf(nm,64,"attn%d.value_w",i),nm)); A->Wv=fm_up(hp,(size_t)C*C); HOSTP(hp,(snprintf(nm,64,"attn%d.output_w",i),nm)); A->Wo=fm_up(hp,(size_t)C*C); HOSTP(hp,(snprintf(nm,64,"attn%d.ln2_w",i),nm)); A->ln2_w=fm_up(hp,C); HOSTP(hp,(snprintf(nm,64,"attn%d.ln2_b",i),nm)); A->ln2_b=fm_up(hp,C); HOSTP(hp,(snprintf(nm,64,"attn%d.ffn_x_k",i),nm)); A->cm.x_k=fm_up(hp,C); HOSTP(hp,(snprintf(nm,64,"attn%d.ffn_key_w",i),nm)); A->cm.key_w=fm_up(hp,(size_t)C*F); HOSTP(hp,(snprintf(nm,64,"attn%d.ffn_value_w",i),nm)); A->cm.value_w=fm_up(hp,(size_t)F*C); /* grads (registered) */ snprintf(nm,64,"attn%d.ln1_w",i); fm_upg(m,nm,C); snprintf(nm,64,"attn%d.ln1_b",i); fm_upg(m,nm,C); snprintf(nm,64,"attn%d.receptance_w",i); fm_upg(m,nm,C*C); snprintf(nm,64,"attn%d.key_w",i); fm_upg(m,nm,C*C); snprintf(nm,64,"attn%d.value_w",i); fm_upg(m,nm,C*C); snprintf(nm,64,"attn%d.output_w",i); fm_upg(m,nm,C*C); snprintf(nm,64,"attn%d.ln2_w",i); fm_upg(m,nm,C); snprintf(nm,64,"attn%d.ln2_b",i); fm_upg(m,nm,C); snprintf(nm,64,"attn%d.ffn_x_k",i); fm_upg(m,nm,C); snprintf(nm,64,"attn%d.ffn_key_w",i); fm_upg(m,nm,C*F); snprintf(nm,64,"attn%d.ffn_value_w",i); fm_upg(m,nm,F*C); } #undef HOSTP /* activations */ size_t TC=(size_t)T*C; FCK(cudaMalloc(&m->tok,(size_t)T*sizeof(int))); FCK(cudaMalloc(&m->dtg,(size_t)T*sizeof(int))); /* opt#3 resident targets */ FCK(cudaMalloc(&m->block_in,(size_t)(m->n_blocks+1)*TC*sizeof(float))); FCK(cudaMalloc(&m->v_first,TC*sizeof(float))); FCK(cudaMalloc(&m->lnout,TC*sizeof(float))); FCK(cudaMalloc(&m->logits,(size_t)T*V*sizeof(float))); FCK(cudaMalloc(&m->loss_t,(size_t)T*sizeof(double))); return m; } static void fm_zero_grads(fm_model*m){ for(int i=0;ing;i++) FCK(cudaMemsetAsync(m->gptr[i],0,(size_t)m->gn[i]*sizeof(float),g_fm_stream)); } /* ============================ channel-mix device (resident) ============================ */ /* forward: sh=shift(in); fk=sh@Wk; ka=relu(fk)^2; out=ka@Wv. (x_prev_init=NULL) */ static void fm_cm_fwd(fm_model*m,const fm_cmw*W,const rx_cm_params*P,const float*in,float*out,int T){ int C=P->c,F=P->ffn_dim; size_t BT=(size_t)T, tot=BT*C, totF=BT*F; float*sh=(float*)fmA_alloc(tot*4),*fk=(float*)fmA_alloc(totF*4),*ka=(float*)fmA_alloc(totF*4); k_cm_shift<<>>(in,NULL,W->x_k,sh,1,T,C); FBK(rm_gemm_cm(m->H,0,0,T,F,C,1.0f,sh,W->key_w,0.0f,fk)); k_sqrelu<<>>(fk,ka,totF); FBK(rm_gemm_cm(m->H,0,0,T,C,F,1.0f,ka,W->value_w,0.0f,out)); fmA_free(sh);fmA_free(fk);fmA_free(ka); } /* backward: dy -> dx + grads(G). recomputes sh,fk,ka. grads WRITTEN(=). */ static void fm_cm_bwd(fm_model*m,const fm_cmw*W,fm_cmw*G,const rx_cm_params*P, const float*in,const float*dy,float*dx,int T){ int C=P->c,F=P->ffn_dim; size_t BT=(size_t)T,tot=BT*C,totF=BT*F; float*sh=(float*)fmA_alloc(tot*4),*fk=(float*)fmA_alloc(totF*4),*ka=(float*)fmA_alloc(totF*4); float*dka=(float*)fmA_alloc(totF*4),*dfk=(float*)fmA_alloc(totF*4),*dsh=(float*)fmA_alloc(tot*4); k_cm_shift<<>>(in,NULL,W->x_k,sh,1,T,C); FBK(rm_gemm_cm(m->H,0,0,T,F,C,1.0f,sh,W->key_w,0.0f,fk)); k_sqrelu<<>>(fk,ka,totF); FBK(rm_gemm_cm(m->H,0,1,T,F,C,1.0f,dy,W->value_w,0.0f,dka)); /* dka=dy@Wv^T */ FBK(rm_gemm_cm(m->H,1,0,F,C,T,1.0f,ka,dy,0.0f,G->value_w)); /* G_Wv=ka^T@dy */ k_sqrelu_bwd<<>>(fk,dka,dfk,totF); FBK(rm_gemm_cm(m->H,0,1,T,C,F,1.0f,dfk,W->key_w,0.0f,dsh)); /* dsh=dfk@Wk^T */ FBK(rm_gemm_cm(m->H,1,0,C,F,T,1.0f,sh,dfk,0.0f,G->key_w)); /* G_Wk=sh^T@dfk */ FCK(cudaMemsetAsync(G->x_k,0,(size_t)C*4,g_fm_stream)); k_cm_shift_bwd<<>>(in,NULL,dsh,W->x_k,G->x_k,dx,NULL,1,T,C); fmA_free(sh);fmA_free(fk);fmA_free(ka);fmA_free(dka);fmA_free(dfk);fmA_free(dsh); } /* ============================ time-mix backward core (resident) ============================ * Device-resident port of timemix_backward_cuda's phase-2..4 + token-shift, on a * PRE-FILLED forward cache c and resident weights D / grads Gd (zeroed by caller). * x=h1, ddy=dx1, dvf=v_first values, ddx_out=dh1. xprev/d_xprev = NULL (B=1). */ static void fm_tm_bwd_core(fm_model*m,const rx_tm_params*P,const tm_dw*D,tm_dw*Gd, tm_cache_d*c,const float*x,const float*ddy,const float*dvf, float*ddx_out,const float*d_vf_in,float*d_vf_out,int T){ int C=P->c,nH=P->n_head,N=P->head_size,Dw=P->Dw,Da=P->Da,Dg=P->Dg,Dv=P->Dv,L=P->layer_id; size_t BT=(size_t)T, tot=BT*C; float *dtg,*dyb,*dg_post,*dy_wkv,*dr_post,*dkmod_post,*dvf_post; float *gR,*gW,*gK,*gV,*gA,*gB,*dr_t,*dkmod_t,*dvf_t; float *dkk,*dav,*dkvec,*dvvec,*dtk,*d_oa,*d_ow,*d_ov; float *dsr,*dsk,*dsv,*dsw,*dsa,*dsg; float *d_la,*d_tw,*d_lw,*d_sg,*d_lg,*d_lv=NULL,*d_vf_loc=NULL; #define S(p,n) do{ p=(float*)fmA_alloc((size_t)(n)*sizeof(float)); }while(0) S(dtg,tot);S(dyb,tot);S(dg_post,tot);S(dy_wkv,tot);S(dr_post,tot);S(dkmod_post,tot);S(dvf_post,tot); S(gR,tot);S(gW,tot);S(gK,tot);S(gV,tot);S(gA,tot);S(gB,tot); S(dr_t,tot);S(dkmod_t,tot);S(dvf_t,tot); S(dkk,tot);S(dav,tot);S(dkvec,tot);S(dvvec,tot);S(dtk,tot);S(d_oa,tot);S(d_ow,tot);S(d_ov,tot); S(dsr,tot);S(dsk,tot);S(dsv,tot);S(dsw,tot);S(dsa,tot);S(dsg,tot); S(d_la,BT*Da);S(d_tw,BT*Dw);S(d_lw,BT*Dw);S(d_sg,BT*Dg);S(d_lg,BT*Dg); if(Dv>0) S(d_lv,BT*Dv); if(L>0){ S(d_vf_loc,tot); FCK(cudaMemsetAsync(d_vf_loc,0,tot*4,g_fm_stream)); } #undef S /* PHASE 2 */ FBK(rm_gemm_tm(m->H,0,1,T,C,C,1.0f,ddy,D->output_w,0.0f,dtg)); FBK(rm_gemm_tm(m->H,1,0,C,C,T,1.0f,c->tg,ddy,0.0f,Gd->output_w)); k_split<<>>(dtg,c->gvec,c->yb,dyb,dg_post,tot); k_bonus_bwd<<>>(dyb,c->V,c->R,c->K,D->r_k,c->bonus,dvf_post,dr_post,dkmod_post,Gd->r_k,nH,N,BT); k_groupnorm_bwd<<>>(c->y,dyb,D->ln_x_w,c->gn_mean,c->gn_rstd,dy_wkv,Gd->ln_x_w,Gd->ln_x_b,nH,N,BT); /* PHASE 3: WKV7 */ { size_t stsz=(size_t)nH*N*N, sasz=(size_t)T*nH*N, scsz=(size_t)T*stsz; float*SC=(float*)fmA_alloc(scsz*4); float*SA=(float*)fmA_alloc(sasz*4); size_t shf=((size_t)N*N+7*N)*sizeof(float); wkv7_fwd_kernel<<>>(c->R,c->W,c->K,c->V,c->A,c->Bb,NULL,NULL,NULL,SC,SA,1,T,nH,N); size_t shb=(2*(size_t)N*N+9*N)*sizeof(float); wkv7_bwd_kernel<<>>(c->R,c->W,c->K,c->V,c->A,c->Bb,dy_wkv,SC,SA,gR,gW,gK,gV,gA,gB,NULL,1,T,nH,N); fmA_free(SC);fmA_free(SA); } /* PHASE 4 */ k_p4_seed<<>>(gR,dr_post,gK,dkmod_post,gV,dvf_post,dr_t,dkmod_t,dvf_t,tot); k_comp_keymod<<>>(gA,gB,c->avec,c->kk,c->kvec,D->k_a,dkmod_t,dkk,dav,dkvec,Gd->k_a,C,tot); k_l2norm_bwd<<>>(c->tk,dkk,dtk,nH,N,BT); k_tk_bwd<<>>(dtk,D->k_k,c->kvec,dkvec,Gd->k_k,C,tot); k_avec_bwd<<>>(dav,c->avec,d_oa,Gd->a0,C,tot); FBK(rm_gemm_tm(m->H,0,1,T,Da,C,1.0f,d_oa,D->a2,0.0f,d_la)); FBK(rm_gemm_tm(m->H,1,0,Da,C,T,1.0f,c->la,d_oa,0.0f,Gd->a2)); FBK(rm_gemm_tm(m->H,0,1,T,C,Da,1.0f,d_la,D->a1,0.0f,dsa)); FBK(rm_gemm_tm(m->H,1,0,C,Da,T,1.0f,c->sha,d_la,0.0f,Gd->a1)); k_wdecay_bwd<<>>(gW,c->W,c->sigw,d_ow,Gd->w0,C,tot); FBK(rm_gemm_tm(m->H,0,1,T,Dw,C,1.0f,d_ow,D->w2,0.0f,d_tw)); FBK(rm_gemm_tm(m->H,1,0,Dw,C,T,1.0f,c->tw,d_ow,0.0f,Gd->w2)); k_tanhderiv<<>>(d_tw,c->tw,d_lw,BT*Dw); FBK(rm_gemm_tm(m->H,0,1,T,C,Dw,1.0f,d_lw,D->w1,0.0f,dsw)); FBK(rm_gemm_tm(m->H,1,0,C,Dw,T,1.0f,c->shw,d_lw,0.0f,Gd->w1)); FBK(rm_gemm_tm(m->H,0,1,T,Dg,C,1.0f,dg_post,D->g2,0.0f,d_sg)); FBK(rm_gemm_tm(m->H,1,0,Dg,C,T,1.0f,c->sg,dg_post,0.0f,Gd->g2)); k_sigderiv<<>>(d_sg,c->sg,d_lg,BT*Dg); FBK(rm_gemm_tm(m->H,0,1,T,C,Dg,1.0f,d_lg,D->g1,0.0f,dsg)); FBK(rm_gemm_tm(m->H,1,0,C,Dg,T,1.0f,c->shg,d_lg,0.0f,Gd->g1)); if(L==0){ k_vvec0<<>>(dvf_t,d_vf_in,dvvec,tot); } else{ k_vvec1<<>>(dvf_t,c->blend,dvf,c->vvec,D->v0,dvvec,d_vf_loc,d_ov,Gd->v0,C,tot); FBK(rm_gemm_tm(m->H,0,1,T,Dv,C,1.0f,d_ov,D->v2,0.0f,d_lv)); FBK(rm_gemm_tm(m->H,1,0,Dv,C,T,1.0f,c->lv,d_ov,0.0f,Gd->v2)); FBK(rm_gemm_tm(m->H,0,1,T,C,Dv,1.0f,d_lv,D->v1,0.0f,dsv)); FBK(rm_gemm_tm(m->H,1,0,C,Dv,T,1.0f,c->shv,d_lv,0.0f,Gd->v1)); } FBK(rm_gemm_tm(m->H,0,1,T,C,C,1.0f,dr_t,D->receptance_w,0.0f,dsr)); FBK(rm_gemm_tm(m->H,1,0,C,C,T,1.0f,c->shr,dr_t,0.0f,Gd->receptance_w)); FBK(rm_gemm_tm(m->H,0,1,T,C,C,1.0f,dkvec,D->key_w,0.0f,dsk)); FBK(rm_gemm_tm(m->H,1,0,C,C,T,1.0f,c->shk,dkvec,0.0f,Gd->key_w)); FBK(rm_gemm_tm(m->H,0,1,T,C,C,1.0f,dvvec,D->value_w,(L>0)?1.0f:0.0f,dsv)); FBK(rm_gemm_tm(m->H,1,0,C,C,T,1.0f,c->shv,dvvec,0.0f,Gd->value_w)); /* token-shift backward (dxprev=NULL) */ k_tokenshift_bwd<<>>(x,NULL,dsr,dsk,dsv,dsw,dsa,dsg, D->x_r,D->x_k,D->x_v,D->x_w,D->x_a,D->x_g, Gd->x_r,Gd->x_k,Gd->x_v,Gd->x_w,Gd->x_a,Gd->x_g, ddx_out,NULL,1,T,C); if(L>0 && d_vf_out) FCK(cudaMemcpyAsync(d_vf_out,d_vf_loc,tot*4,cudaMemcpyDeviceToDevice,g_fm_stream)); float*sc[]={dtg,dyb,dg_post,dy_wkv,dr_post,dkmod_post,dvf_post,gR,gW,gK,gV,gA,gB, dr_t,dkmod_t,dvf_t,dkk,dav,dkvec,dvvec,dtk,d_oa,d_ow,d_ov,dsr,dsk,dsv,dsw,dsa,dsg, d_la,d_tw,d_lw,d_sg,d_lg,d_lv,d_vf_loc}; for(unsigned i=0;irw[idx]; int C=m->C; size_t tot=(size_t)T*C; float*h1=(float*)fmA_alloc(tot*4),*tm=(float*)fmA_alloc(tot*4),*h2=(float*)fmA_alloc(tot*4),*cm=(float*)fmA_alloc(tot*4); k_ln_fwd<<>>(xin,B->ln1_w,B->ln1_b,h1,NULL,NULL,C,T); tm_cache_d c; memset(&c,0,sizeof(c)); tm_fwd_device(&B->tmP,m->H,&B->D,h1,NULL,m->v_first,&c,tm,1,T); tmc_free(&c); fm_k_add<<>>(xin,tm,xout,tot); /* xout=x1 */ k_ln_fwd<<>>(xout,B->ln2_w,B->ln2_b,h2,NULL,NULL,C,T); fm_cm_fwd(m,&B->cm,&B->cmP,h2,cm,T); fm_k_addi<<>>(xout,cm,tot); /* xout=x1+cm */ fmA_free(h1);fmA_free(tm);fmA_free(h2);fmA_free(cm); } static void fm_rwkv_bwd(fm_model*m,int idx,const float*xin,const float*dy,float*dx, float*g_vf,int T){ fm_rwkv_blk*B=&m->rw[idx]; int C=m->C; size_t tot=(size_t)T*C; float*h1=(float*)fmA_alloc(tot*4),*tm=(float*)fmA_alloc(tot*4),*x1=(float*)fmA_alloc(tot*4),*h2=(float*)fmA_alloc(tot*4); float*dh2=(float*)fmA_alloc(tot*4),*dxa=(float*)fmA_alloc(tot*4),*dx1=(float*)fmA_alloc(tot*4),*dh1=(float*)fmA_alloc(tot*4); /* recompute forward temporaries */ k_ln_fwd<<>>(xin,B->ln1_w,B->ln1_b,h1,NULL,NULL,C,T); tm_cache_d c; memset(&c,0,sizeof(c)); tm_fwd_device(&B->tmP,m->H,&B->D,h1,NULL,m->v_first,&c,tm,1,T); fm_k_add<<>>(xin,tm,x1,tot); k_ln_fwd<<>>(x1,B->ln2_w,B->ln2_b,h2,NULL,NULL,C,T); /* backward */ fm_cm_bwd(m,&B->cm,&B->cmG,&B->cmP,h2,dy,dh2,T); FCK(cudaMemsetAsync(B->g_ln2_w,0,(size_t)C*4,g_fm_stream)); FCK(cudaMemsetAsync(B->g_ln2_b,0,(size_t)C*4,g_fm_stream)); k_ln_bwd<<>>(x1,B->ln2_w,dh2,dxa,B->g_ln2_w,B->g_ln2_b,C,T); fm_k_add<<>>(dy,dxa,dx1,tot); /* dx1=dy+dxa */ /* time-mix bwd (uses filled cache c) */ if(B->tmP.layer_id==0) fm_tm_bwd_core(m,&B->tmP,&B->D,&B->Gd,&c,h1,dx1,m->v_first,dh1,g_vf,NULL,T); else{ float*d_vf_out=(float*)fmA_alloc(tot*4); fm_tm_bwd_core(m,&B->tmP,&B->D,&B->Gd,&c,h1,dx1,m->v_first,dh1,NULL,d_vf_out,T); fm_k_addi<<>>(g_vf,d_vf_out,tot); fmA_free(d_vf_out); } tmc_free(&c); FCK(cudaMemsetAsync(B->g_ln1_w,0,(size_t)C*4,g_fm_stream)); FCK(cudaMemsetAsync(B->g_ln1_b,0,(size_t)C*4,g_fm_stream)); k_ln_bwd<<>>(xin,B->ln1_w,dh1,dxa,B->g_ln1_w,B->g_ln1_b,C,T); fm_k_add<<>>(dx1,dxa,dx,tot); /* dx=dx1+dxa */ fmA_free(h1);fmA_free(tm);fmA_free(x1);fmA_free(h2);fmA_free(dh2);fmA_free(dxa);fmA_free(dx1);fmA_free(dh1); } /* ============================ Attention block fwd/bwd ============================ */ /* moba core forward: packed q,k,v (device [T,C]) -> y (device [T,C]); leaves P. */ static void fm_attn_core_fwd(fm_model*m,fm_attn_blk*A,const float*q,const float*k,const float*v, float*y,float*P,int T){ int C=m->C,nH=m->nH,N=m->N,cs=A->P.chunk_size<1?1:A->P.chunk_size,topk=A->P.moba_top_k; float scale=1.0f/sqrtf((float)N); size_t tot=(size_t)T*C, nsc=(size_t)nH*T*T; moba_dev d; moba_alloc(&d,tot,nsc); float*Yh=(float*)fmA_alloc(tot*4); int nb=moba_nblk(tot); moba_k_pack<<>>(q,d.qh,1,T,nH,N); moba_k_pack<<>>(k,d.kh,1,T,nH,N); moba_k_pack<<>>(v,d.vh,1,T,nH,N); moba_scores_softmax(m->H,&d,1,nH,T,N,cs,topk,scale); for(int bh=0;bhH,0,0,T,N,T,1.0f,d.P+(size_t)bh*T*T,d.vh+(size_t)bh*T*N,0.0f,Yh+(size_t)bh*T*N); moba_k_unpack<<>>(Yh,y,1,T,nH,N); if(P) FCK(cudaMemcpyAsync(P,d.P,nsc*4,cudaMemcpyDeviceToDevice,g_fm_stream)); moba_free(&d); fmA_free(Yh); } static void fm_attn_fwd(fm_model*m,int idx,const float*xin,float*xout,int T){ fm_attn_blk*A=&m->at[idx]; int C=m->C; size_t tot=(size_t)T*C; float*h1=(float*)fmA_alloc(tot*4),*q=(float*)fmA_alloc(tot*4),*k=(float*)fmA_alloc(tot*4),*v=(float*)fmA_alloc(tot*4); float*y=(float*)fmA_alloc(tot*4),*h2=(float*)fmA_alloc(tot*4),*cm=(float*)fmA_alloc(tot*4); k_ln_fwd<<>>(xin,A->ln1_w,A->ln1_b,h1,NULL,NULL,C,T); FBK(rm_gemm_tm(m->H,0,0,T,C,C,1.0f,h1,A->Wq,0.0f,q)); FBK(rm_gemm_tm(m->H,0,0,T,C,C,1.0f,h1,A->Wk,0.0f,k)); FBK(rm_gemm_tm(m->H,0,0,T,C,C,1.0f,h1,A->Wv,0.0f,v)); fm_attn_core_fwd(m,A,q,k,v,y,NULL,T); FBK(rm_gemm_tm(m->H,0,0,T,C,C,1.0f,y,A->Wo,0.0f,xout)); /* att_out */ fm_k_addi<<>>(xout,xin,tot); /* x1=x+att_out */ k_ln_fwd<<>>(xout,A->ln2_w,A->ln2_b,h2,NULL,NULL,C,T); fm_cmw cmw={A->cm.x_k,A->cm.key_w,A->cm.value_w}; fm_cm_fwd(m,&cmw,&A->P.cm,h2,cm,T); fm_k_addi<<>>(xout,cm,tot); fmA_free(h1);fmA_free(q);fmA_free(k);fmA_free(v);fmA_free(y);fmA_free(h2);fmA_free(cm); } static void fm_attn_bwd(fm_model*m,int idx,const float*xin,const float*dy,float*dx,int T){ fm_attn_blk*A=&m->at[idx]; int C=m->C,nH=m->nH,N=m->N; int cs=A->P.chunk_size<1?1:A->P.chunk_size,topk=A->P.moba_top_k; float scale=1.0f/sqrtf((float)N); size_t tot=(size_t)T*C, nsc=(size_t)nH*T*T; char nm[64]; float*h1,*q,*k,*v,*y,*x1,*h2,*dh2,*dxa,*dx1,*d_attout,*dy_core,*dq,*dk,*dv,*dh1; #define A_(p) do{ p=(float*)fmA_alloc(tot*4); }while(0) A_(h1);A_(q);A_(k);A_(v);A_(y);A_(x1);A_(h2);A_(dh2);A_(dxa);A_(dx1);A_(d_attout);A_(dy_core);A_(dq);A_(dk);A_(dv);A_(dh1); #undef A_ /* recompute forward */ k_ln_fwd<<>>(xin,A->ln1_w,A->ln1_b,h1,NULL,NULL,C,T); FBK(rm_gemm_tm(m->H,0,0,T,C,C,1.0f,h1,A->Wq,0.0f,q)); FBK(rm_gemm_tm(m->H,0,0,T,C,C,1.0f,h1,A->Wk,0.0f,k)); FBK(rm_gemm_tm(m->H,0,0,T,C,C,1.0f,h1,A->Wv,0.0f,v)); fm_attn_core_fwd(m,A,q,k,v,y,NULL,T); FBK(rm_gemm_tm(m->H,0,0,T,C,C,1.0f,y,A->Wo,0.0f,x1)); fm_k_addi<<>>(x1,xin,tot); /* x1 */ k_ln_fwd<<>>(x1,A->ln2_w,A->ln2_b,h2,NULL,NULL,C,T); /* --- backward --- */ fm_cmw cmw={A->cm.x_k,A->cm.key_w,A->cm.value_w}; int nF; float*gcx=fm_gfind(m,(snprintf(nm,64,"attn%d.ffn_x_k",idx),nm),&nF); float*gck=fm_gfind(m,(snprintf(nm,64,"attn%d.ffn_key_w",idx),nm),&nF); float*gcv=fm_gfind(m,(snprintf(nm,64,"attn%d.ffn_value_w",idx),nm),&nF); fm_cmw cmg={gcx,gck,gcv}; fm_cm_bwd(m,&cmw,&cmg,&A->P.cm,h2,dy,dh2,T); float*gl2w=fm_gfind(m,(snprintf(nm,64,"attn%d.ln2_w",idx),nm),&nF); float*gl2b=fm_gfind(m,(snprintf(nm,64,"attn%d.ln2_b",idx),nm),&nF); FCK(cudaMemsetAsync(gl2w,0,(size_t)C*4,g_fm_stream));FCK(cudaMemsetAsync(gl2b,0,(size_t)C*4,g_fm_stream)); k_ln_bwd<<>>(x1,A->ln2_w,dh2,dxa,gl2w,gl2b,C,T); fm_k_add<<>>(dy,dxa,dx1,tot); /* dx1=dy+dxa */ /* output proj: att_out=y@Wo -> dy_core=dx1@Wo^T ; G_Wo=y^T@dx1 */ float*gWo=fm_gfind(m,(snprintf(nm,64,"attn%d.output_w",idx),nm),&nF); FBK(rm_gemm_tm(m->H,0,1,T,C,C,1.0f,dx1,A->Wo,0.0f,dy_core)); FBK(rm_gemm_tm(m->H,1,0,C,C,T,1.0f,y,dx1,0.0f,gWo)); /* attention core backward: dy_core -> dq,dk,dv (selection fixed) */ { moba_dev d; moba_alloc(&d,tot,nsc); float*dyh=(float*)fmA_alloc(tot*4),*dqh=(float*)fmA_alloc(tot*4),*dkh=(float*)fmA_alloc(tot*4),*dvh=(float*)fmA_alloc(tot*4); float*dP=(float*)fmA_alloc(nsc*4),*dS=(float*)fmA_alloc(nsc*4); int nb=moba_nblk(tot); moba_k_pack<<>>(q,d.qh,1,T,nH,N); moba_k_pack<<>>(k,d.kh,1,T,nH,N); moba_k_pack<<>>(v,d.vh,1,T,nH,N); moba_k_pack<<>>(dy_core,dyh,1,T,nH,N); moba_scores_softmax(m->H,&d,1,nH,T,N,cs,topk,scale); for(int bh=0;bhH,0,1,T,T,N,1.0f,dyh+ov,d.vh+ov,0.0f,dP+os); rm_gemm_moba(m->H,1,0,T,N,T,1.0f,d.P+os,dyh+ov,0.0f,dvh+ov); } moba_k_softmax_bwd<<>>(d.P,dP,dS,(long)nH,T,scale); for(int bh=0;bhH,0,0,T,N,T,1.0f,dS+os,d.kh+ov,0.0f,dqh+ov); rm_gemm_moba(m->H,1,0,T,N,T,1.0f,dS+os,d.qh+ov,0.0f,dkh+ov); } moba_k_unpack<<>>(dqh,dq,1,T,nH,N); moba_k_unpack<<>>(dkh,dk,1,T,nH,N); moba_k_unpack<<>>(dvh,dv,1,T,nH,N); moba_free(&d);fmA_free(dyh);fmA_free(dqh);fmA_free(dkh);fmA_free(dvh);fmA_free(dP);fmA_free(dS); } /* q,k,v = h1@{Wq,Wk,Wv} -> dh1 (accumulate) + proj grads */ float*gWq=fm_gfind(m,(snprintf(nm,64,"attn%d.receptance_w",idx),nm),&nF); float*gWk=fm_gfind(m,(snprintf(nm,64,"attn%d.key_w",idx),nm),&nF); float*gWv=fm_gfind(m,(snprintf(nm,64,"attn%d.value_w",idx),nm),&nF); FBK(rm_gemm_tm(m->H,0,1,T,C,C,1.0f,dq,A->Wq,0.0f,dh1)); FBK(rm_gemm_tm(m->H,1,0,C,C,T,1.0f,h1,dq,0.0f,gWq)); FBK(rm_gemm_tm(m->H,0,1,T,C,C,1.0f,dk,A->Wk,1.0f,dh1)); FBK(rm_gemm_tm(m->H,1,0,C,C,T,1.0f,h1,dk,0.0f,gWk)); FBK(rm_gemm_tm(m->H,0,1,T,C,C,1.0f,dv,A->Wv,1.0f,dh1)); FBK(rm_gemm_tm(m->H,1,0,C,C,T,1.0f,h1,dv,0.0f,gWv)); /* ln1: dh1 -> dxa + ln1 grads ; dx = dx1 + dxa */ float*gl1w=fm_gfind(m,(snprintf(nm,64,"attn%d.ln1_w",idx),nm),&nF); float*gl1b=fm_gfind(m,(snprintf(nm,64,"attn%d.ln1_b",idx),nm),&nF); FCK(cudaMemsetAsync(gl1w,0,(size_t)C*4,g_fm_stream));FCK(cudaMemsetAsync(gl1b,0,(size_t)C*4,g_fm_stream)); k_ln_bwd<<>>(xin,A->ln1_w,dh1,dxa,gl1w,gl1b,C,T); fm_k_add<<>>(dx1,dxa,dx,tot); fmA_free(h1);fmA_free(q);fmA_free(k);fmA_free(v);fmA_free(y);fmA_free(x1);fmA_free(h2); fmA_free(dh2);fmA_free(dxa);fmA_free(dx1);fmA_free(d_attout);fmA_free(dy_core);fmA_free(dq);fmA_free(dk);fmA_free(dv);fmA_free(dh1); } /* ============================ full fwd / bwd ============================ */ static void fm_forward(fm_model*m,const int*tokens,int T){ int C=m->C,V=m->V; size_t TC=(size_t)T*C; fmA_reset(); /* opt #2: reclaim last pass's transient scratch */ /* opt#3: during capture the driver has already staged m->tok (resident); * outside capture (eval / non-graph) copy the batch in as before. */ if(!g_fm_capturing) FCK(cudaMemcpyAsync(m->tok,tokens,(size_t)T*sizeof(int),cudaMemcpyHostToDevice,g_fm_stream)); float*x0=m->block_in; fm_k_emb_fwd<<>>(m->emb,m->tok,x0,T,C); for(int pos=0;posn_blocks;pos++){ float*xin=m->block_in+(size_t)pos*TC, *xout=m->block_in+(size_t)(pos+1)*TC; if(m->btype[pos]==0) fm_rwkv_fwd(m,m->bidx[pos],xin,xout,T); else fm_attn_fwd(m,m->bidx[pos],xin,xout,T); } float*final=m->block_in+(size_t)m->n_blocks*TC; k_ln_fwd<<>>(final,m->lnout_w,m->lnout_b,m->lnout,NULL,NULL,C,T); FBK(rm_gemm_tm(m->H,0,0,T,V,C,1.0f,m->lnout,m->head,0.0f,m->logits)); fm_sync(); } static double fm_backward(fm_model*m,const int*targets,int T,int ignore_index){ int C=m->C,V=m->V; size_t TC=(size_t)T*C; fmA_reset(); /* opt #2: reclaim the forward pass's transient scratch */ fm_zero_grads(m); int nv=0; for(int t=0;t=0 && targets[t]==ignore_index)) nv++; /* opt#3: targets live in resident m->dtg (stable ptr); driver stages them * before graph replay, so skip the copy during capture. */ if(!g_fm_capturing) FCK(cudaMemcpyAsync(m->dtg,targets,(size_t)T*sizeof(int),cudaMemcpyHostToDevice,g_fm_stream)); float*dlogits=(float*)fmA_alloc((size_t)T*V*sizeof(float)); fm_k_ce_bwd<<>>(m->logits,m->dtg,dlogits,m->loss_t,T,V,ignore_index,nv); fm_sync(); /* loss = mean over valid. Under capture the per-token loss stays in the * resident m->loss_t buffer; the driver reduces it after replay (a D2H copy * here would abort the capture), so return 0 as a placeholder. */ double loss=0.0; if(!g_fm_capturing){ double*lh=(double*)malloc((size_t)T*sizeof(double)); FCK(cudaMemcpy(lh,m->loss_t,(size_t)T*sizeof(double),cudaMemcpyDeviceToHost)); for(int t=0;t0? loss/(double)nv : 0.0; free(lh); } /* head: dlogits -> dlnout + g_head */ float*dlnout=(float*)fmA_alloc(TC*4),*dfinal=(float*)fmA_alloc(TC*4); float*dcur=(float*)fmA_alloc(TC*4),*dprev=(float*)fmA_alloc(TC*4),*g_vf=(float*)fmA_alloc(TC*4); FCK(cudaMemsetAsync(g_vf,0,TC*4,g_fm_stream)); FBK(rm_gemm_tm(m->H,1,0,C,V,T,1.0f,m->lnout,dlogits,0.0f,m->g_head)); /* g_head=lnout^T@dlogits */ FBK(rm_gemm_tm(m->H,0,1,T,C,V,1.0f,dlogits,m->head,0.0f,dlnout)); /* dlnout=dlogits@head^T */ /* ln_out bwd */ float*final=m->block_in+(size_t)m->n_blocks*TC; k_ln_bwd<<>>(final,m->lnout_w,dlnout,dfinal,m->g_lnout_w,m->g_lnout_b,C,T); /* reverse blocks */ FCK(cudaMemcpyAsync(dcur,dfinal,TC*4,cudaMemcpyDeviceToDevice,g_fm_stream)); for(int pos=m->n_blocks-1;pos>=0;pos--){ float*xin=m->block_in+(size_t)pos*TC; if(m->btype[pos]==0) fm_rwkv_bwd(m,m->bidx[pos],xin,dcur,dprev,g_vf,T); else fm_attn_bwd(m,m->bidx[pos],xin,dcur,dprev,T); float*tmp=dcur;dcur=dprev;dprev=tmp; } /* embedding scatter-add: dcur holds grad on emb output */ FCK(cudaMemsetAsync(m->g_emb,0,(size_t)V*C*sizeof(float),g_fm_stream)); fm_k_emb_bwd<<>>(m->tok,dcur,m->g_emb,T,C); fm_sync(); fmA_free(dlogits);fmA_free(dlnout);fmA_free(dfinal);fmA_free(dcur);fmA_free(dprev);fmA_free(g_vf); return loss; }