1 /*
2 * Copyright 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <array>
18 #include <dlfcn.h>
19 #include <random>
20 #include <vector>
21
22 #include <benchmark/benchmark.h>
23 #include <hardware/audio_effect.h>
24 #include <log/log.h>
25
__anonb8ce4de70102null26 audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = [] {
27 audio_effect_library_t symbol{};
28 void* effectLib = dlopen("libspatialaudio.so", RTLD_NOW);
29 if (effectLib) {
30 audio_effect_library_t* effectInterface =
31 (audio_effect_library_t*)dlsym(effectLib, AUDIO_EFFECT_LIBRARY_INFO_SYM_AS_STR);
32 if (effectInterface == nullptr) {
33 ALOGE("dlsym failed: %s", dlerror());
34 dlclose(effectLib);
35 exit(-1);
36 }
37 symbol = (audio_effect_library_t)(*effectInterface);
38 } else {
39 ALOGE("dlopen failed: %s", dlerror());
40 exit(-1);
41 }
42 return symbol;
43 }();
44
45 // channel masks
46 constexpr int kInputChMask = AUDIO_CHANNEL_OUT_5POINT1;
47
48 // sampleRates
49 constexpr size_t kSampleRates[] = {
50 44100,
51 48000,
52 96000,
53 };
54 constexpr size_t kNumSampleRates = std::size(kSampleRates);
55
56 // duration in ms
57 constexpr size_t kDurations[] = {2, 5, 10};
58 constexpr size_t kNumDurations = std::size(kDurations);
59
60 // effect uuids
61 constexpr effect_uuid_t kEffectUuid = {
62 0xcc4677de, 0xff72, 0x11eb, 0x9a03, {0x02, 0x42, 0xac, 0x13, 0x00, 0x03}};
63
64 constexpr float kMinAmplitude = -1.0f;
65 constexpr float kMaxAmplitude = 1.0f;
66
67 /*******************************************************************
68 * A test result running on Pixel 5 for comparison.
69 * The first parameter indicates the sample rate.
70 * 0: 44100, 1: 48000, 2: 96000
71 * The second parameter indicates the duration in ms.
72 * 0: 2, 1: 5, 2: 10
73 * -------------------------------------------------------------
74 * Benchmark Time CPU Iterations
75 * -------------------------------------------------------------
76 * BM_SPATIALIZER/0/0 739848 ns 738497 ns 934
77 * BM_SPATIALIZER/0/1 1250503 ns 1248337 ns 480
78 * BM_SPATIALIZER/0/2 2094092 ns 2090092 ns 310
79 * BM_SPATIALIZER/1/0 783114 ns 781626 ns 683
80 * BM_SPATIALIZER/1/1 1332951 ns 1330473 ns 452
81 * BM_SPATIALIZER/1/2 2258313 ns 2254022 ns 289
82 * BM_SPATIALIZER/2/0 1210332 ns 1207957 ns 477
83 * BM_SPATIALIZER/2/1 2356259 ns 2351764 ns 269
84 * BM_SPATIALIZER/2/2 4267814 ns 4259567 ns 155
85 *******************************************************************/
86
BM_SPATIALIZER(benchmark::State & state)87 static void BM_SPATIALIZER(benchmark::State& state) {
88 const size_t sampleRate = kSampleRates[state.range(0)];
89 const size_t durationMs = kDurations[state.range(1)];
90 const size_t frameCount = durationMs * sampleRate / 1000;
91 const size_t inputChannelCount = audio_channel_count_from_out_mask(kInputChMask);
92 const size_t outputChannelCount = audio_channel_count_from_out_mask(AUDIO_CHANNEL_OUT_STEREO);
93
94 // Initialize input buffer with deterministic pseudo-random values
95 std::minstd_rand gen(kInputChMask);
96 std::uniform_real_distribution<> dis(kMinAmplitude, kMaxAmplitude);
97 std::vector<float> input(frameCount * inputChannelCount);
98 for (auto& in : input) {
99 in = dis(gen);
100 }
101
102 effect_handle_t effectHandle = nullptr;
103 if (int status = AUDIO_EFFECT_LIBRARY_INFO_SYM.create_effect(&kEffectUuid, 1 /* sessionId */,
104 1 /* ioId */, &effectHandle);
105 status != 0) {
106 ALOGE("create_effect returned an error = %d\n", status);
107 return;
108 }
109
110 effect_config_t config{};
111 config.inputCfg.samplingRate = config.outputCfg.samplingRate = sampleRate;
112 config.inputCfg.channels = kInputChMask;
113 config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
114 config.inputCfg.format = config.outputCfg.format = AUDIO_FORMAT_PCM_FLOAT;
115
116 int reply = 0;
117 uint32_t replySize = sizeof(reply);
118 if (int status = (*effectHandle)
119 ->command(effectHandle, EFFECT_CMD_SET_CONFIG, sizeof(effect_config_t),
120 &config, &replySize, &reply);
121 status != 0) {
122 ALOGE("command returned an error = %d\n", status);
123 return;
124 }
125
126 if (int status = (*effectHandle)
127 ->command(effectHandle, EFFECT_CMD_ENABLE, sizeof(effect_config_t),
128 &config, &replySize, &reply);
129 status != 0) {
130 ALOGE("command returned an error = %d\n", status);
131 return;
132 }
133
134 // Run the test
135 std::vector<float> output(frameCount * outputChannelCount);
136 for (auto _ : state) {
137 benchmark::DoNotOptimize(input.data());
138 benchmark::DoNotOptimize(output.data());
139
140 audio_buffer_t inBuffer = {.frameCount = frameCount, .f32 = input.data()};
141 audio_buffer_t outBuffer = {.frameCount = frameCount, .f32 = output.data()};
142 (*effectHandle)->process(effectHandle, &inBuffer, &outBuffer);
143
144 benchmark::ClobberMemory();
145 }
146
147 state.SetComplexityN(frameCount);
148
149 if (int status = AUDIO_EFFECT_LIBRARY_INFO_SYM.release_effect(effectHandle); status != 0) {
150 ALOGE("release_effect returned an error = %d\n", status);
151 return;
152 }
153 }
154
SPATIALIZERArgs(benchmark::internal::Benchmark * b)155 static void SPATIALIZERArgs(benchmark::internal::Benchmark* b) {
156 for (int i = 0; i < kNumSampleRates; i++) {
157 for (int j = 0; j < kNumDurations; ++j) {
158 b->Args({i, j});
159 }
160 }
161 }
162
163 BENCHMARK(BM_SPATIALIZER)->Apply(SPATIALIZERArgs);
164
165 BENCHMARK_MAIN();
166