From: Ian Lance Taylor Date: Fri, 18 Mar 2011 18:50:19 +0000 (-0700) Subject: net: Use preallocated buffer for epoll. X-Git-Tag: weekly.2011-03-28~91 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=add8c8db60940e8099f5cb9ff9af80c28763499e;p=gostls13.git net: Use preallocated buffer for epoll. Reduces rpc test "mallocs per rpc round trip" by 4. Gains ~8% on a localhost client/server RPC test. R=rsc CC=golang-dev https://golang.org/cl/4284056 --- diff --git a/src/pkg/net/fd_linux.go b/src/pkg/net/fd_linux.go index 505ac4ef73..69fbc02c0c 100644 --- a/src/pkg/net/fd_linux.go +++ b/src/pkg/net/fd_linux.go @@ -22,6 +22,15 @@ type pollster struct { // Events we're already waiting for // Must hold pollServer lock events map[int]uint32 + + // An event buffer for EpollWait. + // Used without a lock, may only be used by WaitFD. + waitEventBuf [10]syscall.EpollEvent + waitEvents []syscall.EpollEvent + + // An event buffer for EpollCtl, to avoid a malloc. + // Must hold pollServer lock. + ctlEvent syscall.EpollEvent } func newpollster() (p *pollster, err os.Error) { @@ -30,7 +39,7 @@ func newpollster() (p *pollster, err os.Error) { // The arg to epoll_create is a hint to the kernel // about the number of FDs we will care about. - // We don't know. + // We don't know, and since 2.6.8 the kernel ignores it anyhow. if p.epfd, e = syscall.EpollCreate(16); e != 0 { return nil, os.NewSyscallError("epoll_create", e) } @@ -41,17 +50,16 @@ func newpollster() (p *pollster, err os.Error) { func (p *pollster) AddFD(fd int, mode int, repeat bool) os.Error { // pollServer is locked. - var ev syscall.EpollEvent var already bool - ev.Fd = int32(fd) - ev.Events, already = p.events[fd] + p.ctlEvent.Fd = int32(fd) + p.ctlEvent.Events, already = p.events[fd] if !repeat { - ev.Events |= syscall.EPOLLONESHOT + p.ctlEvent.Events |= syscall.EPOLLONESHOT } if mode == 'r' { - ev.Events |= readFlags + p.ctlEvent.Events |= readFlags } else { - ev.Events |= writeFlags + p.ctlEvent.Events |= writeFlags } var op int @@ -60,10 +68,10 @@ func (p *pollster) AddFD(fd int, mode int, repeat bool) os.Error { } else { op = syscall.EPOLL_CTL_ADD } - if e := syscall.EpollCtl(p.epfd, op, fd, &ev); e != 0 { + if e := syscall.EpollCtl(p.epfd, op, fd, &p.ctlEvent); e != 0 { return os.NewSyscallError("epoll_ctl", e) } - p.events[fd] = ev.Events + p.events[fd] = p.ctlEvent.Events return nil } @@ -87,10 +95,9 @@ func (p *pollster) StopWaiting(fd int, bits uint) { // event in the kernel. Otherwise, delete it. events &= ^uint32(bits) if int32(events)&^syscall.EPOLLONESHOT != 0 { - var ev syscall.EpollEvent - ev.Fd = int32(fd) - ev.Events = events - if e := syscall.EpollCtl(p.epfd, syscall.EPOLL_CTL_MOD, fd, &ev); e != 0 { + p.ctlEvent.Fd = int32(fd) + p.ctlEvent.Events = events + if e := syscall.EpollCtl(p.epfd, syscall.EPOLL_CTL_MOD, fd, &p.ctlEvent); e != 0 { print("Epoll modify fd=", fd, ": ", os.Errno(e).String(), "\n") } p.events[fd] = events @@ -113,28 +120,31 @@ func (p *pollster) DelFD(fd int, mode int) { } func (p *pollster) WaitFD(s *pollServer, nsec int64) (fd int, mode int, err os.Error) { - s.Unlock() - - // Get an event. - var evarray [1]syscall.EpollEvent - ev := &evarray[0] - var msec int = -1 - if nsec > 0 { - msec = int((nsec + 1e6 - 1) / 1e6) - } - n, e := syscall.EpollWait(p.epfd, evarray[0:], msec) - for e == syscall.EAGAIN || e == syscall.EINTR { - n, e = syscall.EpollWait(p.epfd, evarray[0:], msec) - } + for len(p.waitEvents) == 0 { + var msec int = -1 + if nsec > 0 { + msec = int((nsec + 1e6 - 1) / 1e6) + } - s.Lock() + s.Unlock() + n, e := syscall.EpollWait(p.epfd, p.waitEventBuf[0:], msec) + s.Lock() - if e != 0 { - return -1, 0, os.NewSyscallError("epoll_wait", e) - } - if n == 0 { - return -1, 0, nil + if e != 0 { + if e == syscall.EAGAIN || e == syscall.EINTR { + continue + } + return -1, 0, os.NewSyscallError("epoll_wait", e) + } + if n == 0 { + return -1, 0, nil + } + p.waitEvents = p.waitEventBuf[0:n] } + + ev := &p.waitEvents[0] + p.waitEvents = p.waitEvents[1:] + fd = int(ev.Fd) if ev.Events&writeFlags != 0 {