图解|Go的GMP在计算密集型和IO密集型的区别

写在前面

Go的GMP我们很熟悉了,计算密集型和io密集型我们也很熟悉了,但 Go 的GMP在计算密集型和io密集型上有什么区别呢? 这篇文章我们就来探讨一下!

当然这也只是我自己的理解,如果你有不同的理解,可以评论区留言!

GMP

  • G(Goroutine):用户级轻量线程(协程),包含栈、状态、入口函数等,创建和切换成本很低,默认栈按需伸缩
  • M(Machine):绑定到操作系统线程的运行实体,真正在CPU上执行代码
  • P(Processor):逻辑处理器,承载调度资源(本地运行队列、定时器等),控制并行度 ,上限为 GOMAXPROCS

调度模型:

  • 本地队列优先:每个 P 有自己运行的本地队列,调度时优先从本地取就绪 G 。
  • 全局队列兜底:部分新就绪 G 放入全局队列,P 本地队列空时尝试取全局任务
  • Work Stealing:本地无任务时,从随机其他 P 的队列尾部偷一半任务,均衡负载
  • 自旋与休眠:没有可运行 G 时, M 可短暂自旋寻找任务,找不到则休眠以节省资源。

io密集型

大部分时间在等待外部资源(磁盘、网络、数据库)响应,CPU常处于空闲或低占用。

  • CPU使用率iowait 高(Linux vmstat / iostat )、会存在大量阻塞/超时日志
  • 线程/连接数多、吞吐受外部系统 RT 影响

在IO密集型系统中,我们假设每一个G都是请求外部资源,比如RPC、Redis、DB等等... 当我们代码执行一次网络请求 (如http.Get) 时,GMP发生以下事情:

  1. G (Goroutine): 执行到网络读取操作,发现数据未就绪
  2. G->Netpoller: G将自己 注册到基于epoll的网络轮询器的Netpoller中 ,状态变为waiting并与M分离。
  3. M (Machine): M不会阻塞,会立刻通过 P 获取本地队列中的下一个G继续执行
  4. Netpoller->P: 当网络数据到达,Netpoller检测到事件,将之前的G状态改为running ,并将其注入到全局队列或某个P的本地队列。
  5. P (Processor): 在后续调度中,P再次拿到这个G,M继续执行之前的上下文

⚠️ 注意点:M始终在工作,没有空闲。

所以对于IO密集型系统,GMP 通过挂起当前在等待数据的G,可以使得M能处理其他的G,利用等待时间,把有限的 CPU 放大成海量的并发处理能力,拆分协程收益巨大,达到四两拨千斤的效果。

计算密集型

大部分时间在执行CPU计算(算法、编码/解码、加密、ML推理),CPU长期接近满载。

  • CPU使用率负载 ≈ CPU核心数或更高
  • 性能对算法复杂度、指令效率、缓存命中率敏感(perf 、 pprof 显示大量CPU占用)

当我们代码执行一个死循环计算或复杂哈希时,GMP的工作就会是这样:

  1. G(Goroutine): 在 M 上疯狂消耗 CPU 周期
  2. P(Processor): P 的本地队列里可能还有其他 G 在排队。
  3. Preemption(抢占): Go 的后台监控线程sysmon发现某个 G 运行超过 10ms,会发出抢占信号
  4. G->Global Queue: G 检测到抢占信号,保存上下文,主动让出 CPU,回到 全局队列队尾等待
  • 为什么拆分成多个协程"没有那么高并发"?

在计算密集型场景下,所谓的高并发其实是假象。

  • 真并发 (Parallelism) :同一时刻真的有多个任务在物理CPU上跑,上限就是CPU核数(GOMAXPROCS)。
  • 假并发 (Concurrency) :任务在快速切换,宏观上看好像都在跑,微观上同一时刻只有几个在跑。

对于计算密集型,开一万个G,真正的并行度依然卡死在CPU核数上。反而因为过度的切换(Context Switch)和CPU缓存失效(Cache Miss),导致整体效率不如只开8个G。

所以系统CPU 算力就那么大,在计算密集型系统中,协程越多,调度损耗越多,拆分协程的目的是为了不让某一个协程独占CPU,所以在做计算密集型任务的时候不要滥用 go func() ,最好是 协程数 ≈ CPU 核数

相关推荐
weixin_4997715520 小时前
C++中的组合模式
开发语言·c++·算法
初级代码游戏20 小时前
套路化编程 C# winform 自适应缩放布局
开发语言·c#·winform·自动布局·自动缩放
_waylau20 小时前
鸿蒙架构师修炼之道-架构师的职责是什么?
开发语言·华为·harmonyos·鸿蒙
2的n次方_20 小时前
CANN Ascend C 编程语言深度解析:异构并行架构、显式存储层级与指令级精细化控制机制
c语言·开发语言·架构
java干货21 小时前
为什么 “File 10“ 排在 “File 2“ 前面?解决文件名排序的终极算法:自然排序
开发语言·python·算法
_F_y21 小时前
C语言重点知识总结(含KMP详细讲解)
c语言·开发语言
毕设源码-郭学长21 小时前
【开题答辩全过程】以 基于python的二手房数据分析与可视化为例,包含答辩的问题和答案
开发语言·python·数据分析
无小道21 小时前
Qt——常用控件
开发语言·qt
aini_lovee21 小时前
MATLAB基于小波技术的图像融合实现
开发语言·人工智能·matlab
R1nG8631 天前
多线程安全设计 CANN Runtime关键数据结构的锁优化
开发语言·cann