/* * 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 "WorkDurationProcessor.h" #include #include using std::chrono_literals::operator""ms; using std::chrono_literals::operator""ns; // The standard target duration, based on 60 FPS. Durations submitted with different targets are // normalized against this target. For example, a duration that was at 80% of its target will be // scaled to 0.8 * kNormalTargetDuration. constexpr std::chrono::nanoseconds kNormalTargetDuration = 16666666ns; // All durations shorter than this are ignored. constexpr std::chrono::nanoseconds kMinDuration = 0ns; // All durations longer than this are ignored. constexpr std::chrono::nanoseconds kMaxDuration = 600 * kNormalTargetDuration; // If we haven't processed a lot of batches, stop accepting new ones. In cases where the processing // thread has crashed, but the reporting thread is still reporting, this prevents consuming large // amounts of memory. // TODO(b/213160386): Move to AdaptiveCpuConfig. constexpr size_t kMaxUnprocessedBatches = 1000; namespace aidl { namespace google { namespace hardware { namespace power { namespace impl { namespace pixel { bool WorkDurationProcessor::ReportWorkDurations(const std::vector &workDurations, std::chrono::nanoseconds targetDuration) { ATRACE_CALL(); LOG(VERBOSE) << "Received " << workDurations.size() << " work durations with target " << targetDuration.count() << "ns"; std::unique_lock lock(mMutex); if (mWorkDurationBatches.size() >= kMaxUnprocessedBatches) { LOG(ERROR) << "Adaptive CPU isn't processing work durations fast enough"; mWorkDurationBatches.clear(); return false; } mWorkDurationBatches.emplace_back(workDurations, targetDuration); return true; } WorkDurationFeatures WorkDurationProcessor::GetFeatures() { ATRACE_CALL(); std::vector workDurationBatches; { ATRACE_NAME("lock"); std::unique_lock lock(mMutex); mWorkDurationBatches.swap(workDurationBatches); } std::chrono::nanoseconds durationsSum = 0ns; std::chrono::nanoseconds maxDuration = 0ns; uint32_t numMissedDeadlines = 0; uint32_t numDurations = 0; for (const WorkDurationBatch &batch : workDurationBatches) { for (const WorkDuration workDuration : batch.workDurations) { std::chrono::nanoseconds duration(workDuration.durationNanos); if (duration < kMinDuration || duration > kMaxDuration) { continue; } // Normalise the duration and add it to the total. // kMaxDuration * kStandardTarget.count() fits comfortably within int64_t. std::chrono::nanoseconds durationNormalized = (duration * kNormalTargetDuration.count()) / batch.targetDuration.count(); durationsSum += durationNormalized; maxDuration = std::max(maxDuration, durationNormalized); if (duration > batch.targetDuration) { ++numMissedDeadlines; } ++numDurations; } } std::chrono::nanoseconds averageDuration = durationsSum / numDurations; return { .averageDuration = averageDuration, .maxDuration = maxDuration, .numMissedDeadlines = numMissedDeadlines, .numDurations = numDurations, }; } bool WorkDurationProcessor::HasWorkDurations() { std::unique_lock lock(mMutex); return !mWorkDurationBatches.empty(); } } // namespace pixel } // namespace impl } // namespace power } // namespace hardware } // namespace google } // namespace aidl