libFLAC/lpc_intrin_sse.c : New SSE code to calculate autocorrelation.
authorErik de Castro Lopo <erikd@mega-nerd.com>
Sat, 9 Aug 2014 02:32:57 +0000 (12:32 +1000)
committerErik de Castro Lopo <erikd@mega-nerd.com>
Sat, 9 Aug 2014 02:49:27 +0000 (12:49 +1000)
Accelerate FLAC__lpc_compute_autocorrelation_intrin_sse_lag_NN routines for
AMD and newer Intel CPUs (means Core i aka Nehalem and newer). Unfortunately
it's slower on older Intel CPUs.

According to tests at HA:

    <http://www.hydrogenaud.io/forums/index.php?s=&showtopic=101082&view=findpost&p=870753>

  CPU                 flac -5           flac -8

  Athlon XP           +5 %              +2.4 %
  Athlon 64 X2        +9 %              +4 %
  Core i              +7 %              +1 % ... +2.7 %
  Core 2              ?                 -3.5 %

According to Steam HW survey <http://store.steampowered.com/hwsurvey/>
69% of Steam users have SSE4.2 which means that the new code is faster for
them. There are also AMD users that don't have SSE4.2, so 75% of Steam users
should benefit from this patch.

Patch-from: lvqcl <lvqcl.mail@gmail.com>

src/libFLAC/lpc_intrin_sse.c

index aed6741..9d99d38 100644 (file)
 
 #include <xmmintrin.h> /* SSE */
 
