Skip to content

Commit 3b1fc93

Browse files
ericlagergrengopherbot
authored andcommitted
unix: avoid allocations for common uses of Readv, Writev, etc.
Fixes golang/go#57296 Change-Id: Ifd57487122a590df467e97e2d35f388a58cc080d Reviewed-on: https://go-review.googlesource.com/c/sys/+/457815 Run-TryBot: Ian Lance Taylor <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> Run-TryBot: Ian Lance Taylor <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Auto-Submit: Ian Lance Taylor <[email protected]> Reviewed-by: Benny Siegert <[email protected]> Reviewed-by: Matt Layher <[email protected]>
1 parent 2204b66 commit 3b1fc93

File tree

2 files changed

+70
-13
lines changed

2 files changed

+70
-13
lines changed

unix/syscall_linux.go

+29-13
Original file line numberDiff line numberDiff line change
@@ -1973,17 +1973,27 @@ func Signalfd(fd int, sigmask *Sigset_t, flags int) (newfd int, err error) {
19731973
//sys preadv2(fd int, iovs []Iovec, offs_l uintptr, offs_h uintptr, flags int) (n int, err error) = SYS_PREADV2
19741974
//sys pwritev2(fd int, iovs []Iovec, offs_l uintptr, offs_h uintptr, flags int) (n int, err error) = SYS_PWRITEV2
19751975

1976-
func bytes2iovec(bs [][]byte) []Iovec {
1977-
iovecs := make([]Iovec, len(bs))
1978-
for i, b := range bs {
1979-
iovecs[i].SetLen(len(b))
1976+
// minIovec is the size of the small initial allocation used by
1977+
// Readv, Writev, etc.
1978+
//
1979+
// This small allocation gets stack allocated, which lets the
1980+
// common use case of len(iovs) <= minIovs avoid more expensive
1981+
// heap allocations.
1982+
const minIovec = 8
1983+
1984+
// appendBytes converts bs to Iovecs and appends them to vecs.
1985+
func appendBytes(vecs []Iovec, bs [][]byte) []Iovec {
1986+
for _, b := range bs {
1987+
var v Iovec
1988+
v.SetLen(len(b))
19801989
if len(b) > 0 {
1981-
iovecs[i].Base = &b[0]
1990+
v.Base = &b[0]
19821991
} else {
1983-
iovecs[i].Base = (*byte)(unsafe.Pointer(&_zero))
1992+
v.Base = (*byte)(unsafe.Pointer(&_zero))
19841993
}
1994+
vecs = append(vecs, v)
19851995
}
1986-
return iovecs
1996+
return vecs
19871997
}
19881998

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

19952005
func Readv(fd int, iovs [][]byte) (n int, err error) {
1996-
iovecs := bytes2iovec(iovs)
2006+
iovecs := make([]Iovec, 0, minIovec)
2007+
iovecs = appendBytes(iovecs, iovs)
19972008
n, err = readv(fd, iovecs)
19982009
readvRacedetect(iovecs, n, err)
19992010
return n, err
20002011
}
20012012

20022013
func Preadv(fd int, iovs [][]byte, offset int64) (n int, err error) {
2003-
iovecs := bytes2iovec(iovs)
2014+
iovecs := make([]Iovec, 0, minIovec)
2015+
iovecs = appendBytes(iovecs, iovs)
20042016
lo, hi := offs2lohi(offset)
20052017
n, err = preadv(fd, iovecs, lo, hi)
20062018
readvRacedetect(iovecs, n, err)
20072019
return n, err
20082020
}
20092021

20102022
func Preadv2(fd int, iovs [][]byte, offset int64, flags int) (n int, err error) {
2011-
iovecs := bytes2iovec(iovs)
2023+
iovecs := make([]Iovec, 0, minIovec)
2024+
iovecs = appendBytes(iovecs, iovs)
20122025
lo, hi := offs2lohi(offset)
20132026
n, err = preadv2(fd, iovecs, lo, hi, flags)
20142027
readvRacedetect(iovecs, n, err)
@@ -2035,7 +2048,8 @@ func readvRacedetect(iovecs []Iovec, n int, err error) {
20352048
}
20362049

20372050
func Writev(fd int, iovs [][]byte) (n int, err error) {
2038-
iovecs := bytes2iovec(iovs)
2051+
iovecs := make([]Iovec, 0, minIovec)
2052+
iovecs = appendBytes(iovecs, iovs)
20392053
if raceenabled {
20402054
raceReleaseMerge(unsafe.Pointer(&ioSync))
20412055
}
@@ -2045,7 +2059,8 @@ func Writev(fd int, iovs [][]byte) (n int, err error) {
20452059
}
20462060

20472061
func Pwritev(fd int, iovs [][]byte, offset int64) (n int, err error) {
2048-
iovecs := bytes2iovec(iovs)
2062+
iovecs := make([]Iovec, 0, minIovec)
2063+
iovecs = appendBytes(iovecs, iovs)
20492064
if raceenabled {
20502065
raceReleaseMerge(unsafe.Pointer(&ioSync))
20512066
}
@@ -2056,7 +2071,8 @@ func Pwritev(fd int, iovs [][]byte, offset int64) (n int, err error) {
20562071
}
20572072

20582073
func Pwritev2(fd int, iovs [][]byte, offset int64, flags int) (n int, err error) {
2059-
iovecs := bytes2iovec(iovs)
2074+
iovecs := make([]Iovec, 0, minIovec)
2075+
iovecs = appendBytes(iovecs, iovs)
20602076
if raceenabled {
20612077
raceReleaseMerge(unsafe.Pointer(&ioSync))
20622078
}

unix/syscall_linux_test.go

+41
Original file line numberDiff line numberDiff line change
@@ -1132,3 +1132,44 @@ func TestPwritevOffsets(t *testing.T) {
11321132
t.Fatalf("expected size to be %d, got %d", want, info.Size())
11331133
}
11341134
}
1135+
1136+
func TestReadvAllocate(t *testing.T) {
1137+
f, err := os.Create(filepath.Join(t.TempDir(), "test"))
1138+
if err != nil {
1139+
t.Fatal(err)
1140+
}
1141+
t.Cleanup(func() { f.Close() })
1142+
1143+
test := func(name string, fn func(fd int)) {
1144+
n := int(testing.AllocsPerRun(100, func() {
1145+
fn(int(f.Fd()))
1146+
}))
1147+
if n != 0 {
1148+
t.Errorf("%q got %d allocations, want 0", name, n)
1149+
}
1150+
}
1151+
1152+
iovs := make([][]byte, 8)
1153+
for i := range iovs {
1154+
iovs[i] = []byte{'A'}
1155+
}
1156+
1157+
test("Writev", func(fd int) {
1158+
unix.Writev(fd, iovs)
1159+
})
1160+
test("Pwritev", func(fd int) {
1161+
unix.Pwritev(fd, iovs, 0)
1162+
})
1163+
test("Pwritev2", func(fd int) {
1164+
unix.Pwritev2(fd, iovs, 0, 0)
1165+
})
1166+
test("Readv", func(fd int) {
1167+
unix.Readv(fd, iovs)
1168+
})
1169+
test("Preadv", func(fd int) {
1170+
unix.Preadv(fd, iovs, 0)
1171+
})
1172+
test("Preadv2", func(fd int) {
1173+
unix.Preadv2(fd, iovs, 0, 0)
1174+
})
1175+
}

0 commit comments

Comments
 (0)