動き補償最適化の続き

前回は単純なコピーのみ。今回は演算が平均。

元のコードのアルゴリズム


uint8_t *src, *dst;
for (int y = 0; y < 16; ++y)
for (int x = 0; x < 16; ++x)
dest[y][x] = (src[y][x] + src[y][x + 1] + 1) / 2;
もちろん本物はループ展開などの高速化はしてあるけど、要約するとこうなる。

これを、MMXのように4バイト並列演算するにはこれ。


uint32_t src0_packed, src1_packed;
for (int y = 0; y < 16; ++y) {
src0_packed = (src[y][0] | (src[y][1] << 8) | (src[y][2] << 16) | (src[y][3] << 24);
src1_packed = (src0_packed >> 8) | (src[y][4] << 24);
src0_packed = ((src0_packed & src1_packed)
+ (((src0_packed ^ src1_packed) & 0xfefefefe) >> 1))
+ ((src0_packed ^ src1_packed) & 0x01010101);
// 結果をストア。めんどいので書かない。
// 以上を4回繰りかえす
}

まぁほとんどこちらの方のパクリなんですが(+1するところが違うのでちょっと考えた)。1要素あたり2命令の削減かな。

あとはこれをアセンブラで書くだけ〜。

ちなみにこれが終わればlibmpeg2の最適化は終了っす。プロファイルがこんな感じなんで。


% cumulative self self total
time seconds seconds calls ms/call ms/call name
10.69 9.32 9.32 MC_put_o_16_arm_align0
8.06 16.35 7.03 2559870 0.00 0.00 get_mpeg1_intra_block
6.20 21.76 5.41 8610 0.63 0.69 read_video_packet
5.05 26.16 4.40 IPPIDCTINV_8X8_16S8U_LOOP_
4.82 30.36 4.20 MC_put_o_8_arm_align0
4.42 34.21 3.85 1638717 0.00 0.00 get_mpeg1_non_intra_block
4.00 37.70 3.49 182781 0.02 0.02 MC_put_x_16_c
3.60 40.84 3.14 1638717 0.00 0.00 add_pixels_clamped_ARM
3.57 43.95 3.11 7175 0.43 0.43 copy_chunk
3.15 46.70 2.75 2692 1.02 13.42 mpeg2_slice
3.12 49.42 2.72 152346 0.02 0.02 MC_put_y_16_c
2.65 51.73 2.31 synth_full
2.55 53.95 2.22 main
2.34 55.99 2.04 dct32
2.33 58.02 2.03 75365 0.03 0.03 MC_put_xy_16_c
2.26 59.99 1.97 mad_layer_II
2.00 61.73 1.74 IPPIDCTINV_8X8_16S8U_LOOP_
1.98 63.46 1.73 360818 0.00 0.00 MC_put_x_8_c
1.71 64.95 1.49 12112128 0.00 0.00 scale
1.51 66.27 1.32 IPPIDCTINV_8X8_16S_I_LOOP_
1.51 67.59 1.32 ONLYONE_FIRST_NOZERO_16S8U
1.48 68.88 1.29 2492 0.52 1.16 decode_audio
1.23 69.95 1.07 IPPIDCTINV_8X8_16S_I_LOOP_
1.17 70.97 1.02 2559870 0.00 0.00 slice_intra_DCT
MC_put_o_*とDCT関係は終了。
いまやってるのがMC_put_x_*とMC_put_y_*。
それが終わると、最適化する価値がありそうなのはget_mpeg1_intra_blockとget_mpeg1_non_intra_blockぐらいなんだけど、この二つはかなり長く複雑な関数なんで最適化は難しい、と。速くできるかどうかの見積りもできない。
他の上位を占める関数は単純なアルゴリズムでメモリアクセスが主だから、性能はたぶん頭打ち。

  • -

MC_put_x_*をガリガリアセンブラで書いて動作させたら一発で正常に動作した!慣れてきた証拠かな。

速度は2.6倍に。予想以上に速い!これがfpsにどのくらい影響するか?

1.0pre5.1
BENCHMARKs: VC: 64.769s VO: 55.750s A: 11.241s Sys: 5.105s = 136.864s
BENCHMARK%: VC: 47.3233% VO: 40.7337% A: 8.2130% Sys: 3.7300% = 100.0000%
BENCHMARKn: disp: 2308 (16.86 fps) drop: 980 (29%) total: 3288 (24.02 fps)

適用前
BENCHMARKs: VC: 57.444s VO: 62.594s A: 11.116s Sys: 5.636s = 136.790s
BENCHMARK%: VC: 41.9942% VO: 45.7594% A: 8.1261% Sys: 4.1204% = 100.0000%
BENCHMARKn: disp: 2586 (18.90 fps) drop: 702 (21%) total: 3288 (24.04 fps)

適用後
BENCHMARKs: VC: 56.756s VO: 63.344s A: 11.072s Sys: 5.614s = 136.786s
BENCHMARK%: VC: 41.4925% VO: 46.3086% A: 8.0944% Sys: 4.1045% = 100.0000%
BENCHMARKn: disp: 2619 (19.15 fps) drop: 669 (20%) total: 3288 (24.04 fps)

あんまり変わらねーorz。MC_put_y_*はやめとこう……。