【WALT】update_history() 代码详解

文章详细解析了Linux4.9内核中WALT算法的update_history函数,涉及任务历史数据处理、demand和pred_demand计算,以及如何将这些值应用于CPU负载管理和任务信息更新。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【WALT】update_history() 代码详解

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

代码展示

static void update_history(struct rq *rq, struct task_struct *p,
			 u32 runtime, int samples, int event)
{
	u32 *hist = &p->ravg.sum_history[0];
	int ridx, widx;
	u32 max = 0, avg, demand, pred_demand;
	u64 sum = 0;
	u64 prev_demand;

	// ⑴ 判断是否更新任务信息
	if (!runtime || is_idle_task(p) || exiting_task(p) || !samples)
		goto done;
	
	// ⑵ 更新历史窗口数据
	prev_demand = p->ravg.demand;
	
	widx = sched_ravg_hist_size - 1;
	ridx = widx - samples;
	for (; ridx >= 0; --widx, --ridx) {
		hist[widx] = hist[ridx];
		sum += hist[widx];
		if (hist[widx] > max)
			max = hist[widx];
	}
	for (widx = 0; widx < samples && widx < sched_ravg_hist_size; widx++) {
		hist[widx] = runtime;
		sum += hist[widx];
		if (hist[widx] > max)
			max = hist[widx];
	}

	p->ravg.sum = 0;

	// ⑶ 计算 demand
	if (sched_window_stats_policy == WINDOW_STATS_RECENT) {
		demand = runtime;
	} else if (sched_window_stats_policy == WINDOW_STATS_MAX) {
		demand = max;
	} else {
		avg = div64_u64(sum, sched_ravg_hist_size);
		if (sched_window_stats_policy == WINDOW_STATS_AVG)
			demand = avg;
		else
			demand = max(avg, runtime);
	}
	// ⑷ 计算 pred_demand
	pred_demand = predict_and_update_buckets(rq, p, runtime);

	trace_print_pred_demand(pred_demand, demand);

	// ⑸ 将 demand 与 pred_demand 更新到 rq 中
	if (!task_has_dl_policy(p) || !p->dl.dl_throttled) {
		if (task_on_rq_queued(p))
			p->sched_class->fixup_walt_sched_stats(rq, p, demand,
							       pred_demand);
		else if (rq->curr == p)
			walt_fixup_cum_window_demand(rq, demand);
	}

	// ⑹ 更新任务信息
	p->ravg.demand = demand;
	p->ravg.coloc_demand = div64_u64(sum, sched_ravg_hist_size);
	p->ravg.pred_demand = pred_demand;

done:
	trace_sched_update_history(rq, p, runtime, samples, event);
}

代码逻辑

update_history() 是在 WALT 算法中,任务执行到新的一个窗口的时候,对旧窗口内数据进行更新并对新窗口内 demand 进行预测的一个函数。

参数解释:

  1. update_history(struct rq *rq, struct task_struct *p, u32 runtime, int samples, int event)
    p 和 rq:当前要更新信息的任务以及任务所在的运行队列(就绪队列)

    runtime:在 update_task_demand() 中记录的任务的运行时间

    samples:在 update_task_demand() 中记录的窗口数。如果记录的是上一个窗口的,或者是 n 个窗口前的某个窗口的,samples = 1;如果记录的是前 n 个窗口,samples = n,且每个窗口的执行时间都是窗口大小的归一化值。

  2. u32 hist = &p->ravg.sum_history[0];
    hist 是一个指向 p->ravg.sum_history[0] 的指针。

    p->ravg.sum_history 是一个长度为 RAVG_HIST_SIZE_MAX 的数组,存放的是过去 RAVG_HIST_SIZE_MAX 个窗口中的任务的执行时间。
    此处的任务执行时间是在 update_task_demand() 中通过 scale_exec_time() 进行归一化后的时间。

    RAVG_HIST_SIZE_MAX 是用来计算 demand 所需要的窗口的数量。当前版本内核中 RAVG_HIST_SIZE_MAX 默认为 5。

  3. int ridx, widx;
    用来更新 p->ravg.sum_history 所需的参数。

  4. u32 max = 0, avg, demand, pred_demand;
    max 是 p->ravg.sum_history 中的最大值;avg 是 p->ravg.sum_history 中的平均值。

    demand:通过 p->ravg.sum_history 中的值来预测下一个窗口中负载的情况,即任务可能在下一个窗口中的运行时长的归一化值。这个值通常用于任务选核、迁核。

    pred_demand:根据桶算法来预测下一个窗口中负载的情况。这个值通常用于调频。

  5. u64 sum = 0;
    sum 是 p->ravg.sum_history 中的所有值的和。

  6. u64 prev_demand;
    这个值记录上一个窗口中计算而得的 demand 值。

