亲和性和隔离CPU

一 亲和性

高性能的应用有个技巧就是做任务和cpu核心的绑定, 即设置CPU亲和性,绑定之后就可以让这个任务尽可能在这个核心上长时间运行,而不被切换到其他核心上去.

为什么要设置亲和性那, 要想让程序高性能运行,必须利用好高速缓存,而像L1,L2这类高速缓存,在进行定时切换任务的时候,必然会失效,从而影响程序的性能;另外减少了调度,也提升了此任务的实时性。

是不是设置了任务的亲和性就不会有其他任务调度到这个核心上了嘛?也不是的,亲和性也只是尽力保证任务长时间在这个核心上运行,比如一个cpu一个核心,如果一个任务绑定了核心,那岂不是其他任务就不要执行了,还有个办法就是做CPU核心的隔离。隔离CPU核心即这些CPU不再被用户空间的进程使用,内核仍然可以用,从而禁止用户空间的进程(除非手动设置affinity)调度到DPDK的核心上去.

二 隔离CPU

2.1 Linux内核隔离CPU

vim /etc/default/grub
#编辑
GRUB_CMDLINE_LINUX="crashkernel=auto resume=/dev/mapper/cl-swap rd.lvm.lv=cl/root rd.lvm.lv=cl/swap rhgb quiet  iommu=pt intel_iommu=on   transparent_hugepage=never default_hugepagesz=2M hugepagesz=2M hugepages=1024 isolcpus=4,5,6,7"

增加的选项,即隔离CPU4,5,6,7核心。

 isolcpus=4,5,6,7

然后执行:

grub2-mkconfig -o /boot/grub2/grub.cfg
reboot -h now

重启后就生效了,现在可以通过tophtop命令,查看cpu只有0-3有任务,4-7上没有任务:

%Cpu0  :  0.0 us,  0.0 sy,  0.0 ni, 99.1 id,  0.0 wa,  0.9 hi,  0.0 si,  0.0 st
%Cpu1  :  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu2  :  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu3  :  0.8 us,  0.8 sy,  0.0 ni, 98.3 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu4  :  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu5  :  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu6  :  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu7  :  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st

写个shell来验证下:

while true; do echo hello>/dev/null; done;

CPU调度情况:

%Cpu0  :  7.7 us,  3.3 sy,  0.0 ni, 87.0 id,  0.0 wa,  1.0 hi,  1.0 si,  0.0 st
%Cpu1  : 15.1 us,  7.0 sy,  0.0 ni, 75.5 id,  0.0 wa,  0.3 hi,  2.0 si,  0.0 st
%Cpu2  : 17.3 us,  9.0 sy,  0.0 ni, 72.0 id,  0.0 wa,  0.3 hi,  1.3 si,  0.0 st
%Cpu3  : 23.6 us, 11.0 sy,  0.0 ni, 62.5 id,  0.0 wa,  0.3 hi,  2.7 si,  0.0 st
%Cpu4  :  0.0 us,  0.0 sy,  0.0 ni, 99.7 id,  0.0 wa,  0.3 hi,  0.0 si,  0.0 st
%Cpu5  :  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu6  :  0.0 us,  0.0 sy,  0.0 ni, 99.7 id,  0.0 wa,  0.3 hi,  0.0 si,  0.0 st
%Cpu7  :  0.0 us,  0.0 sy,  0.0 ni, 99.7 id,  0.0 wa,  0.3 hi,  0.0 si,  0.0 st

2.2 查看进程绑定的核心

有没有命令查看进程绑定的核心嘛,当然是有的,命令如下:

[root@localhost spiderFlow]# ps -eo pid,args:50,psr 
    PID COMMAND                                            PSR
      1 /usr/lib/systemd/systemd --switched-root --system    0
      2 [kthreadd]                                           2
      3 [rcu_gp]                                             0
      4 [rcu_par_gp]                                         0
      6 [kworker/0:0H-kblockd]                               0
      9 [mm_percpu_wq]                                       2
       ....
     48 [ksoftirqd/6]                                        6
     49 [kworker/6:0-events]                                 6
     50 [kworker/6:0H]                                       6
     51 [cpuhp/7]                                            7
     52 [watchdog/7]                                         7

PSR:当前分配给该进程的处理器,即进程在哪颗CPU上运行。
可以看到内核任务,也就是用[]扩起来的任务可以运行在4,5,6,7,而用户任务不能运行在4,5,6,7上。
也就是说isolcpus只能隔离用户任务,不能隔离内核任务。 运行在4,5,6,7核心上的任务多是中断任务,我们仍然可以命令,修改中断不跑在这些核心上。

