Skip to content

Conversation

@1911860538
Copy link
Contributor

@1911860538 1911860538 commented Mar 29, 2024

Enhance code readability by replacing the original "switch statement and if conditionals" with a simple "for loop" to iterate over the error slice.

Below is my local test code:

default_validator.go

type SliceValidationError []error

// Error concatenates all error elements in SliceValidationError into a single string separated by \n.
func (err SliceValidationError) Error() string {
	n := len(err)
	switch n {
	case 0:
		return ""
	default:
		var b strings.Builder
		if err[0] != nil {
			fmt.Fprintf(&b, "[%d]: %s", 0, err[0].Error())
		}
		if n > 1 {
			for i := 1; i < n; i++ {
				if err[i] != nil {
					b.WriteString("\n")
					fmt.Fprintf(&b, "[%d]: %s", i, err[i].Error())
				}
			}
		}
		return b.String()
	}
}

func (err SliceValidationError) NewError() string {
	if len(err) == 0 {
		return ""
	}

	var b strings.Builder
	for i := 0; i < len(err); i++ {
		if err[i] != nil {
			if b.Len() > 0 {
				b.WriteString("\n")
			}
			b.WriteString("[" + strconv.Itoa(i) + "]: " + err[i].Error())
		}
	}
	return b.String()
}

default_validator_benchmark_test.go

package binding

import (
	"errors"
	"strconv"
	"testing"

	"github.com/stretchr/testify/assert"
)

func getSliceValidationError(n int) SliceValidationError {
	e := make(SliceValidationError, n)
	for i := 0; i < n; i++ {
		e[i] = errors.New(strconv.Itoa(i))
	}
	return e
}

func TestSameError(t *testing.T) {
	e0 := getSliceValidationError(0)
	assert.Equal(t, e0.Error(), e0.NewError())

	e1 := getSliceValidationError(1)
	assert.Equal(t, e1.Error(), e1.NewError())

	e10 := getSliceValidationError(10)
	assert.Equal(t, e10.Error(), e10.NewError())
}

func BenchmarkSliceValidationError0(b *testing.B) {
	e := getSliceValidationError(0)

	b.ReportAllocs()
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		_ = e.Error()
	}
}

func BenchmarkSliceValidationNewError0(b *testing.B) {
	e := getSliceValidationError(0)

	b.ReportAllocs()
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		_ = e.NewError()
	}
}

func BenchmarkSliceValidationError1(b *testing.B) {
	e := getSliceValidationError(1)

	b.ReportAllocs()
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		_ = e.Error()
	}
}

func BenchmarkSliceValidationNewError1(b *testing.B) {
	e := getSliceValidationError(1)

	b.ReportAllocs()
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		_ = e.NewError()
	}
}

func BenchmarkSliceValidationError10(b *testing.B) {
	e := getSliceValidationError(10)

	b.ReportAllocs()
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		_ = e.Error()
	}
}

func BenchmarkSliceValidationNewError10(b *testing.B) {
	e := getSliceValidationError(10)

	b.ReportAllocs()
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		_ = e.NewError()
	}
}

test output

=== RUN   TestSameError
--- PASS: TestSameError (0.00s)
PASS

Process finished with the exit code 0

benchmark output

goos: darwin
goarch: amd64
pkg: github.com/gin-gonic/gin/binding
cpu: Intel(R) Core(TM) i7-8569U CPU @ 2.80GHz
BenchmarkSliceValidationError0
BenchmarkSliceValidationError0-8       	611569416	         1.911 ns/op	       0 B/op	       0 allocs/op
BenchmarkSliceValidationNewError0
BenchmarkSliceValidationNewError0-8    	614464096	         1.909 ns/op	       0 B/op	       0 allocs/op
BenchmarkSliceValidationError1
BenchmarkSliceValidationError1-8       	 5836453	       196.6 ns/op	      56 B/op	       3 allocs/op
BenchmarkSliceValidationNewError1
BenchmarkSliceValidationNewError1-8    	21680595	        51.82 ns/op	       8 B/op	       1 allocs/op
BenchmarkSliceValidationError10
BenchmarkSliceValidationError10-8      	  705206	      1609 ns/op	     440 B/op	      16 allocs/op
BenchmarkSliceValidationNewError10
BenchmarkSliceValidationNewError10-8   	 2226651	       530.6 ns/op	     248 B/op	       5 allocs/op
PASS

Process finished with the exit code 0

@codecov
Copy link

codecov bot commented Mar 29, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 99.18%. Comparing base (3dc1cd6) to head (345707e).
Report is 55 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #3910      +/-   ##
==========================================
- Coverage   99.21%   99.18%   -0.03%     
==========================================
  Files          42       43       +1     
  Lines        3182     2704     -478     
==========================================
- Hits         3157     2682     -475     
+ Misses         17       12       -5     
- Partials        8       10       +2     
Flag Coverage Δ
?
-tags "sonic avx" 99.17% <100.00%> (?)
-tags go_json 99.17% <100.00%> (?)
-tags nomsgpack 99.17% <100.00%> (?)
go-1.18 99.11% <100.00%> (-0.01%) ⬇️
go-1.19 99.18% <100.00%> (-0.03%) ⬇️
go-1.20 99.18% <100.00%> (-0.03%) ⬇️
go-1.21 99.18% <100.00%> (-0.03%) ⬇️
go-1.22 99.18% <100.00%> (?)
macos-latest 99.18% <100.00%> (-0.03%) ⬇️
ubuntu-latest 99.18% <100.00%> (-0.03%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@appleboy appleboy changed the title Simplify SliceValidationError Error method refactor(slice): simplify SliceValidationError Error method May 13, 2024
@appleboy appleboy added this to the v1.11 milestone May 13, 2024
@appleboy appleboy merged commit 3f5b0af into gin-gonic:master May 13, 2024
@1911860538 1911860538 deleted the optimize-validator-error branch February 26, 2025 04:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants