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 (
"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
// system call to minimize copies.
//
......@@ -33,7 +19,7 @@ func (o *sendfileOp) Name() string {
// if handled == false, sendFile performed no work.
//
// 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
lr, ok := r.(*io.LimitedReader)
......@@ -48,18 +34,18 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
return 0, nil, false
}
if err := c.incref(false); err != nil {
if err := fd.incref(false); err != nil {
return 0, err, true
}
defer c.decref()
c.wio.Lock()
defer c.wio.Unlock()
var o sendfileOp
o.Init(c, 'w')
o.n = uint32(n)
o.src = syscall.Handle(f.Fd())
done, err := iosrv.ExecIO(&o)
defer fd.decref()
o := &fd.wop
o.mu.Lock()
defer o.mu.Unlock()
o.qty = uint32(n)
o.handle = syscall.Handle(f.Fd())
done, err := iosrv.ExecIO(o, "TransmitFile", func(o *operation) error {
return syscall.TransmitFile(o.fd.sysfd, o.handle, o.qty, 0, &o.o, nil, syscall.TF_WRITE_BEHIND)
})
if err != nil {
return 0, err, false
}
......
......@@ -6,6 +6,7 @@ package net
import (
"fmt"
"io"
"reflect"
"runtime"
"sync"
......@@ -327,3 +328,46 @@ func TestTCPConcurrentAccept(t *testing.T) {
ln.Close()
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;
#define INVALID_HANDLE_VALUE ((uintptr)-1)
// net_anOp must be the same as beginning of net.anOp. Keep these in sync.
typedef struct net_anOp net_anOp;
struct net_anOp
// net_op must be the same as beginning of net.operation. Keep these in sync.
typedef struct net_op net_op;
struct net_op
{
// used by windows
Overlapped o;
......@@ -66,7 +66,7 @@ runtime·netpoll(bool block)
{
uint32 wait, qty, key;
int32 mode, errno;
net_anOp *o;
net_op *o;
G *gp;
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