一、孤立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. Windows使用docker踩坑记录

    一.安装踩坑 安装时会让你选择:是否使用Windows容器 Use Windows containers instead of Linux containers(this can be changed ...

  2. OLAP分析型应用场景中,数仓中vacuum为何对列存表无效

    摘要:对列存表执行vacuum为什么是无效的呢?其实这与列存表的存储结构以及数据写入方式有关. 本文分享自华为云社区<GaussDB(DWS)中vacuum为何对列存表无效?[这次高斯不是数学家 ...

  3. 业务并发度不够,数仓的CN可以来帮忙

    摘要: CN全称协调节点(Coordinator Node),是和用户关系最密切也是DWS内部非常重要的一个组件,它负责提供外部应用接口.优化全局执行计划.向Datanode分发执行计划,以及汇总.处 ...

  4. VRAR产业峰会暨第二届华为VR开发应用大赛颁奖典礼在和平区成功举办!

    摘要:近日,由沈阳市和平区人民政府和华为技术有限公司主办的VRAR产业峰会暨第二届华为VR开发应用大赛颁奖典礼在沈阳君悦酒店圆满举办. 12月29日,由沈阳市和平区人民政府和华为技术有限公司主办的VR ...

  5. 【终极教程】Cocos2dx服务端重构(优化cocos2dx服务端)

    [终极教程]Cocos2dx服务端重构(优化cocos2dx服务端) 文章目录 概述 问题概述1. 代码混淆代码加密具体步骤测试和配置阶段IPA 重签名操作步骤2. 缺乏文档3. 缺乏推荐的最佳实践4 ...

  6. Solon Logging 日志插件的记录器、添加器级别控制

    有需要的人可以记下 solon.logging.appender: console: level: TRACE #设置级别,默认为 TRACE enable: true #是否启用,默认为 true ...

  7. Intelij IDEA 隐藏 .idea

    如图 这两文件夹是 IDEA 自动生成的,在开发过程中用不到它.可以把它隐藏(不在 IDEA中显示),操作如下: OK后,立即生效

  8. 开放 LLM 排行榜: 深入研究 DROP

    最近,开放 LLM 排行榜 迎来了 3 个新成员: Winogrande.GSM8k 以及 DROP,它们都使用了 EleutherAI Harness 的原始实现.一眼望去,我们就会发现 DROP ...

  9. 41. 干货系列从零用Rust编写负载均衡及代理,websocket与tcp的映射,WS与TCP互转

    wmproxy wmproxy已用Rust实现http/https代理, socks5代理, 反向代理, 静态文件服务器,四层TCP/UDP转发,七层负载均衡,内网穿透,后续将实现websocket代 ...

  10. SQL Server 时间算差值/常用函数

    项目中需要计算使用年限,按月份算.刚开始踩了坑,不足1年应该按1年算.记录下~ 和当前时间比较,用DATEDIFF函数DateDiff(month,比较的时间,getdate())先算出月份,再除以1 ...