【WALT】update_task_demand() 代码详解

代码版本:Linux4.9 android-msm-crosshatch-4.9-android12

代码展示

static u64 update_task_demand(struct task_struct *p, struct rq *rq,
int event, u64 wallclock)
{
u64 mark_start = p->ravg.mark_start;
u64 delta, window_start = rq->window_start;
int new_window, nr_full_windows;
u32 window_size = sched_ravg_window;
u64 runtime; // 用于判断是否进入新窗口的标志位
new_window = mark_start < window_start;
// ⑴ 不累加任务运行时间的条件判断
if (!account_busy_for_task_demand(rq, p, event)) {
if (new_window)
update_history(rq, p, p->ravg.sum, 1, event);
return 0;
} // ⑵ 仍在旧窗口中
if (!new_window) {
return add_to_task_demand(rq, p, wallclock - mark_start);
} // ⑶ 进入新窗口
delta = window_start - mark_start;
nr_full_windows = div64_u64(delta, window_size);
window_start -= (u64)nr_full_windows * (u64)window_size; runtime = add_to_task_demand(rq, p, window_start - mark_start); update_history(rq, p, p->ravg.sum, 1, event);
if (nr_full_windows) {
u64 scaled_window = scale_exec_time(window_size, rq); update_history(rq, p, scaled_window, nr_full_windows, event);
runtime += nr_full_windows * scaled_window;
} window_start += (u64)nr_full_windows * (u64)window_size; mark_start = window_start;
runtime += add_to_task_demand(rq, p, wallclock - mark_start); // ⑷ 返回值 runtime
return runtime;
}

代码逻辑

用于判断是否进入新窗口的标志位

WALT 算法中,引入了一个新的概念:窗口(sched_ravg_window)

先介绍几个名词:

  • ws:window_start,当前窗口的开始时间
  • ms:mark_start,当前任务的开始时间
  • wc:wallclock,进入 WALT 算法的时间
  • nr_full_windows,如果进入新窗口,则代表旧窗口到当前窗口所经历的完整的窗口个数
  • delta:从任务开始到当前时间/新窗口开始时间所经历的时长

窗口分三种情况进行划分:

  1. 仍在旧窗口中
                    ws   ms  wc
    | | |
    V V V
    |---------------|===============|
    即进入 WALT 算法到时间还在 window_start 到 window_start + sched_ravg_window 之间
    这种情况下,delta = wc - ms,只需要累加进任务时间,不需要更新
  2. 刚离开旧窗口,进入下一个窗口
               ms   ws   wc
    | | |
    V V V
    |---------------|===============|
    即进入 WALT 算法到时间超过了 window_start + sched_ravg_window
    但还没超过 window_start + sched_ravg_window * 2
    这种情况下,delta 分为两块,一块是 ws - ms,一块是 wc - ws
    两块都需要累加进任务时间,但 ws - ms 块需要进行更新,因为它在旧窗口中
  3. 经过了数个窗口后抵达新窗口
               ms   ws_tmp                    ws   wc
    | | | |
    V V V V
    |---------------|----------|...|----------|===============|
    | |
    |<--- nr_full_windows --->|
    即进入 WALT 算法到时间超过了 window_start + sched_ravg_window * 2
    其中经过了 nr_full_windows 个完整窗口
    这种情况下,delta 分为三块,一块是 ws_tmp - ms,一块是 wc - ws,
    一块是 sched_ravg_window * nr_full_windows
    三块都需要累加进任务时间,但只有 wc - ws 块不需要进行更新,因为它在新窗口中

通过 new_window = mark_start < window_start; 来判断是否处在 2、3 种情况之中,如果 new_window == 1,则处在 2、3 种情况之中,否则处于第 1 种情况。

⑴ 不累加任务运行时间的条件判断

static int
account_busy_for_task_demand(struct rq *rq, struct task_struct *p, int event)
{
if (exiting_task(p) || is_idle_task(p))
return 0; if (event == TASK_WAKE || (!SCHED_ACCOUNT_WAIT_TIME &&
(event == PICK_NEXT_TASK || event == TASK_MIGRATE)))
return 0; if (event == TASK_UPDATE) {
if (rq->curr == p)
return 1;
return p->on_rq ? SCHED_ACCOUNT_WAIT_TIME : 0;
} return 1;
}

