Skip to content

Commit cf6da48

Browse files
kmbandyclaude
andcommitted
feat(MAD-223): Phase G — HIP backend dispatch + perf tune (FWHT + BLOCK sweep)
End-to-end ml8-4 GGUF + HIP path landed. Cell E prompt eval at 862 t/s, 1.25x faster than f16 on R9700. PPL Δ +0.44 vs f16 in llama-perplexity (vs +0.11 in HF Python — gap under investigation in G.6.g, not yet shipped). Phase G.1-G.3 (registrations + CPU fallback + sidecar loading): - GGML_TYPE_ML8_4 (48) + GGML_TYPE_F8_E4M3 (49) registrations - CPU dequantize_row_ml8_4 + vec_dot fallback (OMP-safe, malloc/free instead of std::vector to dodge libstdc++ allocator UAF) - Loader recognizes .centroids/.rotation_h_a/.rotation_meta/.awq_scale sidecars; graph build wires ML8_MUL_MAT + ML8_APPLY_ROTATION ops - ml8_to_gguf.py v2: emits native ml8-typed tensors with sidecars (replaces stopgap dequant-to-fp16 patcher) Phase G.4 (HIP backend): - mt_ml8_gemm: multi-shape kernel cache (keyed on N/K/group_size/ n_centroids/M-tier) so gate/up/down + decode/prefill cache separately - New ops GGML_OP_ML8_MUL_MAT + GGML_OP_ML8_APPLY_ROTATION (101 total) - Loader op-swap (MUL_MAT → ML8_MUL_MAT for ML8_4 weights) so the weight_buft_supported probe routes to the ml8 dispatch on HIP - Load-time repack block_ml8_4 → separated (b_packed, b_scale) buffers on first dispatch, cached on void* weight->data - GPU fp32→fp8 activation quant kernel (per-row absmax reduction, round-to-nearest-even via ml8_fp32_to_e4m3 device fn) - Dense HIP dispatch via mt_ml8_gemm (AITER Triton AOT, FP8 WMMA) - GPU apply_rotation: one block per token + H_b cache (now replaced by FWHT in G.6.f below) Phase G.6 (perf — the deferred "Phase F" tune lifted forward): - tune_gemm_ml8.py sweep harness: per-shape (gate/up at K=2560 N=9216, down at K=9216 N=2560) × decode (M=16) and prefill (M=512). 288 cfgs per shape×tier. Emits gemm_ml8_tune.json with the winners. - C++ wrapper picks tuned config per (M-tier, K, N) via ml8_pick_config; ShapeKey extended with prefill bit so decode/prefill cache separately - rocprofv3 --hip-trace identified ml8_apply_rotation_kernel as 92.2% of GPU time (46 ms/call) — gemm tune alone wouldn't move the needle - G.6.f: replace naive O(b²) H_b matmul with row-wise FWHT (existing mt_turbo_fp8_fwht from MAD-227 turbo-fp8 KV work) + a tiny per-token H_a^T multiply kernel. ~100x less compute on rotation. test_fwht_vs_sylvester.py confirms FWHT bit-equivalent to dense Sylvester multiply at fp32 epsilon for D ∈ {2..1024}. Perf (Cell E, Qwen3.5-4B, M=361 prompt eval, --no-mmap, R9700): - Pre-tune kernel: 30.56 ms/tok ( 32.7 t/s) - Tuned BLOCK config: 18.90 ms/tok ( 52.9 t/s) — 1.62x - + FWHT rotation: 1.16 ms/tok (862.5 t/s) — 26x total - f16 baseline reference: 692 t/s (PPL pass throughput) - → ml8 prefill is 1.25x faster than f16 at 1.6x smaller model Quality (llama-perplexity, ctx=512, wikitext-2-raw): - f16: PPL = 9.9448 +/- 0.0715 - ml8-cellE (this branch): PPL = 10.3862 +/- 0.0738 → Δ = +0.441 - Unsloth UD-Q4_K_XL ref: PPL = 10.1345 +/- 0.0731 → Δ = +0.190 ml8 currently loses +0.25 to Unsloth in llama-perplexity but HF Python kernel-path PPL on Cell E was 8.299 (Δ vs HF f16 = -0.019, actually better than f16). The ~+0.33 HIP-vs-Python drift is under investigation in G.6.g — suspect either per-row fp8 quant rounding or new BLOCK_SIZE_M=128 accumulation order changes. Open follow-ups (tracked in session task list): - G.6.d — fuse fp32→fp8 quant prologue + bf16→fp32 epilogue into gemm - G.6.g — bisect the +0.33 HIP-vs-Python PPL drift - G.7 — MoE: wire mt_ml8_moe_gemm HIP dispatch + Qwen3.6-A3B path - G.8 — CPU vec_dot vectorization (AVX2/AVX512) for OSS path Tests: - tests/test-ml8-dequant.cpp — CPU dequantize bit-equivalence - tests/test-ml8-mul-mat.cpp — end-to-end op smoke - tests/test_ml8_*.py — Python overlay + runtime + I/O coverage - scripts/calibration/test_fwht_vs_sylvester.py — FWHT correctness Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 1d09c2c commit cf6da48

