mirror of
https://github.com/valyala/fasthttp.git
synced 2026-06-13 15:46:49 +03:00
f6ba4abd2d
Improve performance of `copyZeroAlloc` function
```
goos: linux
goarch: amd64
pkg: github.com/valyala/fasthttp
cpu: QEMU Virtual CPU version 2.5+
│ old6.txt │ new7.txt │
│ sec/op │ sec/op vs base │
CopyZeroAllocOSFileToBytesBuffer-8 1.802µ ± 3% 1.303µ ± 2% -27.69% (p=0.000 n=25)
CopyZeroAllocBytesBufferToOSFile-8 1.066µ ± 17% 1.048µ ± 1% -1.69% (p=0.043 n=25)
CopyZeroAllocOSFileToStringsBuilder-8 9.477µ ± 0% 1.345µ ± 2% -85.81% (p=0.000 n=25)
CopyZeroAllocIOLimitedReaderToOSFile-8 1.031µ ± 1% 1.092µ ± 4% +5.92% (p=0.000 n=25)
CopyZeroAllocOSFileToOSFile-8 12.132µ ± 1% 2.386µ ± 2% -80.33% (p=0.000 n=25)
CopyZeroAllocOSFileToNetConn-8 2.009µ ± 2% 1.995µ ± 2% ~ (p=0.733 n=25)
CopyZeroAllocNetConnToOSFile-8 21.86µ ± 2% 20.21µ ± 1% -7.56% (p=0.000 n=25)
geomean 3.728µ 2.121µ -43.11%
│ old6.txt │ new7.txt │
│ B/op │ B/op vs base │
CopyZeroAllocOSFileToBytesBuffer-8 40.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=25)
CopyZeroAllocBytesBufferToOSFile-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=25) ¹
CopyZeroAllocOSFileToStringsBuilder-8 32.04Ki ± 0% 0.00Ki ± 0% -100.00% (p=0.000 n=25)
CopyZeroAllocIOLimitedReaderToOSFile-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=25) ¹
CopyZeroAllocOSFileToOSFile-8 32.06Ki ± 0% 0.00Ki ± 0% -100.00% (p=0.000 n=25)
CopyZeroAllocOSFileToNetConn-8 96.00 ± 0% 96.00 ± 0% ~ (p=1.000 n=25) ¹
CopyZeroAllocNetConnToOSFile-8 16.000 ± 6% 8.000 ± 12% -50.00% (p=0.000 n=25)
geomean ² ? ² ³
¹ all samples are equal
² summaries must be >0 to compute geomean
³ ratios must be >0 to compute geomean
│ old6.txt │ new7.txt │
│ allocs/op │ allocs/op vs base │
CopyZeroAllocOSFileToBytesBuffer-8 4.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=25)
CopyZeroAllocBytesBufferToOSFile-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=25) ¹
CopyZeroAllocOSFileToStringsBuilder-8 5.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=25)
CopyZeroAllocIOLimitedReaderToOSFile-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=25) ¹
CopyZeroAllocOSFileToOSFile-8 8.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=25)
CopyZeroAllocOSFileToNetConn-8 6.000 ± 0% 6.000 ± 0% ~ (p=1.000 n=25) ¹
CopyZeroAllocNetConnToOSFile-8 2.000 ± 0% 1.000 ± 0% -50.00% (p=0.000 n=25)
geomean ² ? ² ³
¹ all samples are equal
² summaries must be >0 to compute geomean
³ ratios must be >0 to compute geomean
```
```
goos: windows
goarch: amd64
pkg: github.com/valyala/fasthttp
cpu: Intel(R) Core(TM) i5-8250U CPU @ 1.60GHz
│ old_win.txt │ new_win.txt │
│ sec/op │ sec/op vs base │
CopyZeroAllocOSFileToBytesBuffer-8 4.347µ ± 7% 4.220µ ± 11% ~ (p=0.211 n=25)
CopyZeroAllocBytesBufferToOSFile-8 1.408µ ± 12% 1.460µ ± 7% ~ (p=0.427 n=25)
CopyZeroAllocOSFileToStringsBuilder-8 17.448µ ± 5% 3.613µ ± 9% -79.29% (p=0.000 n=25)
CopyZeroAllocIOLimitedReaderToOSFile-8 1.324µ ± 8% 1.257µ ± 6% -5.06% (p=0.024 n=25)
CopyZeroAllocOSFileToOSFile-8 19.953µ ± 8% 4.846µ ± 7% -75.71% (p=0.000 n=25)
CopyZeroAllocOSFileToNetConn-8 18.18µ ± 8% 18.22µ ± 7% ~ (p=0.405 n=25)
CopyZeroAllocNetConnToOSFile-8 74.75µ ± 2% 68.10µ ± 3% -8.90% (p=0.000 n=25)
geomean 8.720µ 5.579µ -36.02%
│ old_win.txt │ new_win.txt │
│ B/op │ B/op vs base │
CopyZeroAllocOSFileToBytesBuffer-8 8.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=25)
CopyZeroAllocBytesBufferToOSFile-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=25) ¹
CopyZeroAllocOSFileToStringsBuilder-8 32.01Ki ± 0% 0.00Ki ± 0% -100.00% (p=0.000 n=25)
CopyZeroAllocIOLimitedReaderToOSFile-8 9.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=25)
CopyZeroAllocOSFileToOSFile-8 32.02Ki ± 0% 0.00Ki ± 0% -100.00% (p=0.000 n=25)
CopyZeroAllocOSFileToNetConn-8 32.02Ki ± 0% 32.02Ki ± 0% ~ (p=1.000 n=25) ¹
CopyZeroAllocNetConnToOSFile-8 32.02Ki ± 0% 32.02Ki ± 0% -0.00% (p=0.012 n=25)
geomean ² ? ² ³
¹ all samples are equal
² summaries must be >0 to compute geomean
³ ratios must be >0 to compute geomean
│ old_win.txt │ new_win.txt │
│ allocs/op │ allocs/op vs base │
CopyZeroAllocOSFileToBytesBuffer-8 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=25)
CopyZeroAllocBytesBufferToOSFile-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=25) ¹
CopyZeroAllocOSFileToStringsBuilder-8 2.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=25)
CopyZeroAllocIOLimitedReaderToOSFile-8 2.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=25)
CopyZeroAllocOSFileToOSFile-8 3.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=25)
CopyZeroAllocOSFileToNetConn-8 3.000 ± 0% 3.000 ± 0% ~ (p=1.000 n=25) ¹
CopyZeroAllocNetConnToOSFile-8 3.000 ± 0% 3.000 ± 0% ~ (p=1.000 n=25) ¹
geomean ² ? ² ³
¹ all samples are equal
² summaries must be >0 to compute geomean
³ ratios must be >0 to compute geomean
```
284 lines
4.5 KiB
Go
284 lines
4.5 KiB
Go
package fasthttp
|
|
|
|
import (
|
|
"bytes"
|
|
"io"
|
|
"net"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func BenchmarkCopyZeroAllocOSFileToBytesBuffer(b *testing.B) {
|
|
r, err := os.Open("./README.md")
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
defer r.Close()
|
|
|
|
buf := &bytes.Buffer{}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
buf.Reset()
|
|
_, err = copyZeroAlloc(buf, r)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkCopyZeroAllocBytesBufferToOSFile(b *testing.B) {
|
|
f, err := os.Open("./README.md")
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
defer f.Close()
|
|
|
|
buf := &bytes.Buffer{}
|
|
_, err = io.Copy(buf, f)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
|
|
tmp, err := os.CreateTemp(os.TempDir(), "test_*")
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
defer os.Remove(tmp.Name())
|
|
|
|
w, err := os.OpenFile(tmp.Name(), os.O_WRONLY, 0o444)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
defer w.Close()
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_, err := w.Seek(0, 0)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
_, err = copyZeroAlloc(w, buf)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkCopyZeroAllocOSFileToStringsBuilder(b *testing.B) {
|
|
r, err := os.Open("./README.md")
|
|
if err != nil {
|
|
b.Fatalf("Failed to open testing file: %v", err)
|
|
}
|
|
defer r.Close()
|
|
|
|
w := &strings.Builder{}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
w.Reset()
|
|
_, err = copyZeroAlloc(w, r)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkCopyZeroAllocIOLimitedReaderToOSFile(b *testing.B) {
|
|
f, err := os.Open("./README.md")
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
defer f.Close()
|
|
|
|
r := io.LimitReader(f, 1024)
|
|
|
|
tmp, err := os.CreateTemp(os.TempDir(), "test_*")
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
defer os.Remove(tmp.Name())
|
|
|
|
w, err := os.OpenFile(tmp.Name(), os.O_WRONLY, 0o444)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
defer w.Close()
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_, err := w.Seek(0, 0)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
_, err = copyZeroAlloc(w, r)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkCopyZeroAllocOSFileToOSFile(b *testing.B) {
|
|
r, err := os.Open("./README.md")
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
defer r.Close()
|
|
|
|
f, err := os.CreateTemp(os.TempDir(), "test_*")
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
defer os.Remove(f.Name())
|
|
|
|
w, err := os.OpenFile(f.Name(), os.O_WRONLY, 0o444)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
defer w.Close()
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_, err := w.Seek(0, 0)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
_, err = copyZeroAlloc(w, r)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkCopyZeroAllocOSFileToNetConn(b *testing.B) {
|
|
ln, err := net.Listen("tcp", "127.0.0.1:0")
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
|
|
addr := ln.Addr().String()
|
|
defer ln.Close()
|
|
|
|
done := make(chan struct{})
|
|
defer close(done)
|
|
|
|
go func() {
|
|
conn, err := ln.Accept()
|
|
if err != nil {
|
|
b.Error(err)
|
|
return
|
|
}
|
|
defer conn.Close()
|
|
for {
|
|
select {
|
|
case <-done:
|
|
return
|
|
default:
|
|
_, err := io.Copy(io.Discard, conn)
|
|
if err != nil {
|
|
b.Error(err)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
|
|
conn, err := net.Dial("tcp", addr)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
defer conn.Close()
|
|
|
|
file, err := os.Open("./README.md")
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
defer file.Close()
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
if _, err := copyZeroAlloc(conn, file); err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkCopyZeroAllocNetConnToOSFile(b *testing.B) {
|
|
data, err := os.ReadFile("./README.md")
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
|
|
ln, err := net.Listen("tcp", "127.0.0.1:0")
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
|
|
addr := ln.Addr().String()
|
|
defer ln.Close()
|
|
|
|
done := make(chan struct{})
|
|
defer close(done)
|
|
|
|
writeDone := make(chan struct{})
|
|
go func() {
|
|
for {
|
|
select {
|
|
case <-done:
|
|
return
|
|
default:
|
|
conn, err := ln.Accept()
|
|
if err != nil {
|
|
b.Error(err)
|
|
return
|
|
}
|
|
_, err = conn.Write(data)
|
|
if err != nil {
|
|
b.Error(err)
|
|
}
|
|
conn.Close()
|
|
writeDone <- struct{}{}
|
|
}
|
|
}
|
|
}()
|
|
|
|
tmp, err := os.CreateTemp(os.TempDir(), "test_*")
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
defer os.Remove(tmp.Name())
|
|
|
|
file, err := os.OpenFile(tmp.Name(), os.O_WRONLY, 0o444)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
defer file.Close()
|
|
|
|
conn, err := net.Dial("tcp", addr)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
defer conn.Close()
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
b.StopTimer()
|
|
<-writeDone
|
|
_, err = file.Seek(0, 0)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
b.StartTimer()
|
|
_, err = copyZeroAlloc(file, conn)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
b.StopTimer()
|
|
conn, err = net.Dial("tcp", addr)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
}
|