Last active
December 27, 2018 08:09
-
-
Save lysu/51b7d83753be97690f846110c7c033f3 to your computer and use it in GitHub Desktop.
findrunnable
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
ROUTINE ======================== runtime.findrunnable in /usr/local/go/src/runtime/proc.go | |
4.80s 4.59mins (flat, cum) 41.09% of Total | |
. . 2270: // The conditions here and in handoffp must agree: if | |
. . 2271: // findrunnable would return a G to run, handoffp must start | |
. . 2272: // an M. | |
. . 2273: | |
. . 2274:top: | |
220ms 220ms 2275: _p_ := _g_.m.p.ptr() | |
50ms 50ms 2276: if sched.gcwaiting != 0 { | |
. 7.04s 2277: gcstopm() | |
. . 2278: goto top | |
. . 2279: } | |
10ms 10ms 2280: if _p_.runSafePointFn != 0 { | |
. 930ms 2281: runSafePointFn() | |
. . 2282: } | |
. . 2283: if fingwait && fingwake { | |
. . 2284: if gp := wakefing(); gp != nil { | |
. . 2285: ready(gp, 0, true) | |
. . 2286: } | |
. . 2287: } | |
10ms 10ms 2288: if *cgo_yield != nil { | |
. . 2289: asmcgocall(*cgo_yield, nil) | |
. . 2290: } | |
. . 2291: | |
. . 2292: // local runq | |
. 30ms 2293: if gp, inheritTime := runqget(_p_); gp != nil { | |
. . 2294: return gp, inheritTime | |
. . 2295: } | |
. . 2296: | |
. . 2297: // global runq | |
410ms 410ms 2298: if sched.runqsize != 0 { | |
20ms 1.20mins 2299: lock(&sched.lock) | |
10ms 450ms 2300: gp := globrunqget(_p_, 0) | |
. 53.73s 2301: unlock(&sched.lock) | |
. . 2302: if gp != nil { | |
. . 2303: return gp, false | |
. . 2304: } | |
. . 2305: } | |
. . 2306: | |
. . 2307: // Poll network. | |
. . 2308: // This netpoll is only an optimization before we resort to stealing. | |
. . 2309: // We can safely skip it if there are no waiters or a thread is blocked | |
. . 2310: // in netpoll already. If there is any kind of logical race with that | |
. . 2311: // blocked thread (e.g. it has already returned from netpoll, but does | |
. . 2312: // not set lastpoll yet), this thread will do blocking netpoll below | |
. . 2313: // anyway. | |
340ms 360ms 2314: if netpollinited() && atomic.Load(&netpollWaiters) > 0 && atomic.Load64(&sched.lastpoll) != 0 { | |
30ms 21.35s 2315: if gp := netpoll(false); gp != nil { // non-blocking | |
. . 2316: // netpoll returns list of goroutines linked by schedlink. | |
. 8.83s 2317: injectglist(gp.schedlink.ptr()) | |
. 20ms 2318: casgstatus(gp, _Gwaiting, _Grunnable) | |
10ms 10ms 2319: if trace.enabled { | |
. . 2320: traceGoUnpark(gp, 0) | |
. . 2321: } | |
. . 2322: return gp, false | |
. . 2323: } | |
. . 2324: } | |
. . 2325: | |
. . 2326: // Steal work from other P's. | |
. . 2327: procs := uint32(gomaxprocs) | |
100ms 100ms 2328: if atomic.Load(&sched.npidle) == procs-1 { | |
. . 2329: // Either GOMAXPROCS=1 or everybody, except for us, is idle already. | |
. . 2330: // New work can appear from returning syscall/cgocall, network or timers. | |
. . 2331: // Neither of that submits to local run queues, so no point in stealing. | |
. . 2332: goto stop | |
. . 2333: } | |
. . 2334: // If number of spinning M's >= number of busy P's, block. | |
. . 2335: // This is necessary to prevent excessive CPU consumption | |
. . 2336: // when GOMAXPROCS>>1 but the program parallelism is low. | |
20ms 20ms 2337: if !_g_.m.spinning && 2*atomic.Load(&sched.nmspinning) >= procs-atomic.Load(&sched.npidle) { | |
. . 2338: goto stop | |
. . 2339: } | |
. . 2340: if !_g_.m.spinning { | |
. . 2341: _g_.m.spinning = true | |
180ms 180ms 2342: atomic.Xadd(&sched.nmspinning, 1) | |
. . 2343: } | |
. . 2344: for i := 0; i < 4; i++ { | |
510ms 3.75s 2345: for enum := stealOrder.start(fastrand()); !enum.done(); enum.next() { | |
620ms 620ms 2346: if sched.gcwaiting != 0 { | |
. . 2347: goto top | |
. . 2348: } | |
100ms 100ms 2349: stealRunNextG := i > 2 // first look for ready queues with more than 1 g | |
1.71s 27.55s 2350: if gp := runqsteal(_p_, allp[enum.position()], stealRunNextG); gp != nil { | |
10ms 10ms 2351: return gp, false | |
. . 2352: } | |
. . 2353: } | |
. . 2354: } | |
. . 2355: | |
. . 2356:stop: | |
. . 2357: | |
. . 2358: // We have nothing to do. If we're in the GC mark phase, can | |
. . 2359: // safely scan and blacken objects, and have work to do, run | |
. . 2360: // idle-time marking rather than give up the P. | |
100ms 290ms 2361: if gcBlackenEnabled != 0 && _p_.gcBgMarkWorker != 0 && gcMarkWorkAvailable(_p_) { | |
. . 2362: _p_.gcMarkWorkerMode = gcMarkWorkerIdleMode | |
. 10ms 2363: gp := _p_.gcBgMarkWorker.ptr() | |
. 90ms 2364: casgstatus(gp, _Gwaiting, _Grunnable) | |
. . 2365: if trace.enabled { | |
. . 2366: traceGoUnpark(gp, 0) | |
. . 2367: } | |
. . 2368: return gp, false | |
. . 2369: } | |
. . 2370: | |
. . 2371: // wasm only: | |
. . 2372: // Check if a goroutine is waiting for a callback from the WebAssembly host. | |
. . 2373: // If yes, pause the execution until a callback was triggered. | |
. . 2374: if pauseSchedulerUntilCallback() { | |
. . 2375: // A callback was triggered and caused at least one goroutine to wake up. | |
. . 2376: goto top | |
. . 2377: } | |
. . 2378: | |
. . 2379: // Before we drop our P, make a snapshot of the allp slice, | |
. . 2380: // which can change underfoot once we no longer block | |
. . 2381: // safe-points. We don't need to snapshot the contents because | |
. . 2382: // everything up to cap(allp) is immutable. | |
. . 2383: allpSnapshot := allp | |
. . 2384: | |
. . 2385: // return P and block | |
. 34.53s 2386: lock(&sched.lock) | |
20ms 20ms 2387: if sched.gcwaiting != 0 || _p_.runSafePointFn != 0 { | |
. 930ms 2388: unlock(&sched.lock) | |
. . 2389: goto top | |
. . 2390: } | |
120ms 120ms 2391: if sched.runqsize != 0 { | |
10ms 50ms 2392: gp := globrunqget(_p_, 0) | |
. 14.19s 2393: unlock(&sched.lock) | |
10ms 10ms 2394: return gp, false | |
. . 2395: } | |
. 10ms 2396: if releasep() != _p_ { | |
. . 2397: throw("findrunnable: wrong p") | |
. . 2398: } | |
. 230ms 2399: pidleput(_p_) | |
. 10.47s 2400: unlock(&sched.lock) | |
. . 2401: | |
. . 2402: // Delicate dance: thread transitions from spinning to non-spinning state, | |
. . 2403: // potentially concurrently with submission of new goroutines. We must | |
. . 2404: // drop nmspinning first and then check all per-P queues again (with | |
. . 2405: // #StoreLoad memory barrier in between). If we do it the other way around, | |
. . 2406: // another thread can submit a goroutine after we've checked all run queues | |
. . 2407: // but before we drop nmspinning; as the result nobody will unpark a thread | |
. . 2408: // to run the goroutine. | |
. . 2409: // If we discover new work below, we need to restore m.spinning as a signal | |
. . 2410: // for resetspinning to unpark a new worker thread (because there can be more | |
. . 2411: // than one starving goroutine). However, if after discovering new work | |
. . 2412: // we also observe no idle Ps, it is OK to just park the current thread: | |
. . 2413: // the system is fully loaded so no spinning threads are required. | |
. . 2414: // Also see "Worker thread parking/unparking" comment at the top of the file. | |
20ms 20ms 2415: wasSpinning := _g_.m.spinning | |
. . 2416: if _g_.m.spinning { | |
. . 2417: _g_.m.spinning = false | |
40ms 40ms 2418: if int32(atomic.Xadd(&sched.nmspinning, -1)) < 0 { | |
. . 2419: throw("findrunnable: negative nmspinning") | |
. . 2420: } | |
. . 2421: } | |
. . 2422: | |
. . 2423: // check all runqueues once again | |
40ms 40ms 2424: for _, _p_ := range allpSnapshot { | |
30ms 310ms 2425: if !runqempty(_p_) { | |
. 4.89s 2426: lock(&sched.lock) | |
. 20ms 2427: _p_ = pidleget() | |
30ms 3.62s 2428: unlock(&sched.lock) | |
. . 2429: if _p_ != nil { | |
. 10ms 2430: acquirep(_p_) | |
. . 2431: if wasSpinning { | |
. . 2432: _g_.m.spinning = true | |
. . 2433: atomic.Xadd(&sched.nmspinning, 1) | |
. . 2434: } | |
. . 2435: goto top | |
. . 2436: } | |
. . 2437: break | |
. . 2438: } | |
. . 2439: } | |
. . 2440: | |
. . 2441: // Check for idle-priority GC work again. | |
. . 2442: if gcBlackenEnabled != 0 && gcMarkWorkAvailable(nil) { | |
. 20ms 2443: lock(&sched.lock) | |
. . 2444: _p_ = pidleget() | |
. . 2445: if _p_ != nil && _p_.gcBgMarkWorker == 0 { | |
. . 2446: pidleput(_p_) | |
. . 2447: _p_ = nil | |
. . 2448: } | |
. . 2449: unlock(&sched.lock) | |
. . 2450: if _p_ != nil { | |
. . 2451: acquirep(_p_) | |
. . 2452: if wasSpinning { | |
. . 2453: _g_.m.spinning = true | |
. . 2454: atomic.Xadd(&sched.nmspinning, 1) | |
. . 2455: } | |
. . 2456: // Go back to idle GC check. | |
. . 2457: goto stop | |
. . 2458: } | |
. . 2459: } | |
. . 2460: | |
. . 2461: // poll network | |
10ms 10ms 2462: if netpollinited() && atomic.Load(&netpollWaiters) > 0 && atomic.Xchg64(&sched.lastpoll, 0) != 0 { | |
. . 2463: if _g_.m.p != 0 { | |
. . 2464: throw("findrunnable: netpoll with p") | |
. . 2465: } | |
. . 2466: if _g_.m.spinning { | |
. . 2467: throw("findrunnable: netpoll with spinning") | |
. . 2468: } | |
. 660ms 2469: gp := netpoll(true) // block until new work is available | |
10ms 10ms 2470: atomic.Store64(&sched.lastpoll, uint64(nanotime())) | |
. . 2471: if gp != nil { | |
. 810ms 2472: lock(&sched.lock) | |
. . 2473: _p_ = pidleget() | |
. 810ms 2474: unlock(&sched.lock) | |
. . 2475: if _p_ != nil { | |
. . 2476: acquirep(_p_) | |
. 430ms 2477: injectglist(gp.schedlink.ptr()) | |
. . 2478: casgstatus(gp, _Gwaiting, _Grunnable) | |
. . 2479: if trace.enabled { | |
. . 2480: traceGoUnpark(gp, 0) | |
. . 2481: } | |
. . 2482: return gp, false | |
. . 2483: } | |
. 400ms 2484: injectglist(gp) | |
. . 2485: } | |
. . 2486: } | |
. 4.63s 2487: stopm() | |
. . 2488: goto top | |
. . 2489:} | |
. . 2490: | |
. . 2491:// pollWork returns true if there is non-background work this P could | |
. . 2492:// be doing. This is a fairly lightweight check to be used for |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment