Skip to content

Commit 1b600da

Browse files
lukaszluba-armrafaeljw
authored andcommitted
PM: EM: Optimize em_cpu_energy() and remove division
The Energy Model (EM) can be modified at runtime which brings new possibilities. The em_cpu_energy() is called by the Energy Aware Scheduler (EAS) in its hot path. The energy calculation uses power value for a given performance state (ps) and the CPU busy time as percentage for that given frequency. It is possible to avoid the division by 'scale_cpu' at runtime, because EM is updated whenever new max capacity CPU is set in the system. Use that feature and do the needed division during the calculation of the coefficient 'ps->cost'. That enhanced 'ps->cost' value can be then just multiplied simply by utilization: pd_nrg = ps->cost * \Sum cpu_util to get the needed energy for whole Performance Domain (PD). With this optimization and earlier removal of map_util_freq(), the em_cpu_energy() should run faster on the Big CPU by 1.43x and on the Little CPU by 1.69x (RockPi 4B board). Reviewed-by: Dietmar Eggemann <dietmar.eggemann@arm.com> Tested-by: Dietmar Eggemann <dietmar.eggemann@arm.com> Signed-off-by: Lukasz Luba <lukasz.luba@arm.com> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
1 parent e3f1164 commit 1b600da

File tree

2 files changed

+18
-44
lines changed

2 files changed

+18
-44
lines changed

include/linux/energy_model.h

Lines changed: 15 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -115,27 +115,6 @@ struct em_perf_domain {
115115
#define EM_MAX_NUM_CPUS 16
116116
#endif
117117

118-
/*
119-
* To avoid an overflow on 32bit machines while calculating the energy
120-
* use a different order in the operation. First divide by the 'cpu_scale'
121-
* which would reduce big value stored in the 'cost' field, then multiply by
122-
* the 'sum_util'. This would allow to handle existing platforms, which have
123-
* e.g. power ~1.3 Watt at max freq, so the 'cost' value > 1mln micro-Watts.
124-
* In such scenario, where there are 4 CPUs in the Perf. Domain the 'sum_util'
125-
* could be 4096, then multiplication: 'cost' * 'sum_util' would overflow.
126-
* This reordering of operations has some limitations, we lose small
127-
* precision in the estimation (comparing to 64bit platform w/o reordering).
128-
*
129-
* We are safe on 64bit machine.
130-
*/
131-
#ifdef CONFIG_64BIT
132-
#define em_estimate_energy(cost, sum_util, scale_cpu) \
133-
(((cost) * (sum_util)) / (scale_cpu))
134-
#else
135-
#define em_estimate_energy(cost, sum_util, scale_cpu) \
136-
(((cost) / (scale_cpu)) * (sum_util))
137-
#endif
138-
139118
struct em_data_callback {
140119
/**
141120
* active_power() - Provide power at the next performance state of
@@ -249,8 +228,7 @@ static inline unsigned long em_cpu_energy(struct em_perf_domain *pd,
249228
{
250229
struct em_perf_table *em_table;
251230
struct em_perf_state *ps;
252-
unsigned long scale_cpu;
253-
int cpu, i;
231+
int i;
254232

255233
#ifdef CONFIG_SCHED_DEBUG
256234
WARN_ONCE(!rcu_read_lock_held(), "EM: rcu read lock needed\n");
@@ -267,9 +245,7 @@ static inline unsigned long em_cpu_energy(struct em_perf_domain *pd,
267245
* max utilization to the allowed CPU capacity before calculating
268246
* effective performance.
269247
*/
270-
cpu = cpumask_first(to_cpumask(pd->cpus));
271-
scale_cpu = arch_scale_cpu_capacity(cpu);
272-
248+
max_util = map_util_perf(max_util);
273249
max_util = min(max_util, allowed_cpu_cap);
274250

275251
/*
@@ -282,22 +258,23 @@ static inline unsigned long em_cpu_energy(struct em_perf_domain *pd,
282258
ps = &em_table->state[i];
283259

284260
/*
285-
* The capacity of a CPU in the domain at the performance state (ps)
286-
* can be computed as:
261+
* The performance (capacity) of a CPU in the domain at the performance
262+
* state (ps) can be computed as:
287263
*
288-
* ps->freq * scale_cpu
289-
* ps->cap = -------------------- (1)
290-
* cpu_max_freq
264+
* ps->freq * scale_cpu
265+
* ps->performance = -------------------- (1)
266+
* cpu_max_freq
291267
*
292268
* So, ignoring the costs of idle states (which are not available in
293269
* the EM), the energy consumed by this CPU at that performance state
294270
* is estimated as:
295271
*
296272
* ps->power * cpu_util
297273
* cpu_nrg = -------------------- (2)
298-
* ps->cap
274+
* ps->performance
299275
*
300-
* since 'cpu_util / ps->cap' represents its percentage of busy time.
276+
* since 'cpu_util / ps->performance' represents its percentage of busy
277+
* time.
301278
*
302279
* NOTE: Although the result of this computation actually is in
303280
* units of power, it can be manipulated as an energy value
@@ -307,9 +284,9 @@ static inline unsigned long em_cpu_energy(struct em_perf_domain *pd,
307284
* By injecting (1) in (2), 'cpu_nrg' can be re-expressed as a product
308285
* of two terms:
309286
*
310-
* ps->power * cpu_max_freq cpu_util
311-
* cpu_nrg = ------------------------ * --------- (3)
312-
* ps->freq scale_cpu
287+
* ps->power * cpu_max_freq
288+
* cpu_nrg = ------------------------ * cpu_util (3)
289+
* ps->freq * scale_cpu
313290
*
314291
* The first term is static, and is stored in the em_perf_state struct
315292
* as 'ps->cost'.
@@ -319,11 +296,9 @@ static inline unsigned long em_cpu_energy(struct em_perf_domain *pd,
319296
* total energy of the domain (which is the simple sum of the energy of
320297
* all of its CPUs) can be factorized as:
321298
*
322-
* ps->cost * \Sum cpu_util
323-
* pd_nrg = ------------------------ (4)
324-
* scale_cpu
299+
* pd_nrg = ps->cost * \Sum cpu_util (4)
325300
*/
326-
return em_estimate_energy(ps->cost, sum_util, scale_cpu);
301+
return ps->cost * sum_util;
327302
}
328303

329304
/**

kernel/power/energy_model.c

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -192,11 +192,9 @@ static int em_compute_costs(struct device *dev, struct em_perf_state *table,
192192
unsigned long flags)
193193
{
194194
unsigned long prev_cost = ULONG_MAX;
195-
u64 fmax;
196195
int i, ret;
197196

198197
/* Compute the cost of each performance state. */
199-
fmax = (u64) table[nr_states - 1].frequency;
200198
for (i = nr_states - 1; i >= 0; i--) {
201199
unsigned long power_res, cost;
202200

@@ -208,8 +206,9 @@ static int em_compute_costs(struct device *dev, struct em_perf_state *table,
208206
return -EINVAL;
209207
}
210208
} else {
211-
power_res = table[i].power;
212-
cost = div64_u64(fmax * power_res, table[i].frequency);
209+
/* increase resolution of 'cost' precision */
210+
power_res = table[i].power * 10;
211+
cost = power_res / table[i].performance;
213212
}
214213

215214
table[i].cost = cost;

0 commit comments

Comments
 (0)