一、孤立CPU

1、孤立CPU简介

针对CPU密集型的任务,CPU负载较高,推荐设置CPU Affinity,以提高任务执行效率,避免CPU进行上下文切换,提高CPU Cache命中率

默认情况下,Linux内核调度器可以使用任意CPU核心,如果特定任务(进程/线程)需要独占一个CPU核心并且不想让其它任务(进程/线程)使用时,可以把指定CPU孤立出来,不让其它进程使用。

2、孤立CPU的特点

孤立CPU可以有效地提高孤立CPU上任务运行的实时性,在保证孤立CPU上任务运行的同时会减少了其它任务可以运行的CPU资源,因此需要对计算机CPU资源进行规划。

3、孤立CPU设置

Linux Kernel中isolcpus启动参数用于在SMP均衡调度算法中将一个或多个CPU孤立出来,通过CPU Affinity设置将指定进程置于孤立CPU运行。

isolcpus= cpu_number [, cpu_number ,...]

(1)修改grub配置文件

默认grub配置为/etc/default/grub,GRUB_CMDLINE_LINUX值中加入isolcpus=11,12,13,14,15,所有CPU核心必须用逗号进行分隔,不支持区域范围。

GRUB_CMDLINE_LINUX="isolcpus=1,2 crashkernel=auto rd.lvm.lv=rhel/root rd.lvm.lv=rhel/swap rhgb quiet"

(2)更新grub

重新生成grub引导文件/boot/grub/grub.cfg,重启系统生效。


  1. // Ubuntu
  2. update-grub
  3. update-grub2
  4. // CentOS 7
  5. grub-mkconfig -o /boot/grub2/grub.cfg

一旦Linux Kernel使用isolcpus参数启动,Linux Kernel任务均衡调度器不会再将进程调度给指定CPU核心,用户通常需要使用taskset或cset命令将进程绑定到CPU核心。

二、CPU绑定简介

1、CPU核心简介

超线程技术(Hyper-Threading)是利用特殊的硬件指令,把两个逻辑内核(CPU core)模拟成两个物理芯片,让单个处理器都能使用线程级并行计算,进而兼容多线程操作系统和软件,减少了CPU的闲置时间,提高CPU的运行效率。

物理CPU是计算机主板上安装的CPU。

逻辑CPU是一颗物理CPU上的物理CPU核心,通常一颗物理CPU有多颗物理内核,即有多个逻辑CPU。如果支持Intel超线程技术(HT),可以在逻辑CPU上再分一倍数量的CPU Core。

cat /proc/cpuinfo|grep "physical id"|sort -u|wc -l

查看物理CPU个数

cat /proc/cpuinfo|grep "cpu cores"|uniq

查看每个物理CPU中core的个数(即核数)

cat /proc/cpuinfo|grep "processor"|wc -l

查看逻辑CPU的个数

cat /proc/cpuinfo|grep "name"|cut -f2 -d:|uniq

查看CPU的名称型号

ps -eo pid,args,psr

查看进程运行的逻辑CPU

2、CPU绑定简介

CPU绑定是对进程或线程设置相应的CPU Affinity,确保进程或线程只会在设置有相应标志位的CPU上运行,进而提高应用程序对CPU的使用效率。如果应用可以在多个CPU上运行,操作系统会在CPU之间频繁切换应用,引起CPU缓存失效,降低缓存的命中率,导致CPU使用效率下降。使用CPU绑定技术可以在一定程度上会避免CPU Cache失效,提升系统性能。

CPU affinity是一种调度属性(scheduler property),可以将一个进程绑定到一个或一组CPU上。

在SMP(Symmetric Multi-Processing对称多处理)架构下,Linux调度器(scheduler)会根据CPU affinity设置让指定的进程运行在绑定的CPU上,而不会在其它CPU上运行.,

Linux调度器同样支持自然CPU亲和性(natural CPU affinity): 调度器会试图保持进程在相同的CPU上运行,进程通常不会在处理器之间频繁迁移,进程迁移的频率小就意味着产生的负载小。

