先前在社区里分享了关于 golang 行情推送[1]的分享,有人针对 ppt 的内容问了我两个问题,一个是在 docker 下 golang 的 gomaxprocs 初始化混乱问题,另一个是 golang runtime.gomaxprocs 配置多少为合适?

golang runtime

Golang 的 runtime 调度是依赖 pmg 的角色抽象,p 为逻辑处理器,m 为执行体(线程),g 为协程。p 的 runq 队列中放着可执行的 goroutine 结构。golang 默认 p 的数量为 cpu core 数目,比如物理核心为 8 cpu core,那么 go processor 的数量就为 8。另外,同一时间一个 p 只能绑定一个 m 线程,pm 绑定后自然就找 g 和运行 g。

那么增加 processor 的数量,是否可以用来加大 runtime 对于协程的调度吞吐?

大多 golang 的项目偏重网络 io,network io 在 netpoll 设计下都是非阻塞的,所涉及到的 syscall 不会阻塞。如果是 cpu 密集的业务,增加多个 processor 也没用,毕竟 cpu 计算资源就这些,居然还想着来回的切换????? 所以,多数场景下单纯增加 processor 是没什么用的。

当然,话不绝对,如果你的逻辑含有不少的 cgo 及阻塞 syscall 的操作,那么增加 processor 还是有效果的,最少在我实际项目中有效果。原因是这类操作有可能因为过慢引起阻塞,在阻塞期间的 p 被 mg 绑定一起,其他 m 无法获取 p 的所有权。虽然在 findrunnable steal 机制里,其他 p 的 m 可以偷该 p 的任务,但在解绑 p 之前终究还是少了一条并行通道。另外,runtime 的 sysmon 周期性的检查长时间阻塞的 pmg, 并抢占并解绑 p。

golang 在 docker 下的问题

在微服务体系下服务的部署通常是放在 docker 里的。一个宿主机里跑着大量的不同服务的容器。为了避免资源冲突,通常会合理地对每个容器做 cpu 资源控制。比如给一个 golang 服务的容器限定了 2 cpu core 的资源,容器内的服务不管怎么折腾,也确实只能用到大约 2 个 cpu core 的资源。

但 golang 初始化 processor 数量是依赖 /proc/cpuinfo 信息的,容器内的 cpuinfo 是跟宿主机一致的,这样导致容器只能用到 2 个 cpu core,但 golang 初始化了跟物理 cpu core 相同数量的 processor。

// xiaorui.cc限制2核左右
root@xiaorui.cc:~# docker run -tid --cpu-period 100000 --cpu-quota 200000 ubuntu容器内
root@a4f33fdd0240:/# cat /proc/cpuinfo| grep "processor"| wc -l
48

runtime processor 多了会出现什么问题?

一个是 runtime findrunnable 时产生的损耗,另一个是线程引起的上下文切换。

runtime 的 findrunnable 方法是解决 m 找可用的协程的函数,当从绑定 p 本地 runq 上找不到可执行的 goroutine 后,尝试从全局链表中拿,再拿不到从 netpoll 和事件池里拿,最后会从别的 p 里偷任务。全局 runq 是有锁操作,其他偷任务使用了 atomic 原子操作来规避 futex 竞争下陷入切换等待问题,但 lock free 在竞争下也会有忙轮询的状态,比如不断的尝试。

// xiaorui.cc// 全局 runq
if sched.runqsize != 0 {lock(&sched.lock)gp := globrunqget(_p_, 0)unlock(&sched.lock)if gp != nil {return gp, false}
}
...// 尝试4次从别的p偷任务for i := 0; i < 4; i++ {for enum := stealOrder.start(fastrand()); !enum.done(); enum.next() {if sched.gcwaiting != 0 {goto top}stealRunNextG := i > 2 // first look for ready queues with more than 1 gif gp := runqsteal(_p_, allp[enum.position()], stealRunNextG); gp != nil {return gp, false}}
}
...

通过 godebug 可以看到全局队列及各个 p 的 runq 里等待调度的任务量。有不少 p 是空的,那么势必会引起 steal 偷任务。另外,runqueue 的大小远超其他 p 的总和,说明大部分任务在全局里,全局又是把大锁。

随着调多 runtime processor 数量,相关的 m 线程自然也就跟着多了起来。linux 内核为了保证可执行的线程在调度上雨露均沾,按照内核调度算法来切换就绪状态的线程,切换又引起上下文切换。上下文切换也是性能的一大杀手。findrunnable 的某些锁竞争也会触发上下文切换。

下面是我这边一个行情推送服务压测下的 vmstat 监控数据。首先把容器的的 cpu core 限制为 8,再先后测试 processor 为 8 和 48 的情况。图的上面是 processor 为 8 的情况,下面为 processor 为 48 的情况。看图可以直观地发现当 processor 调大后,上下文切换(cs)明显多起来,另外等待调度的线程也多了。

另外从 qps 的指标上也可以反映多 processor 带来的性能损耗。通过下图可以看到当 runtime.GOMAXPROCS 为固定的 cpu core 数时,性能最理想。后面随着 processor 数量的增长,qps 指标有所下降。

通过 golang tool trace 可以分析出协程调度等待时间越来越长了。

解决 docker 下的 golang gomaxprocs 校对问题

有两个方法可以准确校对 golang 在 docker 的 cpu 获取问题。

要么在 k8s pod 里加入 cpu 限制的环境变量,容器内的 golang 服务在启动时获取关于 cpu 的信息。

要么解析 cpu.cfs_period_us 和 cpu.cfs_quota_us 配置来计算 cpu 资源。社区里有不少这类的库可以使用,uber的automaxprocs[2]可以兼容 docker 的各种 cpu 配置。