2.3 设置任务的亲和性

刚才我们聊到,隔离的cpu 上可以跑内核任务,也可以跑设置亲和性的任务,那么如何设置任务的cpu亲和性那。
通过sched_setaffinity函数,可以设置CPU的亲和性,这个函数可以决定线程在指定的 cpu 中运行。在多进程系统中,适当的为线程指定 cpu 可以提升效率,比如,指定线程 A 在 cpu 0 中运行,限定其他线程在其他 cpu 运行,那么线程 A 的执行速度和实时性就可以得到最大程度的保障。另外,cpu 也是有高速缓存的,执行线程频繁切换 cpu 也会导致缓存的命中率大大降低,同样影响执行效率。

#include <sched.h>
#include <stdio.h>

int main()
{
    cpu_set_t mask;
    CPU_ZERO(&mask);
    CPU_SET(1, &mask);
    if (sched_setaffinity(0, sizeof(cpu_set_t), &mask) == -1)
        printf("sched_setaffinity set error!");
    while (1) {
    }
    return 0;
}

将进程绑定到cpu1上,编译和查看结果:

g++ setaff.c -o setaf
[root@localhost ctest]# ./setaf 

# 在另一个中断查看:
[root@localhost ~]# ps -eo pid,args:50,psr |grep set
  46061 ./setaf                                              1

从结果来看是cpu号,从1号开始,可以修改下,CPU_SET(5,&mask);,然后编译后重新运行:

[root@localhost ~]# taskset -p `pgrep setaf`
pid 46683 的当前亲和力掩码:20


>>> bin(0x20)
'0b100000'

[root@localhost ~]# ps -eo pid,args:50,psr |grep set
  46764 ./setaf                                              5

可以看到隔离的线程可以通过这种绑定的形式进行进程和cpu核心的绑定。

sched_setaffinity(pid_t pid, unsigned int cpusetsize, cpu_set_t *mask)
该函数设置进程为pid的这个进程,让它运行在mask所设定的CPU上.如果pid的值为0,则表示指定的是当前进程,使当前进程运行在mask所设定的那些CPU上.第二个参数cpusetsize是mask所指定的数的长度.通常设定为sizeof(cpu_set_t).如果当前pid所指定的进程此时没有运行在mask所指定的任意一个CPU上,则该指定的进程会从其它CPU上迁移到mask的指定的一个CPU上运行.

sched_getaffinity(pid_t pid, unsigned int cpusetsize, cpu_set_t *mask)
该函数获得pid所指示的进程的CPU位掩码,并将该掩码返回到mask所指向的结构中.即获得指定pid当前可以运行在哪些CPU上.同样,如果pid的值为0.也表示的是当前进程

设置线程亲和性
和进程设置cpu亲和性比较像,在网上找到一段代码:

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>

int GetCpuCount()
{
    return (int)sysconf(_SC_NPROCESSORS_ONLN);
}

void* thread_fun(void*)
{
    int i;
    while (1) {
        i = 0;
    }

    return NULL;
}

int main()
{
    int cpu_num = 0;
    cpu_num = GetCpuCount();
    printf("The number of cpu is %d\n", cpu_num);

    pthread_t t1;
    pthread_t t2;
    pthread_attr_t attr1;
    pthread_attr_t attr2;

    pthread_attr_init(&attr1);
    pthread_attr_init(&attr2);

    cpu_set_t cpu_info;
    CPU_ZERO(&cpu_info);
    CPU_SET(4, &cpu_info);
    if (0 != pthread_attr_setaffinity_np(&attr1, sizeof(cpu_set_t), &cpu_info)) {
        printf("set affinity failed");
        return -1;
    }
    CPU_ZERO(&cpu_info);
    CPU_SET(5, &cpu_info);
    if (0 != pthread_attr_setaffinity_np(&attr2, sizeof(cpu_set_t), &cpu_info)) {
        printf("set affinity failed");
    }
    if (0 != pthread_create(&t1, &attr1, thread_fun, NULL)) {
        printf("create thread 1 error\n");
        return -1;
    }
    if (0 != pthread_create(&t2, &attr2, thread_fun, NULL)) {
        printf("create thread 2 error\n");
        return -1;
    }
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
}

编译运行:

g++ thsetaff.c -o thset -lpthread

分析下绑定核心:

[root@localhost ~]# top -Hp 54962
    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND                                                                                                             
  54963 root      20   0   32328   1872   1716 R  99.9   0.0   0:23.09 thset                                                                                                               
  54964 root      20   0   32328   1872   1716 R  99.9   0.0   0:23.09 thset                                                                                                               
  54962 root      20   0   32328   1872   1716 S   0.0   0.0   0:00.00 thset   

