As VLSE128.V does not exist, we have no other way to deal with latency.
T-Head C908:
h264_weight16_8_c: 989.4 ( 1.00x)
h264_weight16_8_rvv_i32: 193.2 ( 5.12x)
SpacemiT X60:
h264_weight16_8_c: 874.1 ( 1.00x)
h264_weight16_8_rvv_i32: 196.9 ( 4.44x)
The height is a power of two of up to 16 rows. The current code was
optimised for large sample counts.
T-Head C908:
h264_weight2_8_c: 211.7 ( 1.00x)
h264_weight2_8_rvv_i32: before 184.0 ( 1.15x)
h264_weight2_8_rvv_i32: after 54.2 ( 3.90x)
h264_weight4_8_c: 285.7 ( 1.00x)
h264_weight4_8_rvv_i32: before 341.2 ( 0.86x)
h264_weight4_8_rvv_i32: after 82.2 ( 3.47x)
h264_weight8_8_c: 498.7 ( 1.00x)
h264_weight8_8_rvv_i32: before 683.7 ( 0.73x)
h264_weight8_8_rvv_i64: after 128.5 ( 3.95x)
h264_weight16_8_c: 878.2 ( 1.00x)
h264_weight16_8_rvv_i32: unchanged 239.5 ( 3.67x)
SpacemiT X60:
h264_weight2_8_c: 207.2 ( 1.00x)
h264_weight2_8_rvv_i32: before 259.6 ( 0.80x)
h264_weight2_8_rvv_i32: after 82.2 ( 2.52x)
h264_weight4_8_c: 290.8 ( 1.00x)
h264_weight4_8_rvv_i32: before 509.6 ( 0.57x)
h264_weight4_8_rvv_i32: after 61.5 ( 4.73x)
h264_weight8_8_c: 498.8 ( 1.00x)
h264_weight8_8_rvv_i32: before 1019.8 ( 0.49x)
h264_weight8_8_rvv_i64: after 71.8 ( 6.95x)
h264_weight16_8_c: 874.0 ( 1.00x)
h264_weight16_8_rvv_i32: unchanged 249.0 ( 3.51x)
There is no known (real) hardware with V and without the complete B
extension. B was indeed required in the RISC-V application profile from
2022, earlier than V. There should not be any relevant hardware in the
future either.
In practice, different R-V Vector optimisations in FFmpeg already depend on
every constituent of the B extension anyhow, so it would not work well.
These are really just wrappers for idct4_add16intra functions, which are in
turn mostly wrappers for idct4_add and idct4_dc_add functions.
For benchmarks refer to the later two sets.
Unlike the 8-bit version, we need two iterations to process this within
128-bit vectors. This adds some extra complexity for pointer arithmetic
and counting down which is unnecessary in the 8-bit variant.
Accordingly the gain relative to C are just slight better than half as
good with 128-bit vectors as with 256-bit ones.
T-Head C908 (2 iterations):
h264_idct8_add_9bpp_c: 17.5
h264_idct8_add_9bpp_rvv_i32: 10.0
h264_idct8_add_10bpp_c: 17.5
h264_idct8_add_10bpp_rvv_i32: 9.7
h264_idct8_add_12bpp_c: 17.7
h264_idct8_add_12bpp_rvv_i32: 9.7
h264_idct8_add_14bpp_c: 17.7
h264_idct8_add_14bpp_rvv_i32: 9.7
SpacemiT X60 (single iteration):
h264_idct8_add_9bpp_c: 15.2
h264_idct8_add_9bpp_rvv_i32: 5.0
h264_idct8_add_10bpp_c: 15.2
h264_idct8_add_10bpp_rvv_i32: 5.0
h264_idct8_add_12bpp_c: 14.7
h264_idct8_add_12bpp_rvv_i32: 5.0
h264_idct8_add_14bpp_c: 14.7
h264_idct8_add_14bpp_rvv_i32: 4.7
There are two implementations here:
- a generic scalable one processing two columns at a time,
- a specialised processing one (fixed-size) row at a time.
Unsurprisingly, the generic one works out better with smaller widths.
With larger widths, the gains from filling vectors are outweighed by
the extra cost of strided loads and stores. In other words, memory
accesses become the bottleneck.
T-Head C908:
h264_weight2_8_c: 54.5
h264_weight2_8_rvv_i32: 13.7
h264_weight4_8_c: 101.7
h264_weight4_8_rvv_i32: 27.5
h264_weight8_8_c: 197.0
h264_weight8_8_rvv_i32: 75.5
h264_weight16_8_c: 385.0
h264_weight16_8_rvv_i32: 74.2
SpacemiT X60:
h264_weight2_8_c: 48.5
h264_weight2_8_rvv_i32: 8.2
h264_weight4_8_c: 90.7
h264_weight4_8_rvv_i32: 16.5
h264_weight8_8_c: 175.0
h264_weight8_8_rvv_i32: 37.7
h264_weight16_8_c: 342.2
h264_weight16_8_rvv_i32: 66.0
While this *tends* to be faster than plain C, the performance numbers
are all over the place, presuambly due to the conditional character of
the main loop.
Some additional micro-optimisations should be feasible after the
underlying h264_idct_add and h264_idct_dc_add functions are also
implemented. Then it will no longer be necesseray to stricly abide by
the C ABI.
Performance is (unfortunately) the same as with non-MBAFF, since the
hardware under test does not short-circuit vector tail calculations.
(IMO, a generic solution or work-around should be agreed on, rather
than bespoke approaches all over the place.)
T-Head C908 (cycles):
h264_h_loop_filter_luma_8bpp_c: 297.5
h264_h_loop_filter_luma_8bpp_rvv_i32: 369.2
h264_v_loop_filter_luma_8bpp_c: 862.7
h264_v_loop_filter_luma_8bpp_rvv_i32: 199.7
Performance in the horizontal scenario seems worse than scalar. x86
SSE2 and AVX optimisations are similarly affected. This is presumably
caused by unlucky inputs from checkasm, such that the C code
short-circuits almost all filter calculations.
The main loop processes 8 bytes in 5 instructions.
For comparison, the optimal plain strnlen() requires 4 instructions per
byte (6.4x worse): LBU; ADDI; BEQZ; BNE. The current libavcodec C code
involves 5 instructions per byte (8x worse). Actual benchmarks may be
slightly less favourable due to latency from ORC.B to BNE.