ggml: INT8/ternary types and fused CPU ops for the VibeASR pipeline - #438
Closed
XsquirrelC wants to merge 3 commits into
Closed
ggml: INT8/ternary types and fused CPU ops for the VibeASR pipeline#438XsquirrelC wants to merge 3 commits into
XsquirrelC wants to merge 3 commits into
Conversation
Owner
|
@XsquirrelC Thanks for bringing the official VibeASR integration to audio.cpp! One minor issue. I built the unittests but failed. Could you document the exact CMake command you used to build and run it? That would make the expected validation path clear. Alternatively, the test could run through the public ggml backend registry API so it works in both static and dynamic backend builds, but documenting the intended test build configuration is enough. |
Author
|
Closing in favour of #447. You asked for two PRs in microsoft/VibeASR.cpp#10 — one for the additive ggml changes, one for the model integration — so this four-PR stack has been reorganized into exactly that: #447 (ggml) and #448 (model). Same code, no functional change; sorry for the churn. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds the ggml pieces VibeASR's INT8 CPU pipeline needs, ahead of the model port itself. Purely additive: two new types, five new ops, no change to any existing type, op, or code path.
What's here
GGML_TYPE_I8_S/GGML_TYPE_I2_S— int8 and ternary weights with a per-tensor scale, one F32 stored after the payload (padded to 32 bytes so arena alignment holds). Per-tensor rather than per-block is deliberate: a 32-value-block int8 type would just beQ8_0.ggml_type_extra_bytes()is the single place the layout is encoded; it returns 0 for every other type, so nothing existing changes size.Five fused ops —
mul_mat_add,mul_mat_add_relu,add_scaled,rms_norm_scaled,im2col_asym. They keep activations int8 end to end: each op requantizes its own output and writes the new scale in-band, so the chain never detours through F32.im2col_asymexists becauseggml_im2colcannot express asymmetric padding, which the VAE's causal convs need.Requantization is three-phase — stage F32 into
params->wdata,ggml_barrierand reduce absmax across threads, then quantize the slice — so results are independent of thread count.AVX2 + NEON kernels for the I8_S ops. The dot product uses the
maddubssign trick on AVX2 andvdotq_s32(orvmull_s8+vpadalq_s16without dotprod) on NEON, widening to int32 every block rather than batching in int16, since eachmaddubslane already reaches 32512. Anything past the vector width is done scalar, so contraction lengths shorter than one step are handled rather than truncated away.Measurements
x86-64 AVX2, single thread. Three changes, separately attributed:
roundf→rintfMost of the matmul gain came from the requantizer, not the dot product:
roundfwas a PLT call per element.rintfalso fixes a consistency problem — it rounds half-to-even, matching_mm256_cvtps_epi32andvcvtnq_s32_f32, so each vector body agrees with its own scalar tail.Both
mul_mat_addpaths batch their dot products, with the operand roles inverted between them: the matmul path batches over output channels, the depthwise path over positions — in each case so the strided operand becomes the batched one and the results land contiguous, which is what lets the scale+bias+absmax pass vectorize too.Why not
TQ2_0Worth addressing up front, since ggml already ships a ternary type. Every I2_S tensor in the model is shape-compatible with it (
n_embd=1536,n_ff=8960, both divisible by 256), andTQ2_0costs only 3% more (2.0625 vs 2.0000 bit/weight) with finer per-block scales. So it looked like the right answer until measured — same machine, same shapes, single thread:The prefill gap is multi-column GEMM blocking, which
TQ2_0's per-columnvec_dotpath does not have. Over 28 layers that is 3.31 s vs 1.69 s for a 130-token prefill. The I2_S kernels are not in this PR yet — this is the argument for why they are coming rather than being dropped in favour ofTQ2_0.Tests
tests/unittests/test_i8_s_fused_ops.cpp, 24 cases, every op against a plain-loop reference computed from the same dequantized inputs. Comparisons are byte-exact wherever both sides do the same arithmetic; where the op reaches the same value by different arithmetic (integer sum-of-squares vs float normalize) the tolerance is one quantization step.Shapes are chosen to hit the edges rather than round numbers:
IC ∈ {13, 64, 67}for all-scalar / no-tail / has-tail contractions,OC ∈ {48, 100}for chunk wraparound, depthwiseK ∈ {7, 36} × N ∈ {19, 150}. Every case runs at 1 and 4 threads, so a missing barrier shows up as a scale that depends on thread count.Two mutation tests confirm the assertions have teeth: disabling the scalar tail fails
IC=67, and reintroducing int16 accumulation failsIC=64by 31%.Full suite: 40/40, no skips.