-
Andy Pan authored
For the moment, Go calls sendfile(2) to transfer at most 4MB at a time while sendfile(2) actually allows a larger amount of data on one call. To reduce system calls of sendfile(2) during data copying, we should specify the number of bytes to copy as large as possible. This optimization is especially advantageous for bulky file-to-file copies, it would lead to a performance boost, the magnitude of this performance increase may not be very exciting, but it can also cut down the CPU overhead by decreasing the number of system calls. This is also how we've done in sendfile_windows.go with TransmitFile. goos: linux goarch: amd64 pkg: os cpu: DO-Premium-AMD │ old │ new │ │ sec/op │ sec/op vs base │ SendFile-8 1.135 ± 4% 1.052 ± 3% -7.24% (p=0.000 n=10) │ old │ new │ │ B/s │ B/s vs base │ SendFile-8 902.5Mi ± 4% 973.0Mi ± 3% +7.81% (p=0.000 n=10) │ old │ new │ │ B/op │ B/op vs base │ SendFile-8 272.0 ± 0% 272.0 ± 0% ~ (p=1.000 n=10) ¹ ¹ all samples are equal │ old │ new │ │ allocs/op │ allocs/op vs base │ SendFile-8 20.00 ± 0% 20.00 ± 0% ~ (p=1.000 n=10) ¹ ¹ all samples are equal Change-Id: Ib4d4c6bc693e23db24697363b29226f0c9776bb0 Reviewed-on: https://go-review.googlesource.com/c/go/+/605235 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Auto-Submit: Ian Lance Taylor <iant@golang.org> TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by:
Jorropo <jorropo.pgm@gmail.com> Run-TryBot: Andy Pan <panjf2000@gmail.com> Reviewed-by:
Ian Lance Taylor <iant@google.com> Reviewed-by:
Carlos Amedee <carlos@golang.org>
Andy Pan authoredFor the moment, Go calls sendfile(2) to transfer at most 4MB at a time while sendfile(2) actually allows a larger amount of data on one call. To reduce system calls of sendfile(2) during data copying, we should specify the number of bytes to copy as large as possible. This optimization is especially advantageous for bulky file-to-file copies, it would lead to a performance boost, the magnitude of this performance increase may not be very exciting, but it can also cut down the CPU overhead by decreasing the number of system calls. This is also how we've done in sendfile_windows.go with TransmitFile. goos: linux goarch: amd64 pkg: os cpu: DO-Premium-AMD │ old │ new │ │ sec/op │ sec/op vs base │ SendFile-8 1.135 ± 4% 1.052 ± 3% -7.24% (p=0.000 n=10) │ old │ new │ │ B/s │ B/s vs base │ SendFile-8 902.5Mi ± 4% 973.0Mi ± 3% +7.81% (p=0.000 n=10) │ old │ new │ │ B/op │ B/op vs base │ SendFile-8 272.0 ± 0% 272.0 ± 0% ~ (p=1.000 n=10) ¹ ¹ all samples are equal │ old │ new │ │ allocs/op │ allocs/op vs base │ SendFile-8 20.00 ± 0% 20.00 ± 0% ~ (p=1.000 n=10) ¹ ¹ all samples are equal Change-Id: Ib4d4c6bc693e23db24697363b29226f0c9776bb0 Reviewed-on: https://go-review.googlesource.com/c/go/+/605235 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Auto-Submit: Ian Lance Taylor <iant@golang.org> TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by:
Jorropo <jorropo.pgm@gmail.com> Run-TryBot: Andy Pan <panjf2000@gmail.com> Reviewed-by:
Ian Lance Taylor <iant@google.com> Reviewed-by:
Carlos Amedee <carlos@golang.org>
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
sendfile_solaris.go 2.29 KiB
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package poll
import "syscall"
//go:cgo_ldflag "-lsendfile"
// Not strictly needed, but very helpful for debugging, see issue #10221.
//
//go:cgo_import_dynamic _ _ "libsendfile.so"
//go:cgo_import_dynamic _ _ "libsocket.so"
// maxSendfileSize is the largest chunk size we ask the kernel to copy
// at a time.
// sendfile(2)s on SunOS derivatives don't have a limit on the size of
// data to copy at a time, we pick the typical SSIZE_MAX on 32-bit systems,
// which ought to be sufficient for all practical purposes.
const maxSendfileSize int = 1<<31 - 1
// SendFile wraps the sendfile system call.
func SendFile(dstFD *FD, src int, pos, remain int64) (written int64, err error, handled bool) {
defer func() {
TestHookDidSendFile(dstFD, src, written, err, handled)
}()
if err := dstFD.writeLock(); err != nil {
return 0, err, false
}
defer dstFD.writeUnlock()
if err := dstFD.pd.prepareWrite(dstFD.isFile); err != nil {
return 0, err, false
}
dst := dstFD.Sysfd
for remain > 0 {
n := maxSendfileSize
if int64(n) > remain {
n = int(remain)
}
pos1 := pos
n, err = syscall.Sendfile(dst, src, &pos1, n)
if err == syscall.EAGAIN || err == syscall.EINTR || err == syscall.EINVAL {
// Partial write or other quirks may have occurred.
//
// For EINVAL, this is another quirk on SunOS: sendfile() claims to support
// out_fd as a regular file but returns EINVAL when the out_fd is not a
// socket of SOCK_STREAM, while it actually sends out data anyway and updates
// the file offset.
n = int(pos1 - pos)
}
if n > 0 {
pos += int64(n)
written += int64(n)
remain -= int64(n)
continue
} else if err != syscall.EAGAIN && err != syscall.EINTR {
// This includes syscall.ENOSYS (no kernel
// support) and syscall.EINVAL (fd types which
// don't implement sendfile), and other errors.
// We should end the loop when there is no error
// returned from sendfile(2) or it is not a retryable error.
break
}
if err == syscall.EINTR {
continue
}
if err = dstFD.pd.waitWrite(dstFD.isFile); err != nil {
break
}
}
handled = written != 0 || (err != syscall.ENOSYS && err != syscall.EINVAL)
return
}