因为程序的作者比调度器更了解程序,所以我们可以手动地为其分配CPU核,而不会过多地占用CPU0,或是让我们关键进程和一堆别的进程挤在一起,所有设置CPU亲和性可以使某些程序提高性能。

Linux内核进程调度器天生具有软CPU亲和性(affinity)特性,进程通常不会在处理器之间频繁迁移。

查看所有进程CPU分配情况

ps -eo pid,cmd,psr

查看进程的所有线程的CPU分配情况

ps -To 'pid,lwp,psr,cmd' -p [PID]

3、CPU绑定的特点

将进程/线程与CPU绑定,可以显著提高CPU Cache命中率,从而减少内存访问损耗,提高应用性能。我觉得在NUMA架构下,这个操作对系统运行速度的提升有较大的意义,而在SMP架构下,这个提升可能就比较小。这主要是因为两者对于cache、总线这些资源的分配使用方式不同造成的,NUMA架构下,每个CPU有自己的一套资源体系;SMP架构下,每个核心还是需要共享这些资源的。

每个CPU核运行一个进程的时候,由于每个进程的资源都独立,所以CPU核心之间切换的时候无需考虑上下文;每个CPU核运行一个线程的时候,有时线程之间需要共享资源,所以共享资源必须从CPU的一个核心被复制到另外一个核心,造成额外开销。

4、taskset绑定进程

yum install util-linux

安装taskset工具

taskset [options] [mask] -p pid

查看进程的CPU Affinity,使用-p选项指定PID,默认打印十六进制数,如果指定-cp选项打印CPU核列表。3的二进制形式是0011,对应-cp打印0和1,表示进程只能运行在CPU的第0个核和第1个核。

taskset -c -p pid

查看指定进程的CPU Affinity


  1. taskset -p mask pid
  2. taskset -c [CPU NUMBER] -p PID

设置指定进程的CPU Affinity,对于孤立CPU,只有第一个CPU有效。

使用11,12,13,14,15号CPU运行进程


  1. taskset -c 11,12,13,14,15 python xx.py
  2. taskset -c 11-15 python xx.py

Docker容器中,孤立CPU仍然可以被使用;创建Docker容器时可以通过参数--cpuset-cpus指定容器只能使用哪些CPU,实现Docker容器内孤立CPU。

5、cset绑定进程

cset set --cpu CPU CPUSET NAME

定义CPU核心集合,对于独立CPU,只有第一个CPU核心有效。

cset proc --move --pid=PID,...,PID --toset=CPUSET NAME

移动多个进程到指定CPU集合

三、进程绑定CPU

1、系统调用API


  1. #define _GNU_SOURCE        
  2. #include <sched.h>
  3. int sched_setaffinity(pid_t pid, size_t cpusetsize, cpu_set_t *mask);
  4. int sched_getaffinity(pid_t pid, size_t cpusetsize, cpu_set_t *mask);

参数:

pid:进程号,如果pid值为0,则表示指定当前进程。

cpusetsize:mask参数所指定数的长度,通常设定为sizeof(cpu_set_t)。

mask:CPU掩码

