01540edacf
* from android-13.0.0_r3 * Remove audio hints while at it Signed-off-by: clarencelol <clarencekuiek@icloud.com> Signed-off-by: pix106 <sbordenave@gmail.com>
233 lines
8.2 KiB
C++
233 lines
8.2 KiB
C++
/*
|
|
* Copyright (C) 2021 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 "powerhal-adaptivecpu"
|
|
#define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
|
|
|
|
#include "AdaptiveCpu.h"
|
|
|
|
#include <android-base/file.h>
|
|
#include <android-base/logging.h>
|
|
#include <android-base/properties.h>
|
|
#include <perfmgr/HintManager.h>
|
|
#include <sys/resource.h>
|
|
#include <utils/Trace.h>
|
|
|
|
#include <chrono>
|
|
#include <deque>
|
|
#include <numeric>
|
|
|
|
#include "CpuLoadReaderSysDevices.h"
|
|
#include "Model.h"
|
|
|
|
namespace aidl {
|
|
namespace google {
|
|
namespace hardware {
|
|
namespace power {
|
|
namespace impl {
|
|
namespace pixel {
|
|
|
|
using ::android::perfmgr::HintManager;
|
|
|
|
// We pass the previous N ModelInputs to the model, including the most recent ModelInput.
|
|
constexpr uint32_t kNumHistoricalModelInputs = 3;
|
|
|
|
// TODO(b/207662659): Add config for changing between different reader types.
|
|
AdaptiveCpu::AdaptiveCpu() {}
|
|
|
|
bool AdaptiveCpu::IsEnabled() const {
|
|
return mIsEnabled;
|
|
}
|
|
|
|
void AdaptiveCpu::HintReceived(bool enable) {
|
|
ATRACE_CALL();
|
|
LOG(INFO) << "AdaptiveCpu received hint: enable=" << enable;
|
|
if (enable) {
|
|
StartThread();
|
|
} else {
|
|
SuspendThread();
|
|
}
|
|
}
|
|
|
|
void AdaptiveCpu::StartThread() {
|
|
ATRACE_CALL();
|
|
std::lock_guard lock(mThreadCreationMutex);
|
|
LOG(INFO) << "Starting AdaptiveCpu thread";
|
|
mIsEnabled = true;
|
|
mShouldReloadConfig = true;
|
|
mLastEnabledHintTime = mTimeSource.GetTime();
|
|
if (!mLoopThread.joinable()) {
|
|
mLoopThread = std::thread([&]() {
|
|
pthread_setname_np(pthread_self(), "AdaptiveCpu");
|
|
// Parent threads may have higher priorities, so we reset to the default.
|
|
int ret = setpriority(PRIO_PROCESS, 0, 0);
|
|
if (ret != 0) {
|
|
PLOG(ERROR) << "setpriority on AdaptiveCpu thread failed: " << ret;
|
|
}
|
|
LOG(INFO) << "Started AdaptiveCpu thread successfully";
|
|
RunMainLoop();
|
|
LOG(ERROR) << "AdaptiveCpu thread ended, this should never happen!";
|
|
});
|
|
}
|
|
}
|
|
|
|
void AdaptiveCpu::SuspendThread() {
|
|
ATRACE_CALL();
|
|
LOG(INFO) << "Stopping AdaptiveCpu thread";
|
|
// This stops the thread from receiving work durations in ReportWorkDurations, which means the
|
|
// thread blocks indefinitely.
|
|
mIsEnabled = false;
|
|
}
|
|
|
|
void AdaptiveCpu::ReportWorkDurations(const std::vector<WorkDuration> &workDurations,
|
|
std::chrono::nanoseconds targetDuration) {
|
|
ATRACE_CALL();
|
|
if (!mIsEnabled) {
|
|
return;
|
|
}
|
|
if (!mWorkDurationProcessor.ReportWorkDurations(workDurations, targetDuration)) {
|
|
mIsEnabled = false;
|
|
return;
|
|
}
|
|
mWorkDurationsAvailableCondition.notify_one();
|
|
}
|
|
|
|
void AdaptiveCpu::WaitForEnabledAndWorkDurations() {
|
|
ATRACE_CALL();
|
|
std::unique_lock<std::mutex> lock(mWaitMutex);
|
|
// TODO(b/188770301) Once the gating logic is implemented, don't block indefinitely.
|
|
mWorkDurationsAvailableCondition.wait(
|
|
lock, [&] { return mIsEnabled && mWorkDurationProcessor.HasWorkDurations(); });
|
|
}
|
|
|
|
void AdaptiveCpu::RunMainLoop() {
|
|
ATRACE_CALL();
|
|
|
|
std::deque<ModelInput> historicalModelInputs;
|
|
ThrottleDecision previousThrottleDecision = ThrottleDecision::NO_THROTTLE;
|
|
while (true) {
|
|
ATRACE_NAME("loop");
|
|
WaitForEnabledAndWorkDurations();
|
|
|
|
if (mLastEnabledHintTime + mConfig.enabledHintTimeout < mTimeSource.GetTime()) {
|
|
LOG(INFO) << "Adaptive CPU hint timed out, last enabled time="
|
|
<< mLastEnabledHintTime.count() << "ns";
|
|
mIsEnabled = false;
|
|
continue;
|
|
}
|
|
|
|
if (mShouldReloadConfig) {
|
|
if (!AdaptiveCpuConfig::ReadFromSystemProperties(&mConfig)) {
|
|
mIsEnabled = false;
|
|
continue;
|
|
}
|
|
LOG(INFO) << "Read config: " << mConfig;
|
|
mShouldReloadConfig = false;
|
|
}
|
|
|
|
ATRACE_BEGIN("compute");
|
|
mAdaptiveCpuStats.RegisterStartRun();
|
|
|
|
if (!mIsInitialized) {
|
|
if (!mKernelCpuFeatureReader.Init()) {
|
|
mIsEnabled = false;
|
|
continue;
|
|
}
|
|
mDevice = ReadDevice();
|
|
mIsInitialized = true;
|
|
}
|
|
|
|
ModelInput modelInput;
|
|
modelInput.previousThrottleDecision = previousThrottleDecision;
|
|
|
|
modelInput.workDurationFeatures = mWorkDurationProcessor.GetFeatures();
|
|
LOG(VERBOSE) << "Got work durations: count=" << modelInput.workDurationFeatures.numDurations
|
|
<< ", average=" << modelInput.workDurationFeatures.averageDuration.count()
|
|
<< "ns";
|
|
if (modelInput.workDurationFeatures.numDurations == 0) {
|
|
continue;
|
|
}
|
|
|
|
if (!mKernelCpuFeatureReader.GetRecentCpuFeatures(&modelInput.cpuPolicyAverageFrequencyHz,
|
|
&modelInput.cpuCoreIdleTimesPercentage)) {
|
|
mIsEnabled = false;
|
|
continue;
|
|
}
|
|
|
|
modelInput.LogToAtrace();
|
|
historicalModelInputs.push_back(modelInput);
|
|
if (historicalModelInputs.size() > kNumHistoricalModelInputs) {
|
|
historicalModelInputs.pop_front();
|
|
}
|
|
|
|
const ThrottleDecision throttleDecision = mModel.Run(historicalModelInputs, mConfig);
|
|
LOG(VERBOSE) << "Model decision: " << static_cast<uint32_t>(throttleDecision);
|
|
ATRACE_INT("AdaptiveCpu_throttleDecision", static_cast<uint32_t>(throttleDecision));
|
|
|
|
{
|
|
ATRACE_NAME("sendHints");
|
|
const auto now = mTimeSource.GetTime();
|
|
// Resend the throttle hints, even if they've not changed, if the previous send is close
|
|
// to timing out. We define "close to" as half the hint timeout, as we can't guarantee
|
|
// we will run again before the actual timeout.
|
|
const bool throttleHintMayTimeout =
|
|
now - mLastThrottleHintTime > mConfig.hintTimeout / 2;
|
|
if (throttleDecision != previousThrottleDecision || throttleHintMayTimeout) {
|
|
ATRACE_NAME("sendNewHints");
|
|
mLastThrottleHintTime = now;
|
|
for (const auto &hintName : THROTTLE_DECISION_TO_HINT_NAMES.at(throttleDecision)) {
|
|
HintManager::GetInstance()->DoHint(hintName, mConfig.hintTimeout);
|
|
}
|
|
}
|
|
if (throttleDecision != previousThrottleDecision) {
|
|
ATRACE_NAME("endOldHints");
|
|
for (const auto &hintName :
|
|
THROTTLE_DECISION_TO_HINT_NAMES.at(previousThrottleDecision)) {
|
|
HintManager::GetInstance()->EndHint(hintName);
|
|
}
|
|
previousThrottleDecision = throttleDecision;
|
|
}
|
|
}
|
|
|
|
mAdaptiveCpuStats.RegisterSuccessfulRun(previousThrottleDecision, throttleDecision,
|
|
modelInput.workDurationFeatures, mConfig);
|
|
ATRACE_END(); // compute
|
|
{
|
|
ATRACE_NAME("sleep");
|
|
std::this_thread::sleep_for(mConfig.iterationSleepDuration);
|
|
}
|
|
}
|
|
}
|
|
|
|
void AdaptiveCpu::DumpToFd(int fd) const {
|
|
std::stringstream result;
|
|
result << "========== Begin Adaptive CPU stats ==========\n";
|
|
result << "Enabled: " << mIsEnabled << "\n";
|
|
result << "Config: " << mConfig << "\n";
|
|
mKernelCpuFeatureReader.DumpToStream(result);
|
|
mAdaptiveCpuStats.DumpToStream(result);
|
|
result << "========== End Adaptive CPU stats ==========\n";
|
|
if (!::android::base::WriteStringToFd(result.str(), fd)) {
|
|
PLOG(ERROR) << "Failed to dump state to fd";
|
|
}
|
|
}
|
|
|
|
} // namespace pixel
|
|
} // namespace impl
|
|
} // namespace power
|
|
} // namespace hardware
|
|
} // namespace google
|
|
} // namespace aidl
|