+#if 1
+/* Faster on current Intel (starting from Core i aka Nehalem) and all AMD CPUs */
+
+FLAC__SSE_TARGET("sse")
+void FLAC__lpc_compute_autocorrelation_intrin_sse_lag_4(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[])
+{
+       int i;
+       int limit = data_len - 4;
+       __m128 sum0;
+
+       (void) lag;
+       FLAC__ASSERT(lag <= 4);
+       FLAC__ASSERT(lag <= data_len);
+
+       sum0 = _mm_setzero_ps();
+
+       for(i = 0; i <= limit; i++) {
+               __m128 d, d0;
+               d0 = _mm_loadu_ps(data+i);
+               d = d0; d = _mm_shuffle_ps(d, d, 0);
+               sum0 = _mm_add_ps(sum0, _mm_mul_ps(d0, d));
+       }
+
+       {
+               __m128 d0 = _mm_setzero_ps();
+               limit++; if(limit < 0) limit = 0;
+
+               for(i = data_len-1; i >= limit; i--) {
+                       __m128 d;
+                       d = _mm_load_ss(data+i); d = _mm_shuffle_ps(d, d, 0);
+                       d0 = _mm_shuffle_ps(d0, d0, _MM_SHUFFLE(2,1,0,3));
+                       d0 = _mm_move_ss(d0, d);
+                       sum0 = _mm_add_ps(sum0, _mm_mul_ps(d, d0));
+               }
+       }
+
+       _mm_storeu_ps(autoc,   sum0);
+}
+
+FLAC__SSE_TARGET("sse")
+void FLAC__lpc_compute_autocorrelation_intrin_sse_lag_8(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[])
+{
+       int i;
+       int limit = data_len - 8;
+       __m128 sum0, sum1;
+
+       (void) lag;
+       FLAC__ASSERT(lag <= 8);
+       FLAC__ASSERT(lag <= data_len);
+
+       sum0 = _mm_setzero_ps();
+       sum1 = _mm_setzero_ps();
+
+       for(i = 0; i <= limit; i++) {
+               __m128 d, d0, d1;
+               d0 = _mm_loadu_ps(data+i);
+               d1 = _mm_loadu_ps(data+i+4);
+               d = d0; d = _mm_shuffle_ps(d, d, 0);
+               sum0 = _mm_add_ps(sum0, _mm_mul_ps(d0, d));
+               sum1 = _mm_add_ps(sum1, _mm_mul_ps(d1, d));
+       }
+
+       {
+               __m128 d0 = _mm_setzero_ps();
+               __m128 d1 = _mm_setzero_ps();
+               limit++; if(limit < 0) limit = 0;
+
+               for(i = data_len-1; i >= limit; i--) {
+                       __m128 d;
+                       d = _mm_load_ss(data+i); d = _mm_shuffle_ps(d, d, 0);
+                       d1 = _mm_shuffle_ps(d1, d1, _MM_SHUFFLE(2,1,0,3));
+                       d0 = _mm_shuffle_ps(d0, d0, _MM_SHUFFLE(2,1,0,3));
+                       d1 = _mm_move_ss(d1, d0);
+                       d0 = _mm_move_ss(d0, d);
+                       sum1 = _mm_add_ps(sum1, _mm_mul_ps(d, d1));
+                       sum0 = _mm_add_ps(sum0, _mm_mul_ps(d, d0));
+               }
+       }
+
+       _mm_storeu_ps(autoc,   sum0);
+       _mm_storeu_ps(autoc+4, sum1);
+}
+
+FLAC__SSE_TARGET("sse")
+void FLAC__lpc_compute_autocorrelation_intrin_sse_lag_12(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[])
+{
+       int i;
+       int limit = data_len - 12;
+       __m128 sum0, sum1, sum2;
+
+       (void) lag;
+       FLAC__ASSERT(lag <= 12);
+       FLAC__ASSERT(lag <= data_len);
+
+       sum0 = _mm_setzero_ps();
+       sum1 = _mm_setzero_ps();
+       sum2 = _mm_setzero_ps();
+
+       for(i = 0; i <= limit; i++) {
+               __m128 d, d0, d1, d2;
+               d0 = _mm_loadu_ps(data+i);
+               d1 = _mm_loadu_ps(data+i+4);
+               d2 = _mm_loadu_ps(data+i+8);
+               d = d0; d = _mm_shuffle_ps(d, d, 0);
+               sum0 = _mm_add_ps(sum0, _mm_mul_ps(d0, d));
+               sum1 = _mm_add_ps(sum1, _mm_mul_ps(d1, d));
+               sum2 = _mm_add_ps(sum2, _mm_mul_ps(d2, d));
+       }
+
+       {
+               __m128 d0 = _mm_setzero_ps();
+               __m128 d1 = _mm_setzero_ps();
+               __m128 d2 = _mm_setzero_ps();
+               limit++; if(limit < 0) limit = 0;
+
+               for(i = data_len-1; i >= limit; i--) {
+                       __m128 d;
+                       d = _mm_load_ss(data+i); d = _mm_shuffle_ps(d, d, 0);
+                       d2 = _mm_shuffle_ps(d2, d2, _MM_SHUFFLE(2,1,0,3));
+                       d1 = _mm_shuffle_ps(d1, d1, _MM_SHUFFLE(2,1,0,3));
+                       d0 = _mm_shuffle_ps(d0, d0, _MM_SHUFFLE(2,1,0,3));
+                       d2 = _mm_move_ss(d2, d1);
+                       d1 = _mm_move_ss(d1, d0);
+                       d0 = _mm_move_ss(d0, d);
+                       sum2 = _mm_add_ps(sum2, _mm_mul_ps(d, d2));
+                       sum1 = _mm_add_ps(sum1, _mm_mul_ps(d, d1));
+                       sum0 = _mm_add_ps(sum0, _mm_mul_ps(d, d0));
+               }
+       }
+
+       _mm_storeu_ps(autoc,   sum0);
+       _mm_storeu_ps(autoc+4, sum1);
+       _mm_storeu_ps(autoc+8, sum2);
+}
+
+FLAC__SSE_TARGET("sse")
+void FLAC__lpc_compute_autocorrelation_intrin_sse_lag_16(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[])
+{
+       int i;
+       int limit = data_len - 16;
+       __m128 sum0, sum1, sum2, sum3;
+
+       (void) lag;
+       FLAC__ASSERT(lag <= 16);
+       FLAC__ASSERT(lag <= data_len);
+
+       sum0 = _mm_setzero_ps();
+       sum1 = _mm_setzero_ps();
+       sum2 = _mm_setzero_ps();
+       sum3 = _mm_setzero_ps();
+
+       for(i = 0; i <= limit; i++) {
+               __m128 d, d0, d1, d2, d3;
+               d0 = _mm_loadu_ps(data+i);
+               d1 = _mm_loadu_ps(data+i+4);
+               d2 = _mm_loadu_ps(data+i+8);
+               d3 = _mm_loadu_ps(data+i+12);
+               d = d0; d = _mm_shuffle_ps(d, d, 0);
+               sum0 = _mm_add_ps(sum0, _mm_mul_ps(d0, d));
+               sum1 = _mm_add_ps(sum1, _mm_mul_ps(d1, d));
+               sum2 = _mm_add_ps(sum2, _mm_mul_ps(d2, d));
+               sum3 = _mm_add_ps(sum3, _mm_mul_ps(d3, d));
+       }
+
+       {
+               __m128 d0 = _mm_setzero_ps();
+               __m128 d1 = _mm_setzero_ps();
+               __m128 d2 = _mm_setzero_ps();
+               __m128 d3 = _mm_setzero_ps();
+               limit++; if(limit < 0) limit = 0;
+
+               for(i = data_len-1; i >= limit; i--) {
+                       __m128 d;
+                       d = _mm_load_ss(data+i); d = _mm_shuffle_ps(d, d, 0);
+                       d3 = _mm_shuffle_ps(d3, d3, _MM_SHUFFLE(2,1,0,3));
+                       d2 = _mm_shuffle_ps(d2, d2, _MM_SHUFFLE(2,1,0,3));
+                       d1 = _mm_shuffle_ps(d1, d1, _MM_SHUFFLE(2,1,0,3));
+                       d0 = _mm_shuffle_ps(d0, d0, _MM_SHUFFLE(2,1,0,3));
+                       d3 = _mm_move_ss(d3, d2);
+                       d2 = _mm_move_ss(d2, d1);
+                       d1 = _mm_move_ss(d1, d0);
+                       d0 = _mm_move_ss(d0, d);
+                       sum3 = _mm_add_ps(sum3, _mm_mul_ps(d, d3));
+                       sum2 = _mm_add_ps(sum2, _mm_mul_ps(d, d2));
+                       sum1 = _mm_add_ps(sum1, _mm_mul_ps(d, d1));
+                       sum0 = _mm_add_ps(sum0, _mm_mul_ps(d, d0));
+               }
+       }
+
+       _mm_storeu_ps(autoc,   sum0);
+       _mm_storeu_ps(autoc+4, sum1);
+       _mm_storeu_ps(autoc+8, sum2);
+       _mm_storeu_ps(autoc+12,sum3);
+}
+
+#else
+/* Faster on older Intel CPUs (up to Core 2) */
+
 FLAC__SSE_TARGET("sse")
 void FLAC__lpc_compute_autocorrelation_intrin_sse_lag_4(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[])
 {
@@ -245,6 +443,7 @@ void FLAC__lpc_compute_autocorrelation_intrin_sse_lag_16(const FLAC__real data[]
        _mm_storeu_ps(autoc+8, xmm8);
        _mm_storeu_ps(autoc+12,xmm9);
 }
+#endif
 
 #endif /* FLAC__SSE_SUPPORTED */
 #endif /* (FLAC__CPU_IA32 || FLAC__CPU_X86_64) && FLAC__HAS_X86INTRIN */