对于网关服务来说,需要支撑海量的请求,那必然要使用到多线程,也就不可避免的会导致线程切换。如果可以将这些繁忙的线程绑定到一个cpu核上,可以确保该线程的最大执行速度,实现低延迟,消除操作系统进行调度过程导致线程迁移所造成的抖动影响,还可以避免由于缓存失效而导致的性能开销。
本文以开源项目SONA为例,介绍了一种通过将java线程与CPU绑定的方法,将服务整体性能提升了约25%。本文最后附上开源项目地址。
前言
Sona 平台是一个搭建语音房产品的全端解决方案,包含了房间管理、实时音视频、房间IM、长连接网关等能力。其中最基础核心的就是长连接网关。
对于网关服务来说,需要支撑海量的请求,那必然要使用到多线程,也就不可避免的会导致线程切换。
线程切换的常见原因:
-
当前执行任务的时间片用完之后,系统CPU正常调度下一个任务。
-
当前执行任务碰到IO阻塞, 调度器将挂起此任务, 继续下一任务
-
多个任务抢占锁资源, 当前任务没有抢到,被调度器挂起, 继续下一任务
-
用户代码挂起当前任务, 让出CPU时间
-
中断。硬件中断:外设发送电信号给处理器,异步,IRQ(interrupt request);软中断:异常情况 or 特殊指令集,软中断可以用来实现system call。
之前的文章也介绍过,为了支撑海量请求 ,SONA网关使用了三层的网络处理模型,其中最重要的就是 Netty 的 boss 与work eventloop (千万不要在 eventloop 中做任何可能导致阻塞的操作,否则会严重影响系统的 qps),如果可以将这些繁忙的线程绑定到一个cpu核上,可以确保该线程的最大执行速度,实现低延迟,消除操作系统进行调度过程导致线程迁移所造成的抖动影响,还可以避免由于缓存失效而导致的性能开销。
一、CPU绑定
在Linux系统中,进程的调度切换是由内核自动完成的,在多核CPU上,进程有可能在不同的CPU核上来回切换执行,这对CPU的缓存不是很有利。
因为在多核CPU结构中,每个核心有各自的L1、L2缓存,而L3缓存是共用的。如果一个进程在核心间来回切换,各个核心的缓存命中率就会受到影响。相反如果进程不管如何调度,都始终可以在一个核心上执行,那么其数据的L1、L2 缓存的命中率可以显著提高。
另外使用CPU绑定可以将关键的进程隔离开,对于部分实时进程调度优先级提高,可以将其绑定到一个指定CPU核上,可以保证实时进程的调度,也可以避免其他CPU上进程被该实时进程干扰。