2、编程实现


  1. #include<stdlib.h>
  2. #include<stdio.h>
  3. #include<sys/types.h>
  4. #include<sys/sysinfo.h>
  5. #include<unistd.h>
  6. #define __USE_GNU
  7. #include<sched.h>
  8. #include<ctype.h>
  9. #include<string.h>
  10. #include<pthread.h>
  11. #define THREAD_MAX_NUM 10  //1个CPU内的最多进程数
  12. int CPU_NUM = 0;  //cpu中核数
  13. int CPU = 3; // CPU编号
  14. void* threadFun(void* arg)
  15. {
  16.     cpu_set_t mask;  //CPU核的集合
  17.     CPU_ZERO(&mask);
  18.     // set CPU MASK
  19.     CPU_SET(CPU, &mask);
  20.     //设置当前进程的CPU Affinity
  21.     if (sched_setaffinity(0, sizeof(mask), &mask) == -1)
  22.     {
  23.         printf("warning: could not set CPU affinity, continuing...\n");
  24.     }
  25.     cpu_set_t affinity;   //获取在集合中的CPU
  26.     CPU_ZERO(&affinity);
  27.     // 获取当前进程的CPU Affinity
  28.     if (sched_getaffinity(0, sizeof(affinity), &affinity) == -1)
  29.     {
  30.         printf("warning: cound not get Process affinity, continuing...\n");
  31.     }
  32.     int i = 0;
  33.     for (i = 0; i < CPU_NUM; i++)
  34.     {
  35.         if (CPU_ISSET(i, &affinity))//判断线程与哪个CPU有亲和力
  36.         {
  37.             printf("this thread %d is running processor : %d\n", *((int*)arg), i);
  38.         }
  39.     }
  40.     return NULL;
  41. }
  42. int main(int argc, char* argv[])
  43. {
  44.     int tid[THREAD_MAX_NUM];
  45.     pthread_t thread[THREAD_MAX_NUM];
  46.     // 获取核数
  47.     CPU_NUM = sysconf(_SC_NPROCESSORS_CONF);
  48.     printf("System has %i processor(s). \n", CPU_NUM);
  49.     int i = 0;
  50.     for(i=0;i<THREAD_MAX_NUM;i++)
  51.     {
  52.         tid[i] = i;
  53.         pthread_create(&thread[i],NULL,threadFun, &tid[i]);
  54.     }
  55.     for(i=0; i< THREAD_MAX_NUM; i++)
  56.     {
  57.         pthread_join(thread[i],NULL);
  58.     }
  59.     return 0;
  60. }

编译:

gcc -o test test.c -pthread

运行结果:


  1. System has 4 processor(s).
  2. this thread 1 is running processor : 3
  3. this thread 0 is running processor : 3
  4. this thread 4 is running processor : 3
  5. this thread 9 is running processor : 3
  6. this thread 7 is running processor : 3
  7. this thread 5 is running processor : 3
  8. this thread 6 is running processor : 3
  9. this thread 8 is running processor : 3
  10. this thread 3 is running processor : 3
  11. this thread 2 is running processor : 3

3、taskset绑定进程至CPU

(1)绑定进程至指定CPU


  1. taskset -pc CPU_NUMBER  PID
  2. taskset -p PID

查看进程的CPU Affinity

(2)进程启动时绑定至CPU

taskset -c CPU_NUMBER PROGRAM&

启动PROGRAM程序后台运行,绑定进程至CPU_NUMBER核心,

taskset -p PID

查看进程的CPU Affinity

四、线程绑定CPU

1、系统调用API


  1. #define _GNU_SOURCE            
  2. #include <pthread.h>
  3. int pthread_setaffinity_np(pthread_t thread, size_t cpusetsize, const cpu_set_t *cpuset);
  4. int pthread_getaffinity_np(pthread_t thread, size_t cpusetsize, cpu_set_t *cpuset)

参数:

pthead:线程对象

cpusetsize:mask参数所指定数的长度,通常设定为sizeof(cpu_set_t)。

mask:CPU掩码