⑴ 判断是否更新任务信息

if (!runtime || is_idle_task(p) || exiting_task(p) || !samples)

  • 如果上个窗口内没有执行时间;
  • 如果任务是 idle 任务;
  • 如果任务正在退出;
  • 如果 samples == 0,

就直接结束,不进行更新。

⑵ 更新历史窗口数据

先通过 prev_demand = p->ravg.demand; 保存上一个窗口的 demand。

sum_history[RAVG_HIST_SIZE_MAX]

介绍一下保存历史窗口负载的数组 sum_history[RAVG_HIST_SIZE_MAX]。

默认情况下,sched_ravg_hist_size = RAVG_HIST_SIZE_MAX = 5,其中 sched_ravg_hist_size 可以根据需求来进行调整。

widx = sched_ravg_hist_size - 1; widx 是设定中最旧窗口的下标。“设定中”的意思是,如果 sched_ravg_hist_size 不等于 RAVG_HIST_SIZE_MAX,那么 sum_history[widx] 指向的就是我们调整窗口数量之后的最旧的窗口。

ridx = widx - samples; ridx 是更新之后的最旧窗口的下标。“更新之后”的意思是,我们要保证 sum_history[0] 始终是上一个窗口的执行时间,如果更新了不止一个窗口,如更新了 samples 个窗口,那么最旧的 samples 个窗口就要被舍弃,同时将前面还未被舍弃的窗口往后挪 samples 个位置,这样才能将第 0 个位置留给上一个窗口。

执行过程如下:

设 samples = 2,即本次更新了 2 个窗口
sum_history:[  a  ] [  b  ] [  c  ] [  d  ] [  e  ]
               0       1       2       3       4
               ↑       ↑       ↑       ↑       ↑
                              ridx            widx
第一个 for 循环:
	for (; ridx >= 0; --widx, --ridx) {
		hist[widx] = hist[ridx];
		// 累加剩余的旧窗口的值
		sum += hist[widx];
		// 计算最大值
		if (hist[widx] > max)
			max = hist[widx];
	}
第一次循环后结果:
sum_history:[  a  ] [  b  ] [  c  ] [  d  ] [  c  ]
               0       1       2       3       4
               ↑       ↑       ↑       ↑       ↑
                      ridx            widx
第二次循环后结果:
sum_history:[  a  ] [  b  ] [  c  ] [  b  ] [  c  ]
               0       1       2       3       4
               ↑       ↑       ↑       ↑       ↑
              ridx            widx
第三次循环后结果:
sum_history:[  a  ] [  b  ] [  a  ] [  b  ] [  c  ]
               0       1       2       3       4
               ↑       ↑       ↑       ↑       ↑
      ridx            widx
第二个 for 循环的开头将 widx 设置为 0:
sum_history:[  a  ] [  b  ] [  a  ] [  b  ] [  c  ]
               0       1       2       3       4
               ↑       ↑       ↑       ↑       ↑
      ridx    widx      
	for (widx = 0; widx < samples && widx < sched_ravg_hist_size; widx++) {
		hist[widx] = runtime;
		// 累加新窗口的值
		sum += hist[widx];
		// 计算最大值
		if (hist[widx] > max)
			max = hist[widx];
	}
第一次循环后结果:
sum_history:[ s_w ] [  b  ] [  a  ] [  b  ] [  c  ]
               0       1       2       3       4
               ↑       ↑       ↑       ↑       ↑
      ridx            widx      
第一次循环后结果:
sum_history:[ s_w ] [ s_w ] [  a  ] [  b  ] [  c  ]
               0       1       2       3       4
               ↑       ↑       ↑       ↑       ↑
      ridx                    widx
因为 samples > 1 的时候 runtime 都是 scaled_window,所以填入的都是 s_w。 

最后令 p->ravg.sum = 0; ,进行下一次 WALT 算法中 runtime 的累加。

⑶ 计算 demand

在当前版本中,WALT 算法设置了四种策略,分别通过不同的方法来计算 demand。
其中,avg = div64_u64(sum, sched_ravg_hist_size);

  1. sched_window_stats_policy == WINDOW_STATS_RECENT
    demand = runtime; 直接将 demand 设置为上一个窗口的 runtime。
  2. sched_window_stats_policy == WINDOW_STATS_MAX
    demand = max; 直接将 demand 设置为历史窗口中 runtime 的最大值。
  3. sched_window_stats_policy == WINDOW_STATS_AVG
    demand = avg; 直接将 demnad 设置为历史窗口中 runtime 的平均值。
  4. sched_window_stats_policy == WINDOW_STATS_MAX_RECENT_AVG(默认情况)
    demand = max(avg, runtime); 在上一个窗口的 runtime 和 历史窗口中 runtime 的平均值中选取最大值作为 demand。