48 files changed

Lines changed: 10316 additions & 89 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

ggml/include/ggml-ml8.h

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// ggml-ml8.h
2+
//
3+
// MAD-223 Phase G.3: ml8-4 matmul graph node (CPU-resident via GGML_OP_CUSTOM).
4+
//
5+
// `ggml_ml8_mul_mat` constructs a CUSTOM op that performs a quantized matmul
6+
// `y = w @ x.T` where `w` is `GGML_TYPE_ML8_4` and `centroids` is its per-K-group
7+
// fp8 LUT sidecar. The CPU compute callback dequantizes `w` block-by-block via
8+
// `dequantize_row_ml8_4_with_lut` and then runs a standard fp32 dot product
9+
// against `x`.
10+
//
11+
// Rotation + AWQ are NOT inside this op — those are constructed as separate
12+
// ggml nodes (element-wise multiply + small matmuls) by the model graph builder
13+
// (G.3b). Keeping this op narrow lets us test the matmul in isolation.
14+
//
15+
// Backend support:
16+
// - CPU: dispatched via GGML_OP_CUSTOM compute callback (in ml8.c).
17+
// - HIP: NOT yet — G.4 will replace this with a typed `GGML_OP_ML8_MUL_MAT`
18+
// (or extend `GGML_OP_MUL_MAT`) and call into `mt_ml8_gemm`.
19+
//
20+
// See aiter-integration/ML8_GGUF_INTEGRATION_DESIGN.md §2.
21+
#pragma once
22+
23+
#include "ggml.h"
24+
25+
#ifdef __cplusplus
26+
extern "C" {
27+
#endif
28+
29+
// Construct a graph node computing y = w @ x.T using the ml8-4 quantized
30+
// weight `w` and its per-K-group centroid LUT `centroids`.
31+
//
32+
// Tensor shapes (ggml row-major convention):
33+
// w : [K, N] GGML_TYPE_ML8_4 (K = product of K-groups; N = out features)
34+
// centroids : [16, n_groups_k] GGML_TYPE_F8_E4M3 sidecar LUT
35+
// x : [K, M] GGML_TYPE_F32 activations
36+
//
37+
// Output:
38+
// y : [N, M] GGML_TYPE_F32 (matches plain ggml_mul_mat layout)
39+
//
40+
// Constraints:
41+
// - K must be a multiple of QK_ML8 (64)
42+
// - n_groups_k (centroids ne1) must equal K / QK_ML8
43+
// - centroids ne0 must equal 16
44+
GGML_API struct ggml_tensor * ggml_ml8_mul_mat(
45+
struct ggml_context * ctx,
46+
struct ggml_tensor * w,
47+
struct ggml_tensor * centroids,
48+
struct ggml_tensor * x);
49+
50+
// Apply a Kronecker rotation Q = H_a ⊗ H_b to the leading dim of `x`.
51+
//
52+
// Math (matches scripts/calibration/kronecker_rotation.py::KroneckerRotation.forward):
53+
// Reshape x along its leading dim from d = a*b → (b, a), then per token compute
54+
// Y = H_a^T @ X @ H_b, reshape back to d. H_b is the Sylvester Hadamard of size
55+
// b_dim, constructed internally (deterministic, no storage needed).
56+
//
57+
// Tensor shapes:
58+
// x : [d, n_tokens] GGML_TYPE_F32 (d == a_dim * b_dim)
59+
// h_a : [a_dim, a_dim] GGML_TYPE_F32 from the GGUF rotation_h_a sidecar
60+
//
61+
// Output:
62+
// y : [d, n_tokens] GGML_TYPE_F32
63+
//
64+
// Constraints:
65+
// - b_dim must be a positive power of 2
66+
// - a_dim * b_dim must equal x->ne[0]
67+
// - h_a->ne[0] == h_a->ne[1] == a_dim
68+
//
69+
// CPU-only for now (G.4 will lift this to GPU). When `h_a` is NULL, returns `x`
70+
// unchanged so callers can plumb the optional rotation uniformly.
71+
GGML_API struct ggml_tensor * ggml_ml8_apply_rotation(
72+
struct ggml_context * ctx,
73+
struct ggml_tensor * x,
74+
struct ggml_tensor * h_a,
75+
int64_t a_dim,
76+
int64_t b_dim);
77+
78+
#ifdef __cplusplus
79+
} // extern "C"
80+
#endif

