[pulseaudio-discuss] [PATCH] tests: add test/performance comparison for mixing special-case code

Peter Meerwald pmeerw at pmeerw.net
Fri Feb 15 14:00:37 PST 2013


From: Peter Meerwald <p.meerwald at bct-electronic.com>

x86-64, i7-870, 3 GHz gcc -O0, 100 TIMES

mix s16 generic 1 channel: 286503 usec (avg: 2865.03, min = 2673, max = 6892, stddev = 425.996).
mix s16 2 streams 1 channel: 136490 usec (avg: 1364.9, min = 1304, max = 1686, stddev = 73.4594).

mix s16 generic 2 channels: 590472 usec (avg: 5904.72, min = 5342, max = 16027, stddev = 1079.34).
mix s16 2 channels: 380369 usec (avg: 3803.69, min = 3570, max = 5041, stddev = 234.831).
mix s16 2 streams: 316254 usec (avg: 3162.54, min = 3034, max = 3536, stddev = 107.716).
mix s16 2 streams 2 channels: 269645 usec (avg: 2696.45, min = 2585, max = 3027, stddev = 87.9661).

x86-64, i7-870, 3 GHz gcc -O2, 1000 TIMES

mix s16 generic 1 channel: 371550 usec (avg: 3715.5, min = 3515, max = 10534, stddev = 760.071).
mix s16 2 streams 1 channel: 207750 usec (avg: 2077.5, min = 2009, max = 2271, stddev = 58.6076).

mix s16 generic 2 channels: 724294 usec (avg: 7242.94, min = 6937, max = 10350, stddev = 363.451).
mix s16 2 channels: 345661 usec (avg: 3456.61, min = 3291, max = 5586, stddev = 256.309).
mix s16 2 streams: 559243 usec (avg: 5592.43, min = 5349, max = 6705, stddev = 223.271).
mix s16 2 streams 2 channels: 390101 usec (avg: 3901.01, min = 3696, max = 5345, stddev = 213.482).

Signed-off-by: Peter Meerwald <pmeerw at pmeerw.net>
---
 src/Makefile.am              |    8 +-
 src/tests/mix-special-test.c |  354 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 361 insertions(+), 1 deletion(-)
 create mode 100644 src/tests/mix-special-test.c

diff --git a/src/Makefile.am b/src/Makefile.am
index 2bc80c9..f514712 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -246,7 +246,8 @@ TESTS_default = \
 		proplist-test \
 		cpu-test \
 		lock-autospawn-test \
-		mult-s16-test
+		mult-s16-test \
+		mix-special-test
 
 TESTS_norun = \
 		mcalign-test \
@@ -509,6 +510,11 @@ mult_s16_test_LDADD = $(AM_LDADD) libpulsecore- at PA_MAJORMINOR@.la libpulse.la li
 mult_s16_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS)
 mult_s16_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
 
+mix_special_test_SOURCES = tests/mix-special-test.c
+mix_special_test_LDADD = $(AM_LDADD) libpulsecore- at PA_MAJORMINOR@.la libpulse.la libpulsecommon- at PA_MAJORMINOR@.la
+mix_special_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS)
+mix_special_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
+
 rtstutter_SOURCES = tests/rtstutter.c
 rtstutter_LDADD = $(AM_LDADD) libpulsecore- at PA_MAJORMINOR@.la libpulse.la libpulsecommon- at PA_MAJORMINOR@.la
 rtstutter_CFLAGS = $(AM_CFLAGS)