在函数 account_busy_for_task_demand() 中会判断任务经过的时间是否是 runnable 或 running 时间,返回 1 则是,返回 0 则不是。

  1. 任务经过的时间是 runnable 或 running,即返回 1 的情况

    在当前版本内核中,SCHED_ACCOUNT_WAIT_TIME 默认为 1

    • 任务更新且任务在就绪队列中,无论是不是当前任务
    • 其他情况
  2. 任务经过的时间不是 runnable 或 running,即返回 0 的情况
    • 任务正在退出
    • 任务是 idle 任务
    • 任务刚被唤醒
    • 任务更新切任务不在就绪队列中

如果任务经过的时间不是 runnable 或 running 时间,且正好进入新窗口,就不累加任务时间,直接通过 update_history() 将上一个窗口中已经累加的时间更新至任务结构体中(task_struct)。

点击此处查看 update_history() 代码详解。

⑵ 仍在旧窗口中

根据开头的分析,我们知道这种情况下不需要通过 update_history() 更新时间,只需要通过 add_to_task_demand() 累加任务时间。

static u64 add_to_task_demand(struct rq *rq, struct task_struct *p, u64 delta)
{
// 1. 将 delta 时间进行归一化
delta = scale_exec_time(delta, rq);
// 2. 累加进 p->ravg.sum 中
p->ravg.sum += delta;
if (unlikely(p->ravg.sum > sched_ravg_window))
p->ravg.sum = sched_ravg_window; return delta;
}

将归一化后的任务时间累加进 p->ravg.sum 中,在之后的 update_history() 中会将 p->ravg.sum 放进 p->ravg.sum_history 结构体中。

其中,任务时间的归一化是 WALT 算法中的重要部分。点击此处查看 scale_exec_time() 代码详解。

⑶ 进入新窗口

根据开头的分析,我们知道进入新窗口分为两种情况,无论是哪种情况,都需要累加 ws_tmp - ms 和 wc - ws 两部分。其中,如果刚离开旧窗口进入下一个窗口,则 ws = ws_tmp。

我们先处理 ws_tmp - ms 部分:

  • 先通过 delta = window_start - mark_start; 计算总体经过的时间;
  • 再通过 nr_full_windows = div64_u64(delta, window_size); 计算经过的完整窗口的数量;
  • 最后得到 ws_tmp:window_start -= (u64)nr_full_windows * (u64)window_size;
  • 累加 ws_tmp - ms 部分时间:runtime = add_to_task_demand(rq, p, window_start - mark_start);
  • 更新 ws_tmp - ms 部分时间:update_history(rq, p, p->ravg.sum, 1, event);

然后针对经过多个完整窗口情况进行时间更新。此处不需要通过 add_to_task_demand() 累加任务时间,因为任务在这些完整窗口中的时间都是从窗口开始到窗口结束。

  • 先对窗口时间进行归一化:scaled_window = scale_exec_time(window_size, rq);
  • 更新时间:update_history(rq, p, scaled_window, nr_full_windows, event);

最后处理 wc - ws 部分。

  • 把 ws 时间还原:window_start += (u64)nr_full_windows * (u64)window_size;
  • mark_start = window_start; 此处不是更新任务的开始时间,任务开始时间在 WALT 算法的 done 部分进行更新。如果任务开始时间在此处更新,会影响到 update_cpu_busy_time() 中的计算。
  • 累加 wc - ws 部分时间:runtime += add_to_task_demand(rq, p, wallclock - mark_start);

⑷ 返回值 runtime

最后的返回值 runtime 在该版本内核中并未使用到,它是此次执行 update_task_demand() 时一共累加的任务 runnable 和 running 时间,也就是上一次 WALT 算法开始到这一次 WALT 算法开始过程中,该任务的 runnable 和 running 时间。

点击此处回到 WALT 入口函数 update_task_ravg()