ggml/include/ggml.h

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,9 @@ extern "C" {
435435
GGML_TYPE_TQ3_1S = 45, // TurboQuant 3-bit weight: WHT-rotated 8-level Lloyd-Max, block_size=32
436436
GGML_TYPE_TQ4_1S = 46, // TurboQuant 4-bit weight: WHT-rotated 16-level Lloyd-Max, block_size=32
437437
GGML_TYPE_TURBO4_FP8_BS256 = 47, // MAD-214: turbo-FP8 KV cache, 4-bit centroid idx + sign + fp16 scale, BS=256, decoded via E4M3 LUT (per-(kv,layer) runtime LUT)
438-
GGML_TYPE_COUNT = 48,
438+
GGML_TYPE_ML8_4 = 48, // MAD-223 Phase G: ml8-4 weight quant, 4-bit centroid idx + fp32 scale per 64-element block, centroid LUT in per-K-group sidecar tensor
439+
GGML_TYPE_F8_E4M3 = 49, // MAD-223 Phase G: fp8 e4m3 1-byte storage, used as sidecar dtype for ml8 centroids
440+
GGML_TYPE_COUNT = 50,
439441
};
440442

441443
// precision
@@ -592,6 +594,25 @@ extern "C" {
592594

593595
GGML_OP_GLU,
594596

597+
// MAD-223 G.4.b — ml8-4 quantized matmul with separate fp8 centroid LUT.
598+
// src[0] = w (GGML_TYPE_ML8_4, [K, N])
599+
// src[1] = centroids (GGML_TYPE_F8_E4M3, [16, K/QK_ML8])
600+
// src[2] = x (GGML_TYPE_F32, [K, M])
601+
// dst = y (GGML_TYPE_F32, [N, M])
602+
// CPU backend dequantizes block-by-block; HIP backend (G.4.f) dispatches
603+
// to mt_ml8_gemm on the native fp8 WMMA path.
604+
GGML_OP_ML8_MUL_MAT,
605+
// ml8-4 Kronecker rotation (MAD-223 Phase G.4.g). Applies the
606+
// Hadamard rotation Q = H_a ⊗ H_b to the leading dim of an
607+
// activation. H_b is the Sylvester Hadamard of size b_dim,
608+
// built deterministically — only H_a needs to be supplied.
609+
// op_params[0] = a_dim (int32)
610+
// op_params[1] = b_dim (int32, power of 2)
611+
// src[0] = x (GGML_TYPE_F32, [d=a*b, n_tokens])
612+
// src[1] = h_a (GGML_TYPE_F32, [a, a])
613+
// dst = y (GGML_TYPE_F32, [d, n_tokens])
614+
GGML_OP_ML8_APPLY_ROTATION,
615+
595616
GGML_OP_COUNT,
596617
};
597618

