{"slug": "show-hn-faster-enhancer-c-c-library-for-stable-real-time-on-device-denoising", "title": "Show HN: faster-enhancer.c – C library for stable real-time on-device denoising", "summary": "A dependency-free C/SIMD int8 runtime for FastEnhancer-Medium at 48 kHz achieves a 0.069 real-time factor on a single Apple M2 core and 0.096 on a Galaxy S23+, with no inference framework, no heap allocation after startup, and no retraining. The library, published by developer aask1357, runs the unchanged FastEnhancer weights using six hand-written int8 GEMM kernel tiers and a 565,108-byte W8A8 weight blob, producing byte-identical output across ARM NEON, DOTPROD, and I8MM tiers.", "body_md": "A dependency-free C/SIMD int8 runtime for **FastEnhancer-Medium** at 48 kHz —\nstreaming speech enhancement that runs in a fraction of one CPU core, with no\ninference framework, no heap allocation after startup, and no retraining.\n\nIt runs the [published FastEnhancer weights](https://github.com/aask1357/fastenhancer)\nunchanged. The architecture is untouched; only the runtime is specialized.\n\n```\n0.069 real-time factor   on one Apple M2 core      (I8MM, racing)\n0.096                    on a Galaxy S23+          (Snapdragon 8 Gen 2)\n565,108 bytes            W8A8 weight blob\n162 KiB                  static library, macOS arm64\n```\n\nOne fixed model, six hand-written int8 GEMM kernel tiers, one of which is\nselected at initialization. There is no scalar fallback: a host that does not\nmeet the baseline ISA fails at `fe_init`\n\nrather than silently taking a slow\npath.\n\n**Training-free.** Post-training quantization only. No QAT and no calibration set — activation ranges are recomputed from each frame.**Streaming and causal.** 320 samples in, 320 out, no look-ahead. The STFT contributes a fixed 704-sample (14.67 ms) alignment delay.**Allocation-free after init.** All state lives in one 432,384-byte structure, so a long run cannot drift into allocator jitter.**Byte-identical across tiers.** Within an architecture family, NEON, DOTPROD and I8MM produce bit-for-bit identical output, enforced by a regression gate. The same holds for the x86 tiers.\n\nThe public API is four functions.\n\n``` js\n#include \"fe.h\"\n\nint  fe_init (const void *weights_blob, int weights_size);\nvoid fe_run  (const float *in, float *out);   /* 320 in -> 320 out */\nvoid fe_reset(void);\nvoid fe_free (void);\n```\n\nA minimal streaming loop:\n\n```\nif (fe_init(weights, weights_size) != 0) return -1;\nwhile (read_320_samples(in_buf)) {\n    fe_run(in_buf, out_buf);   /* in_buf == out_buf is fine (in-place) */\n    write_320_samples(out_buf);\n}\nfe_free();\ncmake -S . -B build -DCMAKE_BUILD_TYPE=Release\ncmake --build build -j\n```\n\nNo external dependencies. Produces `libfe.a`\n\nplus the runners in `build/`\n\n.\n\nTo try it on real audio, fetch the test clips first (needs `curl`\n\nand\n`ffmpeg`\n\n; the clips are downloaded from their original location rather than\nredistributed here):\n\n```\n./testaudio/fetch.sh\n./build/fe_run_file weights/fe.q8 testaudio/street_5dB.f32 out.wav 5569 i8mm\n```\n\n`fe_run_file`\n\nreports per-frame percentiles against the 6.667 ms frame budget.\nTwo environment knobs are useful: `FE_QOS=hi|lo|off`\n\npicks a QoS class, and\n`FE_PACE=1`\n\nfeeds one frame per frame period instead of racing.\n\nMeasured with 5569-frame clips, steady-state percentiles after discarding the first 200 frames, median over repeats.\n\n| Device | Tier | p50 RTF | p99 RTF | ms/frame |\n|---|---|---|---|---|\n| Apple M2 | I8MM | 0.069 |\n0.084 | 0.458 |\n| Apple M2 | DOTPROD | 0.068 | 0.081 | 0.452 |\n| Apple M2 | NEON | 0.163 | 0.187 | 1.085 |\n| Galaxy S23+ | I8MM | 0.096 |\n0.105 | 0.640 |\n| Galaxy S23+ | DOTPROD | 0.113 | 0.122 | 0.753 |\n| Galaxy S23+ | NEON | 0.344 | 0.365 | 2.293 |\n\nTwo things worth knowing before reading those as deployment cost.\n\n**A benchmark races; an audio callback does not.** Feeding frames as fast as\nthe core accepts them holds the clock at maximum. A real callback delivers one\nframe per 6.667 ms and lets the core idle, and the governor responds. Paced on\nan M2 P-core the same work costs 4.2x more per frame (0.286 RTF) while using\n49% less energy. Both are real-time; they answer different questions.\n\n**The x86 tiers are not timed here.** They build and pass the tier-equality\ngate, but this table is ARM only.\n\nThe int8 engine is compared against the fp32 ONNX graph of the same weights, on the engine's own causal analysis grid. Scoring a streaming engine against a default centered-STFT reference measures framing phase rather than quantization error: the two grids differ by 192 samples, which is not a multiple of the 320-sample hop, so the offset does not cancel.\n\nOver the 824-utterance VoiceBank-DEMAND test set at its native 48 kHz:\n\n| PESQ | STOI | SNR | LSD | |\n|---|---|---|---|---|\n| noisy input | 1.967 | 0.9211 | 8.39 | 14.72 |\n| fp32 ONNX | 3.060 | 0.9512 | 19.43 | 12.56 |\nfe q8 |\n3.054 | 0.9509 | 19.35 | 12.33 |\n\nThe port tracks the fp32 model to -0.006 PESQ and -0.08 dB SNR. Quantization costs about 1.6% of what the enhancement itself gains.\n\n| ISA | Tier | Instruction | Requires |\n|---|---|---|---|\n| arm64 | NEON | `vmull_s8` -> `vmlal_s8` -> `vpadalq_s16` |\nbaseline |\n| arm64 | DOTPROD | `vdotq_laneq_s32` |\nFEAT_DotProd |\n| arm64 | I8MM | `vmmlaq_s32` |\nFEAT_I8MM |\n| x86-64 | AVX2 | `vpmovsxbw` + `vpmaddwd` |\nAVX2 + FMA3 + F16C |\n| x86-64 | AVX-VNNI | `vpdpbusd ymm` |\nAlder Lake+, Zen 4 |\n| x86-64 | AVX-512 VNNI | `vpdpbusd zmm` |\nIce Lake / SPR, Zen 4 |\n\nSSE4.1 is deliberately unsupported: without FMA3 the dequantization epilogue becomes a two-step rounding that drifts until bit-identity breaks.\n\nThe AVX-512 tier is verified functionally under Intel SDE; no AVX-512 host was available to time it.\n\nBit-identity across tiers is a gate, not an aspiration:\n\n```\nRUNNER=build/fe_run_file FRAMES=500 ./tools/sha256_matrix.sh\n```\n\nEvery tier within an architecture family must produce the same SHA-256 per clip. Run it before and after any non-trivial change and diff the output; an empty diff means no byte-level regression.\n\n`build/fe_test_cross_tier`\n\nperforms the same check in-process and also reports\ncross-tier SNR.\n\n`weights/fe.q8`\n\nis the production blob (565,108 bytes, 511,754 parameters). To\nregenerate it from the upstream ONNX export:\n\n```\npython3 tools/onnx_to_bin.py --onnx fastenhancer_m_spec.onnx \\\n        --variant medium --out weights/fe.fp32.bin\npython3 tools/quantize_bin.py --in weights/fe.fp32.bin --out weights/fe.q8\n```\n\nWeights are per-output-row symmetric int8; activations are per-tensor\nasymmetric uint8, recomputed every frame. Both are clamped to `[-127, 127]`\n\nrather than the full int8 range. Discarding one code point makes int16\naccumulation provably overflow-free and removes the one value that breaks\ncross-tier reproducibility, at a measured cost of 0.000 PESQ to three\ndecimals.\n\n```\ninclude/fe.h          the only public header\nsrc/\n  fe_pipeline.c       public ABI\n  fe_engine.c         per-frame pipeline\n  fe_stft.c fe_fft.c  causal STFT/iSTFT, tiered 1024-point real FFT\n  nn/                 conv, GRU, MHSA, activations, GEMM wrappers\n  qgemm/{arm,x86}/    per-tier int8 GEMM kernels\n  winograd/           F(2,3) microkernels\n  fft/                per-tier FFT kernels\ntests/                fe_run_file, fe_test_cross_tier, fe_bench_qgemm\ntools/                weight pipeline + regression gate\ndocs/                 architecture, optimization notes\n```\n\n[docs/architecture.md](/kdrkdrkdr/faster-enhancer.c/blob/main/docs/architecture.md)— layer inventory, shapes, and the per-frame dataflow.[docs/optimizations.md](/kdrkdrkdr/faster-enhancer.c/blob/main/docs/optimizations.md)— quantization design, the six kernel tiers, fp16 cross-stage storage, op fusion, and measurement methodology. It also separates numbers backed by deposited artifacts from numbers that exist only as prose in that document.\n\nThe model, its architecture and its weights are FastEnhancer, by Sunghwan Ahn\net al. — see [aask1357/fastenhancer](https://github.com/aask1357/fastenhancer).\nThis project is an independent runtime port and is not affiliated with the\nauthors.\n\nKernel patterns were informed by ggml/llama.cpp, XNNPACK, oneDNN and ARM\nKleidiAI; see `NOTICE`\n\nand the Sources table in `docs/optimizations.md`\n\n.\n\nTest clips are the RNNoise demo samples, fetched from their original location\nby `testaudio/fetch.sh`\n\n.", "url": "https://wpnews.pro/news/show-hn-faster-enhancer-c-c-library-for-stable-real-time-on-device-denoising", "canonical_source": "https://github.com/kdrkdrkdr/faster-enhancer.c", "published_at": "2026-07-28 09:25:39+00:00", "updated_at": "2026-07-28 09:52:31.637605+00:00", "lang": "en", "topics": ["machine-learning", "developer-tools"], "entities": ["FastEnhancer-Medium", "Apple M2", "Galaxy S23+", "Snapdragon 8 Gen 2", "aask1357"], "alternates": {"html": "https://wpnews.pro/news/show-hn-faster-enhancer-c-c-library-for-stable-real-time-on-device-denoising", "markdown": "https://wpnews.pro/news/show-hn-faster-enhancer-c-c-library-for-stable-real-time-on-device-denoising.md", "text": "https://wpnews.pro/news/show-hn-faster-enhancer-c-c-library-for-stable-real-time-on-device-denoising.txt", "jsonld": "https://wpnews.pro/news/show-hn-faster-enhancer-c-c-library-for-stable-real-time-on-device-denoising.jsonld"}}