Repository navigation
fix(sycl): return the CPU's psnr_hvs scores bit for bit on SYCL and HIP - #1689
Merged
Merged
Conversation
13 of 26 tasks
lusoris
force-pushed
the
fix/psnr-hvs-sycl-hip-exact-sum
branch
from
October 1, 2026 12:54
af99885 to
05a2421
Compare
9 tasks done
lusoris
force-pushed
the
fix/psnr-hvs-sycl-hip-exact-sum
branch
3 times, most recently
from
October 1, 2026 14:25
31c7bc7 to
fa073b5
Compare
psnr_hvs_sycl and psnr_hvs_hip now return the CPU extractor's scores bit for bit, as ADR-1397 decided for the GPU twins and psnr_hvs_cuda already did. Before, both summed each block on the device and were up to 1.7e-2 dB from --backend cpu at 3840x2160, beyond the ADR-1361 parity tolerance (3.34e-3 dB). - Both kernels store the 64 terms of every block, computed in the CPU's arithmetic, and the hosts call psnr_hvs_score.c, which adds them in the CPU's order. - HIP: masking table and threshold in double, integer coefficient difference, module built with -ffp-contract=off and correctly rounded fp32 division. - SYCL: the kernel stays fp64-free (ADR-0220). The masking table is a compile-time constant. The CPU's sqrt((double)s_mask * s_gvar) comes from sqrt_prod_rn() (new in sycl_exact_fp.h): an integer product of the two significands and an integer square root, rounded to nearest. It equals the CPU's two roundings because the root of a 48-bit integer is never within a double rounding of the midpoint of two floats. The kernel uses no scratch memory: private_mem_size and spill_memory_size are 0 on an Arc A380 at SIMD16 and SIMD32. - The parity gates list sycl and hip in EXACT_TWINS, so every psnr_hvs cell is compared with tolerance 0 at --precision max. - test_sycl_psnr_hvs_parity and test_hip_psnr_hvs_parity assert equality on all four outputs, 3840x2160 included; they share core/test/psnr_hvs_twin_parity.h with the CUDA test. test_sycl_fp_arith_contract checks sqrt_prod_rn() on the device against the host's double expression. test_psnr_hvs_twin_exact_sum_contract.py covers the three twins without a device. Measured on an Arc A380 (xe driver) and a gfx1036 at --precision max: every frame of the Netflix 576x324 pairs (8, 10, 12 bits, 4:2:2), the 1920x1080 checkerboard pairs and BBB 1920x1080 / 3840x2160 (8 and 10 bits) equals the CPU extractor of the same binary. CPU scores are unchanged. The dB value uses the host's log10, which differs by one unit in the last place between an icx and a gcc build, so a twin is compared with the CPU extractor of its own binary. The exact sum costs throughput: 35.9 ms per 3840x2160 frame instead of 22.4 ms on the A380 and about 38 ms instead of 18 ms on the gfx1036, and 256 bytes of readback per block. Tuning is tracked as T-SYCL-HIP-PSNR-HVS-EXACT-SUM-THROUGHPUT-2026-10-01. Two more rows are opened: both twins refuse 4:0:0 input (T-SYCL-HIP-PSNR-HVS-YUV400-REFUSED-2026-10-01), and one of 132 runs of the HIP twin at 3840x2160 10-bit ended in a GPU memory access fault that did not recur (T-HIP-GFX1036-SDMA-READ-FAULT-2026-10-01). Closes T-SYCL-PSNR-HVS-EXACT-SUM-2026-10-01 and T-HIP-PSNR-HVS-EXACT-SUM-2026-10-01. ADR-1401, Research-1401.
lusoris
force-pushed
the
fix/psnr-hvs-sycl-hip-exact-sum
branch
from
October 1, 2026 14:39
fa073b5 to
33df23d
Compare
This was referenced Oct 1, 2026
This was referenced Oct 2, 2026
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.
Summary
psnr_hvs_syclandpsnr_hvs_hipnow return the CPU extractor's scores bit for bit, as ADR-1397 decided for the GPU twins and #1666 did forpsnr_hvs_cuda. Before, both summed each block on the device and were up to 1.66e-2 dB from--backend cpuat 3840x2160, beyond the ADR-1361 tolerance (3.34e-3 dB). ClosesT-SYCL-PSNR-HVS-EXACT-SUM-2026-10-01andT-HIP-PSNR-HVS-EXACT-SUM-2026-10-01.Based on
masterafter #1666 merged (the branch was developed on top of #1666 and rebased once it landed); it reusescore/src/feature/psnr_hvs_score.cfrom it.Both kernels store the 64 terms of every block in the CPU's arithmetic and the hosts call
psnr_hvs_score.c, which adds them in the CPU's order. The HIP kernel takes the CUDA kernel's form (masking table and threshold indouble, module built with-ffp-contract=offand correctly rounded division). The SYCL kernel has no fp64 (ADR-0220): its masking table is a compile-time constant, and the CPU'ssqrt((double)s_mask * s_gvar)comes from a newsqrt_prod_rn()insycl_exact_fp.h, an integer product of the two significands and an integer square root rounded to nearest. That equals the CPU's two roundings because the root of a 48-bit integer is never within adoublerounding of the midpoint of twofloatvalues (ADR-1401, Research-1401).This costs throughput (numbers below), which the ADR-1397 decision accepts: correctness first, tuning afterwards.
Type
feat— new featurefix— bug fixperf— performance improvementrefactor— no behavior changedocs— documentation onlytest— test-onlybuild/ci— tooling / infraport— cherry-pick from upstream Netflix/vmafsycl/cuda/simd— backend-specificChecklist
make format && make lintis green locally. The pre-commit hook set passed on the whole diff. The clang-tidy ratchet was measured on every touched translation unit in its lane (--only):integer_psnr_hvs_sycl.cpp,test_sycl_psnr_hvs_parity.c,test_sycl_fp_arith_contract.candtest_sycl_fp_arith_probe.cpp(SYCL),integer_psnr_hvs_hip.candtest_hip_psnr_hvs_parity.c(HIP),test_cuda_psnr_hvs_parity.c(CUDA), withsycl_exact_fp.handpsnr_hvs_twin_parity.hthrough them: 0 findings in the touched files.test_hip_psnr_hvs_parity.cwent from 18 to 0, sotidy-baseline-hip.jsonis tightened (make tidy-ratchet-write, scoped). The whole-tree lint was not run.python3 scripts/ci/run_meson_test.py -- -C build --suite=faston a CPU build and on a CPU + CUDA build (RTX 4090); on the SYCL build (Arc A380)test_sycl_psnr_hvs_parity,_simd32,_large,test_sycl_fp_arith_contract,test_sycl_shared_planes,test_sycl_kernel_scratch; on the HIP build (gfx1036)test_hip_psnr_hvs_parity,_large;test_psnr_hvs_twin_exact_sum_contractandtest_psnr_hvs_scoreeverywhere./cross-backend-diffand the worst ULP is ≤ 2. The worst difference is 0: every output of every frame is bit-identical to the CPU (table below)..c/.cpp/.cu/.h/.hpp, it has the appropriate license header (seeCONTRIBUTING.md).!orBREAKING CHANGE:and the migration path is documented below.docs/adr/_index_fragments/<NNNN-slug>.mdand the slug is appended todocs/adr/_index_fragments/_order.txt— do not editdocs/adr/README.mddirectly (regenerated byscripts/docs/concat-adr-index.sh; see ADR-0221).Bug-status hygiene (ADR-0165)
docs/state.mdupdated in this PR:T-SYCL-PSNR-HVS-EXACT-SUM-2026-10-01andT-HIP-PSNR-HVS-EXACT-SUM-2026-10-01moved to Recently closed;T-SYCL-HIP-PSNR-HVS-EXACT-SUM-THROUGHPUT-2026-10-01(RC3),T-SYCL-HIP-PSNR-HVS-YUV400-REFUSED-2026-10-01(RC3) andT-HIP-GFX1036-SDMA-READ-FAULT-2026-10-01(deferred) opened.Netflix golden-data gate (ADR-0024)
assertAlmostEqual(...)score in the Netflix golden Python tests.No CPU extractor code changes.
Cross-backend numerical results
--precision max,psnr_hvson--backend cpuagainst the twin of the same binary. Arc A380 (dg2-g11,xedriver, compute runtime 26.35.39758.10, IGC 2.41.5, oneAPI 2026.0.0, JIT) and gfx1036 (ROCm 7.2.4). Largest absolute difference in dB overpsnr_hvs,psnr_hvs_y,psnr_hvs_cbandpsnr_hvs_cr; "before" isorigin/masterc66d28b2fwith #1666 applied.Identical frames: 210 of 210 on each twin (0 of 210 before). The parity gate on the Netflix pair and on all 200 frames of BBB 3840x2160 reports
tol=0.0e+00 (exact:ADR-1397) max_abs_diff=0.000e+00 OKforcpu ↔ sycland forcpu ↔ hip. The 10-bit 1920x1080 and 3840x2160 fixtures are ffmpeg conversions of the 8-bit ones whose distorted side carriesnoise=alls=6:allf=t; BBB 1920x1080 is a bicubic downscale.Further checks:
private_mem_size0 andspill_memory_size0 at SIMD16 and withIGC_ForceOCLSIMDWidth=32.sqrt_prod_rn()has 0 mismatches against the host's(float)sqrt((double)a * (double)b)on 1 312 917 operand pairs on the A380 (test_sycl_fp_arith_contract: random, boundary, and thek (k + 1)pairs nearest a rounding midpoint) and on 450 million on the host. Afloatproduct and root differ for 34 % of random pairs.floatproduct and root in the threshold on 3 of the 48 Netflix frames (4.4e-7 dB), afloatmasking table on 9 of them (7.4e-7 dB).log10: the SYCL build (icx, Intellibimf) and a gcc build (glibc) differ by one unit in the last place on 3 of the 48 Netflix frames, on the CPU extractor and on the twin alike.Performance (if
perforfeat)Slower, as expected. ms per frame of
vmaf --backend <b> --no_prediction --feature psnr_hvs, (t(N) − t(2)) / (N − 2), N = 960 at 576x324 (the Netflix pair concatenated 20 times) and 102 at the larger sizes; each cell is the median over four passes (three for the 10-bit row) of the median of three runs, before and after binaries back to back. Other sessions shared the host (load average 12 to 29); each device was held under its lock.The SYCL passes agree within 0.8 ms at 3840x2160. The HIP figures are noisy: the integrated GPU shares memory with the busy host, and its 3840x2160 passes spread from 17.6 to 28.7 ms before and from 30.6 to 44.9 ms after. Both twins were slower than sixteen CPU threads before this change as well. The increase is the readback (64.8 MB per 3840x2160 frame instead of 1.0 MB) and the sequential host sum;
T-SYCL-HIP-PSNR-HVS-EXACT-SUM-THROUGHPUT-2026-10-01tracks the tuning, with the candidates of the CUDA row.Deep-dive deliverables (ADR-0108)
docs/research/1401-psnr-hvs-sycl-hip-exact-twins.md.## Alternatives considered.AGENTS.mdinvariant note —core/src/feature/sycl/AGENTS.md(the exact-sum contract andsqrt_prod_rn()),core/src/feature/hip/AGENTS.md,core/src/feature/AGENTS.md(psnr_hvs_score.ccallers, thelog10note),core/src/feature/cuda/AGENTS.md(shared test header),scripts/ci/AGENTS.md(EXACT_TWINS), and the index entry indocs/development/rebase-sensitive-invariants.md.changelog.d/fixed/sycl-hip-psnr-hvs-cpu-float-sum.md.docs/rebase-notes.md, "ADR-1401 — psnr_hvs_sycl and psnr_hvs_hip return the CPU's scores bit for bit".Reproducer
On the previous twins
test_sycl_psnr_hvs_parityandtest_hip_psnr_hvs_parityfail intest_psnr_hvs_cpu_*_identical(3.8e-6 dB at 256x144), the contract test reports 27 failures, and the gate reportsmax_abs_diff=8.370e-05 FAILunder the new contract.Known follow-ups
T-SYCL-HIP-PSNR-HVS-EXACT-SUM-THROUGHPUT-2026-10-01: both exact twins are slower than sixteen CPU threads at every size measured.T-SYCL-HIP-PSNR-HVS-YUV400-REFUSED-2026-10-01: both twins refuse 4:0:0 input, which the CPU extractor andpsnr_hvs_cudascore on luma. Found adding the shared parity cases;docs/metrics/psnr-hvs.mdclaimed luma-only scoring for all three twins and is corrected here.T-HIP-GFX1036-SDMA-READ-FAULT-2026-10-01: one run of the new HIP twin on 24 frames of the 3840x2160 10-bit fixture was killed by a GPU memory access fault that the kernel log attributes to the copy engine (SDMA0, a read). 131 further runs of that command and 126 runs of the previous twin did not fault. The cause is not established; the twin now moves 65 times more data through that engine per frame.scripts/ci/test_cross_backend_feature_names.pyfails onmaster(3 tests still expect thevulkanbackend removed by ADR-0726);T-CI-PARITY-GATE-STALE-METRIC-KEYS-2026-09-29already tracks it.(ADR-ADRNUM)placeholder perf(hip): upload native samples and convert on device in psnr_hvs #1658 left inpsnr_hvs_score.hip; a GPU parity test without a device now exits 77 (skipped) instead of 0 for the threepsnr_hvstwin tests.Breaking changes / migration
None in the API.
psnr_hvs_syclandpsnr_hvs_hipscores change in their last digits (by up to 8.4e-5 dB at 576x324 and 1.7e-2 dB at 3840x2160) to the values--backend cpureturns.