From 7ec159e52ddab9bf478887e67c996f47b169f7e5 Mon Sep 17 00:00:00 2001 From: arrow <130365147+merkalev@users.noreply.github.com> Date: Tue, 25 Aug 2026 09:12:34 +0700 Subject: [PATCH] Retune default ladder scale to 2.5; fix inverted native speedup ratio in benchmark summary --- .github/workflows/tuning.yml | 14 +++++++++----- CHANGELOG.md | 8 ++++++++ benchmarks/codec_summary.py | 8 +++++--- docs/known-flaws.md | 7 ++++--- src/v2_core.cpp | 12 +++++++----- tools/wimf_rd_sweep.cpp | 2 +- wiki/Known-Flaws.md | 7 ++++--- 7 files changed, 38 insertions(+), 20 deletions(-) diff --git a/.github/workflows/tuning.yml b/.github/workflows/tuning.yml index 4b2b18b..9ffec0a 100644 --- a/.github/workflows/tuning.yml +++ b/.github/workflows/tuning.yml @@ -1,11 +1,12 @@ name: RD tuning sweep # Parallel parameter sweep for known-flaws A3/A4. Dispatch builds the codec -# with 8 different ladder/divisor combinations simultaneously. Each job +# with 9 different ladder/divisor combinations simultaneously. Each job # produces an RD table in the step summary. Compare all tables to pick # winning constants, then bake them into v2_core.cpp as the new defaults. -# Stock values: scale=1.5, divisor=16.0 (retuned after the photo-pattern RD -# sweep; divisor 16 adds intermediate lossy ladder steps with no regressions). +# Stock values: scale=2.5, divisor=16.0 (retuned after the photo-pattern RD +# sweeps; divisor 16 adds intermediate lossy ladder steps and scale 2.5 yields +# the finest usable ladder - nine lossy steps on natural content). on: workflow_dispatch: @@ -21,6 +22,9 @@ jobs: matrix: include: - label: stock + scale: "2.5" + divisor: "16.0" + - label: legacy-ladder scale: "1.5" divisor: "16.0" - label: steep-ladder @@ -41,8 +45,8 @@ jobs: - label: gentle-divisor scale: "1.5" divisor: "4.0" - - label: steep-divisor - scale: "2.5" + - label: steepest + scale: "3.5" divisor: "16.0" steps: - uses: actions/checkout@v6 diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b8a070..92a43ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,14 @@ All notable WIMF changes are recorded here. The project follows semantic version ## 2.2.0 - Unreleased +- Retuned the default quality ladder from scale 1.5 to 2.5 (divisor stays 16) + based on the photo/natural RD sweeps: the lossy ladder gains a ninth usable + step on natural content and every quality index produces smaller files at + smoothly increasing PSNR. Same-quality-index output is more compressed and + slightly softer than 2.1; lossless output is unchanged. Old files decode + identically - the container format is untouched. +- Fixed the codec benchmark summary printing "Native speedup 0x": the ratio + was computed inverted and is now guarded against zero reference rates. - Upgraded the scalar CRC-32 to slice-by-8: all eight slice tables are derived from the polynomial at compile time, and the bulk loop consumes eight bytes per iteration instead of one (typically 4-6x table throughput on every diff --git a/benchmarks/codec_summary.py b/benchmarks/codec_summary.py index 13fa504..fd47cc2 100644 --- a/benchmarks/codec_summary.py +++ b/benchmarks/codec_summary.py @@ -26,9 +26,11 @@ def main() -> int: ) print(native_row) print(reference_row) - if native["encode_mpx_s"] > 0 and native["decode_mpx_s"] > 0: - encode_speedup = reference["encode_mpx_s"] / native["encode_mpx_s"] - decode_speedup = reference["decode_mpx_s"] / native["decode_mpx_s"] + if reference["encode_mpx_s"] > 0 and reference["decode_mpx_s"] > 0: + # Speedup is native relative to the Python reference; skip the row + # when a reference rate rounds to zero so the ratio stays finite. + encode_speedup = native["encode_mpx_s"] / reference["encode_mpx_s"] + decode_speedup = native["decode_mpx_s"] / reference["decode_mpx_s"] print(f"| Native speedup | {encode_speedup:.0f}x | {decode_speedup:.0f}x | - |") print() print("Each runner is different hardware: compare within a table, not across tables or runs.") diff --git a/docs/known-flaws.md b/docs/known-flaws.md index ee619c1..c97d2d3 100644 --- a/docs/known-flaws.md +++ b/docs/known-flaws.md @@ -33,9 +33,10 @@ determinism, corruption rejection, and memory guards all audited clean streams. Still missing: context modeling of residuals/subbands - the structural advantage modern image codecs exploit. - **[P1] A4 Coarse quantizer dead zone.** Largely fixed: monotonic scoring - (A4), the divisor retune 8.0→16.0, and the 0.9x quantizer sub-step in tile - scoring removed the non-monotonicity and the one-ladder-notch gap that showed - as a 34-43 dB jump on the photo pattern. Remaining coarseness is minor; the + (A4), the divisor retune 8.0→16.0, the 0.9x quantizer sub-step in tile + scoring, and the ladder scale retune 1.5→2.5 leave natural-content ladders + with nine smooth lossy steps. The photo pattern retains one Q5→Q6 jump from + per-tile energy bimodality (sharp edges next to flat regions); the structural win left is the context-modeled entropy stage (A3). - **[P2] A5 Per-tile framing overhead.** Default 128 px tiles give ~2.8k independent Zstd frames per 45 MP encode (per scored mode), with no diff --git a/src/v2_core.cpp b/src/v2_core.cpp index e01490d..b961a74 100644 --- a/src/v2_core.cpp +++ b/src/v2_core.cpp @@ -18,12 +18,14 @@ #include "zstd.h" #include "v2_simd.hpp" -// Tunable codec constants. The ladder scale reproduces the historical quality -// ladder; the scoring divisor default was retuned from 8.0 to 16.0 after an -// RD sweep showed it adds intermediate lossy ladder steps with no regressions. -// The tuning workflow overrides these via -D flags to sweep candidate curves. +// Tunable codec constants. The scoring divisor and ladder scale were retuned +// from the historical 8.0/1.5 after photo-pattern RD sweeps: divisor 16 adds +// intermediate lossy ladder steps, and scale 2.5 yields the finest usable +// ladder (nine lossy steps on natural content, smaller files at every quality +// index). The tuning workflow overrides these via -D flags to sweep candidate +// curves. #ifndef WIMF_LADDER_SCALE -#define WIMF_LADDER_SCALE 1.5f +#define WIMF_LADDER_SCALE 2.5f #endif #ifndef WIMF_SCORING_DIVISOR #define WIMF_SCORING_DIVISOR 16.0 diff --git a/tools/wimf_rd_sweep.cpp b/tools/wimf_rd_sweep.cpp index 936c8f6..1e433c1 100644 --- a/tools/wimf_rd_sweep.cpp +++ b/tools/wimf_rd_sweep.cpp @@ -22,7 +22,7 @@ #include #ifndef WIMF_LADDER_SCALE -#define WIMF_LADDER_SCALE 1.5f +#define WIMF_LADDER_SCALE 2.5f #endif #ifndef WIMF_SCORING_DIVISOR #define WIMF_SCORING_DIVISOR 16.0 diff --git a/wiki/Known-Flaws.md b/wiki/Known-Flaws.md index ee619c1..c97d2d3 100644 --- a/wiki/Known-Flaws.md +++ b/wiki/Known-Flaws.md @@ -33,9 +33,10 @@ determinism, corruption rejection, and memory guards all audited clean streams. Still missing: context modeling of residuals/subbands - the structural advantage modern image codecs exploit. - **[P1] A4 Coarse quantizer dead zone.** Largely fixed: monotonic scoring - (A4), the divisor retune 8.0→16.0, and the 0.9x quantizer sub-step in tile - scoring removed the non-monotonicity and the one-ladder-notch gap that showed - as a 34-43 dB jump on the photo pattern. Remaining coarseness is minor; the + (A4), the divisor retune 8.0→16.0, the 0.9x quantizer sub-step in tile + scoring, and the ladder scale retune 1.5→2.5 leave natural-content ladders + with nine smooth lossy steps. The photo pattern retains one Q5→Q6 jump from + per-tile energy bimodality (sharp edges next to flat regions); the structural win left is the context-modeled entropy stage (A3). - **[P2] A5 Per-tile framing overhead.** Default 128 px tiles give ~2.8k independent Zstd frames per 45 MP encode (per scored mode), with no