1 /*
2  * Copyright (C) 2020 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 #define LOG_TAG "VibratorHalControllerBenchmarks"
18 
19 #include <benchmark/benchmark.h>
20 #include <binder/ProcessState.h>
21 #include <vibratorservice/VibratorHalController.h>
22 #include <future>
23 
24 using ::android::enum_range;
25 using ::android::hardware::vibrator::CompositeEffect;
26 using ::android::hardware::vibrator::CompositePrimitive;
27 using ::android::hardware::vibrator::Effect;
28 using ::android::hardware::vibrator::EffectStrength;
29 using ::benchmark::Counter;
30 using ::benchmark::Fixture;
31 using ::benchmark::kMicrosecond;
32 using ::benchmark::State;
33 using ::benchmark::internal::Benchmark;
34 
35 using std::chrono::milliseconds;
36 
37 using namespace android;
38 using namespace std::chrono_literals;
39 
40 // Fixed number of iterations for benchmarks that trigger a vibration on the loop.
41 // They require slow cleanup to ensure a stable state on each run and less noisy metrics.
42 static constexpr auto VIBRATION_ITERATIONS = 500;
43 
44 // Timeout to wait for vibration callback completion.
45 static constexpr auto VIBRATION_CALLBACK_TIMEOUT = 100ms;
46 
47 // Max duration the vibrator can be turned on, in milliseconds.
48 static constexpr auto MAX_ON_DURATION_MS = milliseconds(UINT16_MAX);
49 
50 // Helper to wait for the vibrator to become idle between vibrate bench iterations.
51 class HalCallback {
52 public:
HalCallback(std::function<void ()> && waitFn,std::function<void ()> && completeFn)53     HalCallback(std::function<void()>&& waitFn, std::function<void()>&& completeFn)
54           : mWaitFn(std::move(waitFn)), mCompleteFn(std::move(completeFn)) {}
55     ~HalCallback() = default;
56 
completeFn() const57     std::function<void()> completeFn() const { return mCompleteFn; }
58 
waitForComplete() const59     void waitForComplete() const { mWaitFn(); }
60 
61 private:
62     std::function<void()> mWaitFn;
63     std::function<void()> mCompleteFn;
64 };
65 
66 // Helper for vibration callbacks, kept by the Fixture until all pending callbacks are done.
67 class HalCallbacks {
68 public:
next()69     HalCallback next() {
70         std::unique_lock<std::mutex> lock(mMutex);
71         auto id = mCurrentId++;
72         mPendingPromises[id] = std::promise<void>();
73         mPendingFutures[id] = mPendingPromises[id].get_future(); // Can only be called once.
74         return HalCallback([&, id]() { waitForComplete(id); }, [&, id]() { onComplete(id); });
75     }
76 
onComplete(int32_t id)77     void onComplete(int32_t id) {
78         std::unique_lock<std::mutex> lock(mMutex);
79         auto promise = mPendingPromises.find(id);
80         if (promise != mPendingPromises.end()) {
81             promise->second.set_value();
82             mPendingPromises.erase(promise);
83         }
84     }
85 
waitForComplete(int32_t id)86     void waitForComplete(int32_t id) {
87         // Wait until the HAL has finished processing previous vibration before starting a new one,
88         // so the HAL state is consistent on each run and metrics are less noisy. Some of the newest
89         // HAL implementations are waiting on previous vibration cleanup and might be significantly
90         // slower, so make sure we measure vibrations on a clean slate.
91         if (mPendingFutures[id].wait_for(VIBRATION_CALLBACK_TIMEOUT) == std::future_status::ready) {
92             mPendingFutures.erase(id);
93         }
94     }
95 
waitForPending()96     void waitForPending() {
97         // Wait for pending callbacks from the test, possibly skipped with error.
98         for (auto& [id, future] : mPendingFutures) {
99             future.wait_for(VIBRATION_CALLBACK_TIMEOUT);
100         }
101         mPendingFutures.clear();
102         {
103             std::unique_lock<std::mutex> lock(mMutex);
104             mPendingPromises.clear();
105         }
106     }
107 
108 private:
109     std::mutex mMutex;
110     std::map<int32_t, std::promise<void>> mPendingPromises GUARDED_BY(mMutex);
111     std::map<int32_t, std::future<void>> mPendingFutures;
112     int32_t mCurrentId;
113 };
114 
115 class VibratorBench : public Fixture {
116 public:
SetUp(State &)117     void SetUp(State& /*state*/) override {
118         android::ProcessState::self()->setThreadPoolMaxThreadCount(1);
119         android::ProcessState::self()->startThreadPool();
120         mController.init();
121     }
122 
TearDown(State &)123     void TearDown(State& /*state*/) override {
124         turnVibratorOff();
125         disableExternalControl();
126         mCallbacks.waitForPending();
127     }
128 
DefaultConfig(Benchmark * b)129     static void DefaultConfig(Benchmark* b) { b->Unit(kMicrosecond); }
130 
DefaultArgs(Benchmark *)131     static void DefaultArgs(Benchmark* /*b*/) {
132         // none
133     }
134 
135 protected:
136     vibrator::HalController mController;
137     HalCallbacks mCallbacks;
138 
SlowBenchConfig(Benchmark * b)139     static void SlowBenchConfig(Benchmark* b) { b->Iterations(VIBRATION_ITERATIONS); }
140 
getOtherArg(const State & state,std::size_t index) const141     auto getOtherArg(const State& state, std::size_t index) const { return state.range(index + 0); }
142 
turnVibratorOff()143     vibrator::HalResult<void> turnVibratorOff() {
144         return mController.doWithRetry<void>([](auto hal) { return hal->off(); }, "off");
145     }
146 
disableExternalControl()147     vibrator::HalResult<void> disableExternalControl() {
148         auto disableExternalControlFn = [](auto hal) { return hal->setExternalControl(false); };
149         return mController.doWithRetry<void>(disableExternalControlFn, "setExternalControl false");
150     }
151 
shouldSkipWithMissingCapabilityMessage(vibrator::Capabilities query,State & state)152     bool shouldSkipWithMissingCapabilityMessage(vibrator::Capabilities query, State& state) {
153         auto result = mController.getInfo().capabilities;
154         if (result.isFailed()) {
155             state.SkipWithError(result.errorMessage());
156             return true;
157         }
158         if (!result.isOk()) {
159             state.SkipWithMessage("capability result is unsupported");
160             return true;
161         }
162         if ((result.value() & query) != query) {
163             state.SkipWithMessage("missing capability");
164             return true;
165         }
166         return false;
167     }
168 
169     template <class R>
shouldSkipWithError(const vibrator::HalFunction<vibrator::HalResult<R>> & halFn,const char * label,State & state)170     bool shouldSkipWithError(const vibrator::HalFunction<vibrator::HalResult<R>>& halFn,
171                              const char* label, State& state) {
172         return shouldSkipWithError(mController.doWithRetry<R>(halFn, label), state);
173     }
174 
175     template <class R>
shouldSkipWithError(const vibrator::HalResult<R> & result,State & state)176     bool shouldSkipWithError(const vibrator::HalResult<R>& result, State& state) {
177         if (result.isFailed()) {
178             state.SkipWithError(result.errorMessage());
179             return true;
180         }
181         return false;
182     }
183 };
184 
185 class SlowVibratorBench : public VibratorBench {
186 public:
DefaultConfig(Benchmark * b)187     static void DefaultConfig(Benchmark* b) {
188         VibratorBench::DefaultConfig(b);
189         SlowBenchConfig(b);
190     }
191 };
192 
193 #define BENCHMARK_WRAPPER(fixt, test, code)                \
194     BENCHMARK_DEFINE_F(fixt, test)                         \
195     /* NOLINTNEXTLINE */                                   \
196     (State& state){code} BENCHMARK_REGISTER_F(fixt, test) \
197             ->Apply(fixt::DefaultConfig)                   \
198             ->Apply(fixt::DefaultArgs)
199 
200 BENCHMARK_WRAPPER(VibratorBench, init, {
201     for (auto _ : state) {
202         // Setup
203         state.PauseTiming();
204         vibrator::HalController controller;
205         state.ResumeTiming();
206 
207         // Test
208         controller.init();
209     }
210 });
211 
212 BENCHMARK_WRAPPER(VibratorBench, initCached, {
213     // First call to cache values.
214     mController.init();
215 
216     for (auto _ : state) {
217         mController.init();
218     }
219 });
220 
221 BENCHMARK_WRAPPER(VibratorBench, ping, {
__anon599d61ff0502(auto hal) 222     auto pingFn = [](auto hal) { return hal->ping(); };
223 
224     for (auto _ : state) {
225         if (shouldSkipWithError<void>(pingFn, "ping", state)) {
226             return;
227         }
228     }
229 });
230 
231 BENCHMARK_WRAPPER(VibratorBench, tryReconnect, {
232     for (auto _ : state) {
233         mController.tryReconnect();
234     }
235 });
236 
237 BENCHMARK_WRAPPER(SlowVibratorBench, on, {
238     auto duration = MAX_ON_DURATION_MS;
239 
240     for (auto _ : state) {
241         // Setup
242         state.PauseTiming();
243         auto cb = mCallbacks.next();
__anon599d61ff0602(auto hal) 244         auto onFn = [&](auto hal) { return hal->on(duration, cb.completeFn()); };
245         state.ResumeTiming();
246 
247         // Test
248         if (shouldSkipWithError<void>(onFn, "on", state)) {
249             return;
250         }
251 
252         // Cleanup
253         state.PauseTiming();
254         if (shouldSkipWithError(turnVibratorOff(), state)) {
255             return;
256         }
257         cb.waitForComplete();
258         state.ResumeTiming();
259     }
260 });
261 
262 BENCHMARK_WRAPPER(SlowVibratorBench, off, {
263     auto duration = MAX_ON_DURATION_MS;
264 
265     for (auto _ : state) {
266         // Setup
267         state.PauseTiming();
268         auto cb = mCallbacks.next();
__anon599d61ff0702(auto hal) 269         auto onFn = [&](auto hal) { return hal->on(duration, cb.completeFn()); };
270         if (shouldSkipWithError<void>(onFn, "on", state)) {
271             return;
272         }
__anon599d61ff0802(auto hal) 273         auto offFn = [&](auto hal) { return hal->off(); };
274         state.ResumeTiming();
275 
276         // Test
277         if (shouldSkipWithError<void>(offFn, "off", state)) {
278             return;
279         }
280 
281         // Cleanup
282         state.PauseTiming();
283         cb.waitForComplete();
284         state.ResumeTiming();
285     }
286 });
287 
288 BENCHMARK_WRAPPER(VibratorBench, setAmplitude, {
289     if (shouldSkipWithMissingCapabilityMessage(vibrator::Capabilities::AMPLITUDE_CONTROL, state)) {
290         return;
291     }
292 
293     auto duration = MAX_ON_DURATION_MS;
294     auto amplitude = 1.0f;
__anon599d61ff0902(auto hal) 295     auto setAmplitudeFn = [&](auto hal) { return hal->setAmplitude(amplitude); };
296 
__anon599d61ff0a02(auto hal) 297     auto onFn = [&](auto hal) { return hal->on(duration, [&]() {}); };
298     if (shouldSkipWithError<void>(onFn, "on", state)) {
299         return;
300     }
301 
302     for (auto _ : state) {
303         if (shouldSkipWithError<void>(setAmplitudeFn, "setAmplitude", state)) {
304             return;
305         }
306     }
307 });
308 
309 BENCHMARK_WRAPPER(VibratorBench, setExternalControl, {
310     if (shouldSkipWithMissingCapabilityMessage(vibrator::Capabilities::EXTERNAL_CONTROL, state)) {
311         return;
312     }
313 
__anon599d61ff0c02(auto hal) 314     auto enableExternalControlFn = [](auto hal) { return hal->setExternalControl(true); };
315 
316     for (auto _ : state) {
317         // Test
318         if (shouldSkipWithError<void>(enableExternalControlFn, "setExternalControl true", state)) {
319             return;
320         }
321 
322         // Cleanup
323         state.PauseTiming();
324         if (shouldSkipWithError(disableExternalControl(), state)) {
325             return;
326         }
327         state.ResumeTiming();
328     }
329 });
330 
331 BENCHMARK_WRAPPER(VibratorBench, setExternalAmplitude, {
332     auto externalAmplitudeControl = vibrator::Capabilities::EXTERNAL_CONTROL &
333             vibrator::Capabilities::EXTERNAL_AMPLITUDE_CONTROL;
334     if (shouldSkipWithMissingCapabilityMessage(externalAmplitudeControl, state)) {
335         return;
336     }
337 
338     auto amplitude = 1.0f;
__anon599d61ff0d02(auto hal) 339     auto setAmplitudeFn = [&](auto hal) { return hal->setAmplitude(amplitude); };
__anon599d61ff0e02(auto hal) 340     auto enableExternalControlFn = [](auto hal) { return hal->setExternalControl(true); };
341 
342     if (shouldSkipWithError<void>(enableExternalControlFn, "setExternalControl true", state)) {
343         return;
344     }
345 
346     for (auto _ : state) {
347         if (shouldSkipWithError<void>(setAmplitudeFn, "setExternalAmplitude", state)) {
348             return;
349         }
350     }
351 });
352 
353 BENCHMARK_WRAPPER(VibratorBench, getInfo, {
354     for (auto _ : state) {
355         // Setup
356         state.PauseTiming();
357         vibrator::HalController controller;
358         controller.init();
359         state.ResumeTiming();
360 
361         controller.getInfo();
362     }
363 });
364 
365 BENCHMARK_WRAPPER(VibratorBench, getInfoCached, {
366     // First call to cache values.
367     mController.getInfo();
368 
369     for (auto _ : state) {
370         mController.getInfo();
371     }
372 });
373 
374 class VibratorEffectsBench : public VibratorBench {
375 public:
DefaultArgs(Benchmark * b)376     static void DefaultArgs(Benchmark* b) {
377         vibrator::HalController controller;
378         auto effectsResult = controller.getInfo().supportedEffects;
379         if (!effectsResult.isOk()) {
380             return;
381         }
382 
383         std::vector<Effect> supported = effectsResult.value();
384         b->ArgNames({"Effect", "Strength"});
385 
386         if (supported.empty()) {
387             b->Args({static_cast<long>(-1), static_cast<long>(-1)});
388             return;
389         }
390 
391         for (const auto& effect : enum_range<Effect>()) {
392             if (std::find(supported.begin(), supported.end(), effect) == supported.end()) {
393                 continue;
394             }
395             for (const auto& strength : enum_range<EffectStrength>()) {
396                 b->Args({static_cast<long>(effect), static_cast<long>(strength)});
397             }
398         }
399     }
400 
401 protected:
hasArgs(const State & state) const402     bool hasArgs(const State& state) const { return this->getOtherArg(state, 0) >= 0; }
403 
getEffect(const State & state) const404     auto getEffect(const State& state) const {
405         return static_cast<Effect>(this->getOtherArg(state, 0));
406     }
407 
getStrength(const State & state) const408     auto getStrength(const State& state) const {
409         return static_cast<EffectStrength>(this->getOtherArg(state, 1));
410     }
411 };
412 
413 class SlowVibratorEffectsBench : public VibratorEffectsBench {
414 public:
DefaultConfig(Benchmark * b)415     static void DefaultConfig(Benchmark* b) {
416         VibratorBench::DefaultConfig(b);
417         SlowBenchConfig(b);
418     }
419 };
420 
421 BENCHMARK_WRAPPER(VibratorEffectsBench, alwaysOnEnable, {
422     if (shouldSkipWithMissingCapabilityMessage(vibrator::Capabilities::ALWAYS_ON_CONTROL, state)) {
423         return;
424     }
425     if (!hasArgs(state)) {
426         state.SkipWithMessage("missing args");
427         return;
428     }
429 
430     int32_t id = 1;
431     auto effect = getEffect(state);
432     auto strength = getStrength(state);
__anon599d61ff0f02(auto hal) 433     auto enableFn = [&](auto hal) { return hal->alwaysOnEnable(id, effect, strength); };
__anon599d61ff1002(auto hal) 434     auto disableFn = [&](auto hal) { return hal->alwaysOnDisable(id); };
435 
436     for (auto _ : state) {
437         // Test
438         if (shouldSkipWithError<void>(enableFn, "alwaysOnEnable", state)) {
439             return;
440         }
441 
442         // Cleanup
443         state.PauseTiming();
444         if (shouldSkipWithError<void>(disableFn, "alwaysOnDisable", state)) {
445             return;
446         }
447         state.ResumeTiming();
448     }
449 });
450 
451 BENCHMARK_WRAPPER(VibratorEffectsBench, alwaysOnDisable, {
452     if (shouldSkipWithMissingCapabilityMessage(vibrator::Capabilities::ALWAYS_ON_CONTROL, state)) {
453         return;
454     }
455     if (!hasArgs(state)) {
456         state.SkipWithMessage("missing args");
457         return;
458     }
459 
460     int32_t id = 1;
461     auto effect = getEffect(state);
462     auto strength = getStrength(state);
__anon599d61ff1102(auto hal) 463     auto enableFn = [&](auto hal) { return hal->alwaysOnEnable(id, effect, strength); };
__anon599d61ff1202(auto hal) 464     auto disableFn = [&](auto hal) { return hal->alwaysOnDisable(id); };
465 
466     for (auto _ : state) {
467         // Setup
468         state.PauseTiming();
469         if (shouldSkipWithError<void>(enableFn, "alwaysOnEnable", state)) {
470             return;
471         }
472         state.ResumeTiming();
473 
474         // Test
475         if (shouldSkipWithError<void>(disableFn, "alwaysOnDisable", state)) {
476             return;
477         }
478     }
479 });
480 
481 BENCHMARK_WRAPPER(SlowVibratorEffectsBench, performEffect, {
482     if (!hasArgs(state)) {
483         state.SkipWithMessage("missing args");
484         return;
485     }
486 
487     auto effect = getEffect(state);
488     auto strength = getStrength(state);
489 
490     for (auto _ : state) {
491         // Setup
492         state.PauseTiming();
493         auto cb = mCallbacks.next();
__anon599d61ff1302(auto hal) 494         auto performFn = [&](auto hal) {
495             return hal->performEffect(effect, strength, cb.completeFn());
496         };
497         state.ResumeTiming();
498 
499         // Test
500         if (shouldSkipWithError<milliseconds>(performFn, "performEffect", state)) {
501             return;
502         }
503 
504         // Cleanup
505         state.PauseTiming();
506         if (shouldSkipWithError(turnVibratorOff(), state)) {
507             return;
508         }
509         cb.waitForComplete();
510         state.ResumeTiming();
511     }
512 });
513 
514 class SlowVibratorPrimitivesBench : public VibratorBench {
515 public:
DefaultConfig(Benchmark * b)516     static void DefaultConfig(Benchmark* b) {
517         VibratorBench::DefaultConfig(b);
518         SlowBenchConfig(b);
519     }
520 
DefaultArgs(Benchmark * b)521     static void DefaultArgs(Benchmark* b) {
522         vibrator::HalController controller;
523         auto primitivesResult = controller.getInfo().supportedPrimitives;
524         if (!primitivesResult.isOk()) {
525             return;
526         }
527 
528         std::vector<CompositePrimitive> supported = primitivesResult.value();
529         b->ArgNames({"Primitive"});
530 
531         if (supported.empty()) {
532             b->Args({static_cast<long>(-1)});
533             return;
534         }
535 
536         for (const auto& primitive : enum_range<CompositePrimitive>()) {
537             if (std::find(supported.begin(), supported.end(), primitive) == supported.end()) {
538                 continue;
539             }
540             if (primitive == CompositePrimitive::NOOP) {
541                 continue;
542             }
543             b->Args({static_cast<long>(primitive)});
544         }
545     }
546 
547 protected:
hasArgs(const State & state) const548     bool hasArgs(const State& state) const { return this->getOtherArg(state, 0) >= 0; }
549 
getPrimitive(const State & state) const550     auto getPrimitive(const State& state) const {
551         return static_cast<CompositePrimitive>(this->getOtherArg(state, 0));
552     }
553 };
554 
555 BENCHMARK_WRAPPER(SlowVibratorPrimitivesBench, performComposedEffect, {
556     if (shouldSkipWithMissingCapabilityMessage(vibrator::Capabilities::COMPOSE_EFFECTS, state)) {
557         return;
558     }
559     if (!hasArgs(state)) {
560         state.SkipWithMessage("missing args");
561         return;
562     }
563 
564     CompositeEffect effect;
565     effect.primitive = getPrimitive(state);
566     effect.scale = 1.0f;
567     effect.delayMs = static_cast<int32_t>(0);
568 
569     std::vector<CompositeEffect> effects = {effect};
570 
571     for (auto _ : state) {
572         // Setup
573         state.PauseTiming();
574         auto cb = mCallbacks.next();
__anon599d61ff1402(auto hal) 575         auto performFn = [&](auto hal) {
576             return hal->performComposedEffect(effects, cb.completeFn());
577         };
578         state.ResumeTiming();
579 
580         // Test
581         if (shouldSkipWithError<milliseconds>(performFn, "performComposedEffect", state)) {
582             return;
583         }
584 
585         // Cleanup
586         state.PauseTiming();
587         if (shouldSkipWithError(turnVibratorOff(), state)) {
588             return;
589         }
590         cb.waitForComplete();
591         state.ResumeTiming();
592     }
593 });
594 
595 BENCHMARK_MAIN();
596