[root@localhost ~]# taskset -p 54963
pid 54963 的当前亲和力掩码:10
[root@localhost ~]# taskset -p 54964
pid 54964 的当前亲和力掩码:20
[root@localhost ~]# taskset -p 54962
pid 54962 的当前亲和力掩码:f
[root@localhost ~]# python3.6
Python 3.6.8 (default, Aug 24 2020, 17:57:11) 
[GCC 8.3.1 20191121 (Red Hat 8.3.1-5)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> bin(0x20)
'0b100000'
>>> bin(0x10)
'0b10000'
>>> bin(0xf)
'0b1111'

三 临时设置程序亲和性

taskset可以更改进程的cpu亲和性。

taskset -h
用法:taskset [选项] [掩码 | cpu列表] [pid|命令 [参数...]]


显示或更改某个进程的 CPU 亲和力。

Options:
 -a, --all-tasks         operate on all the tasks (threads) for a given pid
 -p, --pid               operate on existing given pid
 -c, --cpu-list          display and specify cpus in list format
 -h, --help              display this help
 -V, --version           display version

默认行为是运行一条新命令:
    taskset 03 sshd -b 1024
您可以获取现有任务的掩码:
    taskset -p 700
或设置掩码:
    taskset -p 03 700
使用逗号分隔的列表格式而不是掩码:
    taskset -pc 0,3,7-11 700
列表格式中的范围可以带一个跨度参数:
    例如 0-31:2 与掩码 0x55555555 等效

我们简单编写个程序,运行:

#include <stdio.h>

int main()
{
    while (1) {
    }
    return 0;
}


g++ loop.c -o loop
./loop

更改loop程序的亲和性:

[root@localhost ~]# ps -ef|grep loop
root       45894   45694 99 22:46 pts/4    00:00:09 ./loop
root       45939   45898  0 22:46 pts/6    00:00:00 grep --color=auto loop
[root@localhost ~]# taskset -pc 4,5 45894
pid 45894 的当前亲和力列表:0-3
pid 45894 的新亲和力列表:4,5

[root@localhost ~]# ps -eo pid,args:50,psr |grep loop
  45894 ./loop               

再次查看loop运行的核心,已经运行到了cpu4上。

[root@localhost ~]# taskset -p 1 `pgrep loop`
pid 45894 的当前亲和力掩码:30
pid 45894 的新亲和力掩码:1

注意这里面是掩码的方式,开始时候掩码为30即0011 0000 刚好对应刚才cpu列表4和5.
改成了1 掩码为0000 0001,就只能运行在cpu0上,验证:

[root@localhost ~]# ps -eo pid,args:50,psr |grep loop
  45894 ./loop                                               0

四 去掉Time Tick中断

如果核心上只跑一个线程,就没有必要用TimeTick进行中断,所以可以继续更改去掉Time Tick中断。

GRUB_CMDLINE_LINUX="crashkernel=auto resume=/dev/mapper/cl-swap rd.lvm.lv=cl/root rd.lvm.lv=cl/swap rhgb quiet  iommu=pt intel_iommu=on   transparent_hugepage=never default_hugepagesz=2M hugepagesz=2M hugepages=1024 isolcpus=4,5,6,7 nohz_full=4,5,6,7"
 
#更新和重启
grub2-mkconfig -o /boot/grub2/grub.cfg
reboot -h now

添加:

nohz_full=4,5,6,7

重启后,多次运行cat /proc/interrupts发现时钟中断已经停止,如果多个线程都绑定到隔离的cpu上,仍然会启动tick。

 LOC:      97153      17867      18737      21131        840        842        836        843   Local timer interrupts

五 诗词欣赏

将进酒 

[李白] [〔唐代〕]

君不见黄河之水天上来,奔流到海不复回。
君不见高堂明镜悲白发,朝如青丝暮成雪。
人生得意须尽欢,莫使金樽空对月。
天生我材必有用,千金散尽还复来。
烹羊宰牛且为乐,会须一饮三百杯。
岑夫子,丹丘生,将进酒,杯莫停。
与君歌一曲,请君为我倾耳听。(倾耳听 一作:侧耳听)
钟鼓馔玉不足贵,但愿长醉不愿醒。(不足贵 一作:何足贵;不愿醒 一作:不复醒)
古来圣贤皆寂寞,惟有饮者留其名。(古来 一作:自古;惟 通:唯)
陈王昔时宴平乐,斗酒十千恣欢谑。
主人何为言少钱,径须沽取对君酌。
五花马、千金裘,呼儿将出换美酒,与尔同销万古愁。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容