[FFmpeg-devel] [PATCH 07/11] x86: synth filter float: implement SSE2 version

Christophe Gisquet christophe.gisquet at gmail.com
Thu Feb 6 01:41:58 CET 2014


Timings for Arrandale:
          C    SSE
win32:  2108   334
win64:  1152   322

Factorizing the inner loop with a call/jmp is a >15 cycles cost, even with
the jmp destination being aligned.

Unrolling for ARCH_X86_64 is a 20 cycles gain.
---
 libavcodec/synth_filter.c       |   1 +
 libavcodec/synth_filter.h       |   1 +
 libavcodec/x86/Makefile         |   3 +-
 libavcodec/x86/dcadsp_init.c    |   1 -
 libavcodec/x86/fft_init.c       |  32 ++++++++
 libavcodec/x86/synth_filter.asm | 178 ++++++++++++++++++++++++++++++++++++++++
 6 files changed, 214 insertions(+), 2 deletions(-)
 create mode 100644 libavcodec/x86/synth_filter.asm

diff --git a/libavcodec/synth_filter.c b/libavcodec/synth_filter.c
index 5f10530..d49ffe6 100644
--- a/libavcodec/synth_filter.c
+++ b/libavcodec/synth_filter.c
@@ -61,4 +61,5 @@ av_cold void ff_synth_filter_init(SynthFilterContext *c)
     c->synth_filter_float = synth_filter_float;
 
     if (ARCH_ARM) ff_synth_filter_init_arm(c);
+    if (ARCH_X86) ff_synth_filter_init_x86(c);
 }
diff --git a/libavcodec/synth_filter.h b/libavcodec/synth_filter.h
index 33edcc4..b63fd77 100644
--- a/libavcodec/synth_filter.h
+++ b/libavcodec/synth_filter.h
@@ -33,5 +33,6 @@ typedef struct SynthFilterContext {
 
 void ff_synth_filter_init(SynthFilterContext *c);
 void ff_synth_filter_init_arm(SynthFilterContext *c);
+void ff_synth_filter_init_x86(SynthFilterContext *c);
 
 #endif /* AVCODEC_SYNTH_FILTER_H */
diff --git a/libavcodec/x86/Makefile b/libavcodec/x86/Makefile
index d7ed8ce..66d864f 100644
--- a/libavcodec/x86/Makefile
+++ b/libavcodec/x86/Makefile
@@ -62,7 +62,8 @@ YASM-OBJS                              += x86/deinterlace.o             \
 
 YASM-OBJS-$(CONFIG_AAC_DECODER)        += x86/sbrdsp.o
 YASM-OBJS-$(CONFIG_AC3DSP)             += x86/ac3dsp.o
-YASM-OBJS-$(CONFIG_DCA_DECODER)        += x86/dcadsp.o
+YASM-OBJS-$(CONFIG_DCA_DECODER)        += x86/dcadsp.o                  \
+                                          x86/synth_filter.o
 YASM-OBJS-$(CONFIG_DCT)                += x86/dct32.o
 YASM-OBJS-$(CONFIG_DIRAC_DECODER)      += x86/diracdsp_mmx.o x86/diracdsp_yasm.o\
                                           x86/dwt_yasm.o
diff --git a/libavcodec/x86/dcadsp_init.c b/libavcodec/x86/dcadsp_init.c
index 8376cc4..f9ee8fe 100644
--- a/libavcodec/x86/dcadsp_init.c
+++ b/libavcodec/x86/dcadsp_init.c
@@ -18,7 +18,6 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-#include "libavutil/cpu.h"
 #include "libavutil/attributes.h"
 #include "libavutil/x86/cpu.h"
 #include "libavcodec/dcadsp.h"
diff --git a/libavcodec/x86/fft_init.c b/libavcodec/x86/fft_init.c
index 5682230..51c0552 100644
--- a/libavcodec/x86/fft_init.c
+++ b/libavcodec/x86/fft_init.c
@@ -55,3 +55,35 @@ av_cold void ff_fft_init_x86(FFTContext *s)
         s->fft_permutation = FF_FFT_PERM_AVX;
     }
 }
