Skip to content

perf(cuda): motion — 8-frame SAD batching to reduce per-launch overhead (ADR-0845) - #217

Merged
lusoris merged 1 commit into
masterfrom
perf/cuda-motion-launch-overhead-20260529
Jun 3, 2026
Merged

lusoris merged 1 commit into
masterfrom
perf/cuda-motion-launch-overhead-20260529

Conversation

@lusoris

@lusoris lusoris commented May 29, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Replaces the per-frame cuStreamSynchronize pattern in integer_motion_cuda.c with an 8-frame batch fence (MOTION_BATCH_DEPTH=8), reducing synchronization calls from once-per-frame to once-per-8-frames.
  • Research-0760 (2026-05-29) shows the CUDA motion kernel is dispatch-bottlenecked at all resolutions below 4K: GPU busy fraction <0.1% at 576p, CUDA/CPU ratio 0.22x. Kernel execution (7 µs at 576p) is irrelevant — the bottleneck is ~12.7 ms/frame of driver round-trip overhead.
  • Expected improvement: 576p from ~79 fps to ~800 fps (CUDA/CPU from 0.22x to >2x). 4K performance unchanged.

Design

  • sad[MOTION_BATCH_DEPTH] — ring of 8 independent device SAD buffers (one per frame slot).
  • submit() zeros slot index % 8, launches kernel, chains event to s->str. No DtoH.
  • Batch-boundary collect() (every 8th frame): cuStreamSynchronize(s->str), queue 8 DtoH copies, sync again, emit all 8 scores via emit_batch_scores().
  • flush() handles the final partial batch.
  • emit_batch_scores() saves/restores s->frame_index per frame to preserve ADR-0219 motion3 moving-average semantics.
  • motion_cuda is removed from the drain_batch engine optimization (ADR-0242) — it no longer registers finished events.

Correctness gate

ADR-0214 places=4 cross-backend correctness check required before promoting to READY. The motion3_postprocess_cuda frame_index save/restore pattern is designed to produce bit-identical scores to the pre-patch path.

Test plan

  • Build with -Denable_cuda=true in vmaf-dev-mcp:cuda13.3 container
  • Correctness: vmaf --feature motion --backend cuda on Netflix 576x324 fixture, compare to CPU at places=4
  • A/B fps measurement at 576p, 1080p, 4K vs baseline from Research-0760
  • Full VMAF pipeline correctness (all features, not just motion)
  • Confirm MOTION_BATCH_DEPTH=1 degrades back to per-frame behavior

Reproducer

# Build
docker exec vmaf-dev-mcp bash -c "cd /workspace/core && ninja -C build"

# Correctness (576p, places=4)
docker exec vmaf-dev-mcp /build/vmaf/core/build/tools/vmaf \
  --reference /yuv/src01_hrc00_576x324.yuv \
  --distorted /yuv/src01_hrc01_576x324.yuv \
  --width 576 --height 324 --pixel_format 420 --bitdepth 8 \
  --feature motion --backend cuda --output /tmp/cuda_motion.json

# Compare to CPU reference
docker exec vmaf-dev-mcp /build/vmaf/core/build/tools/vmaf \
  --reference /yuv/src01_hrc00_576x324.yuv \
  --distorted /yuv/src01_hrc01_576x324.yuv \
  --width 576 --height 324 --pixel_format 420 --bitdepth 8 \
  --feature motion --output /tmp/cpu_motion.json

Checklist

  • ADR filed: ADR-0845
  • Research digest — no digest needed: design analysis in ADR-0845; perf baseline is Research-0760 (pre-existing on scaffold branch)
  • Decision matrix — ADR-0845 ## Alternatives considered complete
  • AGENTS.md invariant note — Motion SAD batch fencing: MOTION_BATCH_DEPTH invariants (ADR-0845)
  • Reproducer / smoke-test command — see Reproducer section above
  • CHANGELOG fragment — changelog.d/perf/cuda-motion-batch-launch-adr0845.md
  • Rebase note — docs/rebase-notes.md entry added
  • docs/state.md row added
  • docs/backends/cuda/overview.md drain_batch participation list updated
  • DRAFT — A/B measurement + places=4 correctness check pending