diff --git a/src/tests/mix-special-test.c b/src/tests/mix-special-test.c
new file mode 100644
index 0000000..df86ccb
--- /dev/null
+++ b/src/tests/mix-special-test.c
@@ -0,0 +1,354 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio 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.
+
+  PulseAudio 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <check.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include <pulse/rtclock.h>
+#include <pulsecore/random.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/mix.h>
+#include <pulsecore/sample-util.h>
+
+#define PA_CPU_TEST_RUN_START(l, t1, t2)                        \
+{                                                               \
+    int _j, _k;                                                 \
+    int _times = (t1), _times2 = (t2);                          \
+    pa_usec_t _start, _stop;                                    \
+    pa_usec_t _min = INT_MAX, _max = 0;                         \
+    double _s1 = 0, _s2 = 0;                                    \
+    const char *_label = (l);                                   \
+                                                                \
+    for (_k = 0; _k < _times2; _k++) {                          \
+        _start = pa_rtclock_now();                              \
+        for (_j = 0; _j < _times; _j++)
+
+#define PA_CPU_TEST_RUN_STOP                                    \
+        _stop = pa_rtclock_now();                               \
+                                                                \
+        if (_min > (_stop - _start)) _min = _stop - _start;     \
+        if (_max < (_stop - _start)) _max = _stop - _start;     \
+        _s1 += _stop - _start;                                  \
+        _s2 += (_stop - _start) * (_stop - _start);             \
+    }                                                           \
+    pa_log_debug("%s: %llu usec (avg: %g, min = %llu, max = %llu, stddev = %g).", _label, \
+            (long long unsigned int)_s1,                        \
+            ((double)_s1 / _times2),                            \
+            (long long unsigned int)_min,                       \
+            (long long unsigned int)_max,                       \
+            sqrt(_times2 * _s2 - _s1 * _s1) / _times2);         \
+}
+
+static void acquire_mix_streams(pa_mix_info streams[], unsigned nstreams) {
+    unsigned i;
+
+    for (i = 0; i < nstreams; i++)
+        streams[i].ptr = pa_memblock_acquire_chunk(&streams[i].chunk);
+}
+
+static void release_mix_streams(pa_mix_info streams[], unsigned nstreams) {
+    unsigned i;
+
+    for (i = 0; i < nstreams; i++)
+        pa_memblock_release(streams[i].chunk.memblock);
+}
+
+/* special case: mix 2 s16ne streams, 1 channel each */
+static void pa_mix2_ch1_s16ne(pa_mix_info streams[], int16_t *data, unsigned length) {
+    const int16_t *ptr0 = streams[0].ptr;
+    const int16_t *ptr1 = streams[1].ptr;
+
+    const int32_t cv0 = streams[0].linear[0].i;
+    const int32_t cv1 = streams[1].linear[0].i;
+
+    length /= sizeof(int16_t);
+
+    for (; length > 0; length--) {
+        int32_t sum;
+
+        sum = pa_mult_s16_volume(*ptr0++, cv0);
+        sum += pa_mult_s16_volume(*ptr1++, cv1);
+
+        sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF);
+        *data++ = sum;
+    }
+}
+
+/* special case: mix 2 s16ne streams, 2 channels each */
+static void pa_mix2_ch2_s16ne(pa_mix_info streams[], int16_t *data, unsigned length) {
+    const int16_t *ptr0 = streams[0].ptr;
+    const int16_t *ptr1 = streams[1].ptr;
+
+    length /= sizeof(int16_t) * 2;
+
+    for (; length > 0; length--) {
+        int32_t sum;
+
+        sum = pa_mult_s16_volume(*ptr0++, streams[0].linear[0].i);
+        sum += pa_mult_s16_volume(*ptr1++, streams[1].linear[0].i);
+
+        sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF);
+        *data++ = sum;
+
+        sum = pa_mult_s16_volume(*ptr0++, streams[0].linear[1].i);
+        sum += pa_mult_s16_volume(*ptr1++, streams[1].linear[1].i);
+
+        sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF);
+        *data++ = sum;
+    }
+}
+
+/* special case: mix 2 s16ne streams */
+static void pa_mix2_s16ne(pa_mix_info streams[], unsigned channels, int16_t *data, unsigned length) {
+    const int16_t *ptr0 = streams[0].ptr;
+    const int16_t *ptr1 = streams[1].ptr;
+    unsigned channel = 0;
+
+    length /= sizeof(int16_t);
+
+    for (; length > 0; length--) {
+        int32_t sum;
+
+        sum = pa_mult_s16_volume(*ptr0++, streams[0].linear[channel].i);
+        sum += pa_mult_s16_volume(*ptr1++, streams[1].linear[channel].i);
+
+        sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF);
+        *data++ = sum;
+
+        if (PA_UNLIKELY(++channel >= channels))
+            channel = 0;
+    }
+}
+
+/* special case: mix s16ne streams, 2 channels each */
+static void pa_mix_ch2_s16ne(pa_mix_info streams[], unsigned nstreams, int16_t *data, unsigned length) {
+
+    length /= sizeof(int16_t) * 2;
+
+    for (; length > 0; length--) {
+        int32_t sum0 = 0, sum1 = 0;
+        unsigned i;
+
+        for (i = 0; i < nstreams; i++) {
+            pa_mix_info *m = streams + i;
+            int32_t cv0 = m->linear[0].i;
+            int32_t cv1 = m->linear[1].i;
+
+            sum0 += pa_mult_s16_volume(*((int16_t*) m->ptr), cv0);
+            m->ptr = (uint8_t*) m->ptr + sizeof(int16_t);
+
+            sum1 += pa_mult_s16_volume(*((int16_t*) m->ptr), cv1);
+            m->ptr = (uint8_t*) m->ptr + sizeof(int16_t);
+        }
+
+        *data++ = PA_CLAMP_UNLIKELY(sum0, -0x8000, 0x7FFF);
+        *data++ = PA_CLAMP_UNLIKELY(sum1, -0x8000, 0x7FFF);
+    }
+}
+
+static void pa_mix_generic_s16ne(pa_mix_info streams[], unsigned nstreams, unsigned channels, int16_t *data, unsigned length) {
+    unsigned channel = 0;
+
+    length /= sizeof(int16_t);
+
+    for (; length > 0; length--) {
+        int32_t sum = 0;
+        unsigned i;
+
+        for (i = 0; i < nstreams; i++) {
+            pa_mix_info *m = streams + i;
+            int32_t cv = m->linear[channel].i;
+
+            if (PA_LIKELY(cv > 0))
+                sum += pa_mult_s16_volume(*((int16_t*) m->ptr), cv);
+            m->ptr = (uint8_t*) m->ptr + sizeof(int16_t);
+        }
+
+        sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF);
+        *data++ = sum;
+
+        if (PA_UNLIKELY(++channel >= channels))
+            channel = 0;
+    }
+}
+
+
+#define SAMPLES 1028
+#define TIMES 1000
+#define TIMES2 100
+
+START_TEST (mix_special_1ch_test) {
+    int16_t samples0[SAMPLES];
+    int16_t samples1[SAMPLES];
+    int16_t out[SAMPLES];
+    int16_t out_ref[SAMPLES];
+    pa_mempool *pool;
+    pa_memchunk c0, c1;
+    pa_mix_info m[2];
+    unsigned nsamples = SAMPLES;
+
+    fail_unless((pool = pa_mempool_new(FALSE, 0)) != NULL, NULL);
+
+    pa_random(samples0, nsamples * sizeof(int16_t));
+    c0.memblock = pa_memblock_new_fixed(pool, samples0, nsamples * sizeof(int16_t), FALSE);
+    c0.length = pa_memblock_get_length(c0.memblock);
+    c0.index = 0;
+
+    pa_random(samples1, nsamples * sizeof(int16_t));
+    c1.memblock = pa_memblock_new_fixed(pool, samples1, nsamples * sizeof(int16_t), FALSE);
+    c1.length = pa_memblock_get_length(c1.memblock);
+    c1.index = 0;
+
+    m[0].chunk = c0;
+    m[0].volume.channels = 1;
+    m[0].volume.values[0] = PA_VOLUME_NORM;
+    m[0].linear[0].i = 0x5555;
+
+    m[1].chunk = c1;
+    m[1].volume.channels = 1;
+    m[1].volume.values[0] = PA_VOLUME_NORM;
+    m[1].linear[0].i = 0x6789;
+
+    PA_CPU_TEST_RUN_START("mix s16 generic 1 channel", TIMES, TIMES2) {
+        acquire_mix_streams(m, 2);
+        pa_mix_generic_s16ne(m, 2, 1, out_ref, nsamples * sizeof(int16_t));
+        release_mix_streams(m, 2);
+    } PA_CPU_TEST_RUN_STOP
+
+    PA_CPU_TEST_RUN_START("mix s16 2 streams 1 channel", TIMES, TIMES2) {
+        acquire_mix_streams(m, 2);
+        pa_mix2_ch1_s16ne(m, out, nsamples * sizeof(int16_t));
+        release_mix_streams(m, 2);
+    } PA_CPU_TEST_RUN_STOP
+
+    fail_unless(memcmp(out, out_ref, nsamples * sizeof(int16_t)) == 0);
+
+    pa_memblock_unref(c0.memblock);
+    pa_memblock_unref(c1.memblock);
+
+    pa_mempool_free(pool);
+}
+END_TEST
+
+START_TEST (mix_special_2ch_test) {
+    int16_t samples0[SAMPLES*2];
+    int16_t samples1[SAMPLES*2];
+    int16_t out[SAMPLES*2];
+    int16_t out_ref[SAMPLES*2];
+    int i;
+    pa_mempool *pool;
+    pa_memchunk c0, c1;
+    pa_mix_info m[2];
+    unsigned nsamples = SAMPLES * 2;
+
+    fail_unless((pool = pa_mempool_new(FALSE, 0)) != NULL, NULL);
+
+    pa_random(samples0, nsamples * sizeof(int16_t));
+    c0.memblock = pa_memblock_new_fixed(pool, samples0, nsamples * sizeof(int16_t), FALSE);
+    c0.length = pa_memblock_get_length(c0.memblock);
+    c0.index = 0;
+
+    pa_random(samples1, nsamples * sizeof(int16_t));
+    c1.memblock = pa_memblock_new_fixed(pool, samples1, nsamples * sizeof(int16_t), FALSE);
+    c1.length = pa_memblock_get_length(c1.memblock);
+    c1.index = 0;
+
+    m[0].chunk = c0;
+    m[0].volume.channels = 2;
+    for (i = 0; i < m[0].volume.channels; i++) {
+        m[0].volume.values[i] = PA_VOLUME_NORM;
+        m[0].linear[i].i = 0x5555;
+    }
+
+    m[1].chunk = c1;
+    m[1].volume.channels = 2;
+    for (i = 0; i < m[1].volume.channels; i++) {
+        m[1].volume.values[i] = PA_VOLUME_NORM;
+        m[1].linear[i].i = 0x6789;
+    }
+
+    PA_CPU_TEST_RUN_START("mix s16 generic 2 channels", TIMES, TIMES2) {
+        acquire_mix_streams(m, 2);
+        pa_mix_generic_s16ne(m, 2, 2, out_ref, nsamples * sizeof(int16_t));
+        release_mix_streams(m, 2);
+    } PA_CPU_TEST_RUN_STOP
+
+    PA_CPU_TEST_RUN_START("mix s16 2 channels", TIMES, TIMES2) {
+        acquire_mix_streams(m, 2);
+        pa_mix_ch2_s16ne(m, 2, out, nsamples * sizeof(int16_t));
+        release_mix_streams(m, 2);
+    } PA_CPU_TEST_RUN_STOP
+
+    fail_unless(memcmp(out, out_ref, nsamples * sizeof(int16_t)) == 0);
+
+    PA_CPU_TEST_RUN_START("mix s16 2 streams", TIMES, TIMES2) {
+        acquire_mix_streams(m, 2);
+        pa_mix2_s16ne(m, 2, out, nsamples * sizeof(int16_t));
+        release_mix_streams(m, 2);
+    } PA_CPU_TEST_RUN_STOP
+
+    fail_unless(memcmp(out, out_ref, nsamples * sizeof(int16_t)) == 0);
+
+    PA_CPU_TEST_RUN_START("mix s16 2 streams 2 channels", TIMES, TIMES2) {
+        acquire_mix_streams(m, 2);
+        pa_mix2_ch2_s16ne(m, out, nsamples * sizeof(int16_t));
+        release_mix_streams(m, 2);
+    } PA_CPU_TEST_RUN_STOP
+
+    fail_unless(memcmp(out, out_ref, nsamples * sizeof(int16_t)) == 0);
+
+    pa_memblock_unref(c0.memblock);
+    pa_memblock_unref(c1.memblock);
+
+    pa_mempool_free(pool);
+}
+END_TEST
+
+int main(int argc, char *argv[]) {
+    int failed = 0;
+    Suite *s;
+    TCase *tc;
+    SRunner *sr;
+
+    if (!getenv("MAKE_CHECK"))
+        pa_log_set_level(PA_LOG_DEBUG);
+
+    s = suite_create("Mix-special");
+    tc = tcase_create("mix-special 1ch");
+    tcase_add_test(tc, mix_special_1ch_test);
+    tcase_set_timeout(tc, 120);
+    suite_add_tcase(s, tc);
+    tc = tcase_create("mix-special 2ch");
+    tcase_add_test(tc, mix_special_2ch_test);
+    tcase_set_timeout(tc, 120);
+    suite_add_tcase(s, tc);
+
+    sr = srunner_create(s);
+    srunner_run_all(sr, CK_NORMAL);
+    failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+
+    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
-- 
1.7.9.5



More information about the pulseaudio-discuss mailing list