目录
[性能优化]-CPU与上下文切换
second60 20180802
1 说说进程上下文
经常可以看到不同代码里都有这么一个单词:context,中文翻译:上下文或环境。
在内核的CPU调度中,也有进程的上下文。在CPU执行一个任务(进程)的时候,必须先把进程的上下文加载好,然后才能运行。
那进程上下文到底包括哪些东西呢?
进程上下文实际上是进程执行活动全过程的静态描述。我们把已执行过的进程指令和数据在相关寄存器与堆栈中的内容称为上文,把正在执行的指令和数据在寄存器和堆栈中的内容称为正文,把待执行的指令和数据在寄存器与堆栈中的内容称为下文。具体的说,进程上下文包括计算机系统中与执行该进程有关的各种寄存器(例如通用寄存器,程序计数器PC,程序状态字寄存器PS等)的值,程序段在经过编译过后形成的机器指令代码集,数据集及各种堆栈值PCB结构。这里,有关寄存器和栈区的内容是重要的,例如没有程序计数器PC和程序 状态寄存器PS,CPU将无法知道下一条待执行指令的地址和控制有关操作。(来源:百度百科)
上文:已执行过进程指令和数据在相关寄存器与堆栈中的内容。
正文:正在执行的指令和数据在寄存器和堆栈中的内容。
下文:待执行的指令和数据在寄存器与堆栈中的内容。
学过汇编的同学应该了解,其实就是各种寄存器与堆栈,在汇编中这保存现场和恢复现场。
2 上下文切换
在处理器执行期间,运行进程的信息被存储在处理器的寄存器和高速缓存cache中,执行的进程被另载到寄存器的数据集被称为上下文。
在进程切换过程中,先存储运行进程的上下文,然后将下一个要运行的进程的上下文恢复到寄存器中。这个过程被称为上下文切换(context switching)。
进程描述符task_struct和内核模式堆栈区域用于存储上下文。
一般不能有太多的上下文切换,因为CPU要刷新寄存器和高速缓存(cache),以便释放空间给新的进程。可能会导致性能问题。
3 进程上下文切换与性能优化
上下文切换太频繁,CPU花费很多时间在切换上。那么怎么来避免呢?
这里涉及到两个问题:
1: 单CPU进程上下文切换:这个是必须的,也是不能避免的。
2: 跨CPU进程上下文切换:这个是不必须的,可以避免的。
优化方法:
- 单CPU进程上下文,尽量切换次数少些
- 这个可以通用调整优先级,nice让进程分得的时间更更多些,时间片多,则切换少
- 跨CPU进程上下文切换,这个是可以避免的
- 写代码时,进程绑定到特定的CPU核里,那么就不会涉及到跨CPU切换
- 可参考nginx的做法
4 进程和线程-上下文切换区别
进程切换步骤:
- 切换页目录以使用新(进程)的地址空间
- 切换内核栈和寄存器上下文切换(上面讲的)
线程上下文切换是没有步骤1。因为线程上下文切换不同切换地址空间。
步骤1中,当改变虚拟内存空间时,处理的页表缓冲会被全部刷新,导致内存访问一段时间内相当低效。线程切换过程中不存在这个问题。
总结:线程上下文切换比进程上下文切换消耗的性能少。
5 线程上下文切换与性能优化
这里说的线程,是指单个进程里的多个线程。线程也同样会涉及到上下文切换。
有线程,当然离不开锁的问题,等待的问题,同步的问题。
那么多线程上下切换有什么可以优化呢?
优化方法:
- 线程也可以指定绑定到核中,可以防止跨CPU消耗
- 线程能使用无锁方法,尽量使用无锁,无锁队列等
- 线程数量尽可能和适量,太多的线程会导致无用切换消耗
- 涉计到多线程,尽量设计的简单些
6 总结
了解进程和线程的上下文切换,可以在设计时,尽量减少上下文切换,防止性能的问题。如适量的进程线程数,绑定CPU等。同时也可以学习开源优化的框架是怎么处理的。如nginx,redis等。