Commit 86d37549 authored by Dmitry Vyukov's avatar Dmitry Vyukov

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/146338Reviewed-by: 's avatarIan Lance Taylor <iant@golang.org>
parent a9280fa2
......@@ -56,14 +56,15 @@ type pollDesc struct {
lock mutex // protects the following fields
fd uintptr
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
rt timer // read deadline timer (set if rt.f != nil)
rd int64 // read deadline
wseq uintptr // protects from stale write timers
wg uintptr // pdReady, pdWait, G waiting for write or nil
wt timer // write deadline timer
wd int64 // write deadline
user uint32 // user settable cookie
}
type pollCache struct {
......@@ -112,9 +113,10 @@ func poll_runtime_pollOpen(fd uintptr) (*pollDesc, int) {
}
pd.fd = fd
pd.closing = false
pd.seq++
pd.rseq++
pd.rg = 0
pd.rd = 0
pd.wseq++
pd.wg = 0
pd.wd = 0
unlock(&pd.lock)
......@@ -197,17 +199,8 @@ func poll_runtime_pollSetDeadline(pd *pollDesc, d int64, mode int) {
unlock(&pd.lock)
return
}
pd.seq++ // invalidate current timers
// Reset current timers.
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.
rd0, wd0 := pd.rd, pd.wd
combo0 := rd0 > 0 && rd0 == wd0
if d != 0 && d <= nanotime() {
d = -1
}
......@@ -217,28 +210,43 @@ func poll_runtime_pollSetDeadline(pd *pollDesc, d int64, mode int) {
if mode == 'w' || mode == 'r'+'w' {
pd.wd = d
}
if pd.rd > 0 && pd.rd == pd.wd {
combo := pd.rd > 0 && pd.rd == pd.wd
// Reset current timers if necessary.
if pd.rt.f != nil && (pd.rd != rd0 || combo != combo0) {
pd.rseq++ // invalidate current timers
deltimer(&pd.rt)
pd.rt.f = nil
}
if pd.wt.f != nil && (pd.wd != wd0 || combo != combo0) {
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.seq
pd.rt.seq = pd.rseq
addtimer(&pd.rt)
}
} else {
if pd.rd > 0 {
if pd.rd > 0 && pd.rt.f == nil {
pd.rt.f = netpollReadDeadline
pd.rt.when = pd.rd
pd.rt.arg = pd
pd.rt.seq = pd.seq
pd.rt.seq = pd.rseq
addtimer(&pd.rt)
}
if pd.wd > 0 {
if pd.wd > 0 && pd.wt.f == nil {
pd.wt.f = netpollWriteDeadline
pd.wt.when = pd.wd
pd.wt.arg = pd
pd.wt.seq = pd.seq
pd.wt.seq = pd.wseq
addtimer(&pd.wt)
}
}
......@@ -267,7 +275,8 @@ func poll_runtime_pollUnblock(pd *pollDesc) {
throw("runtime: unblock on closing polldesc")
}
pd.closing = true
pd.seq++
pd.rseq++
pd.wseq++
var rg, wg *g
atomicstorep(unsafe.Pointer(&rg), nil) // full memory barrier between store to closing and read of rg/wg in netpollunblock
rg = netpollunblock(pd, 'r', false)
......@@ -404,7 +413,11 @@ func netpolldeadlineimpl(pd *pollDesc, seq uintptr, read, write bool) {
lock(&pd.lock)
// Seq arg is seq when the timer was set.
// 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.
unlock(&pd.lock)
return
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment