From 947cd85537e938d70bba4a8b52177a728005331d Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Sun, 16 Aug 2026 02:52:19 +0000 Subject: [PATCH] avcodec/ppc: HEVC tuning pass 4 from the 2026-08-16 G5 bench Registration-level changes only; every kernel stays compiled and referenced (the width>=8 loop keeps all of them live), so any of this can be restored by deleting one line. The first 970 bench of the full HEVC import (2.3 GHz G5, checkasm old CLI, runs 1024) draws exactly the same width-8 line PowerVLC's 7447A bench drew: all 273 width>=8 entries win (median 4.14x, not one loser or wash) and every width-4/6 FILTER kernel loses, 0.62-0.94x at C times of 5-70 ticks. The narrow-width filter registration was G5-only on the theory that the 970's strong narrow-width numbers for our own put-qpel kernels would extend to the imported filters; they don't. - MC_SET filter slots (qpel/epel h/v/hv, plain/uni/bi/uni_w, both depths): floor is now width 8 (index 3) on BOTH CPUs. - Whole-pel pixels slots (put/bi/uni_w) split into their own loop keeping the per-CPU floor: they are the only narrow entries the 970 likes (pel_pixels 4/6 1.85-1.98x, bi_pixels6 1.22x, uni_w_pixels6 1.13-1.25x; the pixels4 bi/uni_w forms are 3-5-tick washes), so the 970 keeps every width and the 7447A keeps its width-8 floor. - 8-bit SAO index 0 (width 8): unregistered on both CPUs, was G4-only-off. 7447A: band 0.62x, edge 0.98x; 970: band 0.93x, edge 1.00x. The 10-bit index 0 keeps winning on both (970: band 2.34x, edge 4.26x) and stays. Our own three 8-bit put-qpel slots are untouched and keep every width on both CPUs (970 2026-08-16: h4 2.03x, hv4 4.35x, 10-20x wide). Validated: qemu -cpu 7400 checkasm hevc_pel/sao/idct/add_res seeds 1/42/777 + full suite 897/897 (the G4 shape is behaviour-identical by construction and measures so); -D_ARCH_PWR4 compile warning-free; G5 shape proven by symbol (hevcdsp.o compiled -D_ARCH_PWR4, linked ahead of the G4-shape .a, all 226 dumped init slots nm-resolved): the two shapes now differ in exactly the three narrow whole-pel pixels slots, and sao[0]/narrow-filter slots resolve to the C functions in both. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_016GiBYUZQuCGEQwud2w5who --- libavcodec/ppc/hevcdsp.c | 58 ++++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/libavcodec/ppc/hevcdsp.c b/libavcodec/ppc/hevcdsp.c index 52d7e76..602a1c3 100644 --- a/libavcodec/ppc/hevcdsp.c +++ b/libavcodec/ppc/hevcdsp.c @@ -331,27 +331,34 @@ static void put_hevc_qpel_hv_altivec(int16_t *dst, const uint8_t *src, * copies are already memcpy in C. The ten entries of each array are width * classes and every one of these handles any width. * - * MC_WIDTH_FLOOR: hevc_pel_weight maps block widths 2/4/6 to indices 0/1/2 - * and width 8 to index 3. PowerVLC's checkasm --bench on a 7447A (2026-08-07) - * has every narrow kernel losing to C and every wide one winning, with no + * Width floors: hevc_pel_weight maps block widths 2/4/6 to indices 0/1/2 and + * width 8 to index 3. Below 8 pixels per row, building the permute vectors + * and splicing the unaligned loads costs more than the filter itself, and + * both CPUs agree. PowerVLC's checkasm --bench on a 7447A (2026-08-07) has + * every narrow kernel losing to C and every wide one winning, with no * overlap at all: width 4 42/42 lose (median 0.69x), width 6 42/42 lose * (0.78x), width 8 0/42 lose (1.92x), width 12 0/42 (1.45x), width 16+ 0/168 - * (2.52x-3.14x). Below 8 pixels per row, building the permute vectors and - * splicing the unaligned loads costs more than the filter itself. That bench - * was taken with -mcpu=7400 scheduling and says nothing about the 970, whose - * own numbers for our narrow-width HEVC qpel kernels are strongly pro-vector - * (h4 1.99x, h6 3.15x, hv4 4.32x, 2026-07-28), so the floor is G4-only. + * (2.52x-3.14x). A 2.3 GHz 970 bench (2026-08-16) draws the same width-8 + * line: all 273 width>=8 entries win (median 4.14x, no losers, no washes) + * and every narrow FILTER kernel loses, 0.62-0.94x. So the filter slots + * start at index 3 on both CPUs. The one disagreement is the whole-pel + * pixels family, the only narrow entries the 970 likes (pel_pixels 4/6 + * 1.85-1.98x, bi_pixels6 1.22x, uni_w_pixels6 1.13-1.25x; the pixels4 + * bi/uni_w forms are 3-5-tick washes): those keep every width on the 970 + * and start at 8 on the 7447A, where they lose with the rest of the narrow + * set. */ #ifdef _ARCH_PWR4 -#define MC_WIDTH_FLOOR 0 +#define MC_PIXELS_FLOOR 0 #else -#define MC_WIDTH_FLOOR 3 +#define MC_PIXELS_FLOOR 3 #endif +#define MC_FILTER_FLOOR 3 #define MC_SET(depth) \ do { \ int i; \ - for (i = MC_WIDTH_FLOOR; i < 10; i++) { \ + for (i = MC_FILTER_FLOOR; i < 10; i++) { \ c->put_hevc_qpel[i][0][1] = ff_hevc_put_qpel_h_ ## depth ## _altivec; \ c->put_hevc_qpel[i][1][0] = ff_hevc_put_qpel_v_ ## depth ## _altivec; \ c->put_hevc_qpel[i][1][1] = ff_hevc_put_qpel_hv_ ## depth ## _altivec; \ @@ -370,8 +377,10 @@ static void put_hevc_qpel_hv_altivec(int16_t *dst, const uint8_t *src, c->put_hevc_epel_bi[i][1][1] = ff_hevc_put_epel_bi_hv_ ## depth ## _altivec; \ c->put_hevc_epel_uni_w[i][1][0] = ff_hevc_put_epel_uni_w_v_ ## depth ## _altivec; \ c->put_hevc_epel_uni_w[i][1][1] = ff_hevc_put_epel_uni_w_hv_ ## depth ## _altivec; \ - /* whole-pel: put shifts, bi adds, uni_w weights -- only \ - * put_uni_pixels is a real memcpy and stays in C */ \ + } \ + /* whole-pel: put shifts, bi adds, uni_w weights -- only \ + * put_uni_pixels is a real memcpy and stays in C */ \ + for (i = MC_PIXELS_FLOOR; i < 10; i++) { \ c->put_hevc_qpel[i][0][0] = ff_hevc_put_pel_pixels_ ## depth ## _altivec; \ c->put_hevc_epel[i][0][0] = ff_hevc_put_pel_pixels_ ## depth ## _altivec; \ c->put_hevc_qpel_bi[i][0][0] = ff_hevc_put_pel_bi_pixels_ ## depth ## _altivec; \ @@ -408,24 +417,21 @@ av_cold void ff_hevc_dsp_init_ppc(HEVCDSPContext *c, const int bit_depth) c->sao_band_filter[3] = c->sao_band_filter[4] = ff_hevc_sao_band_filter_8_altivec; c->sao_edge_filter[1] = c->sao_edge_filter[2] = c->sao_edge_filter[3] = c->sao_edge_filter[4] = ff_hevc_sao_edge_filter_8_altivec; -#ifdef _ARCH_PWR4 /* Index 0 is width 8 exactly (filter.c indexes with - * sao_tab[(FFALIGN(width,8)>>3)-1] and sao_tab[0] == 0), and at 8 bits - * it loses on a 7447A: band 0.62x, edge 0.98x (PowerVLC 2026-08-07). - * Width 16 and up win 2.5x-10.4x there and stay. Unbenched on the 970, - * which is where the whole rest of this file's width-8 work wins, so - * the drop is G4-only. The 10-bit path keeps width 8 on both - it wins - * even on the G4 (band 1.96x, edge 3.05x). */ - c->sao_band_filter[0] = ff_hevc_sao_band_filter_8_altivec; - c->sao_edge_filter[0] = ff_hevc_sao_edge_filter_8_altivec; -#endif + * sao_tab[(FFALIGN(width,8)>>3)-1] and sao_tab[0] == 0), and at 8 + * bits it stays in C on both CPUs: on a 7447A band is 0.62x and edge + * 0.98x (PowerVLC 2026-08-07), on a 2.3 GHz 970 band is 0.93x and + * edge a 1.00x wash (2026-08-16). Width 16 and up win 2.5x-14.4x. + * The 10-bit path keeps width 8 on both - it wins everywhere (G4 + * band 1.96x, edge 3.05x; 970 band 2.34x, edge 4.26x). */ MC_SET(8); /* Our own 8-bit put-qpel kernels (F2, commit abf2865) own these three * slots: they are registered AFTER MC_SET so they win, at every width. - * They are benched on a 2.3 GHz 970 (2026-07-28) and win everywhere - * measured, including the narrow widths MC_SET skips on a G4: - * h4 1.99x, h6 3.15x, h8 9.76x, hv4 4.32x, 14-23x at width >= 24. */ + * They are benched on a 2.3 GHz 970 and win everywhere measured, + * including the narrow widths MC_SET now skips on both CPUs: h4 + * 2.03x, h6 3.13x, h8 9.89x, v4 1.82x, hv4 4.35x, 10-20x at width + * >= 16 (2026-08-16, matching 2026-07-28). */ for (int i = 0; i < 10; i++) { c->put_hevc_qpel[i][0][1] = put_hevc_qpel_h_altivec; c->put_hevc_qpel[i][1][0] = put_hevc_qpel_v_altivec; -- 2.43.0