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