Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reactive policies #74

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/power_policy/_power_policy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
#include "power_policy/minbe.hpp"
#include "power_policy/none.hpp"
#include "power_policy/per_process.hpp"
#include "power_policy/bets_skip.hpp"
#include "power_policy/bets_soft_throttle.hpp"
#include "power_policy/bets_hard_throttle.hpp"



// after multiple template-based attempts, a macro solution was chosen,
Expand Down Expand Up @@ -51,6 +55,9 @@ static const std::map<std::string,
PP("imx8_fixed", PowerPolicy_Imx8_Fixed, 2),
PP("imx8_per_process", PowerPolicy_Imx8_PerProcess, 0), // TODO make alias of PowerPolicy_PerProcess if it works correctly
PP("imx8_per_slice", PowerPolicy_Imx8_PerSlice, 0),
PP("bets_skip", Bets_skip, 1),
PP("bets_soft_throttle", Bets_soft_throttle, 1),
PP("bets_hard_throttle", Bets_hard_throttle, 1),
};


Expand Down
18 changes: 18 additions & 0 deletions src/power_policy/bets_hard_throttle.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#pragma once

#include "bets_policy.hpp"

/**
* In case of temperature threshold overstepping set frequencies to the lowest possible frequency.
*
* Based on imx8_per_slice policy.
*/
class Bets_hard_throttle : public Bets_policy
{
public:
Bets_hard_throttle(const std::string &temperature_threshold) : Bets_policy(temperature_threshold) {}

void execute_policy(CpufreqPolicy *cp, CpuFrequencyHz &freq, Window &win) override {
cp->write_frequency(cp->min_frequency);
}
};
134 changes: 134 additions & 0 deletions src/power_policy/bets_policy.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
#pragma once

#include "_power_policy.hpp"
#include "power_manager.hpp"
#include "lib/file_lib.hpp"
#include <fstream>
#include <iostream>
#include "slice.hpp"
#include <optional>
#include <ctime>
#include <numeric>
#include <vector>


