go调度器注释
Go Preemptive Scheduler Design Doc
go调度器proc.go文件头部的注释中有一些值得关注的技术点,下面会逐条的分析。
// 1. Centralize all scheduler state (would inhibit scalability).
集中管理所有调度器的状态会抑制可扩展性。集中体现在代码中用一个struct
例如sched
管理所有g
的分配,sched
通过lock
变量保证所有共享资源操作的并发安全,这样势必导致cpu在锁竞争上有大量额外的消耗。抑制体现在当用户代码的并发力度越小时,这个损耗占比会越大。这会阻碍go语言释放多核性能的初衷。
// 2. Direct goroutine handoff. That is, when we ready a new goroutine and there
// is a spare P, unpark a thread and handoff it the thread and the goroutine.
// This would lead to thread state thrashing, as the thread that readied the
// goroutine can be out of work the very next moment, we will need to park it.
// Also, it would destroy locality of computation as we want to preserve
// dependent goroutines on the same thread; and introduce additional latency.
直接放手goroutine,当有空闲p
时,unpark一个m
与之关联,并把g
放到p
的本地队列。会导致线程抖动,即线程刚准备好,g
进入非_grunnable
状态。g
在不同进程之间的流转,会损害==局部性=,并引入额外开销。
这里直接的意思是没有倾向,实际上创建这个g
的m
应该有限执行g
,因为共用同一块堆栈空间,开销会小些。局部性这个可能线程内部执行程序时涉及到的一些硬件级别的优化,迁移到其他线程势必会流失掉这些优化点。
// 3. Unpark an additional thread whenever we ready a goroutine and there is an
// idle P, but don't do handoff. This would lead to excessive thread parking/
// unparking as the additional threads will instantly park without discovering
// any work to do.
对于新的g
,如果当前有空闲p
,就会unpark线程(可能新建或者利用已经存在的空闲线程),会导致大量的线程状态变化。
上述问题的解决办法:
unpark线程的条件改为: 空闲p
,但是没有正在找工作的m
-spinning