Skip to content

Commit 3216ba1

Browse files
authored
Merge pull request #11 from mazrean/dev/http-benchmark
ベンチマーク、テストの強化
2 parents 1c81c61 + 6a335f5 commit 3216ba1

File tree

16 files changed

+510
-42
lines changed

16 files changed

+510
-42
lines changed

.github/workflows/benchmark.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ jobs:
2020
mkdir -p ./profile
2121
{
2222
echo "stdout<<EOF"
23-
go test -benchmem -bench . -cpuprofile ./profile/cpu.out -memprofile ./profile/mem.out
23+
go test -short -benchmem -bench . -cpuprofile ./profile/cpu.out -memprofile ./profile/mem.out
2424
echo "EOF"
2525
} >> $GITHUB_OUTPUT
2626
id: bench

.github/workflows/ci.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ jobs:
2727
- uses: actions/setup-go@v5
2828
with:
2929
go-version-file: go.mod
30-
- run: go test ./... -v -coverprofile=./coverage.txt -race -vet=off
30+
- run: go test ./... -short -v -coverprofile=./coverage.txt -race -vet=off
3131
- name: Upload coverage data
3232
uses: codecov/codecov-action@v4.1.0
3333
with:

.golangci.yaml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ linters:
88
- staticcheck
99
- unused
1010
- gosimple
11-
- structcheck
12-
- varcheck
1311
- ineffassign
1412
- typecheck
1513
- revive
@@ -32,7 +30,6 @@ linters:
3230
- nilerr
3331
- nosprintfhostport
3432
- sqlclosecheck
35-
- testpackage
3633
- unconvert
3734
- unparam
3835
- whitespace

docs/images/memory.png

1.32 KB
Loading

docs/images/time.png

1.53 KB
Loading

