| /* |
| * Copyright (C) 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. |
| */ |
| |
| #define LOG_TAG "VibratorHalControllerBenchmarks" |
| |
| #include <benchmark/benchmark.h> |
| #include <vibratorservice/VibratorHalController.h> |
| |
| using ::android::enum_range; |
| using ::android::hardware::vibrator::CompositeEffect; |
| using ::android::hardware::vibrator::CompositePrimitive; |
| using ::android::hardware::vibrator::Effect; |
| using ::android::hardware::vibrator::EffectStrength; |
| using ::benchmark::Counter; |
| using ::benchmark::Fixture; |
| using ::benchmark::kMicrosecond; |
| using ::benchmark::State; |
| using ::benchmark::internal::Benchmark; |
| |
| using namespace android; |
| using namespace std::chrono_literals; |
| |
| class VibratorBench : public Fixture { |
| public: |
| void SetUp(State& /*state*/) override { mController.init(); } |
| |
| void TearDown(State& state) override { turnVibratorOff(state); } |
| |
| static void DefaultConfig(Benchmark* b) { b->Unit(kMicrosecond); } |
| |
| static void DefaultArgs(Benchmark* /*b*/) { |
| // none |
| } |
| |
| protected: |
| vibrator::HalController mController; |
| |
| auto getOtherArg(const State& state, std::size_t index) const { return state.range(index + 0); } |
| |
| bool hasCapabilities(vibrator::Capabilities&& query, State& state) { |
| auto result = mController.getInfo().capabilities; |
| if (result.isFailed()) { |
| state.SkipWithError(result.errorMessage()); |
| return false; |
| } |
| if (!result.isOk()) { |
| return false; |
| } |
| return (result.value() & query) == query; |
| } |
| |
| void turnVibratorOff(State& state) { |
| checkHalResult(halCall<void>(mController, [](auto hal) { return hal->off(); }), state); |
| } |
| |
| template <class R> |
| bool checkHalResult(const vibrator::HalResult<R>& result, State& state) { |
| if (result.isFailed()) { |
| state.SkipWithError(result.errorMessage()); |
| return false; |
| } |
| return true; |
| } |
| |
| template <class R> |
| vibrator::HalResult<R> halCall(vibrator::HalController& controller, |
| const vibrator::HalFunction<vibrator::HalResult<R>>& halFn) { |
| return controller.doWithRetry<R>(halFn, "benchmark"); |
| } |
| }; |
| |
| #define BENCHMARK_WRAPPER(fixt, test, code) \ |
| BENCHMARK_DEFINE_F(fixt, test) \ |
| /* NOLINTNEXTLINE */ \ |
| (State& state){code} BENCHMARK_REGISTER_F(fixt, test) \ |
| ->Apply(fixt::DefaultConfig) \ |
| ->Apply(fixt::DefaultArgs) |
| |
| BENCHMARK_WRAPPER(VibratorBench, init, { |
| for (auto _ : state) { |
| state.PauseTiming(); |
| vibrator::HalController controller; |
| state.ResumeTiming(); |
| controller.init(); |
| } |
| }); |
| |
| BENCHMARK_WRAPPER(VibratorBench, initCached, { |
| for (auto _ : state) { |
| mController.init(); |
| } |
| }); |
| |
| BENCHMARK_WRAPPER(VibratorBench, ping, { |
| for (auto _ : state) { |
| state.ResumeTiming(); |
| auto ret = halCall<void>(mController, [](auto hal) { return hal->ping(); }); |
| state.PauseTiming(); |
| checkHalResult(ret, state); |
| } |
| }); |
| |
| BENCHMARK_WRAPPER(VibratorBench, tryReconnect, { |
| for (auto _ : state) { |
| mController.tryReconnect(); |
| } |
| }); |
| |
| BENCHMARK_WRAPPER(VibratorBench, on, { |
| auto duration = 60s; |
| auto callback = []() {}; |
| |
| for (auto _ : state) { |
| state.ResumeTiming(); |
| auto ret = |
| halCall<void>(mController, [&](auto hal) { return hal->on(duration, callback); }); |
| state.PauseTiming(); |
| if (checkHalResult(ret, state)) { |
| turnVibratorOff(state); |
| } |
| } |
| }); |
| |
| BENCHMARK_WRAPPER(VibratorBench, off, { |
| auto duration = 60s; |
| auto callback = []() {}; |
| |
| for (auto _ : state) { |
| state.PauseTiming(); |
| auto ret = |
| halCall<void>(mController, [&](auto hal) { return hal->on(duration, callback); }); |
| if (!checkHalResult(ret, state)) { |
| continue; |
| } |
| state.ResumeTiming(); |
| turnVibratorOff(state); |
| } |
| }); |
| |
| BENCHMARK_WRAPPER(VibratorBench, setAmplitude, { |
| if (!hasCapabilities(vibrator::Capabilities::AMPLITUDE_CONTROL, state)) { |
| return; |
| } |
| |
| auto duration = 60s; |
| auto callback = []() {}; |
| auto amplitude = 1.0f; |
| |
| for (auto _ : state) { |
| state.PauseTiming(); |
| vibrator::HalController controller; |
| controller.init(); |
| auto result = |
| halCall<void>(controller, [&](auto hal) { return hal->on(duration, callback); }); |
| if (!checkHalResult(result, state)) { |
| continue; |
| } |
| state.ResumeTiming(); |
| auto ret = |
| halCall<void>(controller, [&](auto hal) { return hal->setAmplitude(amplitude); }); |
| state.PauseTiming(); |
| if (checkHalResult(ret, state)) { |
| turnVibratorOff(state); |
| } |
| } |
| }); |
| |
| BENCHMARK_WRAPPER(VibratorBench, setAmplitudeCached, { |
| if (!hasCapabilities(vibrator::Capabilities::AMPLITUDE_CONTROL, state)) { |
| return; |
| } |
| |
| auto duration = 60s; |
| auto callback = []() {}; |
| auto amplitude = 1.0f; |
| |
| auto onResult = |
| halCall<void>(mController, [&](auto hal) { return hal->on(duration, callback); }); |
| checkHalResult(onResult, state); |
| |
| for (auto _ : state) { |
| auto ret = |
| halCall<void>(mController, [&](auto hal) { return hal->setAmplitude(amplitude); }); |
| checkHalResult(ret, state); |
| } |
| }); |
| |
| BENCHMARK_WRAPPER(VibratorBench, setExternalControl, { |
| if (!hasCapabilities(vibrator::Capabilities::EXTERNAL_CONTROL, state)) { |
| return; |
| } |
| |
| for (auto _ : state) { |
| state.PauseTiming(); |
| vibrator::HalController controller; |
| controller.init(); |
| state.ResumeTiming(); |
| auto ret = |
| halCall<void>(controller, [](auto hal) { return hal->setExternalControl(true); }); |
| state.PauseTiming(); |
| if (checkHalResult(ret, state)) { |
| auto result = halCall<void>(controller, |
| [](auto hal) { return hal->setExternalControl(false); }); |
| checkHalResult(result, state); |
| } |
| } |
| }); |
| |
| BENCHMARK_WRAPPER(VibratorBench, setExternalControlCached, { |
| if (!hasCapabilities(vibrator::Capabilities::EXTERNAL_CONTROL, state)) { |
| return; |
| } |
| |
| for (auto _ : state) { |
| state.ResumeTiming(); |
| auto result = |
| halCall<void>(mController, [](auto hal) { return hal->setExternalControl(true); }); |
| state.PauseTiming(); |
| if (checkHalResult(result, state)) { |
| auto ret = halCall<void>(mController, |
| [](auto hal) { return hal->setExternalControl(false); }); |
| checkHalResult(ret, state); |
| } |
| } |
| }); |
| |
| BENCHMARK_WRAPPER(VibratorBench, setExternalAmplitudeCached, { |
| if (!hasCapabilities(vibrator::Capabilities::EXTERNAL_AMPLITUDE_CONTROL, state)) { |
| return; |
| } |
| |
| auto amplitude = 1.0f; |
| |
| auto onResult = |
| halCall<void>(mController, [](auto hal) { return hal->setExternalControl(true); }); |
| checkHalResult(onResult, state); |
| |
| for (auto _ : state) { |
| auto ret = |
| halCall<void>(mController, [&](auto hal) { return hal->setAmplitude(amplitude); }); |
| checkHalResult(ret, state); |
| } |
| |
| auto offResult = |
| halCall<void>(mController, [](auto hal) { return hal->setExternalControl(false); }); |
| checkHalResult(offResult, state); |
| }); |
| |
| BENCHMARK_WRAPPER(VibratorBench, getInfo, { |
| for (auto _ : state) { |
| state.PauseTiming(); |
| vibrator::HalController controller; |
| controller.init(); |
| state.ResumeTiming(); |
| auto result = controller.getInfo(); |
| checkHalResult(result.capabilities, state); |
| checkHalResult(result.supportedEffects, state); |
| checkHalResult(result.supportedPrimitives, state); |
| checkHalResult(result.primitiveDurations, state); |
| checkHalResult(result.resonantFrequency, state); |
| checkHalResult(result.qFactor, state); |
| } |
| }); |
| |
| BENCHMARK_WRAPPER(VibratorBench, getInfoCached, { |
| // First call to cache values. |
| mController.getInfo(); |
| |
| for (auto _ : state) { |
| auto result = mController.getInfo(); |
| checkHalResult(result.capabilities, state); |
| checkHalResult(result.supportedEffects, state); |
| checkHalResult(result.supportedPrimitives, state); |
| checkHalResult(result.primitiveDurations, state); |
| checkHalResult(result.resonantFrequency, state); |
| checkHalResult(result.qFactor, state); |
| } |
| }); |
| |
| class VibratorEffectsBench : public VibratorBench { |
| public: |
| static void DefaultArgs(Benchmark* b) { |
| vibrator::HalController controller; |
| auto effectsResult = controller.getInfo().supportedEffects; |
| if (!effectsResult.isOk()) { |
| return; |
| } |
| |
| std::vector<Effect> supported = effectsResult.value(); |
| b->ArgNames({"Effect", "Strength"}); |
| |
| if (supported.empty()) { |
| b->Args({static_cast<long>(-1), static_cast<long>(-1)}); |
| return; |
| } |
| |
| for (const auto& effect : enum_range<Effect>()) { |
| if (std::find(supported.begin(), supported.end(), effect) == supported.end()) { |
| continue; |
| } |
| for (const auto& strength : enum_range<EffectStrength>()) { |
| b->Args({static_cast<long>(effect), static_cast<long>(strength)}); |
| } |
| } |
| } |
| |
| protected: |
| bool hasArgs(const State& state) const { return this->getOtherArg(state, 0) >= 0; } |
| |
| auto getEffect(const State& state) const { |
| return static_cast<Effect>(this->getOtherArg(state, 0)); |
| } |
| |
| auto getStrength(const State& state) const { |
| return static_cast<EffectStrength>(this->getOtherArg(state, 1)); |
| } |
| }; |
| |
| BENCHMARK_WRAPPER(VibratorEffectsBench, alwaysOnEnable, { |
| if (!hasCapabilities(vibrator::Capabilities::ALWAYS_ON_CONTROL, state)) { |
| return; |
| } |
| if (!hasArgs(state)) { |
| return; |
| } |
| |
| int32_t id = 1; |
| auto effect = getEffect(state); |
| auto strength = getStrength(state); |
| |
| for (auto _ : state) { |
| state.ResumeTiming(); |
| auto ret = halCall<void>(mController, [&](auto hal) { |
| return hal->alwaysOnEnable(id, effect, strength); |
| }); |
| state.PauseTiming(); |
| if (checkHalResult(ret, state)) { |
| auto disableResult = |
| halCall<void>(mController, [&](auto hal) { return hal->alwaysOnDisable(id); }); |
| checkHalResult(disableResult, state); |
| } |
| } |
| }); |
| |
| BENCHMARK_WRAPPER(VibratorEffectsBench, alwaysOnDisable, { |
| if (!hasCapabilities(vibrator::Capabilities::ALWAYS_ON_CONTROL, state)) { |
| return; |
| } |
| if (!hasArgs(state)) { |
| return; |
| } |
| |
| int32_t id = 1; |
| auto effect = getEffect(state); |
| auto strength = getStrength(state); |
| |
| for (auto _ : state) { |
| state.PauseTiming(); |
| auto enableResult = halCall<void>(mController, [&](auto hal) { |
| return hal->alwaysOnEnable(id, effect, strength); |
| }); |
| if (!checkHalResult(enableResult, state)) { |
| continue; |
| } |
| state.ResumeTiming(); |
| auto disableResult = |
| halCall<void>(mController, [&](auto hal) { return hal->alwaysOnDisable(id); }); |
| checkHalResult(disableResult, state); |
| } |
| }); |
| |
| BENCHMARK_WRAPPER(VibratorEffectsBench, performEffect, { |
| if (!hasArgs(state)) { |
| return; |
| } |
| |
| auto effect = getEffect(state); |
| auto strength = getStrength(state); |
| auto callback = []() {}; |
| |
| for (auto _ : state) { |
| state.ResumeTiming(); |
| auto ret = halCall<std::chrono::milliseconds>(mController, [&](auto hal) { |
| return hal->performEffect(effect, strength, callback); |
| }); |
| state.PauseTiming(); |
| if (checkHalResult(ret, state)) { |
| turnVibratorOff(state); |
| } |
| } |
| }); |
| |
| class VibratorPrimitivesBench : public VibratorBench { |
| public: |
| static void DefaultArgs(Benchmark* b) { |
| vibrator::HalController controller; |
| auto primitivesResult = controller.getInfo().supportedPrimitives; |
| if (!primitivesResult.isOk()) { |
| return; |
| } |
| |
| std::vector<CompositePrimitive> supported = primitivesResult.value(); |
| b->ArgNames({"Primitive"}); |
| |
| if (supported.empty()) { |
| b->Args({static_cast<long>(-1)}); |
| return; |
| } |
| |
| for (const auto& primitive : enum_range<CompositePrimitive>()) { |
| if (std::find(supported.begin(), supported.end(), primitive) == supported.end()) { |
| continue; |
| } |
| if (primitive == CompositePrimitive::NOOP) { |
| continue; |
| } |
| b->Args({static_cast<long>(primitive)}); |
| } |
| } |
| |
| protected: |
| bool hasArgs(const State& state) const { return this->getOtherArg(state, 0) >= 0; } |
| |
| auto getPrimitive(const State& state) const { |
| return static_cast<CompositePrimitive>(this->getOtherArg(state, 0)); |
| } |
| }; |
| |
| BENCHMARK_WRAPPER(VibratorPrimitivesBench, performComposedEffect, { |
| if (!hasCapabilities(vibrator::Capabilities::COMPOSE_EFFECTS, state)) { |
| return; |
| } |
| if (!hasArgs(state)) { |
| return; |
| } |
| |
| CompositeEffect effect; |
| effect.primitive = getPrimitive(state); |
| effect.scale = 1.0f; |
| effect.delayMs = static_cast<int32_t>(0); |
| |
| std::vector<CompositeEffect> effects; |
| effects.push_back(effect); |
| auto callback = []() {}; |
| |
| for (auto _ : state) { |
| state.ResumeTiming(); |
| auto ret = halCall<std::chrono::milliseconds>(mController, [&](auto hal) { |
| return hal->performComposedEffect(effects, callback); |
| }); |
| state.PauseTiming(); |
| if (checkHalResult(ret, state)) { |
| turnVibratorOff(state); |
| } |
| } |
| }); |
| |
| BENCHMARK_MAIN(); |