/**
* Based on imx8_per_slice policy, intended for Best Effort Task Scheduler (BETS).
*/
class Bets_policy : public PmPowerPolicy
{
protected:
// currently, this is hardcoded for i.MX8, but should be quite simply extensible
// for other CPU cluster layouts
std::array<CpufreqPolicy *, 2> policies{ &pm.get_policy("policy0"), &pm.get_policy("policy4") };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bylo by lepší to předělat, aby to nebylo platformě závislé. Podobně jako per_process.hpp byla upravena z imx8_per_process.hpp.

float upper_threshold;
float lower_threshold;
Comment on lines +24 to +25
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

V komentáři (nebo rovnou ve jménu proměnné) zmínit, že se jedna o teplotu.

time_t timer;
const time_t limit = 10;
Comment on lines +26 to +27
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

time_t je definován jako čas v sekundách. Myslím, že bychom někdy mohli chtít mít ten čas i kratší než sekundu, tak bych použil čas v milisekundách, jako se používá ve zbytku demosu.

Bylo by lepší použít C++ datové typy ze std::chrono, konkrétně asi time_point a duration.

bool temperature_inertia_period = false;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Je divné, že něco, co se nazývá period je typu bool. V názvu proměnné (asi) chybí sloveso.

int level = 0;
const string temp_path_c0 = "/sys/devices/virtual/thermal/thermal_zone0/temp";
std::vector<float> temperatures;
int oldest = 0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Přidat komentář, že se jedná o index do temperatures. A index nemůže být záporný, tak změnit typ na unsigned, nebo size_t.


public:
Bets_policy(const std::string &temperature_threshold)
{
upper_threshold = std::stof(temperature_threshold);
lower_threshold = upper_threshold - 1;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bylo by lepší, kdyby to byl 2. (nepovinný) parametr.

temperatures = std::vector<float>(10);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inicializovat už v definici třídy.

for (auto &p : pm.policy_iter())
if (!p.available_frequencies)
throw runtime_error("Cannot list available frequencies for the CPU"
" - are you sure you're running DEmOS on an i.MX8?");
}

// Validate that the requested frequencies are available
void validate(const Windows &windows) override
{
for (const Window &win : windows) {
for (const Slice &slice : win.slices) {
if (!slice.requested_frequency) continue;
for (const CpufreqPolicy *policy : policies) {
if (!(slice.cpus & policy->affected_cores)) continue;
policy->validate_frequency(slice.requested_frequency.value());
}
}
}
}
bool supports_per_slice_frequencies() override { return true; }

float get_temperature_on_c0(){
auto is = file_open<std::ifstream>(temp_path_c0);
float temp;
is >> temp;
return temp / 1000.0;
}

float get_temp(){
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lepší jméno by bylo get_avg_temp.

this->temperatures[oldest] = this->get_temperature_on_c0();
oldest = (oldest + 1) % temperatures.size();
float sum = std::accumulate(this->temperatures.begin(), this->temperatures.end(), 0);
return sum / this->temperatures.size();
}

virtual void execute_policy(CpufreqPolicy *cp, CpuFrequencyHz &freq, Window &win){
cp->write_frequency(freq);
}

void change_level(){
float avg_temp = this->get_temp();
if (this->temperature_inertia_period){
if (time(0) - this->timer >= this->limit){
this->temperature_inertia_period = false;
}
return; //Change is already in progress
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nevím, jestli by return neměl být jen ve větvi else.

}

if (avg_temp > upper_threshold){
this->temperature_inertia_period = true;
this->timer = time(0);
this->level++;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Myslím, že by fungovalo lépe, kdyby se level počítal jako K * (avg_temp - upper_threshold), kde konstanta K by byla konfigurovatelná.

std::cout << "level=" << this->level << std::endl;
return;
}

if (avg_temp < lower_threshold && level > 0){
this->temperature_inertia_period = true;
this->timer = time(0);
this->level--;
std::cout << "level=" << this->level << std::endl;
return;
}
}

void on_window_start(Window &win) override
{
for (CpufreqPolicy *cp : policies) {
std::optional<CpuFrequencyHz> freq = std::nullopt;
for (const Slice &slice : win.slices) {
if (slice.requested_frequency && slice.cpus & cp->affected_cores) {
// slice is requesting a fixed frequency, check that all slices on the
// same CPU cluster request the same frequency.
if (freq.has_value() && freq.value() != slice.requested_frequency) {
throw std::runtime_error(fmt::format(
"Scheduled slices require different frequencies on the CPU(s) "
"{}: '{}', '{}'",
cp->affected_cores.as_list(),
freq.value(),
slice.requested_frequency.value()));
}
freq = slice.requested_frequency;
}
}
if (freq.has_value()) {
change_level();
if (this->level == 0){
cp->write_frequency(freq.value());
} else {
execute_policy(cp, freq.value(), win);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Myslím, že execute_policy by se mělo volat vždy a pokud je level == 0, tak nastavení výchozí frekvence může (ale nemusí) udělat přímo execute_policy.

}
}
}
}
};
87 changes: 87 additions & 0 deletions src/power_policy/bets_skip.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#pragma once

#include "bets_policy.hpp"
#include <iostream>
#include <vector>
#include <utility>
#include <algorithm>


bool cmp(std::pair<std::string, int>& a, std::pair<std::string, int>& b) {
return a.second < b.second;
}