⑷ 计算 pred_demand

根据桶算法来计算 pred_demand:pred_demand = predict_and_update_buckets(rq, p, runtime);

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

⑸ 将 demand 与 pred_demand 更新到 CPU 负载中

【WALT】调度与负载计算 中详细描述了 WALT 是如何将之前计算的 demand 和 pred_demand 更新到 CPU 负载之中的。

⑹ 更新任务信息

p->ravg.demand = demand;

p->ravg.coloc_demand = div64_u64(sum, sched_ravg_hist_size);
将历史窗口中的平均值赋进 coloc_demand 中。

p->ravg.pred_demand = pred_demand;

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

<think>好的,我现在需要帮助用户寻找与WALT相关的线程组信息,特别是在IT上下文中。首先,我需要回忆一下WALT(Window-Assisted Load Tracking)的相关知识。WALT主要用于Linux调度器中,特别是在任务负载跟踪和CPU资源分配方面。用户提到了引用[1]和引用[2],这些可能涉及到WALT在窗口翻滚时如何计算任务需求(demand)和预测需求(pred_demand),以及任务效用平均值(util_avg)的处理。 接下来,我需要确定线程组(thread group)在Linux中的定义。线程组通常指的是属于同一进程的多个线程,它们共享相同的进程ID(PID),但有不同的线程ID(TID)。在调度器中,线程组的调度策略和资源分配可能会有特殊处理,特别是当涉及到负载均衡和任务迁移时。 用户的问题是关于WALT相关的线程组信息,所以我需要查找WALT如何与线程组交互。例如,WALT可能在计算任务负载时,是否考虑线程组整体的负载,或者是否有特定的数据结构来管理线程组的调度信息。引用[3]提到了每个CPU有一个就绪队列(struct rq),这可能与线程组的调度有关,因为不同的线程可能分布在不同的CPU上,而调度器需要平衡它们的负载。 根据引用[1],WALT在窗口翻滚时计算demand和pred_demand,这可能涉及到将同一线程组中的线程的负载进行汇总或预测。例如,如果一个线程组包含多个线程,WALT可能需要聚合这些线程的负载需求,以便更准确地分配CPU资源。此外,引用[2]提到task的util_avg在最后一次出队时的值,这可能影响线程组的整体效用计算。 现在,我需要确认在Linux内核代码中,是否有与WALT相关的线程组处理部分。例如,在调度器代码中,当处理线程组时,是否调用了WALT的相关函数来更新负载或预测需求。另外,可能需要查看与cgroup(控制组)相关的部分,因为线程组可能与cgroup相关联,影响资源分配。 可能的代码路径包括kernel/sched目录下的文件,如fair.c、walt.c等。例如,在更新任务负载时,WALT可能会遍历线程组中的所有线程,累加它们的负载需求。或者在任务迁移时,调整线程组的负载统计。 如果用户需要具体的信息,可能需要指导他们查看内核代码中的相关结构体,如struct task_struct中的thread_group字段,或者与调度组(sched_group)相关的部分。此外,WALT可能为线程组维护特定的指标,如组级别的demand或util_avg,这些信息可能存储在特定的结构体中。 总结来说,用户可能需要了解WALT在处理线程组时的负载跟踪机制,包括如何聚合线程的负载、预测需求,以及这些信息如何影响调度决策。建议用户查阅内核源码中与WALT相关的线程组处理部分,特别是涉及任务组负载计算和调度的代码。</think>在 Linux 调度器框架中,WALT (Window-Assisted Load Tracking) 主要通过 `struct walt_task_group` 数据结构管理线程组级别的负载跟踪信息。该结构定义在 `kernel/sched/walt.h` 中,关键字段包括: $$tg_load = \sum_{i=1}^{n} task\_demand_i$$ 其中 $n$ 表示线程组内的任务数量,$task\_demand_i$ 是组内第 $i$ 个任务在窗口期的需求值[^1]。 ### WALT 线程组关联要素 1. **cgroup 层级集成** - 通过 `/sys/fs/cgroup/cpu` 目录下的控制组参数实现层级化负载跟踪 - `cpu.walt_util` 文件节点暴露线程组聚合利用率 2. **调度决策影响** ```c // kernel/sched/fair.c static void update_cpu_busy_time(struct task_struct *p, struct rq *rq) { struct walt_task_group *wtg = p->wtg; if (wtg) { wtg->demand += p->walt_stats.demand; update_tg_load_avg(wtg); } } ``` 该代码段展示了任务负载如何向上聚合到线程组[^2] 3. **跨 CPU 负载平衡** - 就绪队列 `struct rq` 的 `avg_walt_tg_load` 字段记录线程组负载 - 在 `find_busiest_queue()` 中用于比较不同 CPU 的负载压力[^3]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值