2、编程实现


  1. #include<stdlib.h>
  2. #include<stdio.h>
  3. #include<sys/types.h>
  4. #include<sys/sysinfo.h>
  5. #include<unistd.h>
  6. #define __USE_GNU
  7. #include<sched.h>
  8. #include<ctype.h>
  9. #include<string.h>
  10. #include<pthread.h>
  11. #define THREAD_MAX_NUM 10  //1个CPU内的最多进程数
  12. int CPU_NUM = 0;  //cpu中核数
  13. int CPU = 3; // CPU编号
  14. void* threadFun(void* arg)
  15. {
  16.     cpu_set_t affinity;   //获取在集合中的CPU
  17.     CPU_ZERO(&affinity);
  18.     pthread_t thread = pthread_self();
  19.     // 获取当前进程的CPU Affinity
  20.     if (pthread_getaffinity_np(thread, sizeof(affinity), &affinity) == -1)
  21.     {
  22.         printf("warning: cound not get Process affinity, continuing...\n");
  23.     }
  24.     int i = 0;
  25.     for (i = 0; i < CPU_NUM; i++)
  26.     {
  27.         if (CPU_ISSET(i, &affinity))//判断线程与哪个CPU有亲和力
  28.         {
  29.             printf("this thread %d is running processor : %d\n", *((int*)arg), i);
  30.         }
  31.     }
  32.     return NULL;
  33. }
  34. int main(int argc, char* argv[])
  35. {
  36.     int tid[THREAD_MAX_NUM];
  37.     pthread_t thread[THREAD_MAX_NUM];
  38.     // 获取核数
  39.     CPU_NUM = sysconf(_SC_NPROCESSORS_CONF);
  40.     printf("System has %i processor(s). \n", CPU_NUM);
  41.     cpu_set_t mask;  //CPU核的集合
  42.     CPU_ZERO(&mask);
  43.     // set CPU MASK
  44.     CPU_SET(CPU, &mask);
  45.     int i = 0;
  46.     for(i=0;i<THREAD_MAX_NUM;i++)
  47.     {
  48.         tid[i] = i;
  49.         pthread_create(&thread[i],NULL,threadFun, &tid[i]);
  50.         //设置当前进程的CPU Affinity
  51.         if (pthread_setaffinity_np(thread[i], sizeof(mask), &mask) != 0)
  52.         {
  53.             printf("warning: could not set CPU affinity, continuing...\n");
  54.         }
  55.     }
  56.     for(i=0; i< THREAD_MAX_NUM; i++)
  57.     {
  58.         pthread_join(thread[i],NULL);
  59.     }
  60.     return 0;
  61. }

编译:

gcc -o test test.c -pthread

运行结果:


  1. System has 4 processor(s).
  2. this thread 0 is running processor : 3
  3. this thread 1 is running processor : 3
  4. this thread 2 is running processor : 3
  5. this thread 3 is running processor : 3
  6. this thread 5 is running processor : 3
  7. this thread 4 is running processor : 3
  8. this thread 6 is running processor : 3
  9. this thread 9 is running processor : 3
  10. this thread 7 is running processor : 3
  11. this thread 8 is running processor : 3
文章知识点与官方知识档案匹配,可进一步学习相关知识
CS入门技能树Linux入门初识Linux30020 人正在系统学习中
量化IT技术交流群


QQ群名片

[转帖]Linux性能优化(十五)——CPU绑定的更多相关文章

  1. Linux性能优化 第五章 性能工具:特定进程内存

    5.1 Linux内存子系统 在诊断内存性能问题的时候,也许有必要观察应用程序在内存子系统的不同层次上是怎样执行的.在顶层,操作系统决定如何利用交换内存和物理内存.它决定应用程序的哪一块地址空间将被放 ...

  2. 深挖计算机基础:Linux性能优化学习笔记

    参考极客时间专栏<Linux性能优化实战>学习笔记 一.CPU性能:13讲 Linux性能优化实战学习笔记:第二讲 Linux性能优化实战学习笔记:第三讲 Linux性能优化实战学习笔记: ...

  3. Linux 性能优化之 IO 子系统 系列 图

    http://blog.sina.com.cn/s/articlelist_1029388674_11_1.html Linux 性能优化之 IO 子系统(一) 本文介绍了对 Linux IO 子系统 ...

  4. Linux 性能优化解析

    前情概述 进程调度 老板 cpu 任劳任怨的打工仔 线程 工作在做什么 可运行队列 拥有的工作清单 上下文切换 和老板沟通以便得到老板的想法并及时调整自己的工作 中断 部分工作做完以后还需要及时向老板 ...

  5. Linux 性能优化排查工具

    下图1为 Linux 性能优化排查工具的总结 图1 诊断 CPU 工具 查看 CPU 核数 总核数 = 物理CPU个数 X 每颗物理CPU的核数 总逻辑CPU数 = 物理CPU个数 X 每颗物理CPU ...

  6. Linux性能优化从入门到实战:01 Linux性能优化学习路线

      我通过阅读各种相关书籍,从操作系统原理.到 Linux内核,再到硬件驱动程序等等.   把观察到的性能问题跟系统原理关联起来,特别是把系统从应用程序.库函数.系统调用.再到内核和硬件等不同的层级贯 ...

  7. Linux性能优化-平均负载

    Linux性能优化-平均负载 目录 Linux性能优化-平均负载 平均负载的含义 平均负载为多少时合理 平均负载与 CPU 使用率 平均负载案例分析 场景一:CPU 密集型进程 场景二:I/O 密集型 ...

  8. 如何学习Linux性能优化?

    如何学习Linux性能优化? 你是否也曾跟我一样,看了很多书.学了很多 Linux 性能工具,但在面对 Linux 性能问题时,还是束手无策?实际上,性能分析和优化始终是大多数软件工程师的一个痛点.但 ...

  9. Linux性能优化实战学习笔记:第五十讲

    一.上节回顾 上一节,我以 ksoftirqd CPU 使用率高的问题为例,带你一起学习了内核线程 CPU 使用率高时的分析方法.先简单回顾一下. 当碰到内核线程的资源使用异常时,很多常用的进程级性能 ...

  10. Linux性能优化实战学习笔记:第五十二讲

    一.上节回顾 上一节,我们一起学习了怎么使用动态追踪来观察应用程序和内核的行为.先简单来回顾一下.所谓动态追踪,就是在系统或者应用程序还在正常运行的时候,通过内核中提供的探针,来动态追踪它们的行为,从 ...

