Optimizing C63 for x86

Report
Optimizing C63 for
x86
{
Group 9



Bird’s-eye view: gprof of reference encoder
Optimizing SAD
Results
Outline

Each sample counts as 0.01 seconds.

%
cumulative
time
seconds
93.05
37.36
3.04
38.58
1.77
39.29
1.25
39.79
0.35
39.93
0.17
40.00








self
self
total
seconds
calls us/call us/call
37.36 500214528
0.07
0.07
1.22
705672
1.73
54.67
0.71
712800
1.00
1.00
0.50
712800
0.70
0.70
0.14
713100
0.20
0.29
0.07 16398942
0.00
0.00
name
sad_block_8x8
me_block_8x8
dequant_idct_block_8x8
dct_quant_block_8x8
flush_bits
put_bits
granularity: each sample hit covers 2 byte(s) for 0.02% of 40.16 seconds
gprof: reference,
foreman
Optimizing SAD
















void sad_block_8x8(uint8_t *block1, uint8_t *block2, int stride, int *result)
{
int v;
__m128i r = _mm_setzero_si128();
for (v = 0; v < 8; v += 2)
{
const __m128i b1 = _mm_set_epi64(*(__m64 *) &block1[(v+0)*stride],
*(__m64 *) &block1[(v+1)*stride]);
const __m128i b2 = _mm_set_epi64(*(__m64 *) &block2[(v+0)*stride],
*(__m64 *) &block2[(v+1)*stride]);
r = _mm_add_epi16(r, _mm_sad_epu8(b2, b1));
}
*result =
_mm_extract_epi16(r, 0) +
_mm_extract_epi16(r, 4);
}
SAD SSE2
PSAD

Gprof:
Improved SSE2 SAD uses 3.69 s vs. 38.50s*

Cachegrind:
Lots of branch prediction misses in
me_block_8x8
*) Foreman sequence on gpu-7
How well does this
perform?


























void sad_block_8x8x8(uint8_t *block1, uint8_t *block2, int stride, int *best,
int *result)
{
int v;
__m128i r = _mm_setzero_si128();
union {
__m128i v;
struct {
uint16_t sad;
unsigned int index : 3;
} minpos;
} mp;
for (v = 0; v < 8; v += 2) {
const __m128i b1 = _mm_set_epi64(*(__m64 *) &block1[(v+1)*stride],
*(__m64 *) &block1[(v+0)*stride]);
const __m128i b2 = _mm_loadu_si128((__m128i *) &block2[(v+0)*stride]);
const __m128i b3 = _mm_loadu_si128((__m128i *) &block2[(v+1)*stride]);
r = _mm_add_epi16(r, _mm_mpsadbw_epu8(b2, b1, 0b000));
r = _mm_add_epi16(r, _mm_mpsadbw_epu8(b2, b1, 0b101));
r = _mm_add_epi16(r, _mm_mpsadbw_epu8(b3, b1, 0b010));
r = _mm_add_epi16(r, _mm_mpsadbw_epu8(b3, b1, 0b111));
}
mp.v = _mm_minpos_epu16(r);
*result = mp.minpos.sad;
*best
= mp.minpos.index;
}
SAD SSE4.1
MPSADBW+PHMINSUM

Gprof:
Improved SSE4.1 SAD uses 0.90s vs 3.69s

Cachegrind:
Branch prediction misses reduced by a factor of 8

Intel’s IACA tool:
CPU pipeline appears to be filled!
Appears both source and reference block loads compete with
(V)MPSADBW for the CPUs execution ports

Assembly:
Better utilize AVX’s non-destructive instructions (less register
copies)
Better utilize loaded data for SAD computations with same source
block
How well does this
perform?









Load source block once:
.macro .load_src
vmovq (%rdi),
%xmm0
vpinsrq $1,(%rdi,%rdx),%xmm0,%xmm0
vmovq (%rdi,%rdx,2),%xmm1
vmovhps (%rdi,%r8), %xmm1,%xmm1
vmovq (%rdi,%rdx,4),%xmm2
vmovhps (%rdi,%r9), %xmm2,%xmm2
vmovq (%rdi,%r8,2), %xmm3
vmovhps (%rdi,%rax), %xmm3,%xmm3
#
#
#
#
#
#
#
#
src[0]
src[1]
src[2]
src[3]
src[4]
src[5]
src[6]
src[7]






