xref: /aosp_15_r20/frameworks/av/media/psh_utils/include/psh_utils/PerformanceFixture.h (revision ec779b8e0859a360c3d303172224686826e6e0e1)
1 /*
2  * Copyright (C) 2024 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 #pragma once
18 
19 #include <audio_utils/threads.h>
20 #include <benchmark/benchmark.h>
21 #include <psh_utils/PowerStats.h>
22 #include <psh_utils/PowerStatsCollector.h>
23 
24 #include <future>
25 
26 namespace android::media::psh_utils {
27 
28 enum CoreClass {
29     CORE_LITTLE,
30     CORE_MID,
31     CORE_BIG,
32 };
33 
toString(CoreClass coreClass)34 inline std::string toString(CoreClass coreClass) {
35     switch (coreClass) {
36         case CORE_LITTLE: return "LITTLE";
37         case CORE_MID: return "MID";
38         case CORE_BIG: return "BIG";
39         default: return "UNKNOWN";
40     }
41 }
42 
43 /**
44  * A benchmark fixture is used to specify benchmarks that have a custom SetUp() and
45  * TearDown().  This is **required** for performance testing, as a typical benchmark
46  * method **may be called several times** during a run.
47  *
48  * A fixture ensures that SetUp() and TearDown() and the resulting statistics accumulation
49  * is done only once.   Note: BENCHMARK(BM_func)->Setup(DoSetup)->Teardown(DoTeardown)
50  * does something similar, but it requires some singleton to contain the state properly.
51  */
52 class PerformanceFixture : public benchmark::Fixture {
53 public:
54     // call this to start the profiling
startProfiler(CoreClass coreClass)55     virtual void startProfiler(CoreClass coreClass) {
56         mCores = android::audio_utils::get_number_cpus();
57         if (mCores == 0) return;
58         mCoreClass = coreClass;
59         std::array<unsigned, 3> coreSelection{0U, mCores / 2 + 1, mCores - 1};
60         mCore = coreSelection[std::min((size_t)coreClass, std::size(coreSelection) - 1)];
61 
62         auto& collector = android::media::psh_utils::PowerStatsCollector::getCollector();
63         mStartStats = collector.getStats();
64 
65         const pid_t tid = gettid(); // us.
66 
67         // Possibly change priority to improve benchmarking
68         // android::audio_utils::set_thread_priority(gettid(), 98);
69 
70         android::audio_utils::set_thread_affinity(0 /* pid */, 1 << mCore);
71     }
72 
TearDown(benchmark::State & state)73     void TearDown(benchmark::State &state) override {
74         const auto N = state.complexity_length_n();
75         state.counters["N"] = benchmark::Counter(N,
76                 benchmark::Counter::kIsIterationInvariantRate, benchmark::Counter::OneK::kIs1024);
77         if (mStartStats) {
78             auto& collector = android::media::psh_utils::PowerStatsCollector::getCollector();
79             const auto stopStats = collector.getStats();
80             android::media::psh_utils::PowerStats diff = *stopStats - *mStartStats;
81             auto cpuEnergy = diff.energyFrom("CPU");
82             auto memEnergy = diff.energyFrom("MEM");
83 
84             constexpr float kMwToW = 1e-3;
85             state.counters["WCPU"] = benchmark::Counter(std::get<2>(cpuEnergy) * kMwToW,
86                                                           benchmark::Counter::kDefaults,
87                                                           benchmark::Counter::OneK::kIs1000);
88             state.counters["WMem"] = benchmark::Counter(std::get<2>(memEnergy) * kMwToW,
89                                                           benchmark::Counter::kDefaults,
90                                                           benchmark::Counter::OneK::kIs1000);
91             state.counters["JCPU"] = benchmark::Counter(
92                     std::get<1>(cpuEnergy) / N / state.iterations(), benchmark::Counter::kDefaults,
93                     benchmark::Counter::OneK::kIs1000);
94             state.counters["JMem"] = benchmark::Counter(
95                     std::get<1>(memEnergy) / N / state.iterations(), benchmark::Counter::kDefaults,
96                     benchmark::Counter::OneK::kIs1000);
97         }
98     }
99 
100 protected:
101     // these are only initialized upon startProfiler.
102     unsigned mCores = 0;
103     int mCore = 0;
104     CoreClass mCoreClass = CORE_LITTLE;
105     std::shared_ptr<const android::media::psh_utils::PowerStats> mStartStats;
106 };
107 
108 } // namespace android::media::psh_utils
109