【WALT】update_task_demand() 代码详解的更多相关文章

  1. BM算法  Boyer-Moore高质量实现代码详解与算法详解

    Boyer-Moore高质量实现代码详解与算法详解 鉴于我见到对算法本身分析非常透彻的文章以及实现的非常精巧的文章,所以就转载了,本文的贡献在于将两者结合起来,方便大家了解代码实现! 算法详解转自:h ...

  2. ASP.NET MVC 5 学习教程:生成的代码详解

    原文 ASP.NET MVC 5 学习教程:生成的代码详解 起飞网 ASP.NET MVC 5 学习教程目录: 添加控制器 添加视图 修改视图和布局页 控制器传递数据给视图 添加模型 创建连接字符串 ...

  3. Github-karpathy/char-rnn代码详解

    Github-karpathy/char-rnn代码详解 zoerywzhou@gmail.com http://www.cnblogs.com/swje/ 作者:Zhouwan  2016-1-10 ...

  4. 代码详解:TensorFlow Core带你探索深度神经网络“黑匣子”

    来源商业新知网,原标题:代码详解:TensorFlow Core带你探索深度神经网络“黑匣子” 想学TensorFlow?先从低阶API开始吧~某种程度而言,它能够帮助我们更好地理解Tensorflo ...

  5. JAVA类与类之间的全部关系简述+代码详解

    本文转自: https://blog.csdn.net/wq6ylg08/article/details/81092056类和类之间关系包括了 is a,has a, use a三种关系(1)is a ...

  6. Java中String的intern方法,javap&cfr.jar反编译,javap反编译后二进制指令代码详解,Java8常量池的位置

    一个例子 public class TestString{ public static void main(String[] args){ String a = "a"; Stri ...

  7. Kaggle网站流量预测任务第一名解决方案:从模型到代码详解时序预测

    Kaggle网站流量预测任务第一名解决方案:从模型到代码详解时序预测 2017年12月13日 17:39:11 机器之心V 阅读数:5931   近日,Artur Suilin 等人发布了 Kaggl ...

  8. 基础 | batchnorm原理及代码详解

    https://blog.csdn.net/qq_25737169/article/details/79048516 https://www.cnblogs.com/bonelee/p/8528722 ...

  9. 非极大值抑制(NMS,Non-Maximum Suppression)的原理与代码详解

    1.NMS的原理 NMS(Non-Maximum Suppression)算法本质是搜索局部极大值,抑制非极大值元素.NMS就是需要根据score矩阵和region的坐标信息,从中找到置信度比较高的b ...

  10. 委托与事件代码详解与(Object sender,EventArgs e)详解

    委托与事件代码详解 using System;using System.Collections.Generic;using System.Text; namespace @Delegate //自定义 ...

随机推荐

  1. Django笔记二十七之数据库函数之文本函数

    本文首发于公众号:Hunter后端 原文链接:Django笔记二十七之数据库函数之文本函数 这篇笔记将介绍如何使用数据库函数里的文本函数. 顾名思义,文本函数,就是针对文本字段进行操作的函数,如下是目 ...

  2. Python全栈开发工程师 day57 jQuery

    二.jQuery样式操作标签样式操作<!DOCTYPE html><html lang="en"><head> <meta charset ...

  3. Eclipse中添加Shell脚本(如start.sh)

    Eclipse中添加Shell脚本(如start.sh)       使用eclipse时,我们有时候会在自己的工程文件下添加一些脚本(比如将Qt代码在eclipse中运行生成moc文件时,或者要拷贝 ...

  4. Python str 模块

    str字符串 str 转义字符 格式化 内建函数 字符串 表示文字信息 用单引号,双引号,三引号引起来 s = 'i love wangxiaojing' print(s) i love wangxi ...

  5. 驱动开发:通过MDL映射实现多次通信

    在前几篇文章中LyShark通过多种方式实现了驱动程序与应用层之间的通信,这其中就包括了通过运用SystemBuf缓冲区通信,运用ReadFile读写通信,运用PIPE管道通信,以及运用ASYNC反向 ...

  6. Python_16 配置文件与封装

    一.查缺补漏 1. ctrl + alt +L 规范格式 2. Python 使用 ini&yaml 配置文件 http://testingpai.com/article/1621245437 ...

  7. VueUse 是怎么封装Vue3 Provide/Inject 的?

    Provide/Inject Provide 和 Inject 可以解决 Prop 逐级透传问题.注入值类型不会使注入保持响应性,但注入一个响应式对象,仍然有响应式的效果. Provide 的问题是无 ...

  8. 2022-07-07:原本数组中都是大于0、小于等于k的数字,是一个单调不减的数组, 其中可能有相等的数字,总体趋势是递增的。 但是其中有些位置的数被替换成了0,我们需要求出所有的把0替换的方案数量:

    2022-07-07:原本数组中都是大于0.小于等于k的数字,是一个单调不减的数组, 其中可能有相等的数字,总体趋势是递增的. 但是其中有些位置的数被替换成了0,我们需要求出所有的把0替换的方案数量: ...

  9. Selenium - 模拟键盘鼠标操作

    Selenium - 模拟键盘鼠标 鼠标操作 当出现由 Selenium 的 click 无法进行操作,或者需要对元素进行拖拽等操作时,可以采用鼠标模拟人工操作: from selenium impo ...

  10. 非AI文生图,献丑了

    多图预警! 大家好,我是 DOM哥 也许你会好奇上面的这几张图片是怎么生成的,接下来我就开始隆重介绍这款文生图工具 当然,并非 AI 的那个文生图啦 预览地址:https://dombro.site/ ...