Commit 04b1cfa9 authored by Dmitriy Vyukov's avatar Dmitriy Vyukov

net: reduce number of memory allocations during IO operations

Embed all data necessary for read/write operations directly into netFD.

benchmark                    old ns/op    new ns/op    delta
BenchmarkTCP4Persistent          27669        23341  -15.64%
BenchmarkTCP4Persistent-2        18173        12558  -30.90%
BenchmarkTCP4Persistent-4        10390         7319  -29.56%

This change will intentionally break all builders to see
how many allocations they do per read/write.
This will be fixed soon afterwards.

R=golang-dev, alex.brainman
CC=golang-dev
https://golang.org/cl/12413043
parent 9c0500b4
This diff is collapsed.
...@@ -10,20 +10,6 @@ import ( ...@@ -10,20 +10,6 @@ import (
"syscall" "syscall"
) )
type sendfileOp struct {
anOp
src syscall.Handle // source
n uint32
}
func (o *sendfileOp) Submit() (err error) {
return syscall.TransmitFile(o.fd.sysfd, o.src, o.n, 0, &o.o, nil, syscall.TF_WRITE_BEHIND)
}
func (o *sendfileOp) Name() string {
return "TransmitFile"
}
// sendFile copies the contents of r to c using the TransmitFile // sendFile copies the contents of r to c using the TransmitFile
// system call to minimize copies. // system call to minimize copies.
// //
...@@ -33,7 +19,7 @@ func (o *sendfileOp) Name() string { ...@@ -33,7 +19,7 @@ func (o *sendfileOp) Name() string {
// if handled == false, sendFile performed no work. // if handled == false, sendFile performed no work.
// //
// Note that sendfile for windows does not suppport >2GB file. // Note that sendfile for windows does not suppport >2GB file.
func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) { func sendFile(fd *netFD, r io.Reader) (written int64, err error, handled bool) {
var n int64 = 0 // by default, copy until EOF var n int64 = 0 // by default, copy until EOF
lr, ok := r.(*io.LimitedReader) lr, ok := r.(*io.LimitedReader)
...@@ -48,18 +34,18 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) { ...@@ -48,18 +34,18 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
return 0, nil, false return 0, nil, false
} }
if err := c.incref(false); err != nil { if err := fd.incref(false); err != nil {
return 0, err, true return 0, err, true
} }
defer c.decref() defer fd.decref()
c.wio.Lock() o := &fd.wop
defer c.wio.Unlock() o.mu.Lock()
defer o.mu.Unlock()
var o sendfileOp o.qty = uint32(n)
o.Init(c, 'w') o.handle = syscall.Handle(f.Fd())
o.n = uint32(n) done, err := iosrv.ExecIO(o, "TransmitFile", func(o *operation) error {
o.src = syscall.Handle(f.Fd()) return syscall.TransmitFile(o.fd.sysfd, o.handle, o.qty, 0, &o.o, nil, syscall.TF_WRITE_BEHIND)
done, err := iosrv.ExecIO(&o) })
if err != nil { if err != nil {
return 0, err, false return 0, err, false
} }
......
...@@ -6,6 +6,7 @@ package net ...@@ -6,6 +6,7 @@ package net
import ( import (
"fmt" "fmt"
"io"
"reflect" "reflect"
"runtime" "runtime"
"sync" "sync"
...@@ -327,3 +328,46 @@ func TestTCPConcurrentAccept(t *testing.T) { ...@@ -327,3 +328,46 @@ func TestTCPConcurrentAccept(t *testing.T) {
ln.Close() ln.Close()
wg.Wait() wg.Wait()
} }
func TestTCPReadWriteMallocs(t *testing.T) {
maxMallocs := 0
switch runtime.GOOS {
// Add other OSes if you know how many mallocs they do.
case "windows":
maxMallocs = 0
}
ln, err := Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("Listen failed: %v", err)
}
defer ln.Close()
var server Conn
errc := make(chan error)
go func() {
var err error
server, err = ln.Accept()
errc <- err
}()
client, err := Dial("tcp", ln.Addr().String())
if err != nil {
t.Fatalf("Dial failed: %v", err)
}
if err := <-errc; err != nil {
t.Fatalf("Accept failed: %v", err)
}
defer server.Close()
var buf [128]byte
mallocs := testing.AllocsPerRun(1000, func() {
_, err := server.Write(buf[:])
if err != nil {
t.Fatalf("Write failed: %v", err)
}
_, err = io.ReadFull(client, buf[:])
if err != nil {
t.Fatalf("Read failed: %v", err)
}
})
if int(mallocs) > maxMallocs {
t.Fatalf("Got %v allocs, want %v", mallocs, maxMallocs)
}
}
...@@ -16,9 +16,9 @@ extern void *runtime·GetQueuedCompletionStatus; ...@@ -16,9 +16,9 @@ extern void *runtime·GetQueuedCompletionStatus;
#define INVALID_HANDLE_VALUE ((uintptr)-1) #define INVALID_HANDLE_VALUE ((uintptr)-1)
// net_anOp must be the same as beginning of net.anOp. Keep these in sync. // net_op must be the same as beginning of net.operation. Keep these in sync.
typedef struct net_anOp net_anOp; typedef struct net_op net_op;
struct net_anOp struct net_op
{ {
// used by windows // used by windows
Overlapped o; Overlapped o;
...@@ -66,7 +66,7 @@ runtime·netpoll(bool block) ...@@ -66,7 +66,7 @@ runtime·netpoll(bool block)
{ {
uint32 wait, qty, key; uint32 wait, qty, key;
int32 mode, errno; int32 mode, errno;
net_anOp *o; net_op *o;
G *gp; G *gp;
if(iocphandle == INVALID_HANDLE_VALUE) if(iocphandle == INVALID_HANDLE_VALUE)
......
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