ggml/src/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,8 @@ add_library(ggml-base
207207
ggml-quants.c
208208
ggml-turbo-quant.c
209209
ggml-quants.h
210+
ggml-ml8.c
211+
../include/ggml-ml8.h
210212
gguf.cpp)
211213

212214
set_target_properties(ggml-base PROPERTIES

ggml/src/ggml-common.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,30 @@ typedef block_turbo5_fp8_bs256 block_turbo5_fp8;
418418
// scripts/callers that read this constant get the production value by default.
419419
#define QK_TURBO_FP8 256
420420

421+
// ──────────────────────────────────────────────────────────────────────────
422+
// MAD-223 Phase G: ml8-4 weight quantization block.
423+
//
424+
// 64 elements per block (matches calibration's group_size=64, see
425+
// scripts/calibration/calibrate_ml8.py). Each block carries:
426+
// - fp32 scale (= row's b_scale[k_group] post-multiplied by the kernel)
427+
// - 32 bytes of 4-bit centroid indices (lo-nibble first per the on-disk
428+
// .ml8 format produced by ml8_to_packed.py)
429+
//
430+
// The centroid LUT itself is per-K-group, stored as a sidecar tensor of
431+
// dtype GGML_TYPE_F8_E4M3 with shape [n_groups_k, 16]. The sidecar is loaded
432+
// + bound to the matmul graph node at model load time (see design doc
433+
// aiter-integration/ML8_GGUF_INTEGRATION_DESIGN.md §1.2).
434+
//
435+
// Rotation (Kronecker) + AWQ scale are additional sidecars per-Linear; see
436+
// design doc §1.2 / Ml8Linear forward-time math (MAD-245).
437+
// ──────────────────────────────────────────────────────────────────────────
438+
#define QK_ML8 64
439+
typedef struct {
440+
float scale; // per-block fp32 scale (post-multiply on kernel output)
441+
uint8_t qs[QK_ML8 / 2]; // 64 × 4-bit centroid indices, lo-nibble first
442+
} block_ml8_4;
443+
static_assert(sizeof(block_ml8_4) == 4 + 32, "wrong block_ml8_4 size/padding");
444+
421445
// TQ3_1S: WHT-rotated 3-bit weight quantization (8-level Lloyd-Max for N(0,1))
422446
// Block size 32, dual half-block scales (d0 for [0..15], d1 for [16..31])
423447
// Per block: d0(fp16) + d1(fp16) + 3-bit indices packed (12 bytes) = 16 bytes per 32 values

ggml/src/ggml-cpu/ggml-cpu.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2139,6 +2139,16 @@ static void ggml_compute_forward(struct ggml_compute_params * params, struct ggm
21392139
ggml_compute_forward_opt_step_sgd(params, tensor);
21402140
}
21412141
break;
2142+
case GGML_OP_ML8_MUL_MAT:
2143+
{
2144+
ggml_compute_forward_ml8_mul_mat(params, tensor);
2145+
}
2146+
break;
2147+
case GGML_OP_ML8_APPLY_ROTATION:
2148+
{
2149+
ggml_compute_forward_ml8_apply_rotation(params, tensor);
2150+
}
2151+
break;
21422152
case GGML_OP_NONE:
21432153
{
21442154
// nop
@@ -2479,6 +2489,8 @@ static int ggml_get_n_tasks(struct ggml_tensor * node, int n_threads) {
24792489
case GGML_OP_CROSS_ENTROPY_LOSS_BACK:
24802490
case GGML_OP_OPT_STEP_ADAMW:
24812491
case GGML_OP_OPT_STEP_SGD:
2492+
case GGML_OP_ML8_MUL_MAT:
2493+
case GGML_OP_ML8_APPLY_ROTATION:
24822494
{
24832495
n_tasks = n_threads;
24842496
} break;

ggml/src/ggml-cpu/ops.cpp

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@
88
#include "unary-ops.h"
99
#include "vec.h"
1010

11+
// MAD-223 G.4.c — ml8 dequant + block layout for GGML_OP_ML8_MUL_MAT.
12+
#define GGML_COMMON_DECL_CPP
13+
#include "ggml-common.h"
14+
#include "ggml-quants.h"
15+
1116
#include <algorithm>
1217
#include <cfloat>
1318
#include <cmath>
@@ -11405,6 +11410,172 @@ void ggml_compute_forward_opt_step_sgd(const ggml_compute_params * params, ggml_
1140511410
}
1140611411
}
1140711412

11413+
// ─── ggml_compute_forward_ml8_mul_mat (MAD-223 G.4.c) ─────────────────────
11414+
//
11415+
// CPU dispatch for GGML_OP_ML8_MUL_MAT. Same math as the previous custom_4d
11416+
// callback in ggml-ml8.c — dequantize w block-by-block via
11417+
// dequantize_row_ml8_4_with_lut into a per-thread fp32 scratch row, then dot
11418+
// against x. Tiled across N-rows by (ith, nth).
11419+
//
11420+
// Tensor layouts (set by ggml_ml8_mul_mat):
11421+
// dst->src[0] = w (GGML_TYPE_ML8_4 , [K, N])
11422+
// dst->src[1] = centroids (GGML_TYPE_F8_E4M3, [16, K/QK_ML8])
11423+
// dst->src[2] = x (GGML_TYPE_F32 , [K, M])
11424+
// dst = y (GGML_TYPE_F32 , [N, M])
11425+
void ggml_compute_forward_ml8_mul_mat(const ggml_compute_params * params, ggml_tensor * dst) {
11426+
const ggml_tensor * w = dst->src[0];
11427+
const ggml_tensor * lut = dst->src[1];
11428+
const ggml_tensor * x = dst->src[2];
11429+
GGML_ASSERT(w && lut && x);
11430+
GGML_ASSERT(w->type == GGML_TYPE_ML8_4);
11431+
GGML_ASSERT(lut->type == GGML_TYPE_F8_E4M3);
11432+
GGML_ASSERT(x->type == GGML_TYPE_F32);
11433+
GGML_ASSERT(dst->type == GGML_TYPE_F32);
11434+
11435+
const int64_t K = w->ne[0];
11436+
const int64_t N = w->ne[1];
11437+
const int64_t M = x->ne[1];
11438+
GGML_ASSERT(x->ne[0] == K);
11439+
GGML_ASSERT(K % QK_ML8 == 0);
11440+
const int64_t n_groups_k = K / QK_ML8;
11441+
GGML_ASSERT(lut->ne[0] == 16 && lut->ne[1] == n_groups_k);
11442+
11443+
const block_ml8_4 * w_blocks = (const block_ml8_4 *) w->data;
11444+
const uint8_t * lut_fp8 = (const uint8_t *) lut->data;
11445+
const float * x_data = (const float *) x->data;
11446+
float * y_data = (float *) dst->data;
11447+
11448+
const int ith = params->ith;
11449+
const int nth = params->nth;
11450+
const int64_t n_per_thread = (N + nth - 1) / nth;
11451+
const int64_t n_start = (int64_t) ith * n_per_thread;
11452+
const int64_t n_end = (n_start + n_per_thread < N) ? (n_start + n_per_thread) : N;
11453+
11454+
// Per-thread fp32 scratch for one row of dequantized W (K floats).
11455+
// Allocated via plain malloc/free (matches the prior ml8-ml8.c behaviour
11456+
// which ran cleanly under ggml-cpu's threadpool — replacing this with
11457+
// std::vector triggered libgomp "Thread identifier invalid" errors on the
11458+
// second forward pass in llama-cli, likely an interaction with libstdc++
11459+
// allocator thread-local state and ggml-cpu's threadpool fork model).
11460+
float * w_row_fp32 = (float *) malloc((size_t) K * sizeof(float));
11461+
if (!w_row_fp32) {
11462+
GGML_ABORT("ggml_compute_forward_ml8_mul_mat: malloc(%zu) failed",
11463+
(size_t) K * sizeof(float));
11464+
}
11465+
11466+
for (int64_t n = n_start; n < n_end; n++) {
11467+
const block_ml8_4 * w_row = &w_blocks[n * n_groups_k];
11468+
dequantize_row_ml8_4_with_lut(w_row, lut_fp8, w_row_fp32, K);
11469+
for (int64_t m = 0; m < M; m++) {
11470+
const float * x_col = &x_data[m * K];
11471+
float sum = 0.0f;
11472+
for (int64_t k = 0; k < K; k++) {
11473+
sum += w_row_fp32[k] * x_col[k];
11474+
}
11475+
y_data[m * N + n] = sum;
11476+
}
11477+
}
11478+
11479+
free(w_row_fp32);
11480+
}
11481+
11482+
// ─── ggml_compute_forward_ml8_apply_rotation (MAD-223 G.4.g) ──────────────
11483+
//
11484+
// Y[:, t] = unflatten(H_a^T @ flatten(X[:, t]) @ H_b, d=a*b)
11485+
// where H_b is the Sylvester Hadamard of size b_dim, built deterministically.
11486+
//
11487+
// op_params: int32_t[0]=a_dim, int32_t[1]=b_dim.
11488+
// src[0]=x F32 [d=a*b, n_tokens]; src[1]=h_a F32 [a, a]; dst=y F32 [d, n_tokens].
11489+
//
11490+
// Ported from the original ggml_custom_4d implementation in ggml-ml8.c.
11491+
// Uses malloc/free (NOT std::vector) for scratch; std::vector inside a
11492+
// compute callback triggers libstdc++ allocator thread-local state issues
11493+
// with ggml-cpu's OpenMP threadpool (see ggml_compute_forward_ml8_mul_mat
11494+
// above for the same constraint).
11495+
11496+
static void ml8_build_sylvester_cpu(float * H, int64_t b) {
11497+
H[0] = 1.0f;
11498+
for (int64_t n = 1; n < b; n *= 2) {
11499+
const float inv_sqrt2 = 0.70710678118654752440f;
11500+
for (int64_t i = n - 1; i >= 0; i--) {
11501+
for (int64_t j = n - 1; j >= 0; j--) {
11502+
const float v = H[i * b + j] * inv_sqrt2;
11503+
H[(i ) * b + (j )] = v;
11504+
H[(i ) * b + (j + n)] = v;
11505+
H[(i + n) * b + (j )] = v;
11506+
H[(i + n) * b + (j + n)] = -v;
11507+
}
11508+
}
11509+
}
11510+
}
11511+
11512+
void ggml_compute_forward_ml8_apply_rotation(const ggml_compute_params * params, ggml_tensor * dst) {
11513+
const ggml_tensor * x = dst->src[0];
11514+
const ggml_tensor * h_a = dst->src[1];
11515+
11516+
GGML_ASSERT(x->type == GGML_TYPE_F32);
11517+
GGML_ASSERT(h_a->type == GGML_TYPE_F32);
11518+
GGML_ASSERT(dst->type == GGML_TYPE_F32);
11519+
11520+
const int32_t * pp = (const int32_t *) dst->op_params;
11521+
const int64_t a_dim = (int64_t) pp[0];
11522+
const int64_t b_dim = (int64_t) pp[1];
11523+
const int64_t d_dim = a_dim * b_dim;
11524+
GGML_ASSERT(x->ne[0] == d_dim);
11525+
GGML_ASSERT(h_a->ne[0] == a_dim && h_a->ne[1] == a_dim);
11526+
11527+
const int64_t n_tokens = x->ne[1];
11528+
const float * x_data = (const float *) x->data;
11529+
const float * h_a_data = (const float *) h_a->data;
11530+
float * y_data = (float *) dst->data;
11531+
11532+
float * h_b = (float *) malloc((size_t) b_dim * (size_t) b_dim * sizeof(float));
11533+
if (!h_b) {
11534+
GGML_ABORT("ml8_apply_rotation: malloc h_b(%zu) failed",
11535+
(size_t) b_dim * (size_t) b_dim * sizeof(float));
11536+
}
11537+
ml8_build_sylvester_cpu(h_b, b_dim);
11538+
11539+
float * xp = (float *) malloc((size_t) d_dim * sizeof(float));
11540+
if (!xp) {
11541+
free(h_b);
11542+
GGML_ABORT("ml8_apply_rotation: malloc xp(%zu) failed", (size_t) d_dim * sizeof(float));
11543+
}
11544+
11545+
const int64_t per_thread = (n_tokens + params->nth - 1) / params->nth;
11546+
const int64_t t_start = (int64_t) params->ith * per_thread;
11547+
const int64_t t_end = (t_start + per_thread < n_tokens) ? (t_start + per_thread) : n_tokens;
11548+
11549+
for (int64_t t = t_start; t < t_end; t++) {
11550+
const float * xt = x_data + t * d_dim;
11551+
float * yt = y_data + t * d_dim;
11552+
11553+
// Step 1: xp[k, l] = sum_i H_a[i, k] * X[i, l]
11554+
for (int64_t k = 0; k < a_dim; k++) {
11555+
for (int64_t l = 0; l < b_dim; l++) {
11556+
float s = 0.0f;
11557+
for (int64_t i = 0; i < a_dim; i++) {
11558+
s += h_a_data[i * a_dim + k] * xt[i * b_dim + l];
11559+
}
11560+
xp[k * b_dim + l] = s;
11561+
}
11562+
}
11563+
// Step 2: yt[k, l] = sum_j xp[k, j] * H_b[j, l]
11564+
for (int64_t k = 0; k < a_dim; k++) {
11565+
for (int64_t l = 0; l < b_dim; l++) {
11566+
float s = 0.0f;
11567+
for (int64_t j = 0; j < b_dim; j++) {
11568+
s += xp[k * b_dim + j] * h_b[j * b_dim + l];
11569+
}
11570+
yt[k * b_dim + l] = s;
11571+
}
11572+
}
11573+
}
11574+
11575+
free(xp);
11576+
free(h_b);
11577+
}
11578+
1140811579
static void ggml_compute_forward_fwht_f32(const ggml_compute_params * params, ggml_tensor * dst) {
1140911580
const ggml_tensor * src0 = dst->src[0];
1141011581
const ggml_tensor * src1 = dst->src[1];

ggml/src/ggml-cpu/ops.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@ void ggml_compute_forward_opt_step_adamw(const struct ggml_compute_params * para
115115
void ggml_compute_forward_mul_mat(const struct ggml_compute_params * params, struct ggml_tensor * dst);
116116
void ggml_compute_forward_fwht(const struct ggml_compute_params * params, struct ggml_tensor * dst);
117117
void ggml_compute_forward_opt_step_sgd(const struct ggml_compute_params * params, struct ggml_tensor * dst);
118+
void ggml_compute_forward_ml8_mul_mat(const struct ggml_compute_params * params, struct ggml_tensor * dst);
119+
void ggml_compute_forward_ml8_apply_rotation(const struct ggml_compute_params * params, struct ggml_tensor * dst);
118120
#ifdef __cplusplus
119121
}
120122
#endif

0 commit comments

Comments
 (0)