图解|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 核数

相关推荐
superman超哥几秒前
Rust 错误处理模式:Result、?运算符与 anyhow 的最佳实践
开发语言·后端·rust·运算符·anyhow·rust 错误处理
lly2024061 分钟前
Web 品质样式表
开发语言
Wang's Blog1 分钟前
Nodejs-HardCore: 模块管理与I/O操作详解
开发语言·nodejs
微爱帮监所写信寄信2 分钟前
微爱帮监狱寄信写信小程序PHP底层优化框架
java·开发语言·数据库·spring·微信·php·mybatis
lly2024064 分钟前
R 语言注释指南
开发语言
bigHead-11 分钟前
前端双屏显示与通信
开发语言·前端·javascript
richxu2025100115 分钟前
Java是当今最优雅的开发语言
java·开发语言
2501_9181269116 分钟前
用Python开发一个三进制程序开发工具
开发语言·汇编·python·个人开发
zh_xuan32 分钟前
kotlin的常见空检查
android·开发语言·kotlin