/**
* In case of temperature threshold overstepping skip a task in a fair fashion.
*
* Based on imx8_per_slice policy.
*/
class Bets_skip : public Bets_policy
{
private:
std::vector<std::pair<std::string, int>> candidates;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Co je to za kandidáty? Nějaký komentář by byl vhodný. A nebylo by lepší použít std::map, která je setříděná automaticky?

Po přečtení zbytku kódu už to chápu. Myslím, že by bylo lepší používat následující datové sktruktury (nebo něco podobného):

struct SliceProxy {
  Slice &slice;
  SliceProxy(Slice &slice) : slice(slice) {}
};
std::map<Slice*, SliceProxy&> proxies;
std::multimap<chrono::duration, SliceProxy&>

Nejsem si jist, jestli bude potřeba SliceProxy - možná by šlo rovnou používat Slice*. Možná by bylo dobré do SliceProxy přidat i chrono::duration skip_time a místo multimap použít set stetříděnou podle skip_time. Co bude nejlepší záleží na zbytku kódu. Idea je, aby se třídění podle přeskočeného času dělalo automaticky použitím multimap a abychom mohli jednoduše zjistit, jestli se má slice přeskakovat na zákldě ukazatele na Slice (tedy zjištěním zda k němu existuje SliceProxy).

std::vector<std::string> present;
public:
Bets_skip(const std::string &temperature_threshold) : Bets_policy(temperature_threshold) {}


void sort_candidates(){
std::sort(candidates.begin(), candidates.end(), cmp);
}

void safe_pushback(std::string key){
for (auto &p : candidates){
if (p.first == key){
return;
}
}
candidates.push_back(std::pair<std::string, int>(key, 0));
}

Comment on lines +28 to +40
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pokud se použijí datové sktuktury navržené výše, tohle bude zbytečné.

void load(Window &win){
present = std::vector<std::string>();
for (auto &s : win.slices){
std::string key = s.be->get_name();
if (key.find("sleep") == std::string::npos){ //Sleep process is used for idle time. We are not skipping it.
present.push_back(key);
safe_pushback(key);
}
}
}

int is_present(std::string key){
for (int i = 0; i < present.size(); i++){
if (present[i] == key){
return i;
}
}
return -1;
}
Comment on lines +52 to +59
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pokud se použijí datové sktuktury navržené výše, is_present bude zbytečné.


void execute_policy(CpufreqPolicy *cp, CpuFrequencyHz &freq, Window &win) override {
cp->write_frequency(freq);
if (cp->name != "policy0"){ //Skip policy works over all clusters, it makes settings only on the first one.
return;
}

if(this->level > 5){
this->level = 5;
}
int skip_counter = 0;
for (int j = 0; j < win.slices.size(); j++){
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Když iterujete přes container, je přehlednější používat tzv. range-based for: for (auto &slice : win.slices) slice.to_be_skipped = false;.

win.to_be_skipped[j] = false;
}
load(win);
sort_candidates();
Comment on lines +74 to +75
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Myslím, že load a sort_candidate není potřeba volat pokaždé, ale stačilo by to vykonat jednou na začátku. Myslím, že metoda validate je ideální místo, kde by se to mělo udělat.

// Iterate trough candidates, until up to the level amounth of them is skipped, but at least 1 is kept.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Proč je potřeba 1 nechávat? Když by se to hodně přehřívalo, je možné klidně přeskočit všechny.

for(int i = 0; i < candidates.size() && skip_counter < this->level && present.size() - skip_counter > 1; i++){
int idx = is_present(candidates[i].first);
if (idx != -1){
win.to_be_skipped[idx] = true;
candidates[i].second += win.length.count();
skip_counter++;
}
}
}
};

49 changes: 49 additions & 0 deletions src/power_policy/bets_skip.hpp.save
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#pragma once
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bets_skip.hpp.save? To tady asi být nemá.


#include "bets_policy.hpp"
#include <map>
#include <limits>
#include <iostream>

