Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(perf): Optimize the Copy method of the Context struct #3859

Merged
merged 2 commits into from
Mar 5, 2024

Conversation

1911860538
Copy link
Contributor

@1911860538 1911860538 commented Feb 29, 2024

Using 'make' to initialize the map('cp.Keys') with a length of 'c.Keys'.
Avoiding repeatedly assiging the 'params' to 'context'.

Below is my local test code:

context.go

func (c *Context) Copy() *Context {
	cp := Context{
		writermem: c.writermem,
		Request:   c.Request,
		Params:    c.Params,
		engine:    c.engine,
	}
	cp.writermem.ResponseWriter = nil
	cp.Writer = &cp.writermem
	cp.index = abortIndex
	cp.handlers = nil
	cp.Keys = map[string]any{}
	for k, v := range c.Keys {
		cp.Keys[k] = v
	}
	paramCopy := make([]Param, len(cp.Params))
	copy(paramCopy, cp.Params)
	cp.Params = paramCopy
	return &cp
}

func (c *Context) NewCopy() *Context {
	cp := Context{
		writermem: c.writermem,
		Request:   c.Request,
		engine:    c.engine,
	}

	cp.writermem.ResponseWriter = nil
	cp.Writer = &cp.writermem
	cp.index = abortIndex
	cp.handlers = nil

	cKeys := c.Keys
	cp.Keys = make(map[string]any, len(cKeys))
	for k, v := range cKeys {
		cp.Keys[k] = v
	}

	cParams := c.Params
	cp.Params = make([]Param, len(cParams))
	copy(cp.Params, cParams)

	return &cp
}

context_test.go

func getTestContext() *Context {
    return &Context{
        Keys:   map[string]any{
            "k0" :   "v0" ,
            "k1" : 1,
            "k2" : 2.0,
            "k3" : true,
            "k4" :   "v4" ,
        },
        Params: []Param{
            {
                Key:     "k0" ,
                Value:   "v0" ,
            },
            {
                Key:     "k1" ,
                Value:   "v1" ,
            },
            {
                Key:     "k2" ,
                Value:   "v2" ,
            },
        },
    }
}
 
func TestCopySame(t *testing.T) {
    testCtx := getTestContext()
 
    copyCtx := testCtx.Copy()
    newCopyCtx := testCtx.NewCopy()
 
    if !reflect.DeepEqual(copyCtx, newCopyCtx) {
        t.Error("Got different contexts!")
    }
}
 
func BenchmarkContextCopy(b *testing.B) {
    testCtx := getTestContext()
    b.ReportAllocs()
    b.ResetTimer()
 
    for i := 0; i < b.N; i++ {
        _ = testCtx.Copy()
    }
}
 
func BenchmarkContextNewCopy(b *testing.B) {
    testCtx := getTestContext()
    b.ReportAllocs()
    b.ResetTimer()
 
    for i := 0; i < b.N; i++ {
        _ = testCtx.NewCopy()
    }
}

test output

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

Process finished with the exit code 0

bench output

goos: linux
goarch: amd64
pkg: github.com/gin-gonic/gin
cpu: Intel(R) Core(TM) i5-8400 CPU @ 2.80GHz
BenchmarkContextCopy
BenchmarkContextCopy-6             	 2506035	       519.9 ns/op	     688 B/op	       4 allocs/op
BenchmarkContextNewCopy
BenchmarkContextNewCopy-6          	 4579729	       255.8 ns/op	      96 B/op	       1 allocs/op
PASS

Process finished with the exit code 0

…alize the map('cp.Keys') with a length of 'c.Keys'; avoiding repeatedly assiging the 'params' to 'context'.
… from changing during the copying process.
@1911860538
Copy link
Contributor Author

Please help to review this PR. @manucorporat @thinkerou @appleboy @javierprovecho

Copy link

codecov bot commented Mar 4, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 99.36%. Comparing base (3dc1cd6) to head (a0ee712).
Report is 6 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #3859      +/-   ##
==========================================
+ Coverage   99.21%   99.36%   +0.15%     
==========================================
  Files          42       43       +1     
  Lines        3182     2676     -506     
==========================================
- Hits         3157     2659     -498     
+ Misses         17        9       -8     
  Partials        8        8              
Flag Coverage Δ
?
-race ∅ <ø> (?)
-tags "sonic avx" 99.35% <100.00%> (?)
-tags go_json 99.35% <100.00%> (?)
-tags nomsgpack 99.35% <100.00%> (?)
go-1.18 99.28% <100.00%> (+0.16%) ⬆️
go-1.19 99.36% <100.00%> (+0.15%) ⬆️
go-1.20 99.36% <100.00%> (+0.15%) ⬆️
go-1.21 99.36% <100.00%> (+0.15%) ⬆️
go-1.22 99.36% <100.00%> (?)
macos-latest 99.36% <100.00%> (+0.15%) ⬆️
ubuntu-latest 99.36% <100.00%> (+0.15%) ⬆️

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 Optimize the Copy method of the Context struct chore(perf): Optimize the Copy method of the Context struct Mar 5, 2024
@appleboy appleboy added this to the v1.10 milestone Mar 5, 2024
@appleboy appleboy merged commit 739d2d9 into gin-gonic:master Mar 5, 2024
55 checks passed
@1911860538 1911860538 deleted the optimize-context-copy branch March 5, 2024 09:11
@haunt98
Copy link

haunt98 commented Mar 5, 2024

Good catch @1911860538 !

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.

None yet

3 participants