Files
hardware_interfaces/vibrator/bench/benchmark.cpp
Adrian Roos 77659428e3 vibrator HAL bench: Fix broken benchmarks
Fixes an issue with the benchmarks, where they would skip unsupported
situations by early returning without reaching the measure loop and
without notifying the framework.

The framework would then retry the exact same scenario indefinitely.

To fix, makes sure every early return also calls SkipWithMessage.

Fixes: 302845046
Test: adb shell /tmp/foobar/VibratorHalIntegrationBenchmark --v=2
Change-Id: Ib52b8c4d94755a0961bcfb40d508482e5c49cb52
2024-02-09 19:49:32 +00:00

597 lines
18 KiB
C++

/*
* Copyright (C) 2019 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.
*/
#include "benchmark/benchmark.h"
#include <android/hardware/vibrator/1.3/IVibrator.h>
#include <android/hardware/vibrator/BnVibratorCallback.h>
#include <android/hardware/vibrator/IVibrator.h>
#include <binder/IServiceManager.h>
using ::android::enum_range;
using ::android::sp;
using ::android::hardware::hidl_enum_range;
using ::android::hardware::Return;
using ::android::hardware::details::hidl_enum_values;
using ::benchmark::Counter;
using ::benchmark::Fixture;
using ::benchmark::kMicrosecond;
using ::benchmark::State;
using ::benchmark::internal::Benchmark;
using ::std::chrono::duration;
using ::std::chrono::duration_cast;
using ::std::chrono::high_resolution_clock;
namespace Aidl = ::android::hardware::vibrator;
namespace V1_0 = ::android::hardware::vibrator::V1_0;
namespace V1_1 = ::android::hardware::vibrator::V1_1;
namespace V1_2 = ::android::hardware::vibrator::V1_2;
namespace V1_3 = ::android::hardware::vibrator::V1_3;
template <typename I>
class BaseBench : public Fixture {
public:
void TearDown(State& /*state*/) override {
if (!mVibrator) {
return;
}
mVibrator->off();
}
static void DefaultConfig(Benchmark* b) { b->Unit(kMicrosecond); }
static void DefaultArgs(Benchmark* /*b*/) { /* none */
}
protected:
auto getOtherArg(const State& state, std::size_t index) const { return state.range(index + 0); }
protected:
sp<I> mVibrator;
};
template <typename I>
class VibratorBench : public BaseBench<I> {
public:
void SetUp(State& /*state*/) override { this->mVibrator = I::getService(); }
};
enum class EmptyEnum : uint32_t;
template <>
inline constexpr std::array<EmptyEnum, 0> hidl_enum_values<EmptyEnum> = {};
template <typename T, typename U>
std::set<T> difference(const hidl_enum_range<T>& t, const hidl_enum_range<U>& u) {
class Compare {
public:
bool operator()(const T& a, const U& b) { return a < static_cast<T>(b); }
bool operator()(const U& a, const T& b) { return static_cast<T>(a) < b; }
};
std::set<T> ret;
std::set_difference(t.begin(), t.end(), u.begin(), u.end(),
std::insert_iterator<decltype(ret)>(ret, ret.begin()), Compare());
return ret;
}
template <typename I, typename E1, typename E2 = EmptyEnum>
class VibratorEffectsBench : public VibratorBench<I> {
public:
using Effect = E1;
using EffectStrength = V1_0::EffectStrength;
using Status = V1_0::Status;
public:
static void DefaultArgs(Benchmark* b) {
b->ArgNames({"Effect", "Strength"});
for (const auto& effect : difference(hidl_enum_range<E1>(), hidl_enum_range<E2>())) {
for (const auto& strength : hidl_enum_range<EffectStrength>()) {
b->Args({static_cast<long>(effect), static_cast<long>(strength)});
}
}
}
void performBench(State* state, Return<void> (I::*performApi)(Effect, EffectStrength,
typename I::perform_cb)) {
auto effect = getEffect(*state);
auto strength = getStrength(*state);
bool supported = true;
(*this->mVibrator.*performApi)(effect, strength, [&](Status status, uint32_t /*lengthMs*/) {
if (status == Status::UNSUPPORTED_OPERATION) {
supported = false;
}
});
if (!supported) {
state->SkipWithMessage("performApi returned UNSUPPORTED_OPERATION");
return;
}
for (auto _ : *state) {
state->ResumeTiming();
(*this->mVibrator.*performApi)(effect, strength,
[](Status /*status*/, uint32_t /*lengthMs*/) {});
state->PauseTiming();
this->mVibrator->off();
}
}
protected:
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));
}
};
#define BENCHMARK_WRAPPER(fixt, test, code) \
BENCHMARK_DEFINE_F(fixt, test) \
/* NOLINTNEXTLINE */ \
(State & state) { \
if (!mVibrator) { \
state.SkipWithMessage("HAL unavailable"); \
return; \
} \
\
code \
} \
BENCHMARK_REGISTER_F(fixt, test)->Apply(fixt::DefaultConfig)->Apply(fixt::DefaultArgs)
using VibratorBench_V1_0 = VibratorBench<V1_0::IVibrator>;
BENCHMARK_WRAPPER(VibratorBench_V1_0, on, {
uint32_t ms = UINT32_MAX;
for (auto _ : state) {
state.ResumeTiming();
mVibrator->on(ms);
state.PauseTiming();
mVibrator->off();
}
});
BENCHMARK_WRAPPER(VibratorBench_V1_0, off, {
uint32_t ms = UINT32_MAX;
for (auto _ : state) {
state.PauseTiming();
mVibrator->on(ms);
state.ResumeTiming();
mVibrator->off();
}
});
BENCHMARK_WRAPPER(VibratorBench_V1_0, supportsAmplitudeControl, {
for (auto _ : state) {
mVibrator->supportsAmplitudeControl();
}
});
BENCHMARK_WRAPPER(VibratorBench_V1_0, setAmplitude, {
uint8_t amplitude = UINT8_MAX;
if (!mVibrator->supportsAmplitudeControl()) {
state.SkipWithMessage("Amplitude control unavailable");
return;
}
mVibrator->on(UINT32_MAX);
for (auto _ : state) {
mVibrator->setAmplitude(amplitude);
}
mVibrator->off();
});
using VibratorEffectsBench_V1_0 = VibratorEffectsBench<V1_0::IVibrator, V1_0::Effect>;
BENCHMARK_WRAPPER(VibratorEffectsBench_V1_0, perform,
{ performBench(&state, &V1_0::IVibrator::perform); });
using VibratorEffectsBench_V1_1 =
VibratorEffectsBench<V1_1::IVibrator, V1_1::Effect_1_1, V1_0::Effect>;
BENCHMARK_WRAPPER(VibratorEffectsBench_V1_1, perform_1_1,
{ performBench(&state, &V1_1::IVibrator::perform_1_1); });
using VibratorEffectsBench_V1_2 =
VibratorEffectsBench<V1_2::IVibrator, V1_2::Effect, V1_1::Effect_1_1>;
BENCHMARK_WRAPPER(VibratorEffectsBench_V1_2, perform_1_2,
{ performBench(&state, &V1_2::IVibrator::perform_1_2); });
using VibratorBench_V1_3 = VibratorBench<V1_3::IVibrator>;
BENCHMARK_WRAPPER(VibratorBench_V1_3, supportsExternalControl, {
for (auto _ : state) {
mVibrator->supportsExternalControl();
}
});
BENCHMARK_WRAPPER(VibratorBench_V1_3, setExternalControl, {
bool enable = true;
if (!mVibrator->supportsExternalControl()) {
state.SkipWithMessage("external control unavailable");
return;
}
for (auto _ : state) {
state.ResumeTiming();
mVibrator->setExternalControl(enable);
state.PauseTiming();
mVibrator->setExternalControl(false);
}
});
BENCHMARK_WRAPPER(VibratorBench_V1_3, supportsExternalAmplitudeControl, {
if (!mVibrator->supportsExternalControl()) {
state.SkipWithMessage("external control unavailable");
return;
}
mVibrator->setExternalControl(true);
for (auto _ : state) {
mVibrator->supportsAmplitudeControl();
}
mVibrator->setExternalControl(false);
});
BENCHMARK_WRAPPER(VibratorBench_V1_3, setExternalAmplitude, {
uint8_t amplitude = UINT8_MAX;
if (!mVibrator->supportsExternalControl()) {
state.SkipWithMessage("external control unavailable");
return;
}
mVibrator->setExternalControl(true);
if (!mVibrator->supportsAmplitudeControl()) {
state.SkipWithMessage("amplitude control unavailable");
return;
}
for (auto _ : state) {
mVibrator->setAmplitude(amplitude);
}
mVibrator->setExternalControl(false);
});
using VibratorEffectsBench_V1_3 = VibratorEffectsBench<V1_3::IVibrator, V1_3::Effect, V1_2::Effect>;
BENCHMARK_WRAPPER(VibratorEffectsBench_V1_3, perform_1_3,
{ performBench(&state, &V1_3::IVibrator::perform_1_3); });
class VibratorBench_Aidl : public BaseBench<Aidl::IVibrator> {
public:
void SetUp(State& /*state*/) override {
this->mVibrator = android::waitForVintfService<Aidl::IVibrator>();
}
};
class HalCallback : public Aidl::BnVibratorCallback {
public:
HalCallback() = default;
~HalCallback() = default;
android::binder::Status onComplete() override { return android::binder::Status::ok(); }
};
BENCHMARK_WRAPPER(VibratorBench_Aidl, on, {
int32_t capabilities = 0;
mVibrator->getCapabilities(&capabilities);
int32_t ms = INT32_MAX;
auto cb = (capabilities & Aidl::IVibrator::CAP_ON_CALLBACK) ? new HalCallback() : nullptr;
for (auto _ : state) {
state.ResumeTiming();
mVibrator->on(ms, cb);
state.PauseTiming();
mVibrator->off();
}
});
BENCHMARK_WRAPPER(VibratorBench_Aidl, off, {
for (auto _ : state) {
state.PauseTiming();
mVibrator->on(INT32_MAX, nullptr);
state.ResumeTiming();
mVibrator->off();
}
});
BENCHMARK_WRAPPER(VibratorBench_Aidl, getCapabilities, {
int32_t capabilities = 0;
for (auto _ : state) {
mVibrator->getCapabilities(&capabilities);
}
});
BENCHMARK_WRAPPER(VibratorBench_Aidl, setAmplitude, {
int32_t capabilities = 0;
mVibrator->getCapabilities(&capabilities);
if ((capabilities & Aidl::IVibrator::CAP_AMPLITUDE_CONTROL) == 0) {
state.SkipWithMessage("amplitude control unavailable");
return;
}
float amplitude = 1.0f;
mVibrator->on(INT32_MAX, nullptr);
for (auto _ : state) {
mVibrator->setAmplitude(amplitude);
}
mVibrator->off();
});
BENCHMARK_WRAPPER(VibratorBench_Aidl, setExternalControl, {
int32_t capabilities = 0;
mVibrator->getCapabilities(&capabilities);
if ((capabilities & Aidl::IVibrator::CAP_EXTERNAL_CONTROL) == 0) {
state.SkipWithMessage("external control unavailable");
return;
}
for (auto _ : state) {
state.ResumeTiming();
mVibrator->setExternalControl(true);
state.PauseTiming();
mVibrator->setExternalControl(false);
}
});
BENCHMARK_WRAPPER(VibratorBench_Aidl, setExternalAmplitude, {
int32_t capabilities = 0;
mVibrator->getCapabilities(&capabilities);
if ((capabilities & Aidl::IVibrator::CAP_EXTERNAL_CONTROL) == 0 ||
(capabilities & Aidl::IVibrator::CAP_EXTERNAL_AMPLITUDE_CONTROL) == 0) {
state.SkipWithMessage("external amplitude control unavailable");
return;
}
float amplitude = 1.0f;
mVibrator->setExternalControl(true);
for (auto _ : state) {
mVibrator->setAmplitude(amplitude);
}
mVibrator->setExternalControl(false);
});
BENCHMARK_WRAPPER(VibratorBench_Aidl, getSupportedEffects, {
std::vector<Aidl::Effect> supportedEffects;
for (auto _ : state) {
mVibrator->getSupportedEffects(&supportedEffects);
}
});
BENCHMARK_WRAPPER(VibratorBench_Aidl, getSupportedAlwaysOnEffects, {
std::vector<Aidl::Effect> supportedEffects;
for (auto _ : state) {
mVibrator->getSupportedAlwaysOnEffects(&supportedEffects);
}
});
BENCHMARK_WRAPPER(VibratorBench_Aidl, getSupportedPrimitives, {
std::vector<Aidl::CompositePrimitive> supportedPrimitives;
for (auto _ : state) {
mVibrator->getSupportedPrimitives(&supportedPrimitives);
}
});
class VibratorEffectsBench_Aidl : public VibratorBench_Aidl {
public:
static void DefaultArgs(Benchmark* b) {
b->ArgNames({"Effect", "Strength"});
for (const auto& effect : enum_range<Aidl::Effect>()) {
for (const auto& strength : enum_range<Aidl::EffectStrength>()) {
b->Args({static_cast<long>(effect), static_cast<long>(strength)});
}
}
}
protected:
auto getEffect(const State& state) const {
return static_cast<Aidl::Effect>(this->getOtherArg(state, 0));
}
auto getStrength(const State& state) const {
return static_cast<Aidl::EffectStrength>(this->getOtherArg(state, 1));
}
};
BENCHMARK_WRAPPER(VibratorEffectsBench_Aidl, alwaysOnEnable, {
int32_t capabilities = 0;
mVibrator->getCapabilities(&capabilities);
if ((capabilities & Aidl::IVibrator::CAP_ALWAYS_ON_CONTROL) == 0) {
state.SkipWithMessage("always on control unavailable");
return;
}
int32_t id = 1;
auto effect = getEffect(state);
auto strength = getStrength(state);
std::vector<Aidl::Effect> supported;
mVibrator->getSupportedAlwaysOnEffects(&supported);
if (std::find(supported.begin(), supported.end(), effect) == supported.end()) {
state.SkipWithMessage("always on effects unavailable");
return;
}
for (auto _ : state) {
state.ResumeTiming();
mVibrator->alwaysOnEnable(id, effect, strength);
state.PauseTiming();
mVibrator->alwaysOnDisable(id);
}
});
BENCHMARK_WRAPPER(VibratorEffectsBench_Aidl, alwaysOnDisable, {
int32_t capabilities = 0;
mVibrator->getCapabilities(&capabilities);
if ((capabilities & Aidl::IVibrator::CAP_ALWAYS_ON_CONTROL) == 0) {
state.SkipWithMessage("always on control unavailable");
return;
}
int32_t id = 1;
auto effect = getEffect(state);
auto strength = getStrength(state);
std::vector<Aidl::Effect> supported;
mVibrator->getSupportedAlwaysOnEffects(&supported);
if (std::find(supported.begin(), supported.end(), effect) == supported.end()) {
state.SkipWithMessage("always on effects unavailable");
return;
}
for (auto _ : state) {
state.PauseTiming();
mVibrator->alwaysOnEnable(id, effect, strength);
state.ResumeTiming();
mVibrator->alwaysOnDisable(id);
}
});
BENCHMARK_WRAPPER(VibratorEffectsBench_Aidl, perform, {
int32_t capabilities = 0;
mVibrator->getCapabilities(&capabilities);
auto effect = getEffect(state);
auto strength = getStrength(state);
auto cb = (capabilities & Aidl::IVibrator::CAP_PERFORM_CALLBACK) ? new HalCallback() : nullptr;
int32_t lengthMs = 0;
std::vector<Aidl::Effect> supported;
mVibrator->getSupportedEffects(&supported);
if (std::find(supported.begin(), supported.end(), effect) == supported.end()) {
state.SkipWithMessage("effects unavailable");
return;
}
for (auto _ : state) {
state.ResumeTiming();
mVibrator->perform(effect, strength, cb, &lengthMs);
state.PauseTiming();
mVibrator->off();
}
});
class VibratorPrimitivesBench_Aidl : public VibratorBench_Aidl {
public:
static void DefaultArgs(Benchmark* b) {
b->ArgNames({"Primitive"});
for (const auto& primitive : enum_range<Aidl::CompositePrimitive>()) {
b->Args({static_cast<long>(primitive)});
}
}
protected:
auto getPrimitive(const State& state) const {
return static_cast<Aidl::CompositePrimitive>(this->getOtherArg(state, 0));
}
};
BENCHMARK_WRAPPER(VibratorBench_Aidl, getCompositionDelayMax, {
int32_t ms = 0;
for (auto _ : state) {
mVibrator->getCompositionDelayMax(&ms);
}
});
BENCHMARK_WRAPPER(VibratorBench_Aidl, getCompositionSizeMax, {
int32_t size = 0;
for (auto _ : state) {
mVibrator->getCompositionSizeMax(&size);
}
});
BENCHMARK_WRAPPER(VibratorPrimitivesBench_Aidl, getPrimitiveDuration, {
int32_t capabilities = 0;
mVibrator->getCapabilities(&capabilities);
if ((capabilities & Aidl::IVibrator::CAP_COMPOSE_EFFECTS) == 0) {
state.SkipWithMessage("compose effects unavailable");
return;
}
auto primitive = getPrimitive(state);
int32_t ms = 0;
std::vector<Aidl::CompositePrimitive> supported;
mVibrator->getSupportedPrimitives(&supported);
if (std::find(supported.begin(), supported.end(), primitive) == supported.end()) {
state.SkipWithMessage("supported primitives unavailable");
return;
}
for (auto _ : state) {
mVibrator->getPrimitiveDuration(primitive, &ms);
}
});
BENCHMARK_WRAPPER(VibratorPrimitivesBench_Aidl, compose, {
int32_t capabilities = 0;
mVibrator->getCapabilities(&capabilities);
if ((capabilities & Aidl::IVibrator::CAP_COMPOSE_EFFECTS) == 0) {
state.SkipWithMessage("compose effects unavailable");
return;
}
Aidl::CompositeEffect effect;
effect.primitive = getPrimitive(state);
effect.scale = 1.0f;
effect.delayMs = 0;
std::vector<Aidl::CompositePrimitive> supported;
mVibrator->getSupportedPrimitives(&supported);
if (std::find(supported.begin(), supported.end(), effect.primitive) == supported.end()) {
state.SkipWithMessage("supported primitives unavailable");
return;
}
auto cb = new HalCallback();
std::vector<Aidl::CompositeEffect> effects;
effects.push_back(effect);
for (auto _ : state) {
state.ResumeTiming();
mVibrator->compose(effects, cb);
state.PauseTiming();
mVibrator->off();
}
});
BENCHMARK_MAIN();