formstream.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ type parserConfig struct {
4141

4242
type ParserOption func(*parserConfig)
4343

44-
type DataSize uint64
44+
type DataSize int64
4545

4646
const (
4747
_ DataSize = 1 << (iota * 10)

formstream_test.go

Lines changed: 99 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"testing"
1313

1414
"github.com/mazrean/formstream"
15+
"github.com/mazrean/formstream/internal/myio"
1516
)
1617

1718
func ExampleNewParser() {
@@ -69,44 +70,69 @@ large file contents
6970

7071
const boundary = "boundary"
7172

72-
func sampleForm(fileSize formstream.DataSize, boundary string, reverse bool) (io.Reader, error) {
73-
b := bytes.NewBuffer(nil)
73+
func sampleForm(fileSize formstream.DataSize, boundary string, reverse bool) (io.ReadSeekCloser, error) {
74+
if fileSize > 1*formstream.GB {
75+
f, err := os.CreateTemp("", "formstream-test-form-")
76+
if err != nil {
77+
return nil, fmt.Errorf("failed to create temp file: %w", err)
78+
}
79+
80+
err = createSampleForm(f, fileSize, boundary, reverse)
81+
if err != nil {
82+
return nil, fmt.Errorf("failed to create sample form: %w", err)
83+
}
84+
85+
return f, nil
86+
}
87+
88+
buf := bytes.NewBuffer(nil)
89+
90+
err := createSampleForm(buf, fileSize, boundary, reverse)
91+
if err != nil {
92+
return nil, fmt.Errorf("failed to create sample form: %w", err)
93+
}
7494

75-
mw := multipart.NewWriter(b)
95+
return myio.NopSeekCloser(bytes.NewReader(buf.Bytes())), nil
96+
}
97+
98+
func createSampleForm(w io.Writer, fileSize formstream.DataSize, boundary string, reverse bool) error {
99+
mw := multipart.NewWriter(w)
76100
defer mw.Close()
77101

78102
err := mw.SetBoundary(boundary)
79103
if err != nil {
80-
return nil, fmt.Errorf("failed to set boundary: %w", err)
104+
return fmt.Errorf("failed to set boundary: %w", err)
81105
}
82106

83107
if !reverse {
84108
err := mw.WriteField("field", "value")
85109
if err != nil {
86-
return nil, fmt.Errorf("failed to write field: %w", err)
110+
return fmt.Errorf("failed to write field: %w", err)
87111
}
88112
}
89113

90114
mh := make(textproto.MIMEHeader)
91115
mh.Set("Content-Disposition", `form-data; name="stream"; filename="file.txt"`)
92116
mh.Set("Content-Type", "text/plain")
93-
w, err := mw.CreatePart(mh)
117+
pw, err := mw.CreatePart(mh)
94118
if err != nil {
95-
return nil, fmt.Errorf("failed to create part: %w", err)
119+
return fmt.Errorf("failed to create part: %w", err)
96120
}
97-
_, err = io.CopyN(w, strings.NewReader(strings.Repeat("a", int(fileSize))), int64(fileSize))
98-
if err != nil {
99-
return nil, fmt.Errorf("failed to copy: %w", err)
121+
for i := 0; i < int(fileSize/formstream.MB); i++ {
122+
_, err := pw.Write([]byte(strings.Repeat("a", int(formstream.MB))))
123+
if err != nil {
124+
return fmt.Errorf("failed to write: %w", err)
125+
}
100126
}
101127

102128
if reverse {
103129
err := mw.WriteField("field", "value")
104130
if err != nil {
105-
return nil, fmt.Errorf("failed to write field: %w", err)
131+
return fmt.Errorf("failed to write field: %w", err)
106132
}
107133
}
108134

109-
return b, nil
135+
return nil
110136
}
111137

112138
func BenchmarkFormStreamFastPath(b *testing.B) {
@@ -122,6 +148,18 @@ func BenchmarkFormStreamFastPath(b *testing.B) {
122148
b.Run("1GB", func(b *testing.B) {
123149
benchmarkFormStream(b, 1*formstream.GB, false)
124150
})
151+
b.Run("5GB", func(b *testing.B) {
152+
if testing.Short() {
153+
b.Skip("skipping test in short mode.")
154+
}
155+
benchmarkFormStream(b, 5*formstream.GB, false)
156+
})
157+
b.Run("10GB", func(b *testing.B) {
158+
if testing.Short() {
159+
b.Skip("skipping test in short mode.")
160+
}
161+
benchmarkFormStream(b, 10*formstream.GB, false)
162+
})
125163
}
126164

127165
func BenchmarkFormStreamSlowPath(b *testing.B) {
@@ -137,12 +175,31 @@ func BenchmarkFormStreamSlowPath(b *testing.B) {
137175
b.Run("1GB", func(b *testing.B) {
138176
benchmarkFormStream(b, 1*formstream.GB, true)
139177
})
178+
b.Run("5GB", func(b *testing.B) {
179+
if testing.Short() {
180+
b.Skip("skipping test in short mode.")
181+
}
182+
benchmarkFormStream(b, 5*formstream.GB, true)
183+
})
184+
b.Run("10GB", func(b *testing.B) {
185+
if testing.Short() {
186+
b.Skip("skipping test in short mode.")
187+
}
188+
benchmarkFormStream(b, 10*formstream.GB, true)
189+
})
140190
}
141191

142192
func benchmarkFormStream(b *testing.B, fileSize formstream.DataSize, reverse bool) {
193+
r, err := sampleForm(fileSize, boundary, reverse)
194+
if err != nil {
195+
b.Fatal(err)
196+
}
197+
defer r.Close()
198+
199+
b.ResetTimer()
143200
for i := 0; i < b.N; i++ {
144201
b.StopTimer()
145-
r, err := sampleForm(fileSize, boundary, reverse)
202+
_, err := r.Seek(0, io.SeekStart)
146203
if err != nil {
147204
b.Fatal(err)
148205
}
@@ -169,32 +226,50 @@ func benchmarkFormStream(b *testing.B, fileSize formstream.DataSize, reverse boo
169226
if err != nil {
170227
b.Fatal(err)
171228
}
172-
173229
}
174230
}
175231

176-
func BenchmarkStdMultipart_ReadForm(b *testing.B) {
177-
// default value in http package
178-
const maxMemory = 32 * formstream.MB
179-
232+
func BenchmarkStdMultipartReadForm(b *testing.B) {
180233
b.Run("1MB", func(b *testing.B) {
181-
benchmarkStdMultipart_ReadForm(b, 1*formstream.MB, maxMemory)
234+
benchmarkStdMultipartReadForm(b, 1*formstream.MB)
182235
})
183236
b.Run("10MB", func(b *testing.B) {
184-
benchmarkStdMultipart_ReadForm(b, 10*formstream.MB, maxMemory)
237+
benchmarkStdMultipartReadForm(b, 10*formstream.MB)
185238
})
186239
b.Run("100MB", func(b *testing.B) {
187-
benchmarkStdMultipart_ReadForm(b, 100*formstream.MB, maxMemory)
240+
benchmarkStdMultipartReadForm(b, 100*formstream.MB)
188241
})
189242
b.Run("1GB", func(b *testing.B) {
190-
benchmarkStdMultipart_ReadForm(b, 1*formstream.GB, maxMemory)
243+
benchmarkStdMultipartReadForm(b, 1*formstream.GB)
244+
})
245+
b.Run("5GB", func(b *testing.B) {
246+
if testing.Short() {
247+
b.Skip("skipping test in short mode.")
248+
}
249+
benchmarkStdMultipartReadForm(b, 5*formstream.GB)
250+
})
251+
b.Run("10GB", func(b *testing.B) {
252+
if testing.Short() {
253+
b.Skip("skipping test in short mode.")
254+
}
255+
benchmarkStdMultipartReadForm(b, 10*formstream.GB)
191256
})
192257
}
193258

194-
func benchmarkStdMultipart_ReadForm(b *testing.B, fileSize formstream.DataSize, maxMemory formstream.DataSize) {
259+
func benchmarkStdMultipartReadForm(b *testing.B, fileSize formstream.DataSize) {
260+
// default value in http package
261+
const maxMemory = 32 * formstream.MB
262+
263+
r, err := sampleForm(fileSize, boundary, false)
264+
if err != nil {
265+
b.Fatal(err)
266+
}
267+
defer r.Close()
268+
269+
b.ResetTimer()
195270
for i := 0; i < b.N; i++ {
196271
b.StopTimer()
197-
r, err := sampleForm(fileSize, boundary, false)
272+
_, err := r.Seek(0, io.SeekStart)
198273
if err != nil {
199274
b.Fatal(err)
200275
}

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ require (
3838
github.com/gin-gonic/gin v1.9.1
3939
github.com/labstack/echo/v4 v4.11.4
4040
golang.org/x/mod v0.11.0 // indirect
41+
golang.org/x/sync v0.6.0
4142
golang.org/x/sys v0.15.0 // indirect
4243
golang.org/x/tools v0.6.0 // indirect
4344
)

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU=
7676
golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
7777
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
7878
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
79+
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
80+
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
7981
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
8082
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
8183
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=

0 commit comments

Comments
 (0)