Merge "Preprocessing benchmark : Initial version" am: 0a1a50433b
Original change: https://android-review.googlesource.com/c/platform/frameworks/av/+/1498299
Change-Id: Ia5224667725dc3762d1b6c1603ba30312e40cb20
diff --git a/media/libeffects/preprocessing/benchmarks/Android.bp b/media/libeffects/preprocessing/benchmarks/Android.bp
new file mode 100644
index 0000000..2808293
--- /dev/null
+++ b/media/libeffects/preprocessing/benchmarks/Android.bp
@@ -0,0 +1,51 @@
+cc_benchmark {
+ name: "preprocessing_legacy_benchmark",
+ vendor: true,
+ relative_install_path: "soundfx",
+ srcs: ["preprocessing_benchmark.cpp"],
+ shared_libs: [
+ "libaudiopreprocessing_legacy",
+ "libaudioutils",
+ "liblog",
+ "libutils",
+ "libwebrtc_audio_preprocessing",
+ ],
+ cflags: [
+ "-DWEBRTC_POSIX",
+ "-DWEBRTC_LEGACY",
+ "-fvisibility=default",
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+ header_libs: [
+ "libaudioeffects",
+ "libhardware_headers",
+ "libwebrtc_absl_headers",
+ ],
+}
+
+cc_benchmark {
+ name: "preprocessing_benchmark",
+ vendor: true,
+ relative_install_path: "soundfx",
+ srcs: ["preprocessing_benchmark.cpp"],
+ shared_libs: [
+ "libaudiopreprocessing",
+ "libaudioutils",
+ "liblog",
+ "libutils",
+ ],
+ cflags: [
+ "-DWEBRTC_POSIX",
+ "-fvisibility=default",
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+ header_libs: [
+ "libaudioeffects",
+ "libhardware_headers",
+ "libwebrtc_absl_headers",
+ ],
+}
diff --git a/media/libeffects/preprocessing/benchmarks/preprocessing_benchmark.cpp b/media/libeffects/preprocessing/benchmarks/preprocessing_benchmark.cpp
new file mode 100644
index 0000000..d4df371
--- /dev/null
+++ b/media/libeffects/preprocessing/benchmarks/preprocessing_benchmark.cpp
@@ -0,0 +1,261 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*******************************************************************
+ * A test result running on Pixel 3 for comparison.
+ * The first parameter indicates the channel mask index.
+ * The second parameter indicates the effect index.
+ * 0: Automatic Gain Control,
+ * 1: Acoustic Echo Canceler,
+ * 2: Noise Suppressor,
+ * 3: Automatic Gain Control 2
+ * ---------------------------------------------------------------
+ * Benchmark Time CPU Iterations
+ * ---------------------------------------------------------------
+ * BM_PREPROCESSING/1/0 59836 ns 59655 ns 11732
+ * BM_PREPROCESSING/1/1 66848 ns 66642 ns 10554
+ * BM_PREPROCESSING/1/2 20726 ns 20655 ns 33822
+ * BM_PREPROCESSING/1/3 5093 ns 5076 ns 137897
+ * BM_PREPROCESSING/2/0 117040 ns 116670 ns 5996
+ * BM_PREPROCESSING/2/1 120600 ns 120225 ns 5845
+ * BM_PREPROCESSING/2/2 38460 ns 38330 ns 18190
+ * BM_PREPROCESSING/2/3 6294 ns 6274 ns 111488
+ * BM_PREPROCESSING/3/0 232272 ns 231528 ns 3025
+ * BM_PREPROCESSING/3/1 226346 ns 225628 ns 3117
+ * BM_PREPROCESSING/3/2 75442 ns 75227 ns 9104
+ * BM_PREPROCESSING/3/3 9782 ns 9750 ns 71805
+ * BM_PREPROCESSING/4/0 290388 ns 289426 ns 2389
+ * BM_PREPROCESSING/4/1 279394 ns 278498 ns 2522
+ * BM_PREPROCESSING/4/2 94029 ns 93759 ns 7307
+ * BM_PREPROCESSING/4/3 11487 ns 11450 ns 61129
+ * BM_PREPROCESSING/5/0 347736 ns 346580 ns 2020
+ * BM_PREPROCESSING/5/1 331853 ns 330788 ns 2122
+ * BM_PREPROCESSING/5/2 112594 ns 112268 ns 6105
+ * BM_PREPROCESSING/5/3 13254 ns 13212 ns 52972
+ *******************************************************************/
+
+#include <array>
+#include <climits>
+#include <cstdlib>
+#include <random>
+#include <vector>
+#include <audio_effects/effect_aec.h>
+#include <audio_effects/effect_agc.h>
+#ifndef WEBRTC_LEGACY
+#include <audio_effects/effect_agc2.h>
+#endif
+#include <audio_effects/effect_ns.h>
+#include <benchmark/benchmark.h>
+#include <hardware/audio_effect.h>
+#include <log/log.h>
+#include <sys/stat.h>
+#include <system/audio.h>
+
+extern audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM;
+
+constexpr int kSampleRate = 16000;
+constexpr float kTenMilliSecVal = 0.01;
+constexpr unsigned int kStreamDelayMs = 0;
+constexpr effect_uuid_t kEffectUuids[] = {
+ // agc uuid
+ {0xaa8130e0, 0x66fc, 0x11e0, 0xbad0, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
+ // aec uuid
+ {0xbb392ec0, 0x8d4d, 0x11e0, 0xa896, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
+ // ns uuid
+ {0xc06c8400, 0x8e06, 0x11e0, 0x9cb6, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
+#ifndef WEBRTC_LEGACY
+ // agc2 uuid
+ {0x89f38e65, 0xd4d2, 0x4d64, 0xad0e, {0x2b, 0x3e, 0x79, 0x9e, 0xa8, 0x86}},
+#endif
+};
+constexpr size_t kNumEffectUuids = std::size(kEffectUuids);
+constexpr audio_channel_mask_t kChMasks[] = {
+ AUDIO_CHANNEL_IN_MONO,
+ AUDIO_CHANNEL_IN_STEREO,
+ AUDIO_CHANNEL_IN_2POINT0POINT2,
+ AUDIO_CHANNEL_IN_2POINT1POINT2,
+ AUDIO_CHANNEL_IN_6,
+};
+constexpr size_t kNumChMasks = std::size(kChMasks);
+
+// types of pre processing modules
+enum PreProcId {
+ PREPROC_AGC, // Automatic Gain Control
+ PREPROC_AEC, // Acoustic Echo Canceler
+ PREPROC_NS, // Noise Suppressor
+#ifndef WEBRTC_LEGACY
+ PREPROC_AGC2, // Automatic Gain Control 2
+#endif
+ PREPROC_NUM_EFFECTS
+};
+
+int preProcCreateEffect(effect_handle_t *pEffectHandle, uint32_t effectType,
+ effect_config_t *pConfig, int sessionId, int ioId) {
+ if (int status = AUDIO_EFFECT_LIBRARY_INFO_SYM.create_effect(
+ &kEffectUuids[effectType], sessionId, ioId, pEffectHandle);
+ status != 0) {
+ ALOGE("Audio Preprocessing create returned an error = %d\n", status);
+ return EXIT_FAILURE;
+ }
+ int reply = 0;
+ uint32_t replySize = sizeof(reply);
+ if (effectType == PREPROC_AEC) {
+ if (int status =
+ (**pEffectHandle)
+ ->command(*pEffectHandle, EFFECT_CMD_SET_CONFIG_REVERSE,
+ sizeof(effect_config_t), pConfig, &replySize, &reply);
+ status != 0) {
+ ALOGE("Set config reverse command returned an error = %d\n", status);
+ return EXIT_FAILURE;
+ }
+ }
+ if (int status =
+ (**pEffectHandle)
+ ->command(*pEffectHandle, EFFECT_CMD_SET_CONFIG,
+ sizeof(effect_config_t), pConfig, &replySize, &reply);
+ status != 0) {
+ ALOGE("Set config command returned an error = %d\n", status);
+ return EXIT_FAILURE;
+ }
+ return reply;
+}
+
+int preProcSetConfigParam(effect_handle_t effectHandle, uint32_t paramType,
+ uint32_t paramValue) {
+ int reply = 0;
+ uint32_t replySize = sizeof(reply);
+ uint32_t paramData[2] = {paramType, paramValue};
+ effect_param_t *effectParam =
+ (effect_param_t *)malloc(sizeof(*effectParam) + sizeof(paramData));
+ memcpy(&effectParam->data[0], ¶mData[0], sizeof(paramData));
+ effectParam->psize = sizeof(paramData[0]);
+ (*effectHandle)
+ ->command(effectHandle, EFFECT_CMD_SET_PARAM, sizeof(effect_param_t),
+ effectParam, &replySize, &reply);
+ free(effectParam);
+ return reply;
+}
+
+short preProcGetShortVal(float paramValue) {
+ return static_cast<short>(paramValue * std::numeric_limits<short>::max());
+}
+
+static void BM_PREPROCESSING(benchmark::State &state) {
+ const size_t chMask = kChMasks[state.range(0) - 1];
+ const size_t channelCount = audio_channel_count_from_in_mask(chMask);
+
+ PreProcId effectType = (PreProcId)state.range(1);
+
+ int32_t sessionId = 1;
+ int32_t ioId = 1;
+ effect_handle_t effectHandle = nullptr;
+ effect_config_t config{};
+ config.inputCfg.samplingRate = config.outputCfg.samplingRate = kSampleRate;
+ config.inputCfg.channels = config.outputCfg.channels = chMask;
+ config.inputCfg.format = config.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
+
+ if (int status = preProcCreateEffect(&effectHandle, state.range(1), &config,
+ sessionId, ioId);
+ status != 0) {
+ ALOGE("Create effect call returned error %i", status);
+ return;
+ }
+
+ int reply = 0;
+ uint32_t replySize = sizeof(reply);
+ if (int status = (*effectHandle)
+ ->command(effectHandle, EFFECT_CMD_ENABLE, 0, nullptr,
+ &replySize, &reply);
+ status != 0) {
+ ALOGE("Command enable call returned error %d\n", reply);
+ return;
+ }
+
+ // Initialize input buffer with deterministic pseudo-random values
+ const int frameLength = (int)(kSampleRate * kTenMilliSecVal);
+ std::minstd_rand gen(chMask);
+ std::uniform_real_distribution<> dis(-1.0f, 1.0f);
+ std::vector<short> in(frameLength * channelCount);
+ for (auto &i : in) {
+ i = preProcGetShortVal(dis(gen));
+ }
+ std::vector<short> farIn(frameLength * channelCount);
+ for (auto &i : farIn) {
+ i = preProcGetShortVal(dis(gen));
+ }
+ std::vector<short> out(frameLength * channelCount);
+
+ // Run the test
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(in.data());
+ benchmark::DoNotOptimize(out.data());
+ benchmark::DoNotOptimize(farIn.data());
+
+ audio_buffer_t inBuffer = {.frameCount = (size_t)frameLength,
+ .s16 = in.data()};
+ audio_buffer_t outBuffer = {.frameCount = (size_t)frameLength,
+ .s16 = out.data()};
+ audio_buffer_t farInBuffer = {.frameCount = (size_t)frameLength,
+ .s16 = farIn.data()};
+
+ if (PREPROC_AEC == effectType) {
+ if (int status = preProcSetConfigParam(effectHandle, AEC_PARAM_ECHO_DELAY,
+ kStreamDelayMs);
+ status != 0) {
+ ALOGE("preProcSetConfigParam returned Error %d\n", status);
+ return;
+ }
+ }
+ if (int status =
+ (*effectHandle)->process(effectHandle, &inBuffer, &outBuffer);
+ status != 0) {
+ ALOGE("\nError: Process i = %d returned with error %d\n",
+ (int)state.range(1), status);
+ return;
+ }
+ if (PREPROC_AEC == effectType) {
+ if (int status =
+ (*effectHandle)
+ ->process_reverse(effectHandle, &farInBuffer, &outBuffer);
+ status != 0) {
+ ALOGE("\nError: Process reverse i = %d returned with error %d\n",
+ (int)state.range(1), status);
+ return;
+ }
+ }
+ }
+ benchmark::ClobberMemory();
+
+ state.SetComplexityN(state.range(0));
+
+ if (int status = AUDIO_EFFECT_LIBRARY_INFO_SYM.release_effect(effectHandle);
+ status != 0) {
+ ALOGE("release_effect returned an error = %d\n", status);
+ return;
+ }
+}
+
+static void preprocessingArgs(benchmark::internal::Benchmark *b) {
+ for (int i = 1; i <= (int)kNumChMasks; i++) {
+ for (int j = 0; j < (int)kNumEffectUuids; ++j) {
+ b->Args({i, j});
+ }
+ }
+}
+
+BENCHMARK(BM_PREPROCESSING)->Apply(preprocessingArgs);
+
+BENCHMARK_MAIN();