调度器45—wake_affine
基于 Linux-5.10
一、wake_affine 简介
1. 背景
在进程唤醒选核路径中, wake_affine 倾向于将被唤醒进程(wakee)尽可能安排在 waker所在 CPU 上, 这样考虑的原因是: 有唤醒关系的进程是相互关联的, 尽可能地运行在具有 cache 共享的调度域中,
这样可以获得一些 chache-hit 带来的性能提升。
将 wakee 主要唤醒在 waker CPU 上, 必然造成 waker 和 wakee 的资源竞争. 特别是对于 1:N 场景下(一个waker唤醒多个wakee), wake_affine 会导致 waker 进程饥饿。
为了解决这一情况,COMMIT 62470419e993 "sched: Implement smarter wake-affine logic" 实现了一种智能 wake-affine 的优化机制. 用于 wake_flips 的巧妙方式, 识别出 1:N 等复杂唤醒模型, 只有在认
为 wake_affine 能提升性能时(want_affine)才进行 wake_affine.
2. COMMIT 62470419e993 翻译
kernel-5.10$ git show 62470419e993
commit 62470419e993f8d9d93db0effd3af4296ecb79a5
Author: Michael Wang <wangyun@linux.vnet.ibm.com>
Date: Thu Jul 4 12:55:51 2013 +0800
sched: Implement smarter wake-affine logic
sched:实现更智能的 wake-affine 逻辑
wake-affine调度器功能目前总是试图将 wakee(被唤醒者) 拉近 waker(唤醒者)。 从理论上讲,如果唤醒者的 CPU 为被唤醒者缓存热数据,这应该是有益的,并且在极端乒乓的高上下文切换率的情况下也是有益的。
测试表明它可以使 hackbench 受益高达 15%。
但是,该功能有些盲目,某些工作负载(例如 pgbench)会受到影响。 它在算法上也很耗时。
测试表明,它可以将 pgbench 损坏高达 50%——远远超过它在最佳情况下带来的好处。
所以 wake-affine 应该更聪明,它应该意识到什么时候应该停止它在寻找合适的 CPU 来唤醒时的吃力不讨好的努力。
此补丁引入了“wakee_flips”,每次任务翻转(切换)其唤醒目标时都会增加(之前唤醒的是A这次唤醒的不是A,则加1)。
所以一个高的'wakee_flips'值意味着任务有多个 wakee(1:N),数字越大,唤醒频率越高。###############
现在在决定是否拉取时,要注意'wakee_flips'高的 wakee,拉取这样的任务可能对 wakee 有利。 也暗示 waker 之后将面临残酷的竞争,可能非常残酷或非常快因此遭受损失,取决于'wakee_flips'背后的故事。
此外,如果 waker 也有很高的 'wakee_flips',这意味着多个任务依赖它(也有很多任务唤醒 waker),那么 waker 的更高延迟会损坏所有这些任务,因此拉动 wakee 似乎是一个糟糕的交易。
因此,当'waker->wakee_flips / wakee->wakee_flips'变得越来越高时,拉动的代价似乎越来越高。
因此,该补丁有助于 wake-affine 功能在以下情况下停止其拉动工作:
wakee->wakee_flips > factor && waker->wakee_flips > (factor * wakee->wakee_flips)
这里的'factor'是当前CPU的NUMA节点中的CPU数量,因此更大的节点将导致更多的拉动,因为试验变得更加严格。
应用补丁后,pgbench 显示出高达 40% 的改进并且没有退化。
使用 12 个 cpu x86 服务器和 tip 3.10.0-rc7 进行测试。
最后一列中的百分比突出显示了获胜最多的领域,所有其他领域也得到了改善:
pgbench base smart
| db_size | clients | tps | | tps |
+---------+---------+-------+ +-------+
| 22 MB | 1 | 10598 | | 10796 |
| 22 MB | 2 | 21257 | | 21336 |
| 22 MB | 4 | 41386 | | 41622 |
| 22 MB | 8 | 51253 | | 57932 |
| 22 MB | 12 | 48570 | | 54000 |
| 22 MB | 16 | 46748 | | 55982 | +19.75%
| 22 MB | 24 | 44346 | | 55847 | +25.93%
| 22 MB | 32 | 43460 | | 54614 | +25.66%
| 7484 MB | 1 | 8951 | | 9193 |
| 7484 MB | 2 | 19233 | | 19240 |
| 7484 MB | 4 | 37239 | | 37302 |
| 7484 MB | 8 | 46087 | | 50018 |
| 7484 MB | 12 | 42054 | | 48763 |
| 7484 MB | 16 | 40765 | | 51633 | +26.66%
| 7484 MB | 24 | 37651 | | 52377 | +39.11%
| 7484 MB | 32 | 37056 | | 51108 | +37.92%
| 15 GB | 1 | 8845 | | 9104 |
| 15 GB | 2 | 19094 | | 19162 |
| 15 GB | 4 | 36979 | | 36983 |
| 15 GB | 8 | 46087 | | 49977 |
| 15 GB | 12 | 41901 | | 48591 |
| 15 GB | 16 | 40147 | | 50651 | +26.16%
| 15 GB | 24 | 37250 | | 52365 | +40.58%
| 15 GB | 32 | 36470 | | 50015 | +37.14%
二、wake_affine 实现
只在fair.c中的选核路径中使用。
1. 相关函数
static void record_wakee(struct task_struct *p)
{
/* 对 wakee_flips 每秒衰减50% */
if (time_after(jiffies, current->wakee_flip_decay_ts + HZ)) {
current->wakee_flips >>= 1;
current->wakee_flip_decay_ts = jiffies;
} /* 唤醒的任务变了,则 wakee_flips 加1 */
if (current->last_wakee != p) {
current->last_wakee = p;
current->wakee_flips++;
}
} static int wake_wide(struct task_struct *p)
{
unsigned int master = current->wakee_flips; //waker的wakee翻转次数
unsigned int slave = p->wakee_flips; //wakee的wakee翻转次数
int factor = __this_cpu_read(sd_llc_size); //本CPU所在Cluster的CPU个数 if (master < slave)
swap(master, slave); /* 此次的waker和wakee的唤醒翻转有一个较小则返回0。或用的是否恰当?#### */
if (slave < factor || master < slave * factor)
return 0; return 1;
} static int wake_affine(struct sched_domain *sd, struct task_struct *p, int this_cpu, int prev_cpu, int sync)
{
int target = nr_cpumask_bits; /* WA_IDLE 默认为真 */
if (sched_feat(WA_IDLE))
/* 根据idle和sync情况看能否选this_cpu或prev_cpu */
target = wake_affine_idle(this_cpu, prev_cpu, sync); /* WA_WEIGHT 默认为真 */
if (sched_feat(WA_WEIGHT) && target == nr_cpumask_bits)
/* 若上面没有选到,根据sync和负载看能否选this_cpu */
target = wake_affine_weight(sd, p, this_cpu, prev_cpu, sync); /* 走到这里会有统计 */
schedstat_inc(p->se.statistics.nr_wakeups_affine_attempts);
/* 若上面都没选到,则选prev_cpu */
if (target == nr_cpumask_bits)
return prev_cpu; schedstat_inc(sd->ttwu_move_affine);
/* 走到这里会有统计 */
schedstat_inc(p->se.statistics.nr_wakeups_affine); return target;
}
2. 调用路径:
static int select_task_rq_fair(struct task_struct *p, int prev_cpu, int sd_flag, int wake_flags)
{
struct sched_domain *tmp, *sd = NULL;
int cpu = smp_processor_id();
int new_cpu = prev_cpu;
int want_affine = 0;
int sync = (wake_flags & WF_SYNC) && !(current->flags & PF_EXITING); ... /* 必须是唤醒选核路径 */
if (sd_flag & SD_BALANCE_WAKE) {
record_wakee(p);
...
/*
* 此次的waker和wakee的唤醒翻转有一个较小,且任务p允许运行在此cpu上,
* 则表示需要 wake_affine。
* want_affine为真下面才会遍历到DIE层级.
*/
want_affine = !wake_wide(p) && cpumask_test_cpu(cpu, p->cpus_ptr);
}
... /* 遍历当前cpu所在调度域,从MC到DIE遍历 */
for_each_domain(cpu, tmp) {
/*
* SD_WAKE_AFFINE: MC和DIE层级都有此标志。
* MC层级sd->span包含此cluster的所有CPU,DIE层级的sd->span包含系统中的所有CPU。
* 需要 wake_affine 且 任务之前运行的CPU和当前CPU属于同一个cluster.
*/
if (want_affine && (tmp->flags & SD_WAKE_AFFINE) && cpumask_test_cpu(prev_cpu, sched_domain_span(tmp))) {
if (cpu != prev_cpu)
new_cpu = wake_affine(tmp, p, cpu, prev_cpu, sync);
break;
} ... if (!want_affine)
break;
} ... return new_cpu;
}
可见原生内核下 wake_affine 对选核影响还挺大的,主要选与当前CPU同Cluster的CPU作为备选CPU。
三、相关DEBUG接口
1. 查看走入wake_affine选核的次数占比
# cat /proc/1265/sched
se.statistics.nr_wakeups : 28
se.statistics.nr_wakeups_sync : 1
se.statistics.nr_wakeups_migrate : 1
se.statistics.nr_wakeups_local : 16
se.statistics.nr_wakeups_remote : 12
se.statistics.nr_wakeups_affine : 0
se.statistics.nr_wakeups_affine_attempts : 10
...
nr_switches : 2
nr_voluntary_switches : 2
nr_involuntary_switches : 0
四、总结
1. wake_affine 主要是想将wakee拉到waker所在的CPU上以便于cache-hit,但是对于1:N的唤醒模型会导致wake饥饿,于是映入 wake_flip 的概念。
2. wake_wide() 用于表示是否需要 wake-affine,使用的却是或,是否合理可能值得再商榷。
调度器45—wake_affine的更多相关文章
- CDN调度器HAProxy、Nginx、Varnish
http://www.ttlsa.com/web/the-cdn-scheduler-nginx-haproxy-varnish/ CDN功能如下:1.将全网IP分为若干个IP段组,分组的依据通常是运 ...
- URL 调度器(URL dispatcher)
URL 调度器(URL dispatcher) 在刚开始接触 django 的时候, 我们尝试着从各种入门文档中创建一个自己的 django 项目, 需要在 mysite.urls.py 中配置 UR ...
- CFS调度器(1)-基本原理
首先需要思考的问题是:什么是调度器(scheduler)?调度器的作用是什么?调度器是一个操作系统的核心部分.可以比作是CPU时间的管理员.调度器主要负责选择某些就绪的进程来执行.不同的调度器根据不同 ...
- Linux进程管理 (2)CFS调度器
关键词: 目录: Linux进程管理 (1)进程的诞生 Linux进程管理 (2)CFS调度器 Linux进程管理 (3)SMP负载均衡 Linux进程管理 (4)HMP调度器 Linux进程管理 ( ...
- 【原创】(五)Linux进程调度-CFS调度器
背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...
- Web集群调度器-Haproxy
Web集群调度器-Haproxy 目录 Web集群调度器-Haproxy 一.Web集群调度器 1.常用的Web集群调度器 2. Haproxy应用分析 3. Haproxy的主要特性 4. 常用集群 ...
- 大数据之Yarn——Capacity调度器概念以及配置
试想一下,你现在所在的公司有一个hadoop的集群.但是A项目组经常做一些定时的BI报表,B项目组则经常使用一些软件做一些临时需求.那么他们肯定会遇到同时提交任务的场景,这个时候到底如何分配资源满足这 ...
- [Spring]支持注解的Spring调度器
概述 如果想在Spring中使用任务调度功能,除了集成调度框架Quartz这种方式,也可以使用Spring自己的调度任务框架. 使用Spring的调度框架,优点是:支持注解(@Scheduler),可 ...
- 编写简单的ramdisk(选择IO调度器)
前言 目前linux中包含anticipatory.cfq.deadline和noop这4个I/O调度器.2.6.18之前的linux默认使用anticipatory,而之后的默认使用cfq.我们在前 ...
- Erlang/OTP 17.0-rc1 新引入的"脏调度器"浅析
最近在做一些和 NIF 有关的事情,看到 OTP 团队发布的 17 rc1 引入了一个新的特性“脏调度器”,为的是解决 NIF 运行时间过长耗死调度器的问题.本文首先简单介绍脏调度器机制的用法,然后简 ...
随机推荐
- 什么是整体设备效率(OEE)?
整体设备效率 (OEE) 用于监控制造效率.得到的OEE百分比是通用的,可以跨不同行业和流程进行比较. OEE可用性 OEE可用性=实际运行时间/生产时间 OEE可用性是实际运行时间和计划生产时间之间 ...
- js原型链污染详解
前言 之前打某湖论剑,两道js的题,给我整懵逼了,发现以前都没对js做过多少研究,趁着被毒打了,先研究一波js原型链,未雨绸缪. 基础 protype 首先我们研究js原型链,得搞明白原型是什么,这里 ...
- JZOJ 3992.Christmas
题目大意 给定一个数列,支持区间加一个数和区间取 \(max\),询问单点询问数值和它被更改的次数 思路 模板的吉司机线段树 维护区间最小值和严格次小值以及最小值的个数 针对询问维护区间和以及区间修改 ...
- 记录一个cpu彪高的BUG处理--jvm调优
业务场景:游戏行业,N个服务器,要进行大批量的合服处理,玩家数据会上升,从新整理和服务器的分配情况和逻辑处理,正常开发后,当天白天正常,然后晚上高峰期开始玩家频繁反馈无法登录~~~ 处理逻辑: 优先确 ...
- 自定义StringByteLength
using HKElectric.ESafety.Utilities; using System.ComponentModel.DataAnnotations; namespace HKElectri ...
- Qt中的多窗体编程(续一)
在前面一节中,已经把所有需要的窗体都创建好了,下面将依次实现预设的功能. 一.实现点击菜单打开模式子窗体的功能. 1.在编辑模式下双击Forms下的"mainWindow.ui", ...
- mysql 根据父id查询下级所有数据
select id,apply_resource_name from ( select t1.id,t1.apply_resource_name, if(find_in_set(parent_id, ...
- svn up 更新失败 Working copy path '' does not exist in repository
转网上全都是,亲测有效,用于本地更新错误可以这么搞: Working copy path '' does not exist in repositorysvn up 更新失败 可以选择 更新深度 为 ...
- 在dockerfile使用定时任务遇见的坑
使用dockerfile做容器时没有问题,但是要在里面加上定时任务时遇见了情况 dockerfile 中 写法 CMD cron 情况1:容器运行起来后 定时任务不生效 情况2:容器起来后定时任务生 ...
- Java控制流程(复习)
流程控制语句 流程控制语句包括:顺序结构,分支结构,循环结构 分支结构 if语句: 第一种: if(关系表达式){ 语句体 } else{ 语句体2 } 第二种: if (){ } else if ( ...