/**
* In case of temperature threshold overstepping set frequencies to the lowest possible frequency.
*
* Based on imx8_per_slice policy.
*/
class Bets_skip : public Bets_policy
{
private:
std::map<std::string, int> skip_counter;
public:
Bets_skip(const std::string &temperature_threshold) : Bets_policy(temperature_threshold) {}

void execute_policy(CpufreqPolicy *cp, CpuFrequencyHz &freq, Window &win) override {
cp->write_frequency(freq);

int min = std::numeric_limits<int>::max();
std::string candidate;
int i = 0;
for (auto &s : win.slices){
std::string key = s.be->get_name();
if (!skip_counter.count(key)){
skip_counter.insert(std::pair<std::string, int>(key, 0));
min = 0;
candidate = key;
} else {
if (skip_counter[key] < min){
min = skip_counter[key];
candidate = key;
}
}
i++;
}
for (auto pair : win.to_){
win.to_be_skipped[j] = false;
}

for (int j = 0; j < )

win.to_be_skipped[candidate] = true;
}
};

34 changes: 34 additions & 0 deletions src/power_policy/bets_soft_throttle.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#pragma once

#include "bets_policy.hpp"


/**
* In case of temperature threshold overstepping lower frequencies by one step (if possible).
*
* Based on imx8_per_slice policy.
*/
class Bets_soft_throttle : public Bets_policy
{
public:
Bets_soft_throttle(const std::string &temperature_threshold) : Bets_policy(temperature_threshold) {}

void execute_policy(CpufreqPolicy *cp, CpuFrequencyHz &freq, Window &win) override {
if (this->level > 3){
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Proč se omezuje jen horní úroveň a ne spodní?

this->level = 3;
}
auto freqs = cp->available_frequencies.value();
auto it = std::find(freqs.begin(), freqs.end(), freq);
if (it != freqs.end()){
for(int i = 0; i < this->level; i++){
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bylo by lepší neomezovat level výše, ale ukončit tento cyklus, když se nazazí na začátek freqs. Pak by to fungovalo i na jiných platformách, které mají větší počet frekvencí.

it = it == freqs.begin() ? it : --it; // If it can be lowered, lower it.
}
cp->write_frequency(*it);
} else {
throw std::runtime_error(fmt::format(
"Frequency not found in available frequencies "
"'{}'",
freq));
}
}
};
11 changes: 9 additions & 2 deletions src/window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Window::Window(ev::loop_ref loop_, std::chrono::milliseconds length_, PowerPolic
Slice &Window::add_slice(Partition *sc, Partition *be, const cpu_set &cpus, std::optional<CpuFrequencyHz> req_freq)
{
auto sc_cb = [this](Slice &s, time_point t) { slice_sc_end_cb(s, t); };
to_be_skipped.push_back(false);
return slices.emplace_back(loop, power_policy, sc_cb, sc, be, req_freq, cpus);
}

Expand All @@ -26,6 +27,7 @@ void Window::start(time_point current_time)
for (auto &s : slices) {
s.start_sc(current_time);
}

// call power policy handlers after we start the slices;
// this way, even if the CPU frequency switching is slow, the processes are
// still executing, although at an incorrect frequency;
Expand Down Expand Up @@ -65,10 +67,15 @@ void Window::slice_sc_end_cb([[maybe_unused]] Slice &slice, time_point current_t
// slice->start_be(current_time);

// option 2) wait until all SC partitions finish

if (has_sc_finished()) {
TRACE("Starting BE partitions");
for (auto &sp : slices) {
sp.start_be(current_time);
int i = 0;
for (auto &s : slices) {
if (!to_be_skipped[i]){
s.start_be(current_time);
}
i++;
}
// see `Window::start` for reasoning on why this is called AFTER partition start
power_policy.on_be_start(*this);
Expand Down
2 changes: 2 additions & 0 deletions src/window.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <ev++.h>
#include <list>
#include <optional>
#include <vector>

class Window;
class PowerPolicy;
Expand All @@ -28,6 +29,7 @@ class Window
const std::chrono::milliseconds length;
// use std::list as Slice doesn't have move and copy constructors
std::list<Slice> slices{};
std::vector<bool> to_be_skipped;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Co je indexem do toho vektoru? Zdá se, že pořadí slice. Pokud to tak je, proč se nepřidá bool proměnná přímo do Slice?


Window(ev::loop_ref loop, std::chrono::milliseconds length, PowerPolicy &power_policy);

Expand Down