随机推荐

  1. 揭秘华为云GaussDB(for Redis)丨大key治理

    本文分享自华为云社区<华为云GaussDB(for Redis)揭秘第31期:大key治理>,作者: 高斯Redis官方博客. 从DBA的视角看,大Key无疑是引起Redis线上问题的常见 ...

  2. 解决大模型“开发难”,昇思MindSpore自动并行技术应用实践

    本文分享自华为云社区<DTSE Tech Talk|第35期:解决大模型"开发难",昇思MindSpore自动并行技术应用实践>,作者华为云社区精选. 昇思MindSp ...

  3. 物联网设备上云难?华为云IoT帮你一键完成模型定义,快速在线调试设备

    摘要:在不到3分钟的操作里,不仅完成了一款智慧烟感设备在云端的模型定义,还通过在线调试了解到了设备和远端通信的过程. 本文分享自华为云社区<物联网设备上云难?华为云IoT帮你一键完成模型定义,快 ...

  4. CWE发布2021年最危险的25种软件缺陷

    摘要:CWE最危险的25种软件缺陷,是NVD过去两年中遇到的最常见和影响最大的问题指示性的列表. CWE Top25 可以帮助开发人员.测试人员和用户,以及项目经理.安全研究人员和教育工作者深入了解最 ...

  5. 火山引擎DataLeap:3步打造“指标管理”体系,幸福里数据中心是这么做的

    更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 一家企业,为什么要搭建指标体系? 一句话总结来说,全面.合理的指标体系可以帮助企业统一目标,将业务环节量化,帮助策 ...

  6. Django中安装websocket

    完整代码: https://gitee.com/mom925/django-system项目结构: 先安装所需库: pip install channels下面将websocket作为插件一样的只需要 ...

  7. Go--变量的声明

    Go语言是静态类型语言,因此变量(variable)是有明确类型的,编译器也会检查变量类型的正确性. 变量是一段或多段用来存储数据的内存,在go中,变量一旦被定义,一定要使用,不然会报错 内建变量类型 ...

  8. HDU - 1711:Number Sequence (KMP模板)

    原题链接 KMP模板:AC,858ms,13112KB内存 消耗太大了 #include<bits/stdc++.h> using namespace std; using namespa ...

  9. 机器学习-决策树系列-决策树-剪枝-CART算法-27

    目录 1. 剪枝 2. CCP-代价复杂度剪枝(CART) 4. α值的确定 1. 剪枝 将子树还原成一个叶子节点: 是解决过拟合的一个有效方法.当树训练得过于茂盛的时候会出现在测试集上的效果比训练集 ...

  10. 星索称重/生产管理软件 联机版V1.0

    星索称重/生产管理软件 联机版V1.0   一.特点 1.支持多用户.多组织管理,灵活控制用户权限. 2.支持地磅秤.智能电子秤.轨道秤等多款称重设备. 3.支持三联单/热敏纸等多种打印模板. 二.系 ...