1
0
mirror of https://github.com/golang/go synced 2024-11-08 18:26:14 -07:00

runtime: don't recreate netpoll timers if they don't change

Currently we always delete both read and write timers and then
add them again. However, if user setups read and write deadline
separately, then we don't need to touch the other one.

name                  old time/op  new time/op  delta
TCP4OneShotTimeout-6  17.2µs ± 0%  17.2µs ± 0%     ~     (p=0.310 n=5+5)
SetReadDeadline-6      319ns ± 1%   274ns ± 2%  -13.94%  (p=0.008 n=5+5)

Update #25729

Change-Id: I4c869c3083521de6d0cd6ca99a7609d4dd84b4e4
Reviewed-on: https://go-review.googlesource.com/c/146338
Reviewed-by: Ian Lance Taylor <iant@golang.org>
This commit is contained in:
Dmitry Vyukov 2018-10-31 16:18:36 +01:00
parent a9280fa2de
commit 86d375498f

View File

@ -56,14 +56,15 @@ type pollDesc struct {
lock mutex // protects the following fields lock mutex // protects the following fields
fd uintptr fd uintptr
closing bool closing bool
seq uintptr // protects from stale timers and ready notifications user uint32 // user settable cookie
rseq uintptr // protects from stale read timers
rg uintptr // pdReady, pdWait, G waiting for read or nil rg uintptr // pdReady, pdWait, G waiting for read or nil
rt timer // read deadline timer (set if rt.f != nil) rt timer // read deadline timer (set if rt.f != nil)
rd int64 // read deadline rd int64 // read deadline
wseq uintptr // protects from stale write timers
wg uintptr // pdReady, pdWait, G waiting for write or nil wg uintptr // pdReady, pdWait, G waiting for write or nil
wt timer // write deadline timer wt timer // write deadline timer
wd int64 // write deadline wd int64 // write deadline
user uint32 // user settable cookie
} }
type pollCache struct { type pollCache struct {
@ -112,9 +113,10 @@ func poll_runtime_pollOpen(fd uintptr) (*pollDesc, int) {
} }
pd.fd = fd pd.fd = fd
pd.closing = false pd.closing = false
pd.seq++ pd.rseq++
pd.rg = 0 pd.rg = 0
pd.rd = 0 pd.rd = 0
pd.wseq++
pd.wg = 0 pd.wg = 0
pd.wd = 0 pd.wd = 0
unlock(&pd.lock) unlock(&pd.lock)
@ -197,17 +199,8 @@ func poll_runtime_pollSetDeadline(pd *pollDesc, d int64, mode int) {
unlock(&pd.lock) unlock(&pd.lock)
return return
} }
pd.seq++ // invalidate current timers rd0, wd0 := pd.rd, pd.wd
// Reset current timers. combo0 := rd0 > 0 && rd0 == wd0
if pd.rt.f != nil {
deltimer(&pd.rt)
pd.rt.f = nil
}
if pd.wt.f != nil {
deltimer(&pd.wt)
pd.wt.f = nil
}
// Setup new timers.
if d != 0 && d <= nanotime() { if d != 0 && d <= nanotime() {
d = -1 d = -1
} }
@ -217,28 +210,43 @@ func poll_runtime_pollSetDeadline(pd *pollDesc, d int64, mode int) {
if mode == 'w' || mode == 'r'+'w' { if mode == 'w' || mode == 'r'+'w' {
pd.wd = d pd.wd = d
} }
if pd.rd > 0 && pd.rd == pd.wd { combo := pd.rd > 0 && pd.rd == pd.wd
pd.rt.f = netpollDeadline // Reset current timers if necessary.
pd.rt.when = pd.rd if pd.rt.f != nil && (pd.rd != rd0 || combo != combo0) {
// Copy current seq into the timer arg. pd.rseq++ // invalidate current timers
// Timer func will check the seq against current descriptor seq, deltimer(&pd.rt)
// if they differ the descriptor was reused or timers were reset. pd.rt.f = nil
pd.rt.arg = pd }
pd.rt.seq = pd.seq if pd.wt.f != nil && (pd.wd != wd0 || combo != combo0) {
addtimer(&pd.rt) pd.wseq++ // invalidate current timers
deltimer(&pd.wt)
pd.wt.f = nil
}
// Setup new timers.
if combo {
if pd.rt.f == nil {
pd.rt.f = netpollDeadline
pd.rt.when = pd.rd
// Copy current seq into the timer arg.
// Timer func will check the seq against current descriptor seq,
// if they differ the descriptor was reused or timers were reset.
pd.rt.arg = pd
pd.rt.seq = pd.rseq
addtimer(&pd.rt)
}
} else { } else {
if pd.rd > 0 { if pd.rd > 0 && pd.rt.f == nil {
pd.rt.f = netpollReadDeadline pd.rt.f = netpollReadDeadline
pd.rt.when = pd.rd pd.rt.when = pd.rd
pd.rt.arg = pd pd.rt.arg = pd
pd.rt.seq = pd.seq pd.rt.seq = pd.rseq
addtimer(&pd.rt) addtimer(&pd.rt)
} }
if pd.wd > 0 { if pd.wd > 0 && pd.wt.f == nil {
pd.wt.f = netpollWriteDeadline pd.wt.f = netpollWriteDeadline
pd.wt.when = pd.wd pd.wt.when = pd.wd
pd.wt.arg = pd pd.wt.arg = pd
pd.wt.seq = pd.seq pd.wt.seq = pd.wseq
addtimer(&pd.wt) addtimer(&pd.wt)
} }
} }
@ -267,7 +275,8 @@ func poll_runtime_pollUnblock(pd *pollDesc) {
throw("runtime: unblock on closing polldesc") throw("runtime: unblock on closing polldesc")
} }
pd.closing = true pd.closing = true
pd.seq++ pd.rseq++
pd.wseq++
var rg, wg *g var rg, wg *g
atomicstorep(unsafe.Pointer(&rg), nil) // full memory barrier between store to closing and read of rg/wg in netpollunblock atomicstorep(unsafe.Pointer(&rg), nil) // full memory barrier between store to closing and read of rg/wg in netpollunblock
rg = netpollunblock(pd, 'r', false) rg = netpollunblock(pd, 'r', false)
@ -404,7 +413,11 @@ func netpolldeadlineimpl(pd *pollDesc, seq uintptr, read, write bool) {
lock(&pd.lock) lock(&pd.lock)
// Seq arg is seq when the timer was set. // Seq arg is seq when the timer was set.
// If it's stale, ignore the timer event. // If it's stale, ignore the timer event.
if seq != pd.seq { currentSeq := pd.rseq
if !read {
currentSeq = pd.wseq
}
if seq != currentSeq {
// The descriptor was reused or timers were reset. // The descriptor was reused or timers were reset.
unlock(&pd.lock) unlock(&pd.lock)
return return