dmz: use sendfile(2) when cloning /proc/self/exe

This results in a 5-20% speedup of dmz.CloneBinary(), depending on the
machine.

io.Copy:

  goos: linux
  goarch: amd64
  pkg: github.com/opencontainers/runc/libcontainer/dmz
  cpu: Intel(R) Core(TM) i5-10210U CPU @ 1.60GHz
  BenchmarkCloneBinary
  BenchmarkCloneBinary-8               139           8075074 ns/op
  PASS
  ok      github.com/opencontainers/runc/libcontainer/dmz 2.286s

unix.Sendfile:

  goos: linux
  goarch: amd64
  pkg: github.com/opencontainers/runc/libcontainer/dmz
  cpu: Intel(R) Core(TM) i5-10210U CPU @ 1.60GHz
  BenchmarkCloneBinary
  BenchmarkCloneBinary-8               192           6382121 ns/op
  PASS
  ok      github.com/opencontainers/runc/libcontainer/dmz 2.415s

Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
This commit is contained in:
Aleksa Sarai
2023-09-22 15:51:36 +10:00
parent f8348f64ae
commit 90c8d36afe
2 changed files with 41 additions and 1 deletions
+40
View File
@@ -5,6 +5,7 @@ package system
import (
"fmt"
"io"
"os"
"os/exec"
"strconv"
@@ -174,3 +175,42 @@ func ExecutableMemfd(comment string, flags int) (*os.File, error) {
}
return os.NewFile(uintptr(memfd), "/memfd:"+comment), nil
}
// Copy is like io.Copy except it uses sendfile(2) if the source and sink are
// both (*os.File) as an optimisation to make copies faster.
func Copy(dst io.Writer, src io.Reader) (copied int64, err error) {
dstFile, _ := dst.(*os.File)
srcFile, _ := src.(*os.File)
if dstFile != nil && srcFile != nil {
fi, err := srcFile.Stat()
if err != nil {
goto fallback
}
size := fi.Size()
for size > 0 {
n, err := unix.Sendfile(int(dstFile.Fd()), int(srcFile.Fd()), nil, int(size))
if n > 0 {
size -= int64(n)
copied += int64(n)
}
if err == unix.EINTR {
continue
}
if err != nil {
if copied == 0 {
// If we haven't copied anything so far, we can safely just
// fallback to io.Copy. We could always do the fallback but
// it's safer to error out in the case of a partial copy
// followed by an error (which should never happen).
goto fallback
}
return copied, fmt.Errorf("partial sendfile copy: %w", err)
}
}
return copied, nil
}
fallback:
return io.Copy(dst, src)
}