no rebase impact: this PR only touches integer_motion_cuda.c (fork-local, no upstream counterpart), AGENTS.md, and docs. No upstream Netflix/vmaf C source is modified.

🤖 Generated with Claude Code

@lusoris

lusoris commented May 31, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by master after 143-PR merge marathon 2026-05-31; diff-extract empty.

@lusoris lusoris closed this May 31, 2026
@lusoris
lusoris deleted the perf/cuda-motion-launch-overhead-20260529 branch May 31, 2026 13:34
@lusoris
lusoris restored the perf/cuda-motion-launch-overhead-20260529 branch May 31, 2026 18:42
@lusoris lusoris reopened this May 31, 2026
@lusoris
lusoris marked this pull request as draft May 31, 2026 18:49
…ad (ADR-0845)

Replaces the per-frame cuStreamSynchronize pattern with an 8-frame batch
fence (MOTION_BATCH_DEPTH=8): submit() queues kernel launches into per-slot
device SAD buffers without DtoH readback; collect() defers sync + readback to
every 8th frame boundary; flush() handles the final partial batch.

Expected improvement: 576p from ~79 fps to ~800 fps (CUDA/CPU 0.22x -> >2x).
ADR-0845 / Research-0760.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@lusoris
lusoris force-pushed the perf/cuda-motion-launch-overhead-20260529 branch from 01b6d23 to 4e941be Compare June 3, 2026 13:30
@lusoris
lusoris marked this pull request as ready for review June 3, 2026 13:30
Copilot AI review requested due to automatic review settings June 3, 2026 13:30
@lusoris
lusoris merged commit f4879cc into master Jun 3, 2026
32 of 37 checks passed
@lusoris
lusoris deleted the perf/cuda-motion-launch-overhead-20260529 branch June 3, 2026 13:30
@lusoris
lusoris removed the request for review from Copilot June 3, 2026 13:54
lusoris added a commit that referenced this pull request Jun 7, 2026
…89 stub (#841)

- docs/state.md: moved T-CUDA-MOTION-SAD-BATCH-PENDING-2026-05-29 from
  Open to Recently Closed; PR #217 merged 2026-06-03 (ADR-0845). Row for
  T-PIC-PREALLOC-RECURRING-FAILURE and T-SYCL-MOTION-ADD-UV-SIGSEGV were
  already present in Recently Closed; no further action required for those.
- changelog.d/changed/hw-backend-audit.md: corrected PR reference from
  #733 to #733.
- changelog.d/fixed/restore-vkpipelinecache-pr867.md: deleted; referenced
  PR #1067 which does not exist on VMAFx/vmafx (confirmed via gh).
- changelog.d/added/vmafx-server-go.md: removed line referencing nonexistent
  PR #1583 (confirmed via gh); remainder of fragment kept intact.
- changelog.d/changed/0573-dev-container-ubuntu-26-04-cuda-13-2.md:
  replaced "Closes PR #1330" (nonexistent PR, confirmed via gh) with a
  neutral past-tense statement.
- docs/rebase-notes.md: updated DEFAULT_FALLBACKS invariant to reflect
  ADR-0726 Vulkan removal — tuple is now ("cuda","sycl","hip","cpu").
  Replaced `libvmaf` with `core` in DNN multi-output smoke command (ADR-0700
  rename). Converted three forward-looking archival claims to past tense:
  PR #351/#374 "in-flight" references, PR #379 "if round-2 not yet merged",
  PR #439 "will rebase cleanly" — all three PRs have since merged.
- docs/adr/0789-rust-crate-audit.md.stub: deleted; 9-day-old reservation
  expired without producing a full ADR file.

Co-authored-by: Lusoris <lusoris@pm.me>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
lusoris added a commit that referenced this pull request Jun 12, 2026
…CI) (#868)

* fix(codeql): resolve HIGH-severity security-cpp-high alerts (23 sites)

- cpp/integer-multiplication-cast-to-long (11): pre-cast one operand to
  size_t / double / ptrdiff_t before int*int multiplications in
  cambi.c, float_vif.c (log message), iqa/convolve.c (img_offset),
  moment.c, psnr.c, and vif_tools.c (four memcpy size expressions).
  Add stddef.h to convolve.c for ptrdiff_t.

- cpp/incomplete-parity-check (3): change `% 2 == 1` to `% 2 != 0`
  in vif_tools.c (assert), svm.cpp (powi loop), pdjson.c (JSON
  object key/value alternation). The == 1 form is wrong for negative
  operands; != 0 is always correct.

- cpp/wrong-type-format-argument (2): fix float_vif.c error log that
  printed size_t fields scaled_w/scaled_h with %d; change to %zu.

- cpp/world-writable-file-creation (1): in vmaf.cpp replace bare
  fopen("wb") with open(O_WRONLY|O_CREAT|O_TRUNC, 0644)+fdopen() on
  POSIX so the created file is never world-writable independent of the
  caller's umask. Add <fcntl.h>.

- cpp/path-injection (4): in test_output.c resolve the mkstemp-created
  path through realpath() immediately after creation, breaking the taint
  chain from getenv("TMPDIR") to the vmaf_write_output call site.

- cpp/toctou-race-condition (2): skipped — both sites are in test
  cleanup (RMDIR after stat assertion). The stat result drives a test
  assertion, not a security-sensitive access decision; no atomic
  replacement of open() is applicable to rmdir. Reported as skipped.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(codeql): resolve security-python-and-ci CodeQL alerts

Fixes 18 open CodeQL alerts across the Python and CI categories:

- yaml.github-actions.security.run-shell-injection (#661): move
  github.event_name, github.base_ref, and github.event.before from
  inline ${{...}} interpolation to env: vars in the SYCL clang-tidy
  detect step of lint-and-format.yml.

- python.lang.security.use-defused-xml-parse (#216, #217): replace
  xml.etree.ElementTree with defusedxml.ElementTree in
  feature_extractor.py and quality_runner.py; add defusedxml>=0.7.1
  to python/pyproject.toml and python/requirements.txt.

- py/undefined-export (#352, #353, #354, #616): restructure
  aiutils/__init__.py to do a conditional eager import of the parquet
  helpers so the names are defined when pyarrow is present, and only
  include them in __all__ when the import succeeded.

- py/stack-trace-exposure (#178, #179, #585): log exception detail
  server-side and return a generic message to the HTTP client in
  http_transport.py _handle_score (invalid JSON, bad params, scorer
  error branches).

- python.lang.security.audit.dangerous-subprocess-use-tainted-env-args
  (#227, #372): add shlex.quote() around user-supplied path arguments
  passed into shell strings in extract_ugc_features.py and
  test_bbb_e2e_v5_bug_cluster.py.

- py/file-not-closed (#677, #678): replace bare open() calls with
  context managers in test_coverage_round3.py.

- py/redundant-comparison (#427, #431): remove redundant
  assert not (x != y) lines that duplicate the preceding assert x == y.

- py/equals-hash-mismatch (#182): convert RdPoint to frozen=True
  dataclass so __eq__ and __hash__ are generated consistently.

- py/inheritance/signature-mismatch (#197): add result_dict=None
  default to EnsembleVmafQualityRunner._populate_result_dict so the
  signature is compatible with the base class.

- py/multiple-definition (#201): drop redundant assignment to
  feature_found in feature_extractor.py wildcard discovery path.

- py/str-format/surplus-named-argument (#204): remove unused
  dataset= kwarg from the format() call in routine.py.

Skipped: python.lang.security.audit.insecure-file-permissions (#373) —
  the Unix socket at 0o660 is intentional (Go sidecar node must write
  to it and runs as the same UNIX group); tightening to 0o644 would
  break the IPC channel.

Skipped: py/path-injection (#180, #181) — _validate_path() already
  resolves the path and checks it against an allowlist before any file
  operation; the data flow is secure and the CodeQL dataflow trace is a
  false positive on this allowlisted pattern.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Lusoris <lusoris@pm.me>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
@lusoris lusoris added this to the 1.0.0 — First release milestone Sep 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant