Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1eb078cf authored by Andy Hung's avatar Andy Hung
Browse files

psh_utils: Internally cache power stats

To avoid frequent (expensive) collection of power stats
allow the use of cached values with a certain time
tolerance.

Flag: com.android.media.audioserver.power_stats
Test: atest powerstats_collector_tests
Test: atest audio_powerstatscollector_benchmark
Bug: 350114693
Change-Id: I952950e5373e3c4b8ebc765bf6fb2cf8def0962e
parent d4e4586f
Loading
Loading
Loading
Loading
+32 −1
Original line number Diff line number Diff line
@@ -32,12 +32,43 @@ PowerStatsCollector& PowerStatsCollector::getCollector() {
    return psc;
}

std::shared_ptr<PowerStats> PowerStatsCollector::getStats() const {
std::shared_ptr<const PowerStats> PowerStatsCollector::getStats(int64_t toleranceNs) {
    // Check if there is a cached PowerStats result available.
    // As toleranceNs may be different between callers, it may be that some callers
    // are blocked on mMutexExclusiveFill for a new stats result, while other callers
    // may find the current cached result acceptable (within toleranceNs).
    if (toleranceNs > 0) {
        auto result = checkLastStats(toleranceNs);
        if (result) return result;
    }

    // Take the mMutexExclusiveFill to ensure only one thread is filling.
    std::lock_guard lg1(mMutexExclusiveFill);
    // As obtaining a new PowerStats snapshot might take some time,
    // check again to see if another waiting thread filled the cached result for us.
    if (toleranceNs > 0) {
        auto result = checkLastStats(toleranceNs);
        if (result) return result;
    }
    auto result = std::make_shared<PowerStats>();
    (void)fill(result.get());
    std::lock_guard lg2(mMutex);
    mLastFetchNs = systemTime(SYSTEM_TIME_BOOTTIME);
    mLastFetchStats = result;
    return result;
}

std::shared_ptr<const PowerStats> PowerStatsCollector::checkLastStats(int64_t toleranceNs) const {
    if (toleranceNs > 0) {
        // see if we can return an old result.
        std::lock_guard lg(mMutex);
        if (mLastFetchStats && systemTime(SYSTEM_TIME_BOOTTIME) - mLastFetchNs < toleranceNs) {
            return mLastFetchStats;
        }
    }
    return {};
}

void PowerStatsCollector::addProvider(std::unique_ptr<PowerStatsProvider>&& powerStatsProvider) {
    mPowerStatsProviders.emplace_back(std::move(powerStatsProvider));
}
+21 −0
Original line number Diff line number Diff line
@@ -28,3 +28,24 @@ cc_benchmark {
        "libutils",
    ],
}

cc_benchmark {
    name: "audio_powerstatscollector_benchmark",

    srcs: ["audio_powerstatscollector_benchmark.cpp"],
    cflags: [
        "-Wall",
        "-Werror",
    ],
    static_libs: [
        "libaudioutils",
        "libpshutils",
    ],
    shared_libs: [
        "libbase",
        "libbinder_ndk",
        "libcutils",
        "liblog",
        "libutils",
    ],
}
+52 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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 "audio_token_benchmark"
#include <utils/Log.h>

#include <psh_utils/PowerStatsCollector.h>

#include <benchmark/benchmark.h>

/*
 Pixel 8 Pro
------------------------------------------------------------------------------------------
 Benchmark                            Time                      CPU             Iteration
------------------------------------------------------------------------------------------
audio_powerstatscollector_benchmark:
  #BM_StatsToleranceMs/0      1.2005660120994434E8 ns            2532739.72 ns          100
  #BM_StatsToleranceMs/50        1281.095987079007 ns     346.0322183913503 ns      2022168
  #BM_StatsToleranceMs/100       459.9668862534226 ns    189.47902626735942 ns      2891307
  #BM_StatsToleranceMs/200       233.8438662484292 ns    149.84041813854736 ns      4407343
  #BM_StatsToleranceMs/500      184.42197142314103 ns    144.86896036787098 ns      7295167
*/

// We check how expensive it is to query stats depending
// on the tolerance to reuse the cached values.
// A tolerance of 0 means we always fetch stats.
static void BM_StatsToleranceMs(benchmark::State& state) {
    auto& collector = android::media::psh_utils::PowerStatsCollector::getCollector();
    const int64_t toleranceNs = state.range(0) * 1'000'000;
    while (state.KeepRunning()) {
        collector.getStats(toleranceNs);
        benchmark::ClobberMemory();
    }
}

// Here we test various time tolerances (given in milliseconds here)
BENCHMARK(BM_StatsToleranceMs)->Arg(0)->Arg(50)->Arg(100)->Arg(200)->Arg(500);

BENCHMARK_MAIN();
+2 −2
Original line number Diff line number Diff line
@@ -59,7 +59,7 @@ public:
        std::array<unsigned, 3> coreSelection{0U, mCores / 2 + 1, mCores - 1};
        mCore = coreSelection[std::min((size_t)coreClass, std::size(coreSelection) - 1)];

        const auto& collector = android::media::psh_utils::PowerStatsCollector::getCollector();
        auto& collector = android::media::psh_utils::PowerStatsCollector::getCollector();
        mStartStats = collector.getStats();

        const pid_t tid = gettid(); // us.
@@ -102,7 +102,7 @@ protected:
    unsigned mCores = 0;
    int mCore = 0;
    CoreClass mCoreClass = CORE_LITTLE;
    std::shared_ptr<android::media::psh_utils::PowerStats> mStartStats;
    std::shared_ptr<const android::media::psh_utils::PowerStats> mStartStats;
};

} // namespace android::media::psh_utils
+11 −2
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
#pragma once

#include "PowerStats.h"
#include <android-base/thread_annotations.h>
#include <memory>
#include <utils/Errors.h> // status_t

@@ -34,17 +35,25 @@ public:
    // singleton getter
    static PowerStatsCollector& getCollector();

    // get a snapshot of the state.
    std::shared_ptr<PowerStats> getStats() const;
    // Returns a snapshot of the state.
    // If toleranceNs > 0, we permit the use of a stale snapshot taken within that tolerance.
    std::shared_ptr<const PowerStats> getStats(int64_t toleranceNs = 0)
            EXCLUDES(mMutex, mMutexExclusiveFill);

private:
    PowerStatsCollector();  // use the singleton getter

    // Returns non-empty PowerStats if we have a previous stats snapshot within toleranceNs.
    std::shared_ptr<const PowerStats> checkLastStats(int64_t toleranceNs) const EXCLUDES(mMutex);
    int fill(PowerStats* stats) const;
    void addProvider(std::unique_ptr<PowerStatsProvider>&& powerStatsProvider);

    mutable std::mutex mMutexExclusiveFill;
    mutable std::mutex mMutex;
    // addProvider is called in the ctor, so effectively const.
    std::vector<std::unique_ptr<PowerStatsProvider>> mPowerStatsProviders;
    int64_t mLastFetchNs GUARDED_BY(mMutex) = 0;
    std::shared_ptr<const PowerStats> mLastFetchStats GUARDED_BY(mMutex);
};

} // namespace android::media::psh_utils
Loading