use super::*;
use core::arch::x86_64::*;
use std::sync::OnceLock;
use std::sync::atomic::AtomicUsize;
const BLK: usize = 16;
struct Act {
s: SplitAct,
neg: Vec<i32>,
}
fn act_of(x: &[f32]) -> Act {
let s = split_act(x);
let neg = s
.xq
.chunks_exact(4)
.map(|c| -8 * (c[0] as i32 + c[1] as i32 + c[2] as i32 + c[3] as i32))
.collect();
Act { s, neg }
}
struct Scratch {
gtab: [f32; BLK * 32],
utab: [f32; BLK * 32],
gsc: Vec<f32>,
usc: Vec<f32>,
}
impl Scratch {
fn new(gpr: usize) -> Self {
let n = gpr.next_multiple_of(16) + 16;
Self {
gtab: [0.0; BLK * 32],
utab: [0.0; BLK * 32],
gsc: vec![0.0; n],
usc: vec![0.0; n],
}
}
}
#[target_feature(enable = "avx2,avx512f,avx512bw,avx512vl,avx512vnni")]
#[inline]
unsafe fn exp2_512(x: __m512) -> __m512 {
unsafe {
let r = _mm512_castps128_ps512(exp2_128(_mm512_castps512_ps128(x)));
let r = _mm512_insertf32x4::<1>(r, exp2_128(_mm512_extractf32x4_ps::<1>(x)));
let r = _mm512_insertf32x4::<2>(r, exp2_128(_mm512_extractf32x4_ps::<2>(x)));
_mm512_insertf32x4::<3>(r, exp2_128(_mm512_extractf32x4_ps::<3>(x)))
}
}
#[target_feature(enable = "avx2,avx512f,avx512bw,avx512vl,avx512vnni")]
#[inline]
fn transpose16(r: &[__m512; 16]) -> [__m512; 16] {
let z = _mm512_setzero_ps();
let (mut t, mut u, mut o) = ([z; 16], [z; 16], [z; 16]);
for i in 0..8 {
t[2 * i] = _mm512_unpacklo_ps(r[2 * i], r[2 * i + 1]);
t[2 * i + 1] = _mm512_unpackhi_ps(r[2 * i], r[2 * i + 1]);
}
for i in 0..4 {
let (a, b) = (_mm512_castps_pd(t[4 * i]), _mm512_castps_pd(t[4 * i + 1]));
let (c, d) = (_mm512_castps_pd(t[4 * i + 2]), _mm512_castps_pd(t[4 * i + 3]));
u[4 * i] = _mm512_castpd_ps(_mm512_unpacklo_pd(a, c));
u[4 * i + 1] = _mm512_castpd_ps(_mm512_unpackhi_pd(a, c));
u[4 * i + 2] = _mm512_castpd_ps(_mm512_unpacklo_pd(b, d));
u[4 * i + 3] = _mm512_castpd_ps(_mm512_unpackhi_pd(b, d));
}
for m in 0..4 {
let a = _mm512_shuffle_f32x4::<0x88>(u[m], u[4 + m]);
let b = _mm512_shuffle_f32x4::<0xdd>(u[m], u[4 + m]);
let c = _mm512_shuffle_f32x4::<0x88>(u[8 + m], u[12 + m]);
let d = _mm512_shuffle_f32x4::<0xdd>(u[8 + m], u[12 + m]);
o[m] = _mm512_shuffle_f32x4::<0x88>(a, c);
o[8 + m] = _mm512_shuffle_f32x4::<0xdd>(a, c);
o[4 + m] = _mm512_shuffle_f32x4::<0x88>(b, d);
o[12 + m] = _mm512_shuffle_f32x4::<0xdd>(b, d);
}
o
}
#[target_feature(enable = "avx2,avx512f,avx512bw,avx512vl,avx512vnni")]
#[inline]
unsafe fn ladders16(params: *const u8, tab: &mut [f32; BLK * 32]) {
unsafe {
let v = _mm512_loadu_si512(params.cast());
let lo = _mm512_cvtph_ps(_mm512_cvtepi32_epi16(v));
let st = _mm512_cvtph_ps(_mm512_cvtepi32_epi16(_mm512_srli_epi32::<16>(v)));
let ratio = exp2_512(st);
let mut t = exp2_512(lo);
let z = _mm512_setzero_ps();
let (mut c0, mut c1) = ([z; 16], [z; 16]);
for c in 0..32 {
if c > 0 {
t = _mm512_mul_ps(t, ratio);
}
if c < 16 {
c0[c] = t;
} else {
c1[c - 16] = t;
}
}
let (r0, r1) = (transpose16(&c0), transpose16(&c1));
for i in 0..BLK {
_mm512_storeu_ps(tab.as_mut_ptr().add(i * 32), r0[i]);
_mm512_storeu_ps(tab.as_mut_ptr().add(i * 32 + 16), r1[i]);
}
}
}
#[target_feature(enable = "avx2,avx512f,avx512bw,avx512vl,avx512vnni")]
#[inline]
unsafe fn scales_row(codes: *const u8, stride: usize, gpr: usize, tab: *const f32, sc: *mut f32) {
unsafe {
let (t_lo, t_hi) = (_mm512_loadu_ps(tab), _mm512_loadu_ps(tab.add(16)));
let sh = _mm512_setr_epi64(0, 5, 10, 15, 20, 25, 30, 35);
let m31 = _mm512_set1_epi32(31);
let (mut g, mut off) = (0usize, 0usize);
while g < gpr {
let left = stride.saturating_sub(off);
let k: __mmask16 = if left >= 10 { 0x03FF } else { (1u16 << left) - 1 };
let x = _mm_maskz_loadu_epi8(k, codes.add(off).cast());
let w0 = _mm_cvtsi128_si64(x);
let w1 = _mm_cvtsi128_si64(_mm_srli_si128::<5>(x));
let a = _mm512_cvtepi64_epi32(_mm512_srlv_epi64(_mm512_set1_epi64(w0), sh));
let b = _mm512_cvtepi64_epi32(_mm512_srlv_epi64(_mm512_set1_epi64(w1), sh));
let idx = _mm512_and_si512(_mm512_inserti64x4::<1>(_mm512_castsi256_si512(a), b), m31);
_mm512_storeu_ps(sc.add(g), _mm512_permutex2var_ps(t_lo, idx, t_hi));
g += 16;
off += 10;
}
}
}
#[target_feature(enable = "avx2,avx512f,avx512bw,avx512vl,avx512vnni")]
#[inline]
unsafe fn dot2<const N: usize>(
gn: *const u8,
un: *const u8,
gsc: *const f32,
usc: *const f32,
steps: usize,
xq: &[*const i8; N],
neg: &[*const i32; N],
) -> ([f32; N], [f32; N]) {
unsafe {
let m = _mm512_set1_epi16(0x0F0F);
let pick = _mm512_setr_epi32(0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1);
let (mut ag, mut au) = ([_mm512_setzero_ps(); N], [_mm512_setzero_ps(); N]);
for s in 0..steps {
let vg = _mm512_cvtepu8_epi16(_mm256_loadu_si256(gn.add(s * 32).cast()));
let vu = _mm512_cvtepu8_epi16(_mm256_loadu_si256(un.add(s * 32).cast()));
let wg = _mm512_and_si512(_mm512_or_si512(vg, _mm512_slli_epi16::<4>(vg)), m);
let wu = _mm512_and_si512(_mm512_or_si512(vu, _mm512_slli_epi16::<4>(vu)), m);
let sg = _mm512_permutexvar_ps(pick, _mm512_castps128_ps512(_mm_loadu_ps(gsc.add(2 * s))));
let su = _mm512_permutexvar_ps(pick, _mm512_castps128_ps512(_mm_loadu_ps(usc.add(2 * s))));
for k in 0..N {
let x = _mm512_loadu_si512(xq[k].add(s * 64).cast());
let n = _mm512_loadu_si512(neg[k].add(s * 16).cast());
ag[k] = _mm512_fmadd_ps(_mm512_cvtepi32_ps(_mm512_dpbusd_epi32(n, wg, x)), sg, ag[k]);
au[k] = _mm512_fmadd_ps(_mm512_cvtepi32_ps(_mm512_dpbusd_epi32(n, wu, x)), su, au[k]);
}
}
let (mut g, mut u) = ([0f32; N], [0f32; N]);
for k in 0..N {
g[k] = _mm512_reduce_add_ps(ag[k]);
u[k] = _mm512_reduce_add_ps(au[k]);
}
(g, u)
}
}
#[target_feature(enable = "avx2,avx512f,avx512bw,avx512vl,avx512vnni")]
#[inline]
unsafe fn dot1<const N: usize>(
nb: *const u8,
sc: *const f32,
steps: usize,
xq: &[*const i8; N],
neg: &[*const i32; N],
) -> [f32; N] {
unsafe {
let m = _mm512_set1_epi16(0x0F0F);
let pick = _mm512_setr_epi32(0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1);
let mut acc = [_mm512_setzero_ps(); N];
for s in 0..steps {
let v = _mm512_cvtepu8_epi16(_mm256_loadu_si256(nb.add(s * 32).cast()));
let w = _mm512_and_si512(_mm512_or_si512(v, _mm512_slli_epi16::<4>(v)), m);
let sv = _mm512_permutexvar_ps(pick, _mm512_castps128_ps512(_mm_loadu_ps(sc.add(2 * s))));
for k in 0..N {
let x = _mm512_loadu_si512(xq[k].add(s * 64).cast());
let n = _mm512_loadu_si512(neg[k].add(s * 16).cast());
acc[k] = _mm512_fmadd_ps(_mm512_cvtepi32_ps(_mm512_dpbusd_epi32(n, w, x)), sv, acc[k]);
}
}
let mut d = [0f32; N];
for k in 0..N {
d[k] = _mm512_reduce_add_ps(acc[k]);
}
d
}
}
#[target_feature(enable = "avx2,avx512f,avx512bw,avx512vl,avx512vnni")]
#[inline]
unsafe fn dot2_chunk<const N: usize>(
gn: *const u8,
un: *const u8,
gsc: *const f32,
usc: *const f32,
steps: usize,
ch: &[&Act],
) -> ([f32; 4], [f32; 4]) {
unsafe {
let xq: [*const i8; N] = std::array::from_fn(|k| ch[k].s.xq.as_ptr());
let neg: [*const i32; N] = std::array::from_fn(|k| ch[k].neg.as_ptr());
let (g, u) = dot2::<N>(gn, un, gsc, usc, steps, &xq, &neg);
let (mut go, mut uo) = ([0f32; 4], [0f32; 4]);
go[..N].copy_from_slice(&g);
uo[..N].copy_from_slice(&u);
(go, uo)
}
}
#[target_feature(enable = "avx2,avx512f,avx512bw,avx512vl,avx512vnni")]
#[inline]
unsafe fn dot1_chunk<const N: usize>(nb: *const u8, sc: *const f32, steps: usize, ch: &[&Act]) -> [f32; 4] {
unsafe {
let xq: [*const i8; N] = std::array::from_fn(|k| ch[k].s.xq.as_ptr());
let neg: [*const i32; N] = std::array::from_fn(|k| ch[k].neg.as_ptr());
let d = dot1::<N>(nb, sc, steps, &xq, &neg);
let mut o = [0f32; 4];
o[..N].copy_from_slice(&d);
o
}
}
#[target_feature(enable = "avx2,avx512f,avx512bw,avx512vl,avx512vnni")]
#[inline]
#[allow(clippy::too_many_arguments)]
unsafe fn gate_up_block(
g: &Q4tpView,
u: &Q4tpView,
r0: usize,
hid: usize,
inter: usize,
acts: &[&Act],
avals: SendMut,
base: usize,
scr: &mut Scratch,
) {
unsafe {
let gpr = hid / GROUP_SIZE;
let steps = hid / 64;
let row = gpr * Q4TP_NIB;
ladders16(g.params.as_ptr().add(r0 * 4), &mut scr.gtab);
ladders16(u.params.as_ptr().add(r0 * 4), &mut scr.utab);
for i in 0..BLK {
let r = r0 + i;
scales_row(g.codes.as_ptr().add(r * g.stride), g.stride, gpr, scr.gtab.as_ptr().add(i * 32), scr.gsc.as_mut_ptr());
scales_row(u.codes.as_ptr().add(r * u.stride), u.stride, gpr, scr.utab.as_ptr().add(i * 32), scr.usc.as_mut_ptr());
let gn = g.nib.as_ptr().add(r * row);
let un = u.nib.as_ptr().add(r * row);
let (gsc, usc) = (scr.gsc.as_ptr(), scr.usc.as_ptr());
let mut k0 = 0;
while k0 < acts.len() {
let n = (acts.len() - k0).min(4);
let ch = &acts[k0..k0 + n];
let (gv, uv) = match n {
1 => dot2_chunk::<1>(gn, un, gsc, usc, steps, ch),
2 => dot2_chunk::<2>(gn, un, gsc, usc, steps, ch),
3 => dot2_chunk::<3>(gn, un, gsc, usc, steps, ch),
_ => dot2_chunk::<4>(gn, un, gsc, usc, steps, ch),
};
for (k, a) in ch.iter().enumerate() {
let (mut gvv, mut uvv) = (gv[k] * a.s.sx, uv[k] * a.s.sx);
for &(j, xv) in &a.s.outliers {
let og = q4tp_outlier(g.nib, r, gpr, j, &scr.gsc[..gpr]);
let ou = q4tp_outlier(u.nib, r, gpr, j, &scr.usc[..gpr]);
gvv += og.0 * og.1 * xv;
uvv += ou.0 * ou.1 * xv;
}
*avals.at((base + k0 + k) * inter + r) = gvv / (1.0 + (-gvv).exp()) * uvv;
}
k0 += n;
}
}
}
}
#[target_feature(enable = "avx2,avx512f,avx512bw,avx512vl,avx512vnni")]
#[inline]
#[allow(clippy::too_many_arguments)]
unsafe fn down_block(
d: &Q4tpView,
r0: usize,
hid: usize,
inter: usize,
acts: &[&Act],
out: SendMut,
base: usize,
scr: &mut Scratch,
) {
unsafe {
let gpr = inter / GROUP_SIZE;
let steps = inter / 64;
let row = gpr * Q4TP_NIB;
ladders16(d.params.as_ptr().add(r0 * 4), &mut scr.gtab);
for i in 0..BLK {
let r = r0 + i;
scales_row(d.codes.as_ptr().add(r * d.stride), d.stride, gpr, scr.gtab.as_ptr().add(i * 32), scr.gsc.as_mut_ptr());
let nb = d.nib.as_ptr().add(r * row);
let sc = scr.gsc.as_ptr();
let mut k0 = 0;
while k0 < acts.len() {
let n = (acts.len() - k0).min(4);
let ch = &acts[k0..k0 + n];
let dv = match n {
1 => dot1_chunk::<1>(nb, sc, steps, ch),
2 => dot1_chunk::<2>(nb, sc, steps, ch),
3 => dot1_chunk::<3>(nb, sc, steps, ch),
_ => dot1_chunk::<4>(nb, sc, steps, ch),
};
for (k, a) in ch.iter().enumerate() {
let mut v = dv[k] * a.s.sx;
for &(j, xv) in &a.s.outliers {
let (w, s) = q4tp_outlier(d.nib, r, gpr, j, &scr.gsc[..gpr]);
v += w * s * xv;
}
*out.at((base + k0 + k) * hid + r) = v;
}
k0 += n;
}
}
}
}
fn wait(done: &AtomicUsize, n: usize) {
let mut spins = 0u32;
while done.load(Ordering::Acquire) < n {
if spins < 1 << 14 {
spins += 1;
std::hint::spin_loop();
} else {
std::thread::yield_now();
}
}
}
pub(super) fn run(
experts: &[(&QTensor, &QTensor, &QTensor)],
groups: &[Vec<usize>],
xs: &[f32],
out: &mut [f32],
pool: Option<&Pool>,
) -> bool {
run_stages(experts, groups, xs, out, pool, None, None)
}
pub(super) fn run_stages(
experts: &[(&QTensor, &QTensor, &QTensor)],
groups: &[Vec<usize>],
xs: &[f32],
out: &mut [f32],
pool: Option<&Pool>,
gs_in: Option<&[f32]>,
gs_out: Option<&mut Vec<f32>>,
) -> bool {
if !(avx512vnni_enabled() && a8w8_enabled()) || experts.is_empty() || experts.len() != groups.len() {
return false;
}
let (inter, hid) = (experts[0].0.rows(), experts[0].0.cols());
if hid == 0 || inter == 0 || hid % 64 != 0 || inter % 64 != 0 || xs.len() % hid != 0 {
return false;
}
let b = xs.len() / hid;
let n_pairs: usize = groups.iter().map(|g| g.len()).sum();
if n_pairs == 0 || out.len() != n_pairs * hid || groups.iter().flatten().any(|&t| t >= b) {
return false;
}
if gs_in.is_some_and(|g| g.len() != n_pairs * inter) {
return false;
}
fn view(t: &QTensor, rows: usize, cols: usize) -> Option<Q4tpView<'_>> {
let q4tp = matches!(
t,
QTensor::Mapped {
dtype: TensorDtype::Q4TiledP,
..
}
);
if !q4tp
|| t.per_tensor_only()
|| t.has_prism_contract()
|| t.exact_cpu_default()
|| t.rows() != rows
|| t.cols() != cols
{
return None;
}
let bytes = t.quant_bytes();
let (_, codes_off, stride) = q4tp_sections(rows, cols);
(bytes.len() >= codes_off + rows * stride).then(|| Q4tpView::new(bytes, rows, cols))
}
let mut views = Vec::with_capacity(experts.len() * 3);
for (g, u, d) in experts {
let (Some(g), Some(u), Some(d)) = (view(g, inter, hid), view(u, inter, hid), view(d, hid, inter)) else {
return false;
};
views.extend([g, u, d]);
}
let mut offs = Vec::with_capacity(experts.len() + 1);
let mut at = 0usize;
for g in groups {
offs.push(at);
at += g.len();
}
offs.push(at);
let mut used = vec![false; b];
for &t in groups.iter().flatten() {
used[t] = true;
}
let toks: Vec<usize> = (0..b).filter(|&t| used[t]).collect();
let tok_acts: Vec<OnceLock<Act>> = (0..b).map(|_| OnceLock::new()).collect();
let pair_acts: Vec<OnceLock<Act>> = (0..n_pairs).map(|_| OnceLock::new()).collect();
let mut avals: Vec<f32> = match gs_in {
Some(g) => g.to_vec(),
None => vec![0f32; n_pairs * inter],
};
let (bg, bd) = (inter / BLK, hid / BLK);
let n_gu = if gs_in.is_some() { 0 } else { experts.len() * bg };
let n_dn = experts.len() * bd;
let (av, ov) = (SendMut(avals.as_mut_ptr()), SendMut(out.as_mut_ptr()));
let gpr_max = hid.max(inter) / GROUP_SIZE;
let cur: [AtomicUsize; 4] = std::array::from_fn(|_| AtomicUsize::new(0));
let done: [AtomicUsize; 4] = std::array::from_fn(|_| AtomicUsize::new(0));
let work = |_w: usize, _n: usize| {
let mut scr = Scratch::new(gpr_max);
let mut ga: Vec<&Act> = Vec::new();
loop {
let i = cur[0].fetch_add(1, Ordering::Relaxed);
if i >= toks.len() {
break;
}
let t = toks[i];
let _ = tok_acts[t].set(act_of(&xs[t * hid..(t + 1) * hid]));
done[0].fetch_add(1, Ordering::Release);
}
wait(&done[0], toks.len());
let mut held = usize::MAX;
loop {
let u0 = cur[1].fetch_add(2, Ordering::Relaxed);
if u0 >= n_gu {
break;
}
let u1 = (u0 + 2).min(n_gu);
for unit in u0..u1 {
let (e, blk) = (unit / bg, unit % bg);
if groups[e].is_empty() {
continue;
}
if held != e {
ga.clear();
ga.extend(groups[e].iter().filter_map(|&t| tok_acts[t].get()));
held = e;
}
if ga.len() == groups[e].len() {
unsafe { gate_up_block(&views[e * 3], &views[e * 3 + 1], blk * BLK, hid, inter, &ga, av, offs[e], &mut scr) };
}
}
done[1].fetch_add(u1 - u0, Ordering::Release);
}
wait(&done[1], n_gu);
loop {
let p = cur[2].fetch_add(1, Ordering::Relaxed);
if p >= n_pairs {
break;
}
let row = unsafe { std::slice::from_raw_parts(av.at(p * inter), inter) };
let _ = pair_acts[p].set(act_of(row));
done[2].fetch_add(1, Ordering::Release);
}
wait(&done[2], n_pairs);
let mut held = usize::MAX;
loop {
let u0 = cur[3].fetch_add(8, Ordering::Relaxed);
if u0 >= n_dn {
break;
}
let u1 = (u0 + 8).min(n_dn);
for unit in u0..u1 {
let (e, blk) = (unit / bd, unit % bd);
if groups[e].is_empty() {
continue;
}
if held != e {
ga.clear();
ga.extend(pair_acts[offs[e]..offs[e + 1]].iter().filter_map(|a| a.get()));
held = e;
}
if ga.len() == groups[e].len() {
unsafe { down_block(&views[e * 3 + 2], blk * BLK, hid, inter, &ga, ov, offs[e], &mut scr) };
}
}
done[3].fetch_add(u1 - u0, Ordering::Release);
}
wait(&done[3], n_dn);
};
match pool {
Some(p) => p.run(&work),
None => work(0, 1),
}
if let Some(g) = gs_out {
*g = avals;
}
true
}
#[cfg(test)]
pub(super) fn block_scales(v: &Q4tpView, r0: usize, gpr: usize) -> Option<Vec<f32>> {
if !avx512vnni_enabled() {
return None;
}
let mut scr = Scratch::new(gpr);
let mut out = Vec::with_capacity(BLK * gpr);
unsafe {
ladders16(v.params.as_ptr().add(r0 * 4), &mut scr.gtab);
for i in 0..BLK {
scales_row(
v.codes.as_ptr().add((r0 + i) * v.stride),
v.stride,
gpr,
scr.gtab.as_ptr().add(i * 32),
scr.gsc.as_mut_ptr(),
);
out.extend_from_slice(&scr.gsc[..gpr]);
}
}
Some(out)
}