Skip to content

Optimize find_best_path C, remove AVX2 RTCD - #5411

Merged
urvangjoshi merged 6 commits into
AOMediaCodec:mainfrom
jjustiss-apple:jjustiss/optimize-find-best-path-c
Sep 22, 2026
Merged

urvangjoshi merged 6 commits into
AOMediaCodec:mainfrom
jjustiss-apple:jjustiss/optimize-find-best-path-c

Conversation

@jjustiss-apple

@jjustiss-apple jjustiss-apple commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

Rewrites find_best_path as scalar-only code. Also creates AVM_PREFETCH in
mem.h and eliminates direct usage of __prefetch_builtin.

The AVX2 specialization of find_best_path used SSE2 intrinsics for only the
non-iqmatrix backtrack path. This folds the software-pipelined backtrack loop
from the AVX2 version into the C implementation, then removes the AVX2 RTCD
entry. The backtrack loop is memory-latency-bound (chasing prevId pointers
through the trellis), so prefetch and pointer arithmetic matter more than SIMD
ALU throughput. Removes ~100 lines of AVX2 code and the RTCD dispatch overhead.

Improvements: row pointer advancement instead of index multiply per iteration,
software prefetch of the next trellis row, precomputed dequant shift/round
constants hoisted out of loop, inlined tcq_quant() as (prev_id >> 1) & 1,
consistent XOR sign trick in both code paths.

Unit tests: FindBestPathInvariantTest (4 tests) verifies output against
independently computed TCQ invariants covering both iqmatrix=NULL and
iqmatrix!=NULL paths. FindBestPathRegressionTest (2 tests) provides bitexact
comparison against frozen pre-optimization C reference.

Micro-benchmark results (Apple Silicon P-core):

Kernel Base C (ns) Optimized C (ns) Speedup
q110_inter 135.7 121.5 1.12x
q110_intra 140.4 121.7 1.15x
q185_inter 139.4 131.2 1.06x
q185_intra 152.8 130.9 1.17x
q235_inter 155.6 148.8 1.05x
q235_intra 147.2 135.8 1.08x

Micro-benchmark results (Xeon, new C vs SSE2 AVX2-dispatch):

Kernel SSE2 (ns) New C (ns) NewC/SSE2
q110_inter 120.5 119.0 1.01x
q110_intra 119.5 118.4 1.01x
q185_inter 109.3 110.9 0.99x
q185_intra 100.8 99.7 1.01x
q235_inter 100.7 98.1 1.03x
q235_intra 93.6 92.4 1.01x

CTC Results (RA, cpu-used=1, 33 frames, A4+A5):

Metric ARM x86
Encode time -0.1% -0.2%
BD-rate Y 0.000% 0.000%
BD-rate Cb 0.000% 0.000%
BD-rate Cr 0.000% 0.000%

@jjustiss-apple
jjustiss-apple force-pushed the jjustiss/optimize-find-best-path-c branch from 6b5c508 to 6692080 Compare September 15, 2026 21:45
@jjustiss-apple

Copy link
Copy Markdown
Contributor Author

@urvangjoshi I'd like to get your advice on how to approach this PR. To give context, I've largely written ARMv7 NEON for most of TCQ and now I am working through structuring the PRs. This is more of a "clean-up" PR that I wanted to get out of the way first.

While optimizing find_best_path I found that optimizing the C scalar code gave the best results for ARM and gave a very slight optimization for x86 too. So instead of keeping a NEON (scalar only) RTCD function, I updated the C code. Since this slightly helps x86 too in my benchmarking (confirmed by CTC results), I opted for the route of eliminating the find_best_path RTCD and unifying on the scalar path.

I was worried about introducing a regression, so I kept the prior method as a unit test to ensure new-C vs old-C compliance and also tightened the overall unit testing. My plan was that the "prior method" unit test could be dropped after confidence is established.

Is this a reasonable approach?

@jjustiss-apple
jjustiss-apple marked this pull request as draft September 16, 2026 15:33
Comment thread av2/encoder/trellis_quant.c Outdated
#include "av2/encoder/tokenize.h"

