Go1.18 调度器-G复用

发表于 · 归类于 代码 · 阅读完需 8 分钟 · 报告错误 · 阅读:

复用

对于执行结束的 dead G 并不会释放,而是缓存在P本地或全局,以待复用。

// runtime2.go

type schedt struct {
    
    // Global cache of dead G's.
    gFree struct {
        lock    mutex
        stack   gList // Gs with stacks
        noStack gList // Gs without stacks
        n       int32
    }    
}
type p struct {
    
    // Available G's (status == Gdead)
    gFree struct {
        gList
        n int32
    }    
}
// runtime2.go, proc.go

// A gList is a list of Gs linked through g.schedlink. A G can only be
// on one gQueue or gList at a time.

type gList struct {
    head guintptr
}

type g struct {
    schedlink    guintptr    
}

type guintptr uintptr

 

获取

从本地获取。如果为空,则从全局转移一批到本地。

// proc.go

// Get from gfree list.
// If local list is empty, grab a batch from global list.

func gfget(_p_ *p) *g {

retry:
    // 本地为空,从全局转移一批后重试。
    if _p_.gFree.empty() && (!sched.gFree.stack.empty() || !sched.gFree.noStack.empty()) {
        lock(&sched.gFree.lock)

        // 直到本地缓存 32 个。
        for _p_.gFree.n < 32 {

            // 优先选择带栈内存的。
            gp := sched.gFree.stack.pop()
            if gp == nil {
                gp = sched.gFree.noStack.pop()
                if gp == nil {
                    break
                }
            }
            
            sched.gFree.n--
            _p_.gFree.push(gp)
            _p_.gFree.n++
        }
        
        unlock(&sched.gFree.lock)
        goto retry
    }
    
    // 从本地提取。
    gp := _p_.gFree.pop()
    if gp == nil {
        return nil
    }
    _p_.gFree.n--
    
    // 分配栈内存。
    if gp.stack.lo == 0 {
        // Stack was deallocated in gfput. Allocate a new one.
        systemstack(func() {
            gp.stack = stackalloc(_FixedStack)
        })
        gp.stackguard0 = gp.stack.lo + _StackGuard
    }
    
    return gp
}

 

放回

直接放回本地缓存。如果本地过多,则转移到全局,以供其他P使用。

// proc.go

// Put on gfree list.
// If local list is too long, transfer a batch to the global list.

func gfput(_p_ *p, gp *g) {

    // 如果栈扩张过,则释放其内存。
    stksize := gp.stack.hi - gp.stack.lo
    if stksize != _FixedStack {
        // non-standard stack size - free it.
        stackfree(gp.stack)
        gp.stack.lo = 0
        gp.stack.hi = 0
        gp.stackguard0 = 0
    }

    // 放回本地链表。
    _p_.gFree.push(gp)
    _p_.gFree.n++
    
    // 本地数量过多。
    if _p_.gFree.n >= 64 {
        var (
            inc      int32
            stackQ   gQueue
            noStackQ gQueue
        )
        
        // 提取多余的,存为链表并转移到全局。
        for _p_.gFree.n >= 32 {
            gp = _p_.gFree.pop()
            _p_.gFree.n--
            
            // 根据是否有栈内存,安排不同链表。
            if gp.stack.lo == 0 {
                noStackQ.push(gp)
            } else {
                stackQ.push(gp)
            }
            inc++
        }
        
        lock(&sched.gFree.lock)
        
        sched.gFree.noStack.pushAll(noStackQ)
        sched.gFree.stack.pushAll(stackQ)

        sched.gFree.n += inc
        unlock(&sched.gFree.lock)
    }
}