总结

建议 gomaxprocs 配置为 cpu core 数量就可以了,Go 默认就是这个配置,无需再介入。如果涉及到阻塞 syscall,可以适当的调整 gomaxprocs 大小,但一定要用指标数据说话!

参考资料

[1]

行情推送: http://xiaorui.cc/?p=6250

[2]

automaxprocs: https://github.com/uber-go/automaxprocs

Go gomaxprocs 调高引起调度性能损耗相关推荐

  1. Go gomaxprocs 调高会引起调度性能损耗

    先前在社区里分享了关于 golang 行情推送[1]的分享,有人针对 ppt 的内容问了我两个问题,一个是在 docker 下 golang 的 gomaxprocs 初始化混乱问题,另一个是 gol ...

  2. Nginx一网打尽:动静分离、压缩、缓存、黑白名单、跨域、高可用、性能优化......

    干货!文章有点长,建议先收藏 引言 一.性能怪兽-Nginx概念深入浅出 二.Nginx环境搭建 三.Nginx反向代理-负载均衡 四.Nginx动静分离 五.Nginx资源压缩 六.Nginx缓冲区 ...

  3. 云原生应用负载均衡系列 (2): 入口流量分发、容错与高可用调度

    引言 在云原生应用负载均衡系列第一篇文章<云原生应用负载均衡选型指南>介绍了云原生容器环境的入口流量管理使用场景与解决方案,用 Envoy 作为数据面代理,搭配 Istio 作控制面的 I ...

  4. Hadoop YARN:调度性能优化实践【转】

    原文地址:https://www.infoq.cn/article/dh5UpM_fJrtj1IgxQDsq 背景 YARN 作为 Hadoop 的资源管理系统,负责 Hadoop 集群上计算资源的管 ...

  5. Hadoop YARN:调度性能优化实践

    背景 YARN作为Hadoop的资源管理系统,负责Hadoop集群上计算资源的管理和作业调度. 美团的YARN以社区2.7.1版本为基础构建分支.目前在YARN上支撑离线业务.实时业务以及机器学习业务 ...

  6. 虚拟化精华问答 | 怎样使虚拟机能够达到比较高的IO性能?

    虚拟化是一种资源管理技术, 是将计算机的各种物理资源, 如服务器.网络.内存及存储等,予以抽象.转换后呈现出来,打破物理设备结构间的不可切割的障碍,使用户可以比原本的架构更好的方式来应用这些资源.这些 ...

  7. PostgreSQL数据库 OLTP高并发请求性能优化

    PostgreSQL数据库 OLTP高并发请求性能优化   2015-10-14 11:00:00|  作者:德哥:分类: PgSQL PerfTuning| 2015年度PG大象会报名地址: htt ...

  8. apache php 调优_Apache的性能优化实例(一)

    本文主要和大家分享Apache的性能优化(一),希望能帮助到大家. Apache缺省的最大用户数是256个:这个配置对于服务器内存还是256M左右的时代是一个非常好的缺省设置,但随着内存成本的急剧下降 ...

  9. 提高计算机主频的方法,cpu主频如何调高

    当我们觉得电脑的cpu主频不够用时!该怎么样去调高呢?下面由学习啦小编给你做出详细的cpu主频调高方法介绍!希望对你有帮助! cpu主频调高方法一 就是开机的时候按一下"点"&qu ...

最新文章

  1. Avdshare Audio Converter 7中文版
  2. b. Suffix Zeroes
  3. shell 文本后几行_shell_wc(统计数目)、head(查看前几行)、tail(查看末尾几行)...
  4. 中方:开展科技合作应秉持开放、合作、包容心态
  5. php读取某类型文件代码,php代码实现读取文件头判断文件类型
  6. 关注微信公众号使其自动发送欢迎你关注消息
  7. 写出记录型信号量中的wait操作代码_操作系统进程的同步与互斥及经典同步与互斥问题...
  8. 微软为 Java 开发者推出 VSCode 安装程序
  9. php hbase thrift,php通过thrift操作hbase
  10. linux安装qq权限不够,[操作系统]Linuxqq安装及其所引发的问题{权限位是 777 (必须 =0755 且 =0755)}...
  11. win10系统中的截图,win+prtSc保存位置
  12. mapbox gl文字标注算法基本介绍
  13. java 定义一个商品类_用java编写一个产品类, 1. 属性:产品编号,产品名称,产品单价,产品库存,2.构造方...
  14. Flutter 修改Slider 滑杆刻度
  15. 计算机房面积设置气消条件,广东省《建筑防烟排烟系统技术标准》问题释疑
  16. 各行各业数据及分析研究报告网站参考
  17. HTTP Header中的内容(请求Header、响应Header)
  18. Linux Kernel PANIC(一)--概述(Hard Panic/Aieee和Soft Panic/Oops)
  19. Oracle——单列函数,多表连接
  20. Swiper Social项目(一): Swiper Social项⽬概览及项⽬⽬标(类似于探探)

热门文章

  1. 200723学习日报
  2. 第五章:系统困境之 你的努力忽略了关键限制因素
  3. [转]Newtonsoft JSON how to dynamically change the date format?
  4. Cobbler体验小记
  5. 删除数据表和清空数据表的内容(保存表结构)的SHELL脚本
  6. fatal error LNK1561: 必须定义入口点问题的我是这样解决的
  7. 数据库异常关闭后无法启动问题处理
  8. 2021牛客多校7 - xay loves monotonicity(线段树区间合并)
  9. HDU多校3 - 6975 Forgiving Matching(多项式匹配字符串)
  10. CodeForces - 456C Boredom(线性dp)