Repository navigation
fix(simd): add the NEON and SVE2 float_moment lanes in the scalar's order so aarch64 returns the scalar bits past 2^53 units - #1923
Merged
Conversation
Contributor
Author
|
This PR also fixes the The self-test runs the CPU Under
|
lusoris
force-pushed
the
fix/arm-moment-scalar-order
branch
from
October 3, 2026 14:43
bb69fb9 to
e1b1503
Compare
…rder so aarch64 returns the scalar bits past 2^53 units (#1923) * fix(simd): add the NEON and SVE2 float_moment lanes in the scalar's order so aarch64 returns the scalar bits past 2^53 units The NEON and SVE2 float_moment kernels now return the scalar function's bits on every input and on every SVE vector length. The NEON kernel added into lane accumulators across the frame. The SVE2 kernel reduced each row's vector sum, grouped by the vector length. On a 16-bit frame the second moment's double passes 2^53 units of 2^-16, and from there every add rounds, so those groupings returned other numbers. On 3841x2160 16-bit noise under qemu-aarch64 the result was 37917.601966364906 against the scalar's 37917.601964216701. Both kernels now store each vector of samples and add the lanes into one double one after the other, in raster order, as moment_avx2.c does. The second moment squares the samples in float first. test_moment_simd asserts == for AVX2, AVX-512, NEON and SVE2 on frames past 2^53 and at the boundary. Under qemu-aarch64 it fails on master for NEON and for SVE2 at 128, 256, 512 and 2048 bits, and it passes with the change. x86 object code does not change. test_moment_simd and test_iqa_convolve read vmaf_get_cpu_flags() without calling vmaf_init_cpu() first, so the call returned 0. Their SVE2 and NEON cases never ran on any aarch64 processor. Both tests now ask vmaf_get_cpu_flags_arm(). ADR-1500 supersedes the 1e-7 tolerance of ADR-0179, ADR-0584 and ADR-0987. * docs: regenerate the indexes and the citation map after rebasing
Merged
12 of 17 tasks
lusoris
force-pushed
the
fix/arm-moment-scalar-order
branch
from
October 3, 2026 14:56
e1b1503 to
219ee7d
Compare
lusoris
added a commit
that referenced
this pull request
Oct 3, 2026
…t results by bits (ADR-1502) (#1927) * fix(codeql): fix the open CodeQL alerts in code and compare exact test results by bits (ADR-1502) Fixes 67 of the 71 open CodeQL alerts without changing a library or tool object file (GCC and clang, x86-64 and aarch64, -Db_lto=false, cmp of every object). No score moves. The exact-twin, replay and recorded-value tests asserted bit identity with `==`, which calls +0 equal to -0. They now use core/test/float_bits.h: vmaf_test_identical_f64/_f32 hold when the bit patterns match and the value is not a NaN, so the helper is never weaker than the `==` it replaces. test_float_bits pins it and fails for `a == b` and for a bit compare without the NaN rule. Other fixes: the three SpEED base-entropy products convert the float product's result explicitly (ADR-1477's arithmetic unchanged); adm.h and motion.h get include guards and adm_tools.h / adm_csf_tools.h open theirs before the includes; adm_options.h names the dead ADM_OPT_DEBUG_DUMP switch in prose; run_netflix_value_tests() keeps one body in both meson variants of test_speed_upstream_form; the CLI test fake keeps a comparison result instead of a caller's pointer. Left open: 1338 and 1339 (white-box tests that include ciede.c and integer_psnr.c, dismissed before as 88 and 96) and 1392 and 1393 (test_float_moment_sum.c, which open PR #1921 moves). * test(float_bits): split the signed-zero and neighbour cases to stay under the branch budget clang-tidy readability-function-size counted 16 branches (threshold 15) in test_signed_zeros_and_neighbours_differ() in all five lanes. The cases are unchanged; they now run as two tests. * test(moment): compare the SIMD moments with the scalar by bits The check_frame() comparison #1923 added asserts "a SIMD moment is not the scalar's bits" with `!=`, which CodeQL reports (cpp/equality-on-floats) and which accepts +0 for -0. It now uses vmaf_test_identical_f64() (ADR-1502). AVX2, AVX-512, NEON and SVE2 (qemu) runs pass.
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
The aarch64 CPU
float_momentnow returns the scalar function's bits on every input and on every SVE vector length.compute_*_moment_neon()added into lane accumulators across the frame.compute_*_moment_sve2()reduced each row's vector sum, so its grouping depended on the vector length. On a 16-bit frame whose sum of float squares passes 2^53 units, the scalardoublerounds on every add, and both groupings returned different numbers. The maintainer movedT-ARM-MOMENT-NEON-SVE2-SUM-ORDER-2026-10-03from RC7 to RC3 (popup, 2026-10-03: "RC3, fix before rc.3 (Recommended)").Both kernels now store each vector of samples (squared in
floatfor the second moment) and add the lanes into onedoubleone after the other in raster order. That is the scalar loop's order, andmoment_avx2.c/moment_avx512.calready use it. The SVE2 kernel adds the firstsvcntp_b32active lanes of asvwhilelt_b32predicate, so it makes the same adds at every vector length. ADR-1500 records the decision. It supersedes the 1e-7 tolerance of ADR-0179, ADR-0584 and ADR-0987 for every moment SIMD kernel.The PR also fixes one test bug found on the way, the same line in two files.
test_moment_simdandtest_iqa_convolvegated their aarch64 cases onvmaf_get_cpu_flags(), which reads 0 untilvmaf_init_cpu()has run, and neither test calls it. As a result, the SVE2 moment cases and the NEON convolution cases were skipped on every processor. Both tests now askvmaf_get_cpu_flags_arm().Closes
T-ARM-MOMENT-NEON-SVE2-SUM-ORDER-2026-10-03andT-ARM-MOMENT-SVE2-TEST-NEVER-RAN-2026-10-03. OpensT-ARM-MOMENT-SCALAR-ORDER-COST-2026-10-03(RC8): the kernels' speed on aarch64 hardware has not been measured.Type
fix— bug fixsycl/cuda/simd— backend-specificChecklist
make format && make lintis green locally:scripts/dev/tidy-lane.sh: cpu 70 = baseline 70. On arm64,test_moment_simd.cwent from 1 warning to 0, and the baseline was tightened by a scoped--write(114). No file is above its baseline.praetorctl auditpasses: HISS 227 within 227, 29 touched files clean.scripts/dev/preflight.sh --stage msvcismpasses.qemu-aarch64: simd suite, 23 OK.test_moment_simdunder the five qemu settings below./cross-backend-diffand the worst ULP is ≤ 2: 0 ULP. Every SIMD moment kernel is==to the scalar function on every test frame (table below)..c/.cpp/.cu/.h/.hpp, it has the appropriate license header (seeCONTRIBUTING.md). No new source file.!orBREAKING CHANGE:and the migration path is documented below. Not a breaking change.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 with a row in the appropriate section:T-ARM-MOMENT-NEON-SVE2-SUM-ORDER-2026-10-03is removed from the RC7 disposition and closed in Recently closed.T-ARM-MOMENT-SVE2-TEST-NEVER-RAN-2026-10-03is added to Recently closed.T-ARM-MOMENT-SCALAR-ORDER-COST-2026-10-03is opened under RC8.T-ARM-SIMD-GATES-SVE2-VECTOR-LENGTH-UNVERIFIED-2026-10-02gets a progress note.Netflix golden-data gate (ADR-0024)
assertAlmostEqual(...)score in the Netflix golden Python tests.make test-netflix-golden-arm64(GCC 16.1 cross build underqemu-aarch64): 280 passed, 3 skipped. It ran on8e4f2f9c5, the same code before the rebase ontoa8723a182, which changed no CPU source. x86 object code is unchanged (below), so the x86 gate cannot move.Cross-backend numerical results
test_moment_simdunderqemu-aarch6411.1.1, kernels of6a3c26270(master) against this PR. Every case compares both moments against the scalar function with==:The NEON second moment on the noise frame was 37917.601966364906, against the scalar's 37917.601964216701. x86: AVX2 and AVX-512 are
==on every case.x86 object code: every object and library of a GCC x86 build (183 libvmaf objects,
libvmaf.so,libvmaf.a) has the same sha256 with and without this change. Onlytest_moment_simd.c.odiffers.Performance (if
perforfeat)Not measured on aarch64 hardware: the kernels have only run under emulation. They now make one dependent add per sample, as the scalar loop and the x86 kernels do. The measurement and any tuning that keeps the bits are tracked in
T-ARM-MOMENT-SCALAR-ORDER-COST-2026-10-03(RC8).Deep-dive deliverables (ADR-0108)
AGENTS.mdinvariant note:core/src/feature/arm64/AGENTS.md: twin-update row, SVE2 section, CPU-flag note for tests.core/src/feature/AGENTS.d/float-moment.mdandcore/src/feature/x86/AGENTS.d/moment.mdupdated.docs/development/rebase-sensitive-invariants.md.changelog.d/fixed/arm-float-moment-scalar-order.md.docs/rebase-notes.md, "The NEON and SVE2float_momentkernels add in the scalar's order (ADR-1500, 2026-10-03)".Reproducer
meson setup build-a64 core --cross-file build-aux/aarch64-linux-gnu.ini \ -Denable_cuda=false -Denable_sycl=false -Denable_hip=false ninja -C build-a64 test/test_moment_simd test/test_iqa_convolve for cpu in max,sve=off max,sve128=on max,sve256=on max,sve512=on \ max,sve2048=on,sve-default-vector-length=256; do qemu-aarch64 -cpu "$cpu" -L /usr/aarch64-linux-gnu build-a64/test/test_moment_simd done qemu-aarch64 -cpu max -L /usr/aarch64-linux-gnu build-a64/test/test_iqa_convolve # 19 cases, was 6Known follow-ups
T-ARM-MOMENT-SCALAR-ORDER-COST-2026-10-03(RC8): measure the kernels on aarch64 hardware.float_psnr_neon: row sums are exact at 16 bits.float_adm_neon: the reductions are built but not dispatched (T-FLOAT-ADM-X86-SCALAR-STAGES-2026-10-02).convolve_neonandspeed_neon: one sum per lane.