+
+#if CONFIG_DCA_DECODER
+# include "libavcodec/synth_filter.h"
+
+void ff_synth_filter_inner_sse2(float *synth_buf_ptr, float synth_buf2[32],
+                                const float window[512],
+                                float out[32], intptr_t offset, float scale);
+
+static void synth_filter_sse2(FFTContext *imdct,
+                              float *synth_buf_ptr, int *synth_buf_offset,
+                              float synth_buf2[32], const float window[512],
+                              float out[32], const float in[32], float scale)
+{
+    float *synth_buf= synth_buf_ptr + *synth_buf_offset;
+
+    imdct->imdct_half(imdct, synth_buf, in);
+
+    ff_synth_filter_inner_sse2(synth_buf, synth_buf2, window,
+                               out, *synth_buf_offset, scale);
+
+    *synth_buf_offset= (*synth_buf_offset - 32)&511;
+}
+
+av_cold void ff_synth_filter_init_x86(SynthFilterContext *s)
+{
+    int mm_flags = av_get_cpu_flags();
+
+    if (EXTERNAL_SSE2(mm_flags)) {
+        s->synth_filter_float = synth_filter_sse2;
+    }
+}
+#endif
diff --git a/libavcodec/x86/synth_filter.asm b/libavcodec/x86/synth_filter.asm
new file mode 100644
index 0000000..831080d
--- /dev/null
+++ b/libavcodec/x86/synth_filter.asm
@@ -0,0 +1,178 @@
+;******************************************************************************
+;* AAC Spectral Band Replication decoding functions
+;* Copyright (C) 2012 Christophe Gisquet <christophe.gisquet at gmail.com>
+;*
+;* This file is part of Libav.
+;*
+;* Libav is free software; you can redistribute it and/or
+;* modify it under the terms of the GNU Lesser General Public
+;* License as published by the Free Software Foundation; either
+;* version 2.1 of the License, or (at your option) any later version.
+;*
+;* Libav is distributed in the hope that it will be useful,
+;* but WITHOUT ANY WARRANTY; without even the implied warranty of
+;* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+;* Lesser General Public License for more details.
+;*
+;* You should have received a copy of the GNU Lesser General Public
+;* License along with Libav; if not, write to the Free Software
+;* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+;******************************************************************************
+
+%include "libavutil/x86/x86util.asm"
+
+SECTION_TEXT
+
+INIT_XMM sse2
+%macro INNER_LOOP   1
+    ; reading backwards:  ptr1=synth_buf+j+i   ptr2=synth_big+j-i
+    ;~ a += window[i + j     ]*(-synth_buf[15 - i + j      ])
+    ;~ b += window[i + j + 16]*( synth_buf[     i + j      ])
+    pshufd        m5, [ptr2 + j + (15-3)*4], q0123
+    mova          m6, [ptr1 + j]
+%if ARCH_X86_64
+    pshufd       m11, [ptr2 + j + (15-3)*4 - mmsize], q0123
+    mova         m12, [ptr1 + j + mmsize]
+%endif
+    mulps         m6, [win  + %1 + j + 16*4]
+    mulps         m5, [win  + %1 + j]
+%if ARCH_X86_64
+    mulps        m12, [win  + %1 + j + mmsize + 16*4]
+    mulps        m11, [win  + %1 + j + mmsize]
+%endif
+    addps         m2, m6
+    subps         m1, m5
+%if ARCH_X86_64
+    addps         m8, m12
+    subps         m7, m11
+%endif
+    ;~ c += window[i + j + 32]*( synth_buf[16 + i + j      ])
+    ;~ d += window[i + j + 48]*( synth_buf[31 - i + j      ])
+    pshufd        m6, [ptr2 + j + (31-3)*4], q0123
+    mova          m5, [ptr1 + j + 16*4]
+%if ARCH_X86_64
+    pshufd       m12, [ptr2 + j + (31-3)*4 - mmsize], q0123
+    mova         m11, [ptr1 + j + mmsize + 16*4]
+%endif
+    mulps         m5, [win  + %1 + j + 32*4]
+    mulps         m6, [win  + %1 + j + 48*4]
+%if ARCH_X86_64
+    mulps        m11, [win  + %1 + j + mmsize + 32*4]
+    mulps        m12, [win  + %1 + j + mmsize + 48*4]
+%endif
+    addps         m3, m5
+    addps         m4, m6
+%if ARCH_X86_64
+    addps         m9, m11
+    addps        m10, m12
+%endif
+    sub            j, 64*4
+%endmacro
+
+; void ff_synth_filter_inner_sse2(float *synth_buf, float synth_buf2[32],
+;                                 const float window[512], float out[32],
+;                                 intptr_t offset, float scale)
+cglobal synth_filter_inner, 0,6+4*ARCH_X86_64,7+6*ARCH_X86_64, \
+                              synth_buf, synth_buf2, window, out, off, scale
+%define scale m0
+%if ARCH_X86_32 || WIN64
+    movd       scale, scalem
+; Make sure offset is in a register and not on the stack  
+%define OFFQ  r4q
+%else
+%define OFFQ  offq
+%endif
+    pshufd        m0, m0, 0
+    ; prepare inner counter limit 1
+    mov          r5q, 480
+    sub          r5q, offmp
+    and          r5q, -64
+    shl          r5q, 2
+    mov         OFFQ, r5q
+%define i        r5q
+    mov            i, 16*4-(ARCH_X86_64+1)*mmsize  ; main loop counter
+
+%define buf2     synth_buf2q
+%if ARCH_X86_32
+    mov         buf2, synth_buf2mp
+%endif
+.mainloop
+    ; m1=a  m2=b  m3=c  m4=d
+    pxor          m3, m3
+    pxor          m4, m4
+    mova          m1, [buf2 + i]
+    mova          m2, [buf2 + i + 16*4]
+%if ARCH_X86_32
+%define ptr1     r0q
+%define ptr2     r1q
+%define win      r2q
+%define j        r3q
+    mov          win, windowm
+    mov         ptr1, synth_bufm
+    add          win, i
+    add         ptr1, i
+%else
+%define ptr1     r6q
+%define ptr2     r7q ; must be loaded
+%define win      r8q
+%define j        r9q
+%if ARCH_X86_64
+    pxor          m9, m9
+    pxor         m10, m10
+    mova          m7, [buf2 + i + mmsize]
+    mova          m8, [buf2 + i + mmsize + 16*4]
+%endif
+    lea          win, [windowq + i]
+    lea         ptr1, [synth_bufq + i]
+%endif
+    mov         ptr2, synth_bufmp
+    ; prepare the inner loop counter
+    mov            j, OFFQ
+    sub         ptr2, i
+.loop1:
+    INNER_LOOP  0
+    jge       .loop1
+
+    mov            j, 448*4
+    sub            j, OFFQ
+    jz          .end
+    sub         ptr1, j
+    sub         ptr2, j
+    add          win, OFFQ ; now at j-64, so define OFFSET
+    sub            j, 64*4
+.loop2:
+    INNER_LOOP  64*4
+    jge       .loop2
+
+.end:
+%if ARCH_X86_32
+    mov         buf2, synth_buf2m ; needed for next iteration anyway
+    mov         outq, outmp       ; j, which will be set again during it
+%endif
+    ;~ out[i     ] = a*scale;
+    ;~ out[i + 16] = b*scale;
+    mulps         m1, scale
+    mulps         m2, scale
+%if ARCH_X86_64
+    mulps         m7, scale
+    mulps         m8, scale
+%endif
+    ;~ synth_buf2[i     ] = c;
+    ;~ synth_buf2[i + 16] = d;
+    mova   [buf2 + i +  0*4], m3
+    mova   [buf2 + i + 16*4], m4
+%if ARCH_X86_64
+    mova   [buf2 + i +  0*4 + mmsize], m9
+    mova   [buf2 + i + 16*4 + mmsize], m10
+%endif
+    ;~ out[i     ] = a;
+    ;~ out[i + 16] = a;
+    mova   [outq + i +  0*4], m1
+    mova   [outq + i + 16*4], m2
+%if ARCH_X86_64
+    mova   [outq + i +  0*4 + mmsize], m7
+    mova   [outq + i + 16*4 + mmsize], m8
+%endif
+    sub            i, (ARCH_X86_64+1)*mmsize
+    jge    .mainloop
+    RET
-- 
1.8.0.msysgit.0



More information about the ffmpeg-devel mailing list