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

Add RegexpCompileFunc to override regexp.Compile #731

Merged
merged 2 commits into from
Apr 2, 2024

Conversation

titpetric
Copy link
Contributor

@titpetric titpetric commented Aug 24, 2023

What type of PR is this? (check all applicable)

  • Refactor
  • Feature
  • Bug Fix
  • Optimization
  • Documentation Update

Description

Route creation is slow as it always invokes regexp.Compile, increasing memory and cpu pressure in cases where route creation is common (e.g. an api gateway reloading changes). This PR adds a exported RegexpCompileFunc variable, that's possible to override with other implementations, adding caches. Added a benchmark.

Related Tickets & Documents

# go test -bench=NewRouter -benchmem -memprofile memprofile.out -cpuprofile profile.out -count 10
goos: linux
goarch: amd64
pkg: github.com/gorilla/mux
cpu: 11th Gen Intel(R) Core(TM) i7-1185G7 @ 3.00GHz
BenchmarkNewRouter-8             	   36786	     34450 ns/op	   26327 B/op	     374 allocs/op
BenchmarkNewRouter-8             	   44839	     31840 ns/op	   26327 B/op	     374 allocs/op
BenchmarkNewRouter-8             	   48939	     23419 ns/op	   26327 B/op	     374 allocs/op
BenchmarkNewRouter-8             	   52255	     25949 ns/op	   26327 B/op	     374 allocs/op
BenchmarkNewRouter-8             	   44684	     23150 ns/op	   26329 B/op	     374 allocs/op
BenchmarkNewRouter-8             	   41522	     35047 ns/op	   26328 B/op	     374 allocs/op
BenchmarkNewRouter-8             	   48354	     35461 ns/op	   26329 B/op	     374 allocs/op
BenchmarkNewRouter-8             	   33850	     38467 ns/op	   26330 B/op	     374 allocs/op
BenchmarkNewRouter-8             	   36920	     33917 ns/op	   26330 B/op	     374 allocs/op
BenchmarkNewRouter-8             	   40767	     29980 ns/op	   26329 B/op	     374 allocs/op
BenchmarkNewRouterRegexpFunc-8   	  303121	      3738 ns/op	    2161 B/op	      73 allocs/op
BenchmarkNewRouterRegexpFunc-8   	  311847	      4184 ns/op	    2161 B/op	      73 allocs/op
BenchmarkNewRouterRegexpFunc-8   	  300144	      4212 ns/op	    2161 B/op	      73 allocs/op
BenchmarkNewRouterRegexpFunc-8   	  329636	      4528 ns/op	    2161 B/op	      73 allocs/op
BenchmarkNewRouterRegexpFunc-8   	  277980	      5136 ns/op	    2161 B/op	      73 allocs/op
BenchmarkNewRouterRegexpFunc-8   	  363416	      4578 ns/op	    2161 B/op	      73 allocs/op
BenchmarkNewRouterRegexpFunc-8   	  328996	      4231 ns/op	    2161 B/op	      73 allocs/op
BenchmarkNewRouterRegexpFunc-8   	  327628	      3699 ns/op	    2161 B/op	      73 allocs/op
BenchmarkNewRouterRegexpFunc-8   	  300370	      3955 ns/op	    2161 B/op	      73 allocs/op
BenchmarkNewRouterRegexpFunc-8   	  297345	      4115 ns/op	    2161 B/op	      73 allocs/op
PASS
ok  	github.com/gorilla/mux	35.091s

Benchmark: NewRouter

Average runtime per iteration: ~34450 ns/op
Average memory usage per iteration: ~26327 B/op
Average allocations per iteration: ~374 allocs/op

Benchmark: NewRouterRegexpFunc

Average runtime per iteration: ~4184 ns/op (approx. 87.8% improvement)
Average memory usage per iteration: ~2161 B/op (approx. 91.8% reduction)
Average allocations per iteration: ~73 allocs/op (approx. 80.5% reduction)

As per go doc, using *regexp.Regexp concurrently is safe.

Added/updated tests?

  • Yes
  • No, and this is why: please replace this line with details on why tests
    have not been included
  • I need help with writing tests

Run verifications and test

  • make verify is passing
  • make test is passing

@codecov
Copy link

codecov bot commented Aug 24, 2023

Codecov Report

Merging #731 (7784a7c) into main (3401478) will not change coverage.
The diff coverage is 100.00%.

❗ Current head 7784a7c differs from pull request most recent head 9efbf04. Consider uploading reports for the commit 9efbf04 to get more accurate results

@@           Coverage Diff           @@
##             main     #731   +/-   ##
=======================================
  Coverage   78.04%   78.04%           
=======================================
  Files           5        5           
  Lines         902      902           
=======================================
  Hits          704      704           
  Misses        142      142           
  Partials       56       56           
Files Changed Coverage Δ
mux.go 83.52% <100.00%> (ø)
regexp.go 83.05% <100.00%> (ø)

@coreydaley
Copy link
Contributor

@titpetric Thank you for the pull request! We will need a little bit of time to digest this and review it, but it is definitely towards the top of our queue.

@titpetric
Copy link
Contributor Author

titpetric commented Aug 28, 2023

@coreydaley Let me know if there's absolutely anything I can do to improve it.

I've considered adding a .SetRegexpCompile(func(...)...) as an API addition, but wen't with the 'less is more' approach. It would be possible to update this in runtime with some mutex protections. That sounds fine until you consider how many extra bugs can rise up when conccurency is at play.

Explicit control of the regex compile func behaviour is a single shot - if someone wants to control that during the router lifetime, just implementing the compile func signature enables it. It's expected there would be some sort of smarter eviction implementations behind it. Overthinking it just leads to more unnecessary problems, keeping things simple is hard sometimes.

@AlexVulaj
Copy link
Member

@coreydaley This looks good to me, though I'll defer to you for a merge since I know you were looking at it first.

While I'm in favor of giving consumers the freedom to optimize their route creation, I believe such an action comes with the implicit disclaimer that if someone is going to play with around with it, they should know what they're doing and are on their own if it breaks something.

route_test.go Outdated Show resolved Hide resolved
@AlexVulaj
Copy link
Member

Another note - all of the changes from this PR seem to also be present in #732

@titpetric
Copy link
Contributor Author

Another note - all of the changes from this PR seem to also be present in #732

Correct, regexp is it's own optimization scope (this PR). The second PR needs a rebase when this is merged, that addresses actual memory pressure, while this one just gives us an ability to use a cached/compiled *regexp.Regexp.

@titpetric titpetric closed this Sep 21, 2023
@titpetric titpetric reopened this Sep 21, 2023
@titpetric titpetric force-pushed the feature/override-regexp-compile branch from 7784a7c to 9efbf04 Compare September 21, 2023 11:41
@titpetric
Copy link
Contributor Author

  • rebased
  • marked testing.TB internal func param as _ (leave in place)

@coreydaley coreydaley removed their assignment Feb 4, 2024
@AlexVulaj AlexVulaj merged commit 7d9a6f7 into gorilla:main Apr 2, 2024
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Status: ✅ Done
Development

Successfully merging this pull request may close these issues.

None yet

4 participants