Skip to content

Commit

Permalink
unix: avoid allocations for common uses of Readv, Writev, etc.
Browse files Browse the repository at this point in the history
Fixes golang/go#57296

Change-Id: Ifd57487122a590df467e97e2d35f388a58cc080d
Reviewed-on: https://go-review.googlesource.com/c/sys/+/457815
Run-TryBot: Ian Lance Taylor <iant@google.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Auto-Submit: Ian Lance Taylor <iant@google.com>
Reviewed-by: Benny Siegert <bsiegert@gmail.com>
Reviewed-by: Matt Layher <mdlayher@gmail.com>
  • Loading branch information
ericlagergren authored and gopherbot committed Dec 21, 2022
1 parent 2204b66 commit 3b1fc93
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 13 deletions.
42 changes: 29 additions & 13 deletions unix/syscall_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -1973,17 +1973,27 @@ func Signalfd(fd int, sigmask *Sigset_t, flags int) (newfd int, err error) {
//sys preadv2(fd int, iovs []Iovec, offs_l uintptr, offs_h uintptr, flags int) (n int, err error) = SYS_PREADV2
//sys pwritev2(fd int, iovs []Iovec, offs_l uintptr, offs_h uintptr, flags int) (n int, err error) = SYS_PWRITEV2

func bytes2iovec(bs [][]byte) []Iovec {
iovecs := make([]Iovec, len(bs))
for i, b := range bs {
iovecs[i].SetLen(len(b))
// minIovec is the size of the small initial allocation used by
// Readv, Writev, etc.
//
// This small allocation gets stack allocated, which lets the
// common use case of len(iovs) <= minIovs avoid more expensive
// heap allocations.
const minIovec = 8

// appendBytes converts bs to Iovecs and appends them to vecs.
func appendBytes(vecs []Iovec, bs [][]byte) []Iovec {
for _, b := range bs {
var v Iovec
v.SetLen(len(b))
if len(b) > 0 {
iovecs[i].Base = &b[0]
v.Base = &b[0]
} else {
iovecs[i].Base = (*byte)(unsafe.Pointer(&_zero))
v.Base = (*byte)(unsafe.Pointer(&_zero))
}
vecs = append(vecs, v)
}
return iovecs
return vecs
}

// offs2lohi splits offs into its low and high order bits.
Expand All @@ -1993,22 +2003,25 @@ func offs2lohi(offs int64) (lo, hi uintptr) {
}

func Readv(fd int, iovs [][]byte) (n int, err error) {
iovecs := bytes2iovec(iovs)
iovecs := make([]Iovec, 0, minIovec)
iovecs = appendBytes(iovecs, iovs)
n, err = readv(fd, iovecs)
readvRacedetect(iovecs, n, err)
return n, err
}

func Preadv(fd int, iovs [][]byte, offset int64) (n int, err error) {
iovecs := bytes2iovec(iovs)
iovecs := make([]Iovec, 0, minIovec)
iovecs = appendBytes(iovecs, iovs)
lo, hi := offs2lohi(offset)
n, err = preadv(fd, iovecs, lo, hi)
readvRacedetect(iovecs, n, err)
return n, err
}

func Preadv2(fd int, iovs [][]byte, offset int64, flags int) (n int, err error) {
iovecs := bytes2iovec(iovs)
iovecs := make([]Iovec, 0, minIovec)
iovecs = appendBytes(iovecs, iovs)
lo, hi := offs2lohi(offset)
n, err = preadv2(fd, iovecs, lo, hi, flags)
readvRacedetect(iovecs, n, err)
Expand All @@ -2035,7 +2048,8 @@ func readvRacedetect(iovecs []Iovec, n int, err error) {
}

func Writev(fd int, iovs [][]byte) (n int, err error) {
iovecs := bytes2iovec(iovs)
iovecs := make([]Iovec, 0, minIovec)
iovecs = appendBytes(iovecs, iovs)
if raceenabled {
raceReleaseMerge(unsafe.Pointer(&ioSync))
}
Expand All @@ -2045,7 +2059,8 @@ func Writev(fd int, iovs [][]byte) (n int, err error) {
}

func Pwritev(fd int, iovs [][]byte, offset int64) (n int, err error) {
iovecs := bytes2iovec(iovs)
iovecs := make([]Iovec, 0, minIovec)
iovecs = appendBytes(iovecs, iovs)
if raceenabled {
raceReleaseMerge(unsafe.Pointer(&ioSync))
}
Expand All @@ -2056,7 +2071,8 @@ func Pwritev(fd int, iovs [][]byte, offset int64) (n int, err error) {
}

func Pwritev2(fd int, iovs [][]byte, offset int64, flags int) (n int, err error) {
iovecs := bytes2iovec(iovs)
iovecs := make([]Iovec, 0, minIovec)
iovecs = appendBytes(iovecs, iovs)
if raceenabled {
raceReleaseMerge(unsafe.Pointer(&ioSync))
}
Expand Down
41 changes: 41 additions & 0 deletions unix/syscall_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1132,3 +1132,44 @@ func TestPwritevOffsets(t *testing.T) {
t.Fatalf("expected size to be %d, got %d", want, info.Size())
}
}

func TestReadvAllocate(t *testing.T) {
f, err := os.Create(filepath.Join(t.TempDir(), "test"))
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { f.Close() })

test := func(name string, fn func(fd int)) {
n := int(testing.AllocsPerRun(100, func() {
fn(int(f.Fd()))
}))
if n != 0 {
t.Errorf("%q got %d allocations, want 0", name, n)
}
}

iovs := make([][]byte, 8)
for i := range iovs {
iovs[i] = []byte{'A'}
}

test("Writev", func(fd int) {
unix.Writev(fd, iovs)
})
test("Pwritev", func(fd int) {
unix.Pwritev(fd, iovs, 0)
})
test("Pwritev2", func(fd int) {
unix.Pwritev2(fd, iovs, 0, 0)
})
test("Readv", func(fd int) {
unix.Readv(fd, iovs)
})
test("Preadv", func(fd int) {
unix.Preadv(fd, iovs, 0)
})
test("Preadv2", func(fd int) {
unix.Preadv2(fd, iovs, 0, 0)
})
}

0 comments on commit 3b1fc93

Please sign in to comment.