mirror of
https://github.com/octoleo/syncthing.git
synced 2024-12-23 03:18:59 +00:00
Reuse a timer instead of allocating a new one in subscription.Poll
This is surprisingly memory expensive when Poll gets called a lot, such as when syncing lots of small files generating itemstarted/itemfinished events. It's line number three in this heap profile on the TestBenchmarkManyFiles test: jb@syno:~/src/github.com/syncthing/syncthing/test (master) $ go tool pprof ../bin/syncthing heap-13194.pprof Entering interactive mode (type "help" for commands) (pprof) top 80.91MB of 83.05MB total (97.42%) Dropped 1024 nodes (cum <= 0.42MB) Showing top 10 nodes out of 85 (cum >= 1.75MB) flat flat% sum% cum cum% 32MB 38.53% 38.53% 32.01MB 38.54% github.com/syndtr/goleveldb/leveldb/memdb.New 22.16MB 26.68% 65.21% 22.16MB 26.68% github.com/syndtr/goleveldb/leveldb/util.(*BufferPool).Get 13.02MB 15.68% 80.89% 13.02MB 15.68% time.NewTimer 6.94MB 8.35% 89.24% 6.94MB 8.35% github.com/syndtr/goleveldb/leveldb/memdb.(*DB).Put 3.18MB 3.82% 93.06% 3.18MB 3.82% github.com/calmh/xdr.(*Reader).ReadBytesMaxInto With this change the allocation is removed entirely.
This commit is contained in:
parent
e215cf6fb8
commit
90a1d99785
@ -103,6 +103,7 @@ type Subscription struct {
|
|||||||
mask EventType
|
mask EventType
|
||||||
id int
|
id int
|
||||||
events chan Event
|
events chan Event
|
||||||
|
timeout *time.Timer
|
||||||
}
|
}
|
||||||
|
|
||||||
var Default = NewLogger()
|
var Default = NewLogger()
|
||||||
@ -152,6 +153,7 @@ func (l *Logger) Subscribe(mask EventType) *Subscription {
|
|||||||
mask: mask,
|
mask: mask,
|
||||||
id: l.nextID,
|
id: l.nextID,
|
||||||
events: make(chan Event, BufferSize),
|
events: make(chan Event, BufferSize),
|
||||||
|
timeout: time.NewTimer(0),
|
||||||
}
|
}
|
||||||
l.nextID++
|
l.nextID++
|
||||||
l.subs[s.id] = s
|
l.subs[s.id] = s
|
||||||
@ -169,19 +171,22 @@ func (l *Logger) Unsubscribe(s *Subscription) {
|
|||||||
l.mutex.Unlock()
|
l.mutex.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Poll returns an event from the subscription or an error if the poll times
|
||||||
|
// out of the event channel is closed. Poll should not be called concurrently
|
||||||
|
// from multiple goroutines for a single subscription.
|
||||||
func (s *Subscription) Poll(timeout time.Duration) (Event, error) {
|
func (s *Subscription) Poll(timeout time.Duration) (Event, error) {
|
||||||
if debug {
|
if debug {
|
||||||
dl.Debugln("poll", timeout)
|
dl.Debugln("poll", timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
to := time.After(timeout)
|
s.timeout.Reset(timeout)
|
||||||
select {
|
select {
|
||||||
case e, ok := <-s.events:
|
case e, ok := <-s.events:
|
||||||
if !ok {
|
if !ok {
|
||||||
return e, ErrClosed
|
return e, ErrClosed
|
||||||
}
|
}
|
||||||
return e, nil
|
return e, nil
|
||||||
case <-to:
|
case <-s.timeout.C:
|
||||||
return Event{}, ErrTimeout
|
return Event{}, ErrTimeout
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user