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