Skip to content

Commit 86d3754

Browse files
committed
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 <[email protected]>
1 parent a9280fa commit 86d3754

File tree

1 file changed

+42
-29
lines changed

1 file changed

+42
-29
lines changed

src/runtime/netpoll.go

Lines changed: 42 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -56,14 +56,15 @@ type pollDesc struct {
5656
lock mutex // protects the following fields
5757
fd uintptr
5858
closing bool
59-
seq uintptr // protects from stale timers and ready notifications
59+
user uint32 // user settable cookie
60+
rseq uintptr // protects from stale read timers
6061
rg uintptr // pdReady, pdWait, G waiting for read or nil
6162
rt timer // read deadline timer (set if rt.f != nil)
6263
rd int64 // read deadline
64+
wseq uintptr // protects from stale write timers
6365
wg uintptr // pdReady, pdWait, G waiting for write or nil
6466
wt timer // write deadline timer
6567
wd int64 // write deadline
66-
user uint32 // user settable cookie
6768
}
6869

6970
type pollCache struct {
@@ -112,9 +113,10 @@ func poll_runtime_pollOpen(fd uintptr) (*pollDesc, int) {
112113
}
113114
pd.fd = fd
114115
pd.closing = false
115-
pd.seq++
116+
pd.rseq++
116117
pd.rg = 0
117118
pd.rd = 0
119+
pd.wseq++
118120
pd.wg = 0
119121
pd.wd = 0
120122
unlock(&pd.lock)
@@ -197,17 +199,8 @@ func poll_runtime_pollSetDeadline(pd *pollDesc, d int64, mode int) {
197199
unlock(&pd.lock)
198200
return
199201
}
200-
pd.seq++ // invalidate current timers
201-
// Reset current timers.
202-
if pd.rt.f != nil {
203-
deltimer(&pd.rt)
204-
pd.rt.f = nil
205-
}
206-
if pd.wt.f != nil {
207-
deltimer(&pd.wt)
208-
pd.wt.f = nil
209-
}
210-
// Setup new timers.
202+
rd0, wd0 := pd.rd, pd.wd
203+
combo0 := rd0 > 0 && rd0 == wd0
211204
if d != 0 && d <= nanotime() {
212205
d = -1
213206
}
@@ -217,28 +210,43 @@ func poll_runtime_pollSetDeadline(pd *pollDesc, d int64, mode int) {
217210
if mode == 'w' || mode == 'r'+'w' {
218211
pd.wd = d
219212
}
220-
if pd.rd > 0 && pd.rd == pd.wd {
221-
pd.rt.f = netpollDeadline
222-
pd.rt.when = pd.rd
223-
// Copy current seq into the timer arg.
224-
// Timer func will check the seq against current descriptor seq,
225-
// if they differ the descriptor was reused or timers were reset.
226-
pd.rt.arg = pd
227-
pd.rt.seq = pd.seq
228-
addtimer(&pd.rt)
213+
combo := pd.rd > 0 && pd.rd == pd.wd
214+
// Reset current timers if necessary.
215+
if pd.rt.f != nil && (pd.rd != rd0 || combo != combo0) {
216+
pd.rseq++ // invalidate current timers
217+
deltimer(&pd.rt)
218+
pd.rt.f = nil
219+
}
220+
if pd.wt.f != nil && (pd.wd != wd0 || combo != combo0) {
221+
pd.wseq++ // invalidate current timers
222+
deltimer(&pd.wt)
223+
pd.wt.f = nil
224+
}
225+
// Setup new timers.
226+
if combo {
227+
if pd.rt.f == nil {
228+
pd.rt.f = netpollDeadline
229+
pd.rt.when = pd.rd
230+
// Copy current seq into the timer arg.
231+
// Timer func will check the seq against current descriptor seq,
232+
// if they differ the descriptor was reused or timers were reset.
233+
pd.rt.arg = pd
234+
pd.rt.seq = pd.rseq
235+
addtimer(&pd.rt)
236+
}
229237
} else {
230-
if pd.rd > 0 {
238+
if pd.rd > 0 && pd.rt.f == nil {
231239
pd.rt.f = netpollReadDeadline
232240
pd.rt.when = pd.rd
233241
pd.rt.arg = pd
234-
pd.rt.seq = pd.seq
242+
pd.rt.seq = pd.rseq
235243
addtimer(&pd.rt)
236244
}
237-
if pd.wd > 0 {
245+
if pd.wd > 0 && pd.wt.f == nil {
238246
pd.wt.f = netpollWriteDeadline
239247
pd.wt.when = pd.wd
240248
pd.wt.arg = pd
241-
pd.wt.seq = pd.seq
249+
pd.wt.seq = pd.wseq
242250
addtimer(&pd.wt)
243251
}
244252
}
@@ -267,7 +275,8 @@ func poll_runtime_pollUnblock(pd *pollDesc) {
267275
throw("runtime: unblock on closing polldesc")
268276
}
269277
pd.closing = true
270-
pd.seq++
278+
pd.rseq++
279+
pd.wseq++
271280
var rg, wg *g
272281
atomicstorep(unsafe.Pointer(&rg), nil) // full memory barrier between store to closing and read of rg/wg in netpollunblock
273282
rg = netpollunblock(pd, 'r', false)
@@ -404,7 +413,11 @@ func netpolldeadlineimpl(pd *pollDesc, seq uintptr, read, write bool) {
404413
lock(&pd.lock)
405414
// Seq arg is seq when the timer was set.
406415
// If it's stale, ignore the timer event.
407-
if seq != pd.seq {
416+
currentSeq := pd.rseq
417+
if !read {
418+
currentSeq = pd.wseq
419+
}
420+
if seq != currentSeq {
408421
// The descriptor was reused or timers were reset.
409422
unlock(&pd.lock)
410423
return

0 commit comments

Comments
 (0)