#if defined(__GNUC__) || defined(__clang__)
#define AVM_PREFETCH(p) __builtin_prefetch((p), 0, 3)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be relocated to avm_ports/mem.h for broader use?

There is a error-silencing macro there for __builtin_prefetch which looks to only be used by reconinter_neon.c. Perhaps creating a more usable project-wide AVM_PREFETCH is worth the trouble?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I think it's good to move it to mem.h which already has this:

#if HAVE_NEON && defined(_MSC_VER)
#define __builtin_prefetch(x)
#endif

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, I moved AVM_PREFETCH to mem.h and updated reconinter_neon.c to use AVM_PREFETCH instead of __builtin_prefetch(x) directly

@jjustiss-apple
jjustiss-apple marked this pull request as ready for review September 16, 2026 15:49
@urvangjoshi

Copy link
Copy Markdown
Contributor

@urvangjoshi I'd like to get your advice on how to approach this PR. To give context, I've largely written ARMv7 NEON for most of TCQ and now I am working through structuring the PRs. This is more of a "clean-up" PR that I wanted to get out of the way first.

While optimizing find_best_path I found that optimizing the C scalar code gave the best results for ARM and gave a very slight optimization for x86 too. So instead of keeping a NEON (scalar only) RTCD function, I updated the C code. Since this slightly helps x86 too in my benchmarking (confirmed by CTC results), I opted for the route of eliminating the find_best_path RTCD and unifying on the scalar path.

I was worried about introducing a regression, so I kept the prior method as a unit test to ensure new-C vs old-C compliance and also tightened the overall unit testing. My plan was that the "prior method" unit test could be dropped after confidence is established.

Is this a reasonable approach?

@yunqingwang1 @jianj-g what are your thoughts on this?

@urvangjoshi
urvangjoshi requested a review from jianj-g September 18, 2026 15:41
@jianj-g

jianj-g commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

@urvangjoshi I'd like to get your advice on how to approach this PR. To give context, I've largely written ARMv7 NEON for most of TCQ and now I am working through structuring the PRs. This is more of a "clean-up" PR that I wanted to get out of the way first.
While optimizing find_best_path I found that optimizing the C scalar code gave the best results for ARM and gave a very slight optimization for x86 too. So instead of keeping a NEON (scalar only) RTCD function, I updated the C code. Since this slightly helps x86 too in my benchmarking (confirmed by CTC results), I opted for the route of eliminating the find_best_path RTCD and unifying on the scalar path.
I was worried about introducing a regression, so I kept the prior method as a unit test to ensure new-C vs old-C compliance and also tightened the overall unit testing. My plan was that the "prior method" unit test could be dropped after confidence is established.
Is this a reasonable approach?

@yunqingwang1 @jianj-g what are your thoughts on this?

I think it's reasonable if the C version is faster. You can also remove the RTCD for av2_find_best_path

@jjustiss-apple

Copy link
Copy Markdown
Contributor Author

@urvangjoshi I'd like to get your advice on how to approach this PR. To give context, I've largely written ARMv7 NEON for most of TCQ and now I am working through structuring the PRs. This is more of a "clean-up" PR that I wanted to get out of the way first.
While optimizing find_best_path I found that optimizing the C scalar code gave the best results for ARM and gave a very slight optimization for x86 too. So instead of keeping a NEON (scalar only) RTCD function, I updated the C code. Since this slightly helps x86 too in my benchmarking (confirmed by CTC results), I opted for the route of eliminating the find_best_path RTCD and unifying on the scalar path.
I was worried about introducing a regression, so I kept the prior method as a unit test to ensure new-C vs old-C compliance and also tightened the overall unit testing. My plan was that the "prior method" unit test could be dropped after confidence is established.
Is this a reasonable approach?

@yunqingwang1 @jianj-g what are your thoughts on this?

I think it's reasonable if the C version is faster. You can also remove the RTCD for av2_find_best_path

Done, all the RTCD boilerplate is removed for av2_find_best_path

@urvangjoshi
urvangjoshi force-pushed the jjustiss/optimize-find-best-path-c branch from 1a46e54 to ea736c7 Compare September 22, 2026 16:12
@urvangjoshi
urvangjoshi merged commit 2886e6a into AOMediaCodec:main Sep 22, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants