aboutsummaryrefslogtreecommitdiff
path: root/src/net
diff options
context:
space:
mode:
authorAndy Pan <panjf2000@gmail.com>2023-11-18 18:59:51 +0800
committerGopher Robot <gobot@golang.org>2024-02-07 20:33:40 +0000
commite55bf08d98b163e6f94d164fc650d2e427bc9c3b (patch)
tree2bb33b67bec076349a2fc5c8c1b5b8928dbf75f6 /src/net
parent6abeffb18ea263b14cbe5936e8bdbbf08546e4b8 (diff)
downloadgo-e55bf08d98b163e6f94d164fc650d2e427bc9c3b.tar.xz
net: support benchmark testing for sendfile on various platforms
When I introduced the benchmark test code for sendfile(2) in CL 425878, I only did it on Linux while the sendfile system call is also available on other Unix-like and Windows platforms, this CL will pick up where I left out. goos: darwin goarch: arm64 pkg: net BenchmarkSendFile/file-to-tcp/1024-10 2240488 749.5 ns/op 1366.30 MB/s 0 B/op 0 allocs/op BenchmarkSendFile/file-to-tcp/2048-10 1956669 850.4 ns/op 2408.38 MB/s 0 B/op 0 allocs/op BenchmarkSendFile/file-to-tcp/4096-10 840103 1593 ns/op 2571.30 MB/s 0 B/op 0 allocs/op BenchmarkSendFile/file-to-tcp/8192-10 449536 2881 ns/op 2843.35 MB/s 0 B/op 0 allocs/op BenchmarkSendFile/file-to-tcp/16384-10 269974 6307 ns/op 2597.86 MB/s 0 B/op 0 allocs/op BenchmarkSendFile/file-to-tcp/32768-10 137210 12646 ns/op 2591.09 MB/s 0 B/op 0 allocs/op BenchmarkSendFile/file-to-tcp/65536-10 66642 24557 ns/op 2668.74 MB/s 0 B/op 0 allocs/op BenchmarkSendFile/file-to-tcp/131072-10 37852 59550 ns/op 2201.03 MB/s 0 B/op 0 allocs/op BenchmarkSendFile/file-to-tcp/262144-10 16288 107859 ns/op 2430.44 MB/s 2 B/op 0 allocs/op BenchmarkSendFile/file-to-tcp/524288-10 10540 249957 ns/op 2097.52 MB/s 3 B/op 0 allocs/op BenchmarkSendFile/file-to-tcp/1048576-10 4982 419750 ns/op 2498.09 MB/s 6 B/op 0 allocs/op BenchmarkSendFile/file-to-unix/1024-10 1180185 1187 ns/op 862.66 MB/s 0 B/op 0 allocs/op BenchmarkSendFile/file-to-unix/2048-10 523159 2294 ns/op 892.78 MB/s 0 B/op 0 allocs/op BenchmarkSendFile/file-to-unix/4096-10 238792 5223 ns/op 784.29 MB/s 0 B/op 0 allocs/op BenchmarkSendFile/file-to-unix/8192-10 116611 10929 ns/op 749.58 MB/s 0 B/op 0 allocs/op BenchmarkSendFile/file-to-unix/16384-10 57568 19870 ns/op 824.57 MB/s 0 B/op 0 allocs/op BenchmarkSendFile/file-to-unix/32768-10 32280 33696 ns/op 972.47 MB/s 1 B/op 0 allocs/op BenchmarkSendFile/file-to-unix/65536-10 17242 72122 ns/op 908.69 MB/s 1 B/op 0 allocs/op BenchmarkSendFile/file-to-unix/131072-10 8350 159131 ns/op 823.67 MB/s 3 B/op 0 allocs/op BenchmarkSendFile/file-to-unix/262144-10 3872 318000 ns/op 824.35 MB/s 8 B/op 0 allocs/op BenchmarkSendFile/file-to-unix/524288-10 1766 600785 ns/op 872.67 MB/s 18 B/op 0 allocs/op BenchmarkSendFile/file-to-unix/1048576-10 993 1138624 ns/op 920.92 MB/s 33 B/op 0 allocs/op goos: linux goarch: amd64 pkg: net cpu: DO-Premium-AMD BenchmarkSendFile/file-to-tcp/1024-8 1796002 716.3 ns/op 1429.59 MB/s 0 B/op 0 allocs/op BenchmarkSendFile/file-to-tcp/2048-8 1196700 896.6 ns/op 2284.23 MB/s 0 B/op 0 allocs/op BenchmarkSendFile/file-to-tcp/4096-8 923604 2385 ns/op 1717.48 MB/s 0 B/op 0 allocs/op BenchmarkSendFile/file-to-tcp/8192-8 638967 9722 ns/op 842.60 MB/s 0 B/op 0 allocs/op BenchmarkSendFile/file-to-tcp/16384-8 357740 18710 ns/op 875.67 MB/s 0 B/op 0 allocs/op BenchmarkSendFile/file-to-tcp/32768-8 147417 17489 ns/op 1873.66 MB/s 0 B/op 0 allocs/op BenchmarkSendFile/file-to-tcp/65536-8 113054 58818 ns/op 1114.21 MB/s 0 B/op 0 allocs/op BenchmarkSendFile/file-to-tcp/131072-8 57981 113202 ns/op 1157.86 MB/s 0 B/op 0 allocs/op BenchmarkSendFile/file-to-tcp/262144-8 26362 253376 ns/op 1034.61 MB/s 0 B/op 0 allocs/op BenchmarkSendFile/file-to-tcp/524288-8 13767 442053 ns/op 1186.03 MB/s 0 B/op 0 allocs/op BenchmarkSendFile/file-to-tcp/1048576-8 4906 829984 ns/op 1263.37 MB/s 0 B/op 0 allocs/op BenchmarkSendFile/file-to-unix/1024-8 2031691 628.9 ns/op 1628.36 MB/s 0 B/op 0 allocs/op BenchmarkSendFile/file-to-unix/2048-8 1294472 965.0 ns/op 2122.30 MB/s 0 B/op 0 allocs/op BenchmarkSendFile/file-to-unix/4096-8 1005753 1203 ns/op 3404.27 MB/s 0 B/op 0 allocs/op BenchmarkSendFile/file-to-unix/8192-8 865448 6412 ns/op 1277.65 MB/s 0 B/op 0 allocs/op BenchmarkSendFile/file-to-unix/16384-8 268946 12801 ns/op 1279.89 MB/s 0 B/op 0 allocs/op BenchmarkSendFile/file-to-unix/32768-8 153398 6691 ns/op 4897.23 MB/s 0 B/op 0 allocs/op BenchmarkSendFile/file-to-unix/65536-8 88911 11969 ns/op 5475.36 MB/s 0 B/op 0 allocs/op BenchmarkSendFile/file-to-unix/131072-8 48639 107538 ns/op 1218.84 MB/s 0 B/op 0 allocs/op BenchmarkSendFile/file-to-unix/262144-8 22720 203199 ns/op 1290.09 MB/s 0 B/op 0 allocs/op BenchmarkSendFile/file-to-unix/524288-8 12034 97126 ns/op 5398.03 MB/s 0 B/op 0 allocs/op BenchmarkSendFile/file-to-unix/1048576-8 5374 202308 ns/op 5183.06 MB/s 0 B/op 0 allocs/op Change-Id: Ib9507bd9837ecb38b1702afa89502da18806929c Reviewed-on: https://go-review.googlesource.com/c/go/+/543276 Reviewed-by: Damien Neil <dneil@google.com> Reviewed-by: Bryan Mills <bcmills@google.com> Auto-Submit: Ian Lance Taylor <iant@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Diffstat (limited to 'src/net')
-rw-r--r--src/net/mockserver_test.go118
-rw-r--r--src/net/sendfile_linux_test.go86
-rw-r--r--src/net/sendfile_test.go73
-rw-r--r--src/net/splice_linux_test.go209
4 files changed, 236 insertions, 250 deletions
diff --git a/src/net/mockserver_test.go b/src/net/mockserver_test.go
index 46b2a57321..d4bd16e4c6 100644
--- a/src/net/mockserver_test.go
+++ b/src/net/mockserver_test.go
@@ -8,8 +8,11 @@ import (
"context"
"errors"
"fmt"
+ "internal/testenv"
+ "log"
"os"
"path/filepath"
+ "strconv"
"sync"
"testing"
"time"
@@ -506,3 +509,118 @@ func packetTransceiver(c PacketConn, wb []byte, dst Addr, ch chan<- error) {
ch <- fmt.Errorf("read %d; want %d", n, len(wb))
}
}
+
+func spawnTestSocketPair(t testing.TB, net string) (client, server Conn) {
+ t.Helper()
+ ln := newLocalListener(t, net)
+ defer ln.Close()
+ var cerr, serr error
+ acceptDone := make(chan struct{})
+ go func() {
+ server, serr = ln.Accept()
+ acceptDone <- struct{}{}
+ }()
+ client, cerr = Dial(ln.Addr().Network(), ln.Addr().String())
+ <-acceptDone
+ if cerr != nil {
+ if server != nil {
+ server.Close()
+ }
+ t.Fatal(cerr)
+ }
+ if serr != nil {
+ if client != nil {
+ client.Close()
+ }
+ t.Fatal(serr)
+ }
+ return client, server
+}
+
+func startTestSocketPeer(t testing.TB, conn Conn, op string, chunkSize, totalSize int) (func(t testing.TB), error) {
+ f, err := conn.(interface{ File() (*os.File, error) }).File()
+ if err != nil {
+ return nil, err
+ }
+
+ cmd := testenv.Command(t, os.Args[0])
+ cmd.Env = []string{
+ "GO_NET_TEST_TRANSFER=1",
+ "GO_NET_TEST_TRANSFER_OP=" + op,
+ "GO_NET_TEST_TRANSFER_CHUNK_SIZE=" + strconv.Itoa(chunkSize),
+ "GO_NET_TEST_TRANSFER_TOTAL_SIZE=" + strconv.Itoa(totalSize),
+ "TMPDIR=" + os.Getenv("TMPDIR"),
+ }
+ cmd.ExtraFiles = append(cmd.ExtraFiles, f)
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+
+ if err := cmd.Start(); err != nil {
+ return nil, err
+ }
+
+ cmdCh := make(chan error, 1)
+ go func() {
+ err := cmd.Wait()
+ conn.Close()
+ f.Close()
+ cmdCh <- err
+ }()
+
+ return func(tb testing.TB) {
+ err := <-cmdCh
+ if err != nil {
+ tb.Errorf("process exited with error: %v", err)
+ }
+ }, nil
+}
+
+func init() {
+ if os.Getenv("GO_NET_TEST_TRANSFER") == "" {
+ return
+ }
+ defer os.Exit(0)
+
+ f := os.NewFile(uintptr(3), "splice-test-conn")
+ defer f.Close()
+
+ conn, err := FileConn(f)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ var chunkSize int
+ if chunkSize, err = strconv.Atoi(os.Getenv("GO_NET_TEST_TRANSFER_CHUNK_SIZE")); err != nil {
+ log.Fatal(err)
+ }
+ buf := make([]byte, chunkSize)
+
+ var totalSize int
+ if totalSize, err = strconv.Atoi(os.Getenv("GO_NET_TEST_TRANSFER_TOTAL_SIZE")); err != nil {
+ log.Fatal(err)
+ }
+
+ var fn func([]byte) (int, error)
+ switch op := os.Getenv("GO_NET_TEST_TRANSFER_OP"); op {
+ case "r":
+ fn = conn.Read
+ case "w":
+ defer conn.Close()
+
+ fn = conn.Write
+ default:
+ log.Fatalf("unknown op %q", op)
+ }
+
+ var n int
+ for count := 0; count < totalSize; count += n {
+ if count+chunkSize > totalSize {
+ buf = buf[:totalSize-count]
+ }
+
+ var err error
+ if n, err = fn(buf); err != nil {
+ return
+ }
+ }
+}
diff --git a/src/net/sendfile_linux_test.go b/src/net/sendfile_linux_test.go
deleted file mode 100644
index 7a66d3645f..0000000000
--- a/src/net/sendfile_linux_test.go
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright 2022 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.
-
-//go:build linux
-
-package net
-
-import (
- "io"
- "os"
- "strconv"
- "testing"
-)
-
-func BenchmarkSendFile(b *testing.B) {
- b.Run("file-to-tcp", func(b *testing.B) { benchmarkSendFile(b, "tcp") })
- b.Run("file-to-unix", func(b *testing.B) { benchmarkSendFile(b, "unix") })
-}
-
-func benchmarkSendFile(b *testing.B, proto string) {
- for i := 0; i <= 10; i++ {
- size := 1 << (i + 10)
- bench := sendFileBench{
- proto: proto,
- chunkSize: size,
- }
- b.Run(strconv.Itoa(size), bench.benchSendFile)
- }
-}
-
-type sendFileBench struct {
- proto string
- chunkSize int
-}
-
-func (bench sendFileBench) benchSendFile(b *testing.B) {
- fileSize := b.N * bench.chunkSize
- f := createTempFile(b, fileSize)
-
- client, server := spliceTestSocketPair(b, bench.proto)
- defer server.Close()
-
- cleanUp, err := startSpliceClient(client, "r", bench.chunkSize, fileSize)
- if err != nil {
- client.Close()
- b.Fatal(err)
- }
- defer cleanUp()
-
- b.ReportAllocs()
- b.SetBytes(int64(bench.chunkSize))
- b.ResetTimer()
-
- // Data go from file to socket via sendfile(2).
- sent, err := io.Copy(server, f)
- if err != nil {
- b.Fatalf("failed to copy data with sendfile, error: %v", err)
- }
- if sent != int64(fileSize) {
- b.Fatalf("bytes sent mismatch, got: %d, want: %d", sent, fileSize)
- }
-}
-
-func createTempFile(b *testing.B, size int) *os.File {
- f, err := os.CreateTemp(b.TempDir(), "linux-sendfile-bench")
- if err != nil {
- b.Fatalf("failed to create temporary file: %v", err)
- }
- b.Cleanup(func() {
- f.Close()
- })
-
- data := make([]byte, size)
- if _, err := f.Write(data); err != nil {
- b.Fatalf("failed to create and feed the file: %v", err)
- }
- if err := f.Sync(); err != nil {
- b.Fatalf("failed to save the file: %v", err)
- }
- if _, err := f.Seek(0, io.SeekStart); err != nil {
- b.Fatalf("failed to rewind the file: %v", err)
- }
-
- return f
-}
diff --git a/src/net/sendfile_test.go b/src/net/sendfile_test.go
index 4cba1ed2b1..c3d5e714bd 100644
--- a/src/net/sendfile_test.go
+++ b/src/net/sendfile_test.go
@@ -14,6 +14,7 @@ import (
"io"
"os"
"runtime"
+ "strconv"
"sync"
"testing"
"time"
@@ -446,3 +447,75 @@ func BenchmarkSendfileZeroBytes(b *testing.B) {
cancel()
}
+
+func BenchmarkSendFile(b *testing.B) {
+ b.Run("file-to-tcp", func(b *testing.B) { benchmarkSendFile(b, "tcp") })
+ b.Run("file-to-unix", func(b *testing.B) { benchmarkSendFile(b, "unix") })
+}
+
+func benchmarkSendFile(b *testing.B, proto string) {
+ for i := 0; i <= 10; i++ {
+ size := 1 << (i + 10)
+ bench := sendFileBench{
+ proto: proto,
+ chunkSize: size,
+ }
+ b.Run(strconv.Itoa(size), bench.benchSendFile)
+ }
+}
+
+type sendFileBench struct {
+ proto string
+ chunkSize int
+}
+
+func (bench sendFileBench) benchSendFile(b *testing.B) {
+ fileSize := b.N * bench.chunkSize
+ f := createTempFile(b, fileSize)
+
+ client, server := spawnTestSocketPair(b, bench.proto)
+ defer server.Close()
+
+ cleanUp, err := startTestSocketPeer(b, client, "r", bench.chunkSize, fileSize)
+ if err != nil {
+ client.Close()
+ b.Fatal(err)
+ }
+ defer cleanUp(b)
+
+ b.ReportAllocs()
+ b.SetBytes(int64(bench.chunkSize))
+ b.ResetTimer()
+
+ // Data go from file to socket via sendfile(2).
+ sent, err := io.Copy(server, f)
+ if err != nil {
+ b.Fatalf("failed to copy data with sendfile, error: %v", err)
+ }
+ if sent != int64(fileSize) {
+ b.Fatalf("bytes sent mismatch, got: %d, want: %d", sent, fileSize)
+ }
+}
+
+func createTempFile(b *testing.B, size int) *os.File {
+ f, err := os.CreateTemp(b.TempDir(), "sendfile-bench")
+ if err != nil {
+ b.Fatalf("failed to create temporary file: %v", err)
+ }
+ b.Cleanup(func() {
+ f.Close()
+ })
+
+ data := make([]byte, size)
+ if _, err := f.Write(data); err != nil {
+ b.Fatalf("failed to create and feed the file: %v", err)
+ }
+ if err := f.Sync(); err != nil {
+ b.Fatalf("failed to save the file: %v", err)
+ }
+ if _, err := f.Seek(0, io.SeekStart); err != nil {
+ b.Fatalf("failed to rewind the file: %v", err)
+ }
+
+ return f
+}
diff --git a/src/net/splice_linux_test.go b/src/net/splice_linux_test.go
index 7082ecdfbe..2edd744406 100644
--- a/src/net/splice_linux_test.go
+++ b/src/net/splice_linux_test.go
@@ -9,14 +9,11 @@ package net
import (
"internal/poll"
"io"
- "log"
"os"
- "os/exec"
"strconv"
"sync"
"syscall"
"testing"
- "time"
)
func TestSplice(t *testing.T) {
@@ -62,30 +59,33 @@ type spliceTestCase struct {
func (tc spliceTestCase) test(t *testing.T) {
hook := hookSplice(t)
- clientUp, serverUp := spliceTestSocketPair(t, tc.upNet)
+ // We need to use the actual size for startTestSocketPeer when testing with LimitedReader,
+ // otherwise the child process created in startTestSocketPeer will hang infinitely because of
+ // the mismatch of data size to transfer.
+ size := tc.totalSize
+ if tc.limitReadSize > 0 {
+ if tc.limitReadSize < size {
+ size = tc.limitReadSize
+ }
+ }
+
+ clientUp, serverUp := spawnTestSocketPair(t, tc.upNet)
defer serverUp.Close()
- cleanup, err := startSpliceClient(clientUp, "w", tc.chunkSize, tc.totalSize)
+ cleanup, err := startTestSocketPeer(t, clientUp, "w", tc.chunkSize, size)
if err != nil {
t.Fatal(err)
}
- defer cleanup()
- clientDown, serverDown := spliceTestSocketPair(t, tc.downNet)
+ defer cleanup(t)
+ clientDown, serverDown := spawnTestSocketPair(t, tc.downNet)
defer serverDown.Close()
- cleanup, err = startSpliceClient(clientDown, "r", tc.chunkSize, tc.totalSize)
+ cleanup, err = startTestSocketPeer(t, clientDown, "r", tc.chunkSize, size)
if err != nil {
t.Fatal(err)
}
- defer cleanup()
+ defer cleanup(t)
- var (
- r io.Reader = serverUp
- size = tc.totalSize
- )
+ var r io.Reader = serverUp
if tc.limitReadSize > 0 {
- if tc.limitReadSize < size {
- size = tc.limitReadSize
- }
-
r = &io.LimitedReader{
N: int64(tc.limitReadSize),
R: serverUp,
@@ -167,31 +167,34 @@ func verifySpliceFds(t *testing.T, c Conn, hook *spliceHook, fdType string) {
func (tc spliceTestCase) testFile(t *testing.T) {
hook := hookSplice(t)
+ // We need to use the actual size for startTestSocketPeer when testing with LimitedReader,
+ // otherwise the child process created in startTestSocketPeer will hang infinitely because of
+ // the mismatch of data size to transfer.
+ actualSize := tc.totalSize
+ if tc.limitReadSize > 0 {
+ if tc.limitReadSize < actualSize {
+ actualSize = tc.limitReadSize
+ }
+ }
+
f, err := os.OpenFile(os.DevNull, os.O_WRONLY, 0)
if err != nil {
t.Fatal(err)
}
defer f.Close()
- client, server := spliceTestSocketPair(t, tc.upNet)
+ client, server := spawnTestSocketPair(t, tc.upNet)
defer server.Close()
- cleanup, err := startSpliceClient(client, "w", tc.chunkSize, tc.totalSize)
+ cleanup, err := startTestSocketPeer(t, client, "w", tc.chunkSize, actualSize)
if err != nil {
client.Close()
t.Fatal("failed to start splice client:", err)
}
- defer cleanup()
+ defer cleanup(t)
- var (
- r io.Reader = server
- actualSize = tc.totalSize
- )
+ var r io.Reader = server
if tc.limitReadSize > 0 {
- if tc.limitReadSize < actualSize {
- actualSize = tc.limitReadSize
- }
-
r = &io.LimitedReader{
N: int64(tc.limitReadSize),
R: r,
@@ -234,9 +237,9 @@ func testSpliceReaderAtEOF(t *testing.T, upNet, downNet string) {
hook := hookSplice(t)
- clientUp, serverUp := spliceTestSocketPair(t, upNet)
+ clientUp, serverUp := spawnTestSocketPair(t, upNet)
defer clientUp.Close()
- clientDown, serverDown := spliceTestSocketPair(t, downNet)
+ clientDown, serverDown := spawnTestSocketPair(t, downNet)
defer clientDown.Close()
defer serverDown.Close()
@@ -343,10 +346,10 @@ func testSpliceIssue25985(t *testing.T, upNet, downNet string) {
}
func testSpliceNoUnixpacket(t *testing.T) {
- clientUp, serverUp := spliceTestSocketPair(t, "unixpacket")
+ clientUp, serverUp := spawnTestSocketPair(t, "unixpacket")
defer clientUp.Close()
defer serverUp.Close()
- clientDown, serverDown := spliceTestSocketPair(t, "tcp")
+ clientDown, serverDown := spawnTestSocketPair(t, "tcp")
defer clientDown.Close()
defer serverDown.Close()
// If splice called poll.Splice here, we'd get err == syscall.EINVAL
@@ -374,7 +377,7 @@ func testSpliceNoUnixgram(t *testing.T) {
t.Fatal(err)
}
defer up.Close()
- clientDown, serverDown := spliceTestSocketPair(t, "tcp")
+ clientDown, serverDown := spawnTestSocketPair(t, "tcp")
defer clientDown.Close()
defer serverDown.Close()
// Analogous to testSpliceNoUnixpacket.
@@ -409,23 +412,23 @@ func (tc spliceTestCase) bench(b *testing.B) {
// To benchmark the genericReadFrom code path, set this to false.
useSplice := true
- clientUp, serverUp := spliceTestSocketPair(b, tc.upNet)
+ clientUp, serverUp := spawnTestSocketPair(b, tc.upNet)
defer serverUp.Close()
- cleanup, err := startSpliceClient(clientUp, "w", tc.chunkSize, tc.chunkSize*b.N)
+ cleanup, err := startTestSocketPeer(b, clientUp, "w", tc.chunkSize, tc.chunkSize*b.N)
if err != nil {
b.Fatal(err)
}
- defer cleanup()
+ defer cleanup(b)
- clientDown, serverDown := spliceTestSocketPair(b, tc.downNet)
+ clientDown, serverDown := spawnTestSocketPair(b, tc.downNet)
defer serverDown.Close()
- cleanup, err = startSpliceClient(clientDown, "r", tc.chunkSize, tc.chunkSize*b.N)
+ cleanup, err = startTestSocketPeer(b, clientDown, "r", tc.chunkSize, tc.chunkSize*b.N)
if err != nil {
b.Fatal(err)
}
- defer cleanup()
+ defer cleanup(b)
b.SetBytes(int64(tc.chunkSize))
b.ResetTimer()
@@ -446,128 +449,6 @@ func (tc spliceTestCase) bench(b *testing.B) {
}
}
-func spliceTestSocketPair(t testing.TB, net string) (client, server Conn) {
- t.Helper()
- ln := newLocalListener(t, net)
- defer ln.Close()
- var cerr, serr error
- acceptDone := make(chan struct{})
- go func() {
- server, serr = ln.Accept()
- acceptDone <- struct{}{}
- }()
- client, cerr = Dial(ln.Addr().Network(), ln.Addr().String())
- <-acceptDone
- if cerr != nil {
- if server != nil {
- server.Close()
- }
- t.Fatal(cerr)
- }
- if serr != nil {
- if client != nil {
- client.Close()
- }
- t.Fatal(serr)
- }
- return client, server
-}
-
-func startSpliceClient(conn Conn, op string, chunkSize, totalSize int) (func(), error) {
- f, err := conn.(interface{ File() (*os.File, error) }).File()
- if err != nil {
- return nil, err
- }
-
- cmd := exec.Command(os.Args[0], os.Args[1:]...)
- cmd.Env = []string{
- "GO_NET_TEST_SPLICE=1",
- "GO_NET_TEST_SPLICE_OP=" + op,
- "GO_NET_TEST_SPLICE_CHUNK_SIZE=" + strconv.Itoa(chunkSize),
- "GO_NET_TEST_SPLICE_TOTAL_SIZE=" + strconv.Itoa(totalSize),
- "TMPDIR=" + os.Getenv("TMPDIR"),
- }
- cmd.ExtraFiles = append(cmd.ExtraFiles, f)
- cmd.Stdout = os.Stdout
- cmd.Stderr = os.Stderr
-
- if err := cmd.Start(); err != nil {
- return nil, err
- }
-
- donec := make(chan struct{})
- go func() {
- cmd.Wait()
- conn.Close()
- f.Close()
- close(donec)
- }()
-
- return func() {
- select {
- case <-donec:
- case <-time.After(5 * time.Second):
- log.Printf("killing splice client after 5 second shutdown timeout")
- cmd.Process.Kill()
- select {
- case <-donec:
- case <-time.After(5 * time.Second):
- log.Printf("splice client didn't die after 10 seconds")
- }
- }
- }, nil
-}
-
-func init() {
- if os.Getenv("GO_NET_TEST_SPLICE") == "" {
- return
- }
- defer os.Exit(0)
-
- f := os.NewFile(uintptr(3), "splice-test-conn")
- defer f.Close()
-
- conn, err := FileConn(f)
- if err != nil {
- log.Fatal(err)
- }
-
- var chunkSize int
- if chunkSize, err = strconv.Atoi(os.Getenv("GO_NET_TEST_SPLICE_CHUNK_SIZE")); err != nil {
- log.Fatal(err)
- }
- buf := make([]byte, chunkSize)
-
- var totalSize int
- if totalSize, err = strconv.Atoi(os.Getenv("GO_NET_TEST_SPLICE_TOTAL_SIZE")); err != nil {
- log.Fatal(err)
- }
-
- var fn func([]byte) (int, error)
- switch op := os.Getenv("GO_NET_TEST_SPLICE_OP"); op {
- case "r":
- fn = conn.Read
- case "w":
- defer conn.Close()
-
- fn = conn.Write
- default:
- log.Fatalf("unknown op %q", op)
- }
-
- var n int
- for count := 0; count < totalSize; count += n {
- if count+chunkSize > totalSize {
- buf = buf[:totalSize-count]
- }
-
- var err error
- if n, err = fn(buf); err != nil {
- return
- }
- }
-}
-
func BenchmarkSpliceFile(b *testing.B) {
b.Run("tcp-to-file", func(b *testing.B) { benchmarkSpliceFile(b, "tcp") })
b.Run("unix-to-file", func(b *testing.B) { benchmarkSpliceFile(b, "unix") })
@@ -598,15 +479,15 @@ func (bench spliceFileBench) benchSpliceFile(b *testing.B) {
totalSize := b.N * bench.chunkSize
- client, server := spliceTestSocketPair(b, bench.proto)
+ client, server := spawnTestSocketPair(b, bench.proto)
defer server.Close()
- cleanup, err := startSpliceClient(client, "w", bench.chunkSize, totalSize)
+ cleanup, err := startTestSocketPeer(b, client, "w", bench.chunkSize, totalSize)
if err != nil {
client.Close()
b.Fatalf("failed to start splice client: %v", err)
}
- defer cleanup()
+ defer cleanup(b)
b.ReportAllocs()
b.SetBytes(int64(bench.chunkSize))