# MiraTTS CPU/Vulkan optimization validation Baseline: `f3bd8255` (upstream main merged into the MiraTTS branch). Tested on Windows, September 8, 2026: Ryzen 9 7950X3D, RTX 3090 (Vulkan device 1), Release/MSVC builds, eight CPU threads. ## Changes - CPU FlashSR: accumulate convolution taps across contiguous output positions rather than calculating one complete output sample at a time. This exposes independent samples to SIMD while retaining the input-channel/tap accumulation order for each sample, including omission of out-of-range taps. Work is still partitioned into disjoint 256-position blocks. No weight or precision changes. - Vulkan MiraTTS: use the existing model-local speech/EOS output-head view on Vulkan as well as CPU. The view shares the original embedding buffer and keeps its existing lifetime. It avoids projecting unused vocabulary rows, not changing the sampling alphabet, weights, or token IDs. The shared Qwen runtime is untouched. The first pass retains the existing Vulkan attention/precision settings; the later decode-only KV-view pass is documented below. - `AUDIOCPP_MIRA_TTS_SPARSE_HEAD=0` disables the local head on CPU or Vulkan for diagnostic comparisons. CUDA retains the existing full-head behavior. ## Method Reference: `assets/resources/b.wav`. Models: local `mira-tts-q8.gguf` and `mira-tts-bf16.gguf`. Request parameters/seeds come from `offline_requests.json` and `streaming_requests.json`. Offline cases cover cold, repeat, changed prompt, long request, and repeat after the long request in one session. The offline token budgets cap outputs at 5.12/15.36 seconds; these are lifecycle/parity tests, not proof that the complete prompt was spoken. Timings are per-request wall times, excluding initial model loading. Cold means the first request in a prepared session, not total process startup. Runs were serial with no competing inference. One before/after sequence per precision is reported; these are local measurements, not statistically bounded guarantees. CPU baseline uses the original FlashSR implementation, candidate uses the new loop. Vulkan compares the full head (`AUDIOCPP_MIRA_TTS_SPARSE_HEAD=0`) with the local head (`1`). Both Vulkan runs use the same candidate binary, so the only runtime difference is the head switch. Reproduction (change backend, device, precision, and mode as appropriate): ```powershell .\build\windows-vulkan-release\bin\mira_tts_warm_bench.exe --model ..\models_v3_test\MiraTTS-GGUF\mira-tts-q8.gguf --backend vulkan --device 1 --threads 8 --voice-ref assets/resources/b.wav --request-file tests/mira_tts/offline_requests.json --run-mode offline --model-spec-override model_specs/mira_tts.json --audio-out-dir ../outputs/candidate --summary-file ../outputs/candidate.json --log-file ../outputs/candidate.log python tests/mira_tts/compare_optimization.py --before ../outputs/baseline.json --after ../outputs/candidate.json ``` ## Results | Backend / weights | Request | Before (s) | After (s) | Time reduction | |---|---|---:|---:|---:| | CPU Q8 | clone_cold | 8.245 | 7.446 | 9.7% | | CPU Q8 | clone_repeat | 8.210 | 7.445 | 9.3% | | CPU Q8 | short_second_prompt | 8.211 | 7.407 | 9.8% | | CPU Q8 | longform | 26.923 | 24.106 | 10.5% | | CPU Q8 | clone_repeat_after_longform | 8.180 | 7.457 | 8.8% | | CPU Q8 | stream_cold | 32.852 | 28.893 | 12.0% | | CPU Q8 | stream_repeat | 33.243 | 28.826 | 13.3% | | CPU BF16 | clone_cold | 8.933 | 8.121 | 9.1% | | CPU BF16 | clone_repeat | 8.851 | 8.170 | 7.7% | | CPU BF16 | short_second_prompt | 8.832 | 8.488 | 3.9% | | CPU BF16 | longform | 28.677 | 26.416 | 7.9% | | CPU BF16 | clone_repeat_after_longform | 8.767 | 8.383 | 4.4% | | CPU BF16 | stream_cold | 36.138 | 33.739 | 6.6% | | CPU BF16 | stream_repeat | 36.528 | 32.801 | 10.2% | | Vulkan Q8 | clone_cold | 0.994 | 0.940 | 5.5% | | Vulkan Q8 | clone_repeat | 0.881 | 0.815 | 7.5% | | Vulkan Q8 | short_second_prompt | 0.930 | 0.829 | 10.8% | | Vulkan Q8 | longform | 2.942 | 2.743 | 6.8% | | Vulkan Q8 | clone_repeat_after_longform | 0.937 | 0.881 | 6.0% | | Vulkan Q8 | stream_cold | 3.747 | 3.444 | 8.1% | | Vulkan Q8 | stream_repeat | 3.739 | 3.432 | 8.2% | | Vulkan BF16 | clone_cold | 1.015 | 1.065 | -4.9% | | Vulkan BF16 | clone_repeat | 0.928 | 0.857 | 7.7% | | Vulkan BF16 | short_second_prompt | 0.951 | 0.863 | 9.3% | | Vulkan BF16 | longform | 3.173 | 2.898 | 8.7% | | Vulkan BF16 | clone_repeat_after_longform | 1.017 | 0.999 | 1.8% | | Vulkan BF16 | stream_cold | 3.973 | 3.687 | 7.2% | | Vulkan BF16 | stream_repeat | 3.969 | 3.681 | 7.3% | All 28 paired requests have identical complete WAV bytes (SHA-256), reported audio hashes, frame counts, sample formats, durations, and event counts. `validate_bench.py` passes for all four backend/precision combinations, including repeat-after-long-form and streaming repeat determinism. Every streaming request produced four audio events. Cross-backend/precision outputs are not expected to be identical; each candidate is compared only to its matching baseline. The BF16 Vulkan cold-request regression in this sequence illustrates startup variability; warm requests and streaming improved. No universal cold-start speedup is claimed. An additional short-prompt Q8 CPU check ("Hello, this is a native Mira TTS test.", seed 1234, max_tokens 1024, same reference and eight threads) produced 6.18 seconds of identical audio: repeated-request wall time 10.222 -> 8.994 s, and FlashSR stage time 2.085 -> 1.110 s. This is separate from the capped fixtures in the table. `flashsr_utility_test` passes its two Python-reference fixtures plus eight input length cases (1, 7, 85, 86, 255, 256, 257, 1025) checking finite output, 3x length, and exact one/eight-thread agreement around convolution block boundaries. The six no-model tests for `compare_optimization.py` also pass, including rejection of changed WAV bytes despite an unchanged reported hash. Local artifacts: `../outputs/perf-cpu-{q8,bf16}-{before,after}-{offline,streaming}` and `../outputs/perf-vk-{q8,bf16}-{0,1}-{offline,streaming}`, with sibling `.json` summaries and `.log` traces. Large WAV/model files are not added to the repository. ## Experiments not adopted - Raising CPU thread count from 4 to 16 improved elapsed time, but changed the generated hash and duration under otherwise identical sampling parameters. It is not an exact-output optimization; the default thread count is unchanged. - CPU graph rebuilding costs only a few milliseconds versus seconds in generation/decoding. Its repeat-determinism safeguard is retained. - No lower precision, changed sampling defaults, shortened token budgets, or reduced audio quality settings were introduced. ## Second CPU pass: local accumulator and pointer-based convolution The local comparison tree (`TEST_22_audio.cpp_qwen_3.8`, `src/community_models/miratts/dsp_internal.h`) uses a small stack accumulator and pointer-based convolution loops to help MSVC vectorize. This pattern is adapted to FlashSR's residual convolutions and final output convolution, rather than replacing MiraTTS's generator/decoder wholesale. Each tile is written to the output once. The input-channel/tap summation order and final `tanhf` are unchanged. Empty tap intersections are rejected before forming input pointers. The short-prompt Q8 comparison was rerun against a saved first-pass binary on the same machine: repeated request 9.005 -> 8.671 s (3.7% less time), FlashSR 1.115 -> 0.637 s (42.8% less time), identical complete WAV. The generator's runtime varied slightly between runs, so stage and total improvements differ. Additional experiments were rejected and removed: - Packing only speech/EOS weight rows: same audio, but CPU slowed slightly and Vulkan's warm improvement was only about 1.5%, insufficient to justify an extra copied weight allocation in this pass. - Skipping odd/noncontributing upsampling taps with a variable-bound loop: preserved audio but did not improve measured performance. - Restricting the eight-thread benchmark process to either 16-logical-CPU group: slower than the unrestricted process. No affinity settings persist, and no affinity/default-thread-count changes are included. | CPU weights | Request | First pass (s) | Second pass (s) | Time reduction | |---|---|---:|---:|---:| | Q8 | clone_cold | 7.446 | 7.018 | 5.7% | | Q8 | clone_repeat | 7.445 | 7.035 | 5.5% | | Q8 | short_second_prompt | 7.407 | 7.082 | 4.4% | | Q8 | longform | 24.106 | 22.381 | 7.2% | | Q8 | clone_repeat_after_longform | 7.457 | 7.016 | 5.9% | | Q8 | stream_cold | 28.893 | 28.053 | 2.9% | | Q8 | stream_repeat | 28.826 | 27.965 | 3.0% | | BF16 | clone_cold | 8.121 | 7.550 | 7.0% | | BF16 | clone_repeat | 8.170 | 7.553 | 7.6% | | BF16 | short_second_prompt | 8.488 | 7.644 | 9.9% | | BF16 | longform | 26.416 | 23.891 | 9.6% | | BF16 | clone_repeat_after_longform | 8.383 | 7.504 | 10.5% | | BF16 | stream_cold | 33.739 | 31.376 | 7.0% | | BF16 | stream_repeat | 32.801 | 31.435 | 4.2% | All 14 second-pass sequence outputs are byte-identical to their first-pass counterparts; both precisions pass the offline/streaming lifecycle validator. The FlashSR reference/boundary tests and comparison-tool unit tests also pass. The first-pass sequence timings were collected earlier on the same machine; the fresh short-prompt comparison above corroborates the stage-level benefit. These are local observations, not multi-trial confidence intervals. Artifacts: `../outputs/round2-cpu-{q8,bf16}-{offline,streaming}` and their sibling JSON/log files. CPU/Vulkan CLI and server targets were rebuilt. There is no additional Vulkan algorithm change in this pass; its earlier optimization is retained. The alternate source tree was inspected read-only. ## Third pass: Vulkan decode-only KV views Vulkan now uses `FlashGroupedViewKV` for single-token decoding, avoiding materialization of grouped cached heads on each token. Prefill remains `FlashGrouped`: the previous strided-prefill precision issue is not bypassed. F32 projection precision, sampling, token budgets and CPU execution are unchanged. Set `AUDIOCPP_MIRA_TTS_VULKAN_VIEW_DECODE=0` to compare the previous decode path. RTX 3090, Vulkan device 1, eight host threads, same fixtures as above: | Weights | Request | Before (s) | After (s) | Time reduction | |---|---|---:|---:|---:| | Q8 | clone_repeat | 0.816 | 0.792 | 2.9% | | Q8 | longform | 2.787 | 2.674 | 4.1% | | Q8 | stream_repeat | 3.395 | 3.424 | -0.9% | | BF16 | clone_repeat | 0.853 | 0.824 | 3.3% | | BF16 | longform | 2.977 | 2.731 | 8.3% | | BF16 | stream_repeat | 3.735 | 3.551 | 4.9% | All 14 paired offline/streaming WAV files are byte-identical, and both lifecycle validators pass. Q8 streaming did not improve in this measurement; the change does not claim universal speedups. Other Vulkan devices are not validated here. The initial BF16 offline runs had abnormal first-request times (61–65 seconds) in **both** modes. Their cause is unconfirmed. These timings are excluded from the table; a fresh paired rerun returned to normal timings. Artifacts are retained for inspection rather than treating the anomaly as an optimization gain. Artifacts: `../outputs/round3-vk-{q8,bf16}-{0,1}-{offline,streaming}`; the BF16 offline table uses `../outputs/round3-rerun-bf16-{0,1}.json`. The final default build is additionally exercised in `../outputs/round3-final-{q8,bf16}.json`. Use `compare_optimization.py` with the corresponding before/after JSON paths. ### CPU pool experiment rejected An opt-in persistent ggml thread pool was tested in an off/on/on/off sequence. Repeated short-request timings were 8.747 / 8.551 / 8.748 / 8.632 seconds, with identical WAVs. This does not establish a repeatable improvement, so the pool code was removed. The current build uses OpenMP, which already reuses OS worker threads; allocating a ggml pool per graph is not equivalent to creating new OS threads per token. The accepted CPU convolution optimizations remain intact. ## Fourth pass: no additional optimization retained The following experiments were removed rather than claiming noise as a gain: - CPU direct-convolution tiles enlarged from 256 to 1024: repeated short Q8 request 8.684 -> 8.660 s, cold request 8.699 -> 8.722 s. Inconclusive. - CPU activation downsampling tiled across positions with the same tap order: repeated request 8.739 s, slower than the 8.684 s baseline. - Channel-major direct interpolation was tried, but initially benchmarked on Vulkan, which uses the GGML upsampler rather than this CPU DSP function. Those measurements do not validate the CPU optimization; it was removed. The CPU tile and downsampling WAVs match the baseline byte-for-byte; FlashSR utility tests pass. Artifacts: `../outputs/round4-cpu-tile-*` and `../outputs/round4-cpu-downsample.*`. The Vulkan `round4-interp-*` and `round4-tile-*` measurements are retained only as diagnostic artifacts, not evidence for a speedup. Previous accepted optimizations remain unchanged.