go调度器注释

Go Preemptive Scheduler Design Doc

Contiguous stacks

Implemetion of Golang:协程栈(三)

Golang调度器源码分析

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在不同进程之间的流转,会损害==局部性=,并引入额外开销。

这里直接的意思是没有倾向,实际上创建这个gm应该有限执行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

go scheduler扩展性设计

go调度器注释
Share this