Do sad for 8x8 - 8x8 blocks (relative y = 0…8, x = 0…8):
.macro .8x8x8x8
vmovdqu (%rsi), %xmm12
# ref[0]
.8x8x1
0, 0, 12, 4 0
vmovdqu (%rsi,%rdx), %xmm13
.8x8x1
0, 1, 13, 4 0
.8x8x1
1, 0, 13, 5 0
# ref[1]
SAD 8x8x8x8:
Less src loads, branches

Gprof:
Improved SAD 8x8x8x8 uses 0.53s vs 0.90s

Valgrind:
Even less branching
How well does this
perform?













sad_4x8x8x8x8:
.load_src
mov
.8x8x8x8
lea
.8x8x8x8
lea
.8x8x8x8
lea
.8x8x8x8
…
vphminposuw
…
ret
%rsi, %rdi
# Load source block from %rdi
# Reference block x,y
8(%rdi), %rsi
# Reference block x+8, y
(%rdi,%rdx,8), %rsi
8(%rdi,%rdx,8), %rsi #
SAD 4x8x8x8x8:
Branchless UV-plane ME

Gprof:
Improved 4x8x8x8x8 SAD uses 0.47s vs 0.53s

Valgrind:
Even less branching!

Total runtime of Foreman sequence reduced
from ~40.1s to ~1.6s (factor of 25)
How well does this
perform?
Instruction
Function variant
Cycle mean, adjusted
for single 8x8 block
MPSADBW
SAD
8x 8x8
7.5
VMPSADBW
SAD
2x8x 8x8
3.6
SAD
4x8x 8x8
4.3
SAD
8x8x 8x8 v1
2.9
SAD
8x8x 8x8 v2
2.8
Future research
SAD 4x 8x8x 8x8
2.7
SAD 16x8x8x8x8
2.6?
Even less branches? 
SAD assembly code:
Iterative improvement
PSNR
Mean
PSNR
95%
SSIM
Mean
SSIM
95%
Tractor reference
33.1 3045
34.46 549
0.9614 4
0.97672
Tractor
33.1 2339
34.46 587
0.9614 6
0.97672
Foreman reference
33.4 6215
35.36 280
0.951 53
0.964 76
Foreman
33.4 4806
35.36 217
0.951 39
0.964 36
Image quality:
comparable

NOT ON NDLAB!


Each sample counts as 0.01 seconds.
%
cumulative
self
self
total
time
seconds
seconds
calls
s/call
s/call name
92.90
124.66
124.66 1809584896
0.00
0.00 sad_block_8x8
3.06
128.76
4.10
49
0.08
2.63 c63_motion_estimate
1.58
130.89
2.13 2448000
0.00
0.00 dct_quant_block_8x8
1.58
133.01
2.13 2448000
0.00
0.00 dequant_idct_block_8x8
0.26
133.36
0.35 2448000
0.00
0.00 write_block
0.18
133.60
0.24
150
0.00
0.02 dequantize_idct
0.16
133.82
0.22
49
0.00
0.00 c63_motion_compensate
0.13
133.99
0.17
150
0.00
0.02 dct_quantize
0.11
134.14
0.15 55939437
0.00
0.00 put_bits

granularity: each sample hit covers 2 byte(s) for 0.01% of 134.21 seconds










gprof: reference,
tractor 50 frames (-O3)

NOT ON NDLAB!

Each sample counts as 0.01 seconds.













%
cumulative
time
seconds
31.15
1.83
24.75
3.28
23.81
4.67
7.17
5.09
4.44
5.35
3.24
5.54
1.71
5.64
1.54
5.73
0.85
5.78
0.68
5.82
0.51
5.85
self
seconds
calls
1.83 6869016
1.45 2448000
1.40 2448000
0.42
50
0.26
150
0.19 55937692
0.10
150
0.09 9792000
0.05
798700
0.04
49
0.03
49
self
ms/call
0.00
0.00
0.00
8.40
1.73
0.00
0.67
0.00
0.00
0.82
0.61
total
ms/call
0.00
0.00
0.00
12.20
11.34
0.00
10.64
0.00
0.00
39.09
0.61
name
sad_4x8x8x8x8
dct_quant_block_8x8
dequant_idct_block_8x8
write_frame
dequantize_idct
put_bits
dct_quantize
transpose_block_avx
sad_8x8x8x8
c63_motion_estimate
c63_motion_compensate

granularity: each sample hit covers 2 byte(s) for 0.17% of 5.86 seconds

134.2/5.86 = ~24
gprof: improved,
tractor 50 frames (-O3)

similar documents