Repository: gorilla/mux
Pull request #691 (2022-08-05 21:45:34)

perf: cut allocations
**Summary of Changes**

Hello!

I found a few "low hanging" allocations that can be deferred until needed or
 even skipped entirely.

With all the optimizations combined, we can see the best improvement for
 simple, static routes (like a `/status` endpoint) that do not read the `Route`
 from the request context via `CurrentRoute` and do not populate any vars.
On these routes, we can process requests with a single allocation for the
 `RouteMatch` object. Previously there were 9 extra allocations.
For said routes the processing overhead (ns/op) in mux dropped by 75%, which is
 a speedup of 4x.

Other routes can expect to see a double-digit percentage reduction in both
 processing overhead (ns/op) and allocations as well.
These are driven by merging the context population into a single operation,
 eliminating two of ten allocations.

(Eliminating that last allocation for the `RouteMatch` in the best case
 requires significant refactoring to maintain full backwards compatibility.
 Something for another day.)

Each commit message contains benchmark results for showcasing particular
 (micro) optimizations in reduced allocations and in a few cases notable direct
 CPU time savings.

I also ran longer benchmarks with 100 repetitions in multiple settings on
 different generations of (server) CPUs.
First, there is the full set of benchmarks in this repository and second, the
 popular benchmarks https://github.com/julienschmidt/go-http-routing-benchmark.

All but the last change are entirely "free", as in they do not cut features for
 gains in performance. The last change for omitting the `Route` from the
context is behind an optional flag that users can opt in when they do not read
 the `Route` from the request context.
Said flag is stored local in a `Router`, so users can enable/disable the flag
 on Subrouters individually.

<details>
<summary> Benchmark results </summary>

I added all the new benchmarks onto a `baseline` branch for comparing the
 performance of the changes, tip is 0eba4f5731f329d9a95b67359e795344343a6119.

I'm running these tests on "shared" compute instances (and my Laptop), so
 expect some noise (and frequency scaling on the i7).

<details>
<summary> mux project benchmarks </summary>

You can reproduce these benchmarks using docker, pinned to CPU 1:

```bash
docker run --rm --pull always -v /logs:/logs --cpuset-cpus 1 -d golang:1.18 bash -exc 'git clone https://github.com/das7pad/mux.git && cd mux && for branch in baseline perf-cut-allocations; do git checkout "$branch" && go test -benchmem -bench . -count 100 -timeout 1h > "/logs/$branch-all.txt"; done; go install golang.org/x/perf/cmd/benchstat@latest; benchstat /logs/baseline-all.txt /logs/perf-cut-allocations-all.txt > /logs/compare-all.txt'
```

<details>
<summary> Modern Xeon E, 3.4 GHz </summary>

```
goos: linux
goarch: amd64
pkg: github.com/gorilla/mux
cpu: Intel(R) Xeon(R) E-2278G CPU @ 3.40GHz

name                                 old time/op    new time/op    delta
Mux                                    1.09µs ± 5%    0.96µs ± 6%  -12.33%  (p=0.000 n=90+90)
MuxSimple/default                       654ns ± 5%     333ns ± 4%  -49.06%  (p=0.000 n=90+94)
MuxSimple/omit_route_from_ctx           655ns ± 5%     146ns ± 3%  -77.78%  (p=0.000 n=93+95)
MuxAlternativeInRegexp                 1.58µs ± 5%    1.30µs ± 3%  -17.75%  (p=0.000 n=92+90)
ManyPathVariables                      1.82µs ± 6%    1.65µs ± 5%   -9.73%  (p=0.000 n=94+93)
PopulateContext/no_populated_vars       665ns ± 5%     339ns ± 5%  -48.97%  (p=0.000 n=95+96)
PopulateContext/empty_var               910ns ± 5%     763ns ± 6%  -16.16%  (p=0.000 n=84+87)
PopulateContext/populated_vars          974ns ±10%     807ns ± 7%  -17.13%  (p=0.000 n=96+89)
PopulateContext/omit_route_/static      664ns ± 5%     148ns ± 3%  -77.66%  (p=0.000 n=94+89)
PopulateContext/omit_route_/dynamic     915ns ± 6%     741ns ± 6%  -18.98%  (p=0.000 n=90+94)
_findQueryKey/0                         158ns ± 2%     146ns ± 2%   -7.07%  (p=0.000 n=91+88)
_findQueryKey/1                         198ns ± 4%     196ns ± 5%   -0.56%  (p=0.001 n=92+95)
_findQueryKey/2                         710ns ± 4%     705ns ± 4%   -0.80%  (p=0.000 n=94+94)
_findQueryKey/3                         804ns ± 4%     798ns ± 5%   -0.82%  (p=0.000 n=90+94)
_findQueryKey/4                        3.85ns ± 2%    3.85ns ± 3%     ~     (p=0.224 n=89+93)
_findQueryKeyGoLib/0                    664ns ± 4%     668ns ± 5%   +0.67%  (p=0.030 n=97+94)
_findQueryKeyGoLib/1                    359ns ± 6%     359ns ± 6%     ~     (p=0.830 n=94+93)
_findQueryKeyGoLib/2                   2.24µs ± 3%    2.23µs ± 4%   -0.37%  (p=0.041 n=95+93)
_findQueryKeyGoLib/3                   2.94µs ± 4%    2.92µs ± 3%   -0.68%  (p=0.001 n=90+94)
_findQueryKeyGoLib/4                   3.63ns ± 4%    3.62ns ± 3%   -0.38%  (p=0.014 n=91+88)

name                                 old alloc/op   new alloc/op   delta
Mux                                    1.31kB ± 0%    0.91kB ± 0%  -30.49%  (p=0.000 n=100+100)
MuxSimple/default                      1.01kB ± 0%    0.50kB ± 0%  -50.79%  (p=0.000 n=100+100)
MuxSimple/omit_route_from_ctx          1.01kB ± 0%    0.05kB ± 0%  -95.24%  (p=0.000 n=100+100)
MuxAlternativeInRegexp                 2.62kB ± 0%    1.82kB ± 0%  -30.49%  (p=0.000 n=100+100)
ManyPathVariables                      1.53kB ± 1%    1.13kB ± 0%  -26.45%  (p=0.000 n=99+93)
PopulateContext/no_populated_vars      1.01kB ± 0%    0.50kB ± 0%  -50.79%  (p=0.000 n=100+100)
PopulateContext/empty_var              1.33kB ± 0%    0.93kB ± 0%  -30.12%  (p=0.000 n=100+100)
PopulateContext/populated_vars         1.31kB ± 0%    0.91kB ± 0%  -30.49%  (p=0.000 n=100+100)
PopulateContext/omit_route_/static     1.01kB ± 0%    0.05kB ± 0%  -95.24%  (p=0.000 n=100+100)
PopulateContext/omit_route_/dynamic    1.33kB ± 0%    0.88kB ± 0%  -33.73%  (p=0.000 n=100+100)
_findQueryKey/0                         0.00B          0.00B          ~     (all equal)
_findQueryKey/1                         40.0B ± 0%     40.0B ± 0%     ~     (all equal)
_findQueryKey/2                          483B ± 0%      483B ± 0%     ~     (all equal)
_findQueryKey/3                          543B ± 0%      543B ± 0%     ~     (all equal)
_findQueryKey/4                         0.00B          0.00B          ~     (all equal)
_findQueryKeyGoLib/0                     864B ± 0%      864B ± 0%     ~     (all equal)
_findQueryKeyGoLib/1                     432B ± 0%      432B ± 0%     ~     (all equal)
_findQueryKeyGoLib/2                   1.54kB ± 0%    1.54kB ± 0%     ~     (all equal)
_findQueryKeyGoLib/3                   1.98kB ± 0%    1.98kB ± 0%     ~     (all equal)
_findQueryKeyGoLib/4                    0.00B          0.00B          ~     (all equal)

name                                 old allocs/op  new allocs/op  delta
Mux                                      10.0 ± 0%       8.0 ± 0%  -20.00%  (p=0.000 n=100+100)
MuxSimple/default                        9.00 ± 0%      4.00 ± 0%  -55.56%  (p=0.000 n=100+100)
MuxSimple/omit_route_from_ctx            9.00 ± 0%      1.00 ± 0%  -88.89%  (p=0.000 n=100+100)
MuxAlternativeInRegexp                   20.0 ± 0%      16.0 ± 0%  -20.00%  (p=0.000 n=100+100)
ManyPathVariables                        14.0 ± 0%      12.0 ± 0%  -14.29%  (p=0.000 n=100+100)
PopulateContext/no_populated_vars        9.00 ± 0%      4.00 ± 0%  -55.56%  (p=0.000 n=100+100)
PopulateContext/empty_var                11.0 ± 0%       9.0 ± 0%  -18.18%  (p=0.000 n=100+100)
PopulateContext/populated_vars           10.0 ± 0%       8.0 ± 0%  -20.00%  (p=0.000 n=100+100)
PopulateContext/omit_route_/static       9.00 ± 0%      1.00 ± 0%  -88.89%  (p=0.000 n=100+100)
PopulateContext/omit_route_/dynamic      11.0 ± 0%       8.0 ± 0%  -27.27%  (p=0.000 n=100+100)
_findQueryKey/0                          0.00           0.00          ~     (all equal)
_findQueryKey/1                          3.00 ± 0%      3.00 ± 0%     ~     (all equal)
_findQueryKey/2                          10.0 ± 0%      10.0 ± 0%     ~     (all equal)
_findQueryKey/3                          11.0 ± 0%      11.0 ± 0%     ~     (all equal)
_findQueryKey/4                          0.00           0.00          ~     (all equal)
_findQueryKeyGoLib/0                     8.00 ± 0%      8.00 ± 0%     ~     (all equal)
_findQueryKeyGoLib/1                     4.00 ± 0%      4.00 ± 0%     ~     (all equal)
_findQueryKeyGoLib/2                     24.0 ± 0%      24.0 ± 0%     ~     (all equal)
_findQueryKeyGoLib/3                     28.0 ± 0%      28.0 ± 0%     ~     (all equal)
_findQueryKeyGoLib/4                     0.00           0.00          ~     (all equal)
```

---

</details>

<details>
<summary> Older Xeon Gold, 2.3 GHz </summary>

(Actual CPU identifier is rather `Intel(R) Xeon(R) Gold 5122 CPU @ 2.30GHz`)
```
goos: linux
goarch: amd64
pkg: github.com/gorilla/mux
cpu: Intel Xeon Processor (Skylake, IBRS) 

name                                 old time/op    new time/op    delta
Mux                                    1.61µs ± 6%    1.43µs ± 6%  -11.38%  (p=0.000 n=97+96)
MuxSimple/default                       979ns ± 8%     508ns ± 7%  -48.15%  (p=0.000 n=93+98)
MuxSimple/omit_route_from_ctx          1.00µs ±12%    0.22µs ± 4%  -77.76%  (p=0.000 n=99+96)
MuxAlternativeInRegexp                 2.40µs ± 9%    2.03µs ±12%  -15.15%  (p=0.000 n=94+98)
ManyPathVariables                      2.77µs ± 8%    2.60µs ±12%   -6.17%  (p=0.000 n=94+99)
PopulateContext/no_populated_vars      1.03µs ±11%    0.54µs ±13%  -47.57%  (p=0.000 n=94+98)
PopulateContext/empty_var              1.35µs ± 6%    1.23µs ±11%   -9.01%  (p=0.000 n=95+96)
PopulateContext/populated_vars         1.40µs ± 8%    1.30µs ±14%   -6.98%  (p=0.000 n=97+99)
PopulateContext/omit_route_/static      989ns ± 7%     239ns ±10%  -75.78%  (p=0.000 n=99+99)
PopulateContext/omit_route_/dynamic    1.34µs ± 7%    1.21µs ±15%  -10.04%  (p=0.000 n=95+99)
_findQueryKey/0                         242ns ± 8%     228ns ± 8%   -6.00%  (p=0.000 n=97+95)
_findQueryKey/1                         304ns ± 7%     309ns ± 9%   +1.60%  (p=0.002 n=97+97)
_findQueryKey/2                        1.08µs ± 8%    1.14µs ±10%   +5.28%  (p=0.000 n=99+97)
_findQueryKey/3                        1.20µs ± 6%    1.23µs ± 9%   +2.03%  (p=0.000 n=92+99)
_findQueryKey/4                        5.86ns ± 4%    5.89ns ± 5%   +0.56%  (p=0.041 n=93+96)
_findQueryKeyGoLib/0                    992ns ± 7%    1067ns ±11%   +7.59%  (p=0.000 n=94+98)
_findQueryKeyGoLib/1                    528ns ± 6%     571ns ±11%   +8.04%  (p=0.000 n=97+100)
_findQueryKeyGoLib/2                   3.46µs ± 6%    3.60µs ±10%   +4.18%  (p=0.000 n=96+95)
_findQueryKeyGoLib/3                   4.54µs ± 6%    4.75µs ±11%   +4.62%  (p=0.000 n=92+98)
_findQueryKeyGoLib/4                   5.50ns ± 4%    5.62ns ± 7%   +2.14%  (p=0.000 n=91+96)

name                                 old alloc/op   new alloc/op   delta
Mux                                    1.31kB ± 0%    0.91kB ± 0%  -30.49%  (p=0.000 n=100+100)
MuxSimple/default                      1.01kB ± 0%    0.50kB ± 0%  -50.79%  (p=0.000 n=100+100)
MuxSimple/omit_route_from_ctx          1.01kB ± 0%    0.05kB ± 0%  -95.24%  (p=0.000 n=100+100)
MuxAlternativeInRegexp                 2.62kB ± 0%    1.82kB ± 0%  -30.49%  (p=0.000 n=100+100)
ManyPathVariables                      1.52kB ± 0%    1.12kB ± 0%  -26.50%  (p=0.000 n=92+97)
PopulateContext/no_populated_vars      1.01kB ± 0%    0.50kB ± 0%  -50.79%  (p=0.000 n=100+100)
PopulateContext/empty_var              1.33kB ± 0%    0.93kB ± 0%  -30.12%  (p=0.000 n=100+100)
PopulateContext/populated_vars         1.31kB ± 0%    0.91kB ± 0%  -30.49%  (p=0.000 n=100+100)
PopulateContext/omit_route_/static     1.01kB ± 0%    0.05kB ± 0%  -95.24%  (p=0.000 n=100+100)
PopulateContext/omit_route_/dynamic    1.33kB ± 0%    0.88kB ± 0%  -33.73%  (p=0.000 n=100+100)
_findQueryKey/0                         0.00B          0.00B          ~     (all equal)
_findQueryKey/1                         40.0B ± 0%     40.0B ± 0%     ~     (all equal)
_findQueryKey/2                          483B ± 0%      483B ± 0%     ~     (all equal)
_findQueryKey/3                          543B ± 0%      543B ± 0%     ~     (all equal)
_findQueryKey/4                         0.00B          0.00B          ~     (all equal)
_findQueryKeyGoLib/0                     864B ± 0%      864B ± 0%     ~     (all equal)
_findQueryKeyGoLib/1                     432B ± 0%      432B ± 0%     ~     (all equal)
_findQueryKeyGoLib/2                   1.54kB ± 0%    1.54kB ± 0%     ~     (all equal)
_findQueryKeyGoLib/3                   1.98kB ± 0%    1.98kB ± 0%     ~     (all equal)
_findQueryKeyGoLib/4                    0.00B          0.00B          ~     (all equal)

name                                 old allocs/op  new allocs/op  delta
Mux                                      10.0 ± 0%       8.0 ± 0%  -20.00%  (p=0.000 n=100+100)
MuxSimple/default                        9.00 ± 0%      4.00 ± 0%  -55.56%  (p=0.000 n=100+100)
MuxSimple/omit_route_from_ctx            9.00 ± 0%      1.00 ± 0%  -88.89%  (p=0.000 n=100+100)
MuxAlternativeInRegexp                   20.0 ± 0%      16.0 ± 0%  -20.00%  (p=0.000 n=100+100)
ManyPathVariables                        14.0 ± 0%      12.0 ± 0%  -14.29%  (p=0.000 n=100+100)
PopulateContext/no_populated_vars        9.00 ± 0%      4.00 ± 0%  -55.56%  (p=0.000 n=100+100)
PopulateContext/empty_var                11.0 ± 0%       9.0 ± 0%  -18.18%  (p=0.000 n=100+100)
PopulateContext/populated_vars           10.0 ± 0%       8.0 ± 0%  -20.00%  (p=0.000 n=100+100)
PopulateContext/omit_route_/static       9.00 ± 0%      1.00 ± 0%  -88.89%  (p=0.000 n=100+100)
PopulateContext/omit_route_/dynamic      11.0 ± 0%       8.0 ± 0%  -27.27%  (p=0.000 n=100+100)
_findQueryKey/0                          0.00           0.00          ~     (all equal)
_findQueryKey/1                          3.00 ± 0%      3.00 ± 0%     ~     (all equal)
_findQueryKey/2                          10.0 ± 0%      10.0 ± 0%     ~     (all equal)
_findQueryKey/3                          11.0 ± 0%      11.0 ± 0%     ~     (all equal)
_findQueryKey/4                          0.00           0.00          ~     (all equal)
_findQueryKeyGoLib/0                     8.00 ± 0%      8.00 ± 0%     ~     (all equal)
_findQueryKeyGoLib/1                     4.00 ± 0%      4.00 ± 0%     ~     (all equal)
_findQueryKeyGoLib/2                     24.0 ± 0%      24.0 ± 0%     ~     (all equal)
_findQueryKeyGoLib/3                     28.0 ± 0%      28.0 ± 0%     ~     (all equal)
_findQueryKeyGoLib/4                     0.00           0.00          ~     (all equal)
```

---

</details>

<details>
<summary> Older Xeon E, 2.4 GHz </summary>

```
goos: linux
goarch: amd64
pkg: github.com/gorilla/mux
cpu: Intel(R) Xeon(R) CPU E5-2680 v4 @ 2.40GHz

name                                 old time/op    new time/op    delta
Mux                                    1.81µs ±13%    1.57µs ±10%  -13.50%  (p=0.000 n=99+97)
MuxSimple/default                      1.09µs ± 8%    0.55µs ±10%  -49.51%  (p=0.000 n=96+95)
MuxSimple/omit_route_from_ctx          1.08µs ±10%    0.25µs ± 8%  -76.66%  (p=0.000 n=95+94)
MuxAlternativeInRegexp                 2.59µs ±12%    2.14µs ±13%  -17.06%  (p=0.000 n=95+92)
ManyPathVariables                      3.11µs ± 9%    2.78µs ±10%  -10.62%  (p=0.000 n=96+99)
PopulateContext/no_populated_vars      1.09µs ± 9%    0.56µs ± 7%  -48.63%  (p=0.000 n=94+93)
PopulateContext/empty_var              1.48µs ± 9%    1.28µs ±12%  -13.52%  (p=0.000 n=97+96)
PopulateContext/populated_vars         1.56µs ±11%    1.34µs ±10%  -13.84%  (p=0.000 n=96+97)
PopulateContext/omit_route_/static     1.08µs ± 9%    0.26µs ± 9%  -75.89%  (p=0.000 n=98+97)
PopulateContext/omit_route_/dynamic    1.50µs ±11%    1.20µs ± 9%  -19.96%  (p=0.000 n=94+93)
_findQueryKey/0                         254ns ± 8%     243ns ± 8%   -4.44%  (p=0.000 n=96+91)
_findQueryKey/1                         336ns ± 7%     336ns ± 8%     ~     (p=0.920 n=96+95)
_findQueryKey/2                        1.25µs ±12%    1.27µs ±14%   +1.55%  (p=0.043 n=97+98)
_findQueryKey/3                        1.37µs ± 8%    1.38µs ± 9%   +0.97%  (p=0.030 n=97+95)
_findQueryKey/4                        5.93ns ± 6%    5.97ns ± 7%     ~     (p=0.074 n=97+97)
_findQueryKeyGoLib/0                   1.07µs ± 9%    1.13µs ±10%   +4.99%  (p=0.000 n=96+95)
_findQueryKeyGoLib/1                    581ns ± 8%     586ns ± 9%     ~     (p=0.089 n=96+99)
_findQueryKeyGoLib/2                   3.93µs ±12%    3.80µs ±10%   -3.31%  (p=0.000 n=96+91)
_findQueryKeyGoLib/3                   5.18µs ±10%    5.06µs ± 8%   -2.27%  (p=0.000 n=98+99)
_findQueryKeyGoLib/4                   6.52ns ± 6%    6.49ns ± 4%     ~     (p=0.232 n=96+94)

name                                 old alloc/op   new alloc/op   delta
Mux                                    1.31kB ± 0%    0.91kB ± 0%  -30.49%  (p=0.000 n=100+100)
MuxSimple/default                      1.01kB ± 0%    0.50kB ± 0%  -50.79%  (p=0.000 n=100+100)
MuxSimple/omit_route_from_ctx          1.01kB ± 0%    0.05kB ± 0%  -95.24%  (p=0.000 n=100+100)
MuxAlternativeInRegexp                 2.62kB ± 0%    1.82kB ± 0%  -30.49%  (p=0.000 n=100+100)
ManyPathVariables                      1.52kB ± 0%    1.12kB ± 1%  -26.58%  (p=0.000 n=92+96)
PopulateContext/no_populated_vars      1.01kB ± 0%    0.50kB ± 0%  -50.79%  (p=0.000 n=100+100)
PopulateContext/empty_var              1.33kB ± 0%    0.93kB ± 0%  -30.12%  (p=0.000 n=100+100)
PopulateContext/populated_vars         1.31kB ± 0%    0.91kB ± 0%  -30.49%  (p=0.000 n=100+100)
PopulateContext/omit_route_/static     1.01kB ± 0%    0.05kB ± 0%  -95.24%  (p=0.000 n=100+100)
PopulateContext/omit_route_/dynamic    1.33kB ± 0%    0.88kB ± 0%  -33.73%  (p=0.000 n=100+100)
_findQueryKey/0                         0.00B          0.00B          ~     (all equal)
_findQueryKey/1                         40.0B ± 0%     40.0B ± 0%     ~     (all equal)
_findQueryKey/2                          483B ± 0%      483B ± 0%     ~     (all equal)
_findQueryKey/3                          543B ± 0%      543B ± 0%     ~     (all equal)
_findQueryKey/4                         0.00B          0.00B          ~     (all equal)
_findQueryKeyGoLib/0                     864B ± 0%      864B ± 0%     ~     (all equal)
_findQueryKeyGoLib/1                     432B ± 0%      432B ± 0%     ~     (all equal)
_findQueryKeyGoLib/2                   1.54kB ± 0%    1.54kB ± 0%     ~     (all equal)
_findQueryKeyGoLib/3                   1.98kB ± 0%    1.98kB ± 0%     ~     (all equal)
_findQueryKeyGoLib/4                    0.00B          0.00B          ~     (all equal)

name                                 old allocs/op  new allocs/op  delta
Mux                                      10.0 ± 0%       8.0 ± 0%  -20.00%  (p=0.000 n=100+100)
MuxSimple/default                        9.00 ± 0%      4.00 ± 0%  -55.56%  (p=0.000 n=100+100)
MuxSimple/omit_route_from_ctx            9.00 ± 0%      1.00 ± 0%  -88.89%  (p=0.000 n=100+100)
MuxAlternativeInRegexp                   20.0 ± 0%      16.0 ± 0%  -20.00%  (p=0.000 n=100+100)
ManyPathVariables                        14.0 ± 0%      12.0 ± 0%  -14.29%  (p=0.000 n=100+100)
PopulateContext/no_populated_vars        9.00 ± 0%      4.00 ± 0%  -55.56%  (p=0.000 n=100+100)
PopulateContext/empty_var                11.0 ± 0%       9.0 ± 0%  -18.18%  (p=0.000 n=100+100)
PopulateContext/populated_vars           10.0 ± 0%       8.0 ± 0%  -20.00%  (p=0.000 n=100+100)
PopulateContext/omit_route_/static       9.00 ± 0%      1.00 ± 0%  -88.89%  (p=0.000 n=100+100)
PopulateContext/omit_route_/dynamic      11.0 ± 0%       8.0 ± 0%  -27.27%  (p=0.000 n=100+100)
_findQueryKey/0                          0.00           0.00          ~     (all equal)
_findQueryKey/1                          3.00 ± 0%      3.00 ± 0%     ~     (all equal)
_findQueryKey/2                          10.0 ± 0%      10.0 ± 0%     ~     (all equal)
_findQueryKey/3                          11.0 ± 0%      11.0 ± 0%     ~     (all equal)
_findQueryKey/4                          0.00           0.00          ~     (all equal)
_findQueryKeyGoLib/0                     8.00 ± 0%      8.00 ± 0%     ~     (all equal)
_findQueryKeyGoLib/1                     4.00 ± 0%      4.00 ± 0%     ~     (all equal)
_findQueryKeyGoLib/2                     24.0 ± 0%      24.0 ± 0%     ~     (all equal)
_findQueryKeyGoLib/3                     28.0 ± 0%      28.0 ± 0%     ~     (all equal)
_findQueryKeyGoLib/4                     0.00           0.00          ~     (all equal)
```

---

</details>

---

</details>


<details>
<summary> Popular go-http-routing-benchmark </summary>

I pushed three branches for comparison to my fork:
https://github.com/das7pad/go-http-routing-benchmark

- `before`, this is the baseline branch mentioned above
- `after`, this is the PR revision
- `after-omit-route`, like `after` with the `OmitRouteFromContext` flag enabled

You can reproduce these benchmarks using docker, pinned to CPU 1:

```bash
docker run --rm --pull always -v /logs:/logs --cpuset-cpus 1 -d golang:1.18 bash -exc 'git clone https://github.com/das7pad/go-http-routing-benchmark.git && cd go-http-routing-benchmark && for branch in before after after-omit-route; do git checkout "$branch" && go test -benchmem -bench Gorilla -count 100 -timeout 1h > "/logs/$branch.txt"; done; go install golang.org/x/perf/cmd/benchstat@latest; benchstat /logs/before.txt /logs/after.txt > /logs/compare-before-vs-after.txt; benchstat /logs/before.txt /logs/after-omit-route.txt > /logs/compare-before-vs-after-omit-route.txt'
```

<details>
<summary> Modern Xeon E, 3.4 GHz </summary>

Before vs After with omit Route flag enabled

```
goos: linux
goarch: amd64
pkg: github.com/gorilla/mux
cpu: Intel(R) Xeon(R) E-2278G CPU @ 3.40GHz

name                     old time/op    new time/op    delta
GorillaMux_Param           1.86µs ±13%    1.37µs ± 6%  -26.08%  (p=0.000 n=94+96)
GorillaMux_Param5          2.61µs ± 4%    2.20µs ± 4%  -15.92%  (p=0.000 n=89+95)
GorillaMux_Param20         6.13µs ± 3%    3.98µs ± 4%  -35.07%  (p=0.000 n=90+94)
GorillaMux_ParamWrite      1.87µs ± 5%    1.43µs ± 8%  -23.51%  (p=0.000 n=95+92)
GorillaMux_GithubStatic    3.77µs ± 5%    2.43µs ± 4%  -35.40%  (p=0.000 n=96+96)
GorillaMux_GithubParam     5.68µs ± 6%    5.21µs ± 5%   -8.32%  (p=0.000 n=93+93)
GorillaMux_GithubAll       2.86ms ± 6%    2.71ms ± 4%   -5.44%  (p=0.000 n=99+96)
GorillaMux_GPlusStatic     1.31µs ± 5%    0.21µs ± 4%  -84.29%  (p=0.000 n=92+95)
GorillaMux_GPlusParam      2.37µs ± 3%    1.96µs ± 2%  -17.42%  (p=0.000 n=96+94)
GorillaMux_GPlus2Params    4.43µs ± 3%    3.90µs ± 3%  -11.89%  (p=0.000 n=91+90)
GorillaMux_GPlusAll        37.1µs ± 6%    28.9µs ± 5%  -22.26%  (p=0.000 n=93+92)
GorillaMux_ParseStatic     1.55µs ± 5%    0.42µs ± 4%  -72.78%  (p=0.000 n=95+91)
GorillaMux_ParseParam      1.88µs ± 7%    1.41µs ± 7%  -24.66%  (p=0.000 n=94+93)
GorillaMux_Parse2Params    2.21µs ± 4%    1.74µs ± 6%  -21.16%  (p=0.000 n=94+96)
GorillaMux_ParseAll        71.8µs ± 4%    51.3µs ± 4%  -28.51%  (p=0.000 n=93+94)
GorillaMux_StaticAll        771µs ± 4%     492µs ± 3%  -36.17%  (p=0.000 n=95+90)

name                     old alloc/op   new alloc/op   delta
GorillaMux_Param           1.31kB ± 0%    0.86kB ± 0%  -34.15%  (p=0.000 n=100+100)
GorillaMux_Param5          1.38kB ± 0%    0.93kB ± 0%  -32.56%  (p=0.000 n=100+100)
GorillaMux_Param20         3.48kB ± 0%    2.10kB ± 0%  -39.86%  (p=0.000 n=100+80)
GorillaMux_ParamWrite      1.31kB ± 0%    0.86kB ± 0%  -34.15%  (p=0.000 n=100+100)
GorillaMux_GithubStatic    1.01kB ± 0%    0.05kB ± 0%  -95.24%  (p=0.000 n=100+100)
GorillaMux_GithubParam     1.33kB ± 0%    0.88kB ± 0%  -33.73%  (p=0.000 n=100+100)
GorillaMux_GithubAll        258kB ± 0%     149kB ± 0%  -42.37%  (p=0.000 n=86+99)
GorillaMux_GPlusStatic     1.01kB ± 0%    0.05kB ± 0%  -95.24%  (p=0.000 n=100+100)
GorillaMux_GPlusParam      1.31kB ± 0%    0.86kB ± 0%  -34.15%  (p=0.000 n=100+100)
GorillaMux_GPlus2Params    1.33kB ± 0%    0.88kB ± 0%  -33.73%  (p=0.000 n=100+100)
GorillaMux_GPlusAll        16.5kB ± 0%     9.7kB ± 0%  -41.43%  (p=0.000 n=100+100)
GorillaMux_ParseStatic     1.01kB ± 0%    0.05kB ± 0%  -95.24%  (p=0.000 n=100+100)
GorillaMux_ParseParam      1.31kB ± 0%    0.86kB ± 0%  -34.15%  (p=0.000 n=100+100)
GorillaMux_Parse2Params    1.33kB ± 0%    0.88kB ± 0%  -33.73%  (p=0.000 n=100+100)
GorillaMux_ParseAll        31.1kB ± 0%    14.4kB ± 0%  -53.88%  (p=0.000 n=100+100)
GorillaMux_StaticAll        158kB ± 0%       8kB ± 0%  -95.24%  (p=0.000 n=100+100)

name                     old allocs/op  new allocs/op  delta
GorillaMux_Param             10.0 ± 0%       7.0 ± 0%  -30.00%  (p=0.000 n=100+100)
GorillaMux_Param5            10.0 ± 0%       7.0 ± 0%  -30.00%  (p=0.000 n=100+100)
GorillaMux_Param20           12.0 ± 0%       7.0 ± 0%  -41.67%  (p=0.000 n=100+100)
GorillaMux_ParamWrite        10.0 ± 0%       7.0 ± 0%  -30.00%  (p=0.000 n=100+100)
GorillaMux_GithubStatic      9.00 ± 0%      1.00 ± 0%  -88.89%  (p=0.000 n=100+100)
GorillaMux_GithubParam       10.0 ± 0%       7.0 ± 0%  -30.00%  (p=0.000 n=100+100)
GorillaMux_GithubAll        1.99k ± 0%     1.21k ± 0%  -39.57%  (p=0.000 n=100+100)
GorillaMux_GPlusStatic       9.00 ± 0%      1.00 ± 0%  -88.89%  (p=0.000 n=100+100)
GorillaMux_GPlusParam        10.0 ± 0%       7.0 ± 0%  -30.00%  (p=0.000 n=100+100)
GorillaMux_GPlus2Params      10.0 ± 0%       7.0 ± 0%  -30.00%  (p=0.000 n=100+100)
GorillaMux_GPlusAll           128 ± 0%        79 ± 0%  -38.28%  (p=0.000 n=100+100)
GorillaMux_ParseStatic       9.00 ± 0%      1.00 ± 0%  -88.89%  (p=0.000 n=100+100)
GorillaMux_ParseParam        10.0 ± 0%       7.0 ± 0%  -30.00%  (p=0.000 n=100+100)
GorillaMux_Parse2Params      10.0 ± 0%       7.0 ± 0%  -30.00%  (p=0.000 n=100+100)
GorillaMux_ParseAll           250 ± 0%       122 ± 0%  -51.20%  (p=0.000 n=100+100)
GorillaMux_StaticAll        1.41k ± 0%     0.16k ± 0%  -88.89%  (p=0.000 n=100+100)
```

Before vs After

```
goos: linux
goarch: amd64
pkg: github.com/gorilla/mux
cpu: Intel(R) Xeon(R) E-2278G CPU @ 3.40GHz

name                     old time/op    new time/op    delta
GorillaMux_Param           1.86µs ±13%    1.47µs ±11%  -21.05%  (p=0.000 n=94+93)
GorillaMux_Param5          2.61µs ± 4%    2.25µs ± 2%  -13.64%  (p=0.000 n=89+90)
GorillaMux_Param20         6.13µs ± 3%    4.05µs ± 4%  -33.96%  (p=0.000 n=90+91)
GorillaMux_ParamWrite      1.87µs ± 5%    1.47µs ± 4%  -21.03%  (p=0.000 n=95+96)
GorillaMux_GithubStatic    3.77µs ± 5%    3.16µs ± 3%  -16.11%  (p=0.000 n=96+95)
GorillaMux_GithubParam     5.68µs ± 6%    5.31µs ± 6%   -6.57%  (p=0.000 n=93+94)
GorillaMux_GithubAll       2.86ms ± 6%    2.72ms ± 4%   -4.97%  (p=0.000 n=99+95)
GorillaMux_GPlusStatic     1.31µs ± 5%    0.66µs ± 6%  -49.50%  (p=0.000 n=92+95)
GorillaMux_GPlusParam      2.37µs ± 3%    2.04µs ± 4%  -13.87%  (p=0.000 n=96+90)
GorillaMux_GPlus2Params    4.43µs ± 3%    3.98µs ± 5%  -10.21%  (p=0.000 n=91+92)
GorillaMux_GPlusAll        37.1µs ± 6%    30.6µs ± 6%  -17.65%  (p=0.000 n=93+94)
GorillaMux_ParseStatic     1.55µs ± 5%    0.90µs ± 4%  -41.88%  (p=0.000 n=95+91)
GorillaMux_ParseParam      1.88µs ± 7%    1.47µs ± 6%  -21.53%  (p=0.000 n=94+95)
GorillaMux_Parse2Params    2.21µs ± 4%    1.80µs ± 5%  -18.26%  (p=0.000 n=94+90)
GorillaMux_ParseAll        71.8µs ± 4%    57.9µs ± 3%  -19.30%  (p=0.000 n=93+97)
GorillaMux_StaticAll        771µs ± 4%     664µs ± 4%  -13.87%  (p=0.000 n=95+95)

name                     old alloc/op   new alloc/op   delta
GorillaMux_Param           1.31kB ± 0%    0.91kB ± 0%  -30.49%  (p=0.000 n=100+100)
GorillaMux_Param5          1.38kB ± 0%    0.98kB ± 0%  -29.07%  (p=0.000 n=100+100)
GorillaMux_Param20         3.48kB ± 0%    2.14kB ± 0%  -38.48%  (p=0.000 n=100+83)
GorillaMux_ParamWrite      1.31kB ± 0%    0.91kB ± 0%  -30.49%  (p=0.000 n=100+100)
GorillaMux_GithubStatic    1.01kB ± 0%    0.50kB ± 0%  -50.79%  (p=0.000 n=100+100)
GorillaMux_GithubParam     1.33kB ± 0%    0.93kB ± 0%  -30.12%  (p=0.000 n=100+100)
GorillaMux_GithubAll        258kB ± 0%     173kB ± 0%  -33.02%  (p=0.000 n=86+100)
GorillaMux_GPlusStatic     1.01kB ± 0%    0.50kB ± 0%  -50.79%  (p=0.000 n=100+100)
GorillaMux_GPlusParam      1.31kB ± 0%    0.91kB ± 0%  -30.49%  (p=0.000 n=100+100)
GorillaMux_GPlus2Params    1.33kB ± 0%    0.93kB ± 0%  -30.12%  (p=0.000 n=100+100)
GorillaMux_GPlusAll        16.5kB ± 0%    11.1kB ± 0%  -32.82%  (p=0.000 n=100+100)
GorillaMux_ParseStatic     1.01kB ± 0%    0.50kB ± 0%  -50.79%  (p=0.000 n=100+100)
GorillaMux_ParseParam      1.31kB ± 0%    0.91kB ± 0%  -30.49%  (p=0.000 n=100+100)
GorillaMux_Parse2Params    1.33kB ± 0%    0.93kB ± 0%  -30.12%  (p=0.000 n=100+100)
GorillaMux_ParseAll        31.1kB ± 0%    19.6kB ± 0%  -37.02%  (p=0.000 n=100+100)
GorillaMux_StaticAll        158kB ± 0%      78kB ± 0%  -50.79%  (p=0.000 n=100+100)

name                     old allocs/op  new allocs/op  delta
GorillaMux_Param             10.0 ± 0%       8.0 ± 0%  -20.00%  (p=0.000 n=100+100)
GorillaMux_Param5            10.0 ± 0%       8.0 ± 0%  -20.00%  (p=0.000 n=100+100)
GorillaMux_Param20           12.0 ± 0%       8.0 ± 0%  -33.33%  (p=0.000 n=100+100)
GorillaMux_ParamWrite        10.0 ± 0%       8.0 ± 0%  -20.00%  (p=0.000 n=100+100)
GorillaMux_GithubStatic      9.00 ± 0%      4.00 ± 0%  -55.56%  (p=0.000 n=100+100)
GorillaMux_GithubParam       10.0 ± 0%       8.0 ± 0%  -20.00%  (p=0.000 n=100+100)
GorillaMux_GithubAll        1.99k ± 0%     1.48k ± 0%  -25.78%  (p=0.000 n=100+100)
GorillaMux_GPlusStatic       9.00 ± 0%      4.00 ± 0%  -55.56%  (p=0.000 n=100+100)
GorillaMux_GPlusParam        10.0 ± 0%       8.0 ± 0%  -20.00%  (p=0.000 n=100+100)
GorillaMux_GPlus2Params      10.0 ± 0%       8.0 ± 0%  -20.00%  (p=0.000 n=100+100)
GorillaMux_GPlusAll           128 ± 0%        96 ± 0%  -25.00%  (p=0.000 n=100+100)
GorillaMux_ParseStatic       9.00 ± 0%      4.00 ± 0%  -55.56%  (p=0.000 n=100+100)
GorillaMux_ParseParam        10.0 ± 0%       8.0 ± 0%  -20.00%  (p=0.000 n=100+100)
GorillaMux_Parse2Params      10.0 ± 0%       8.0 ± 0%  -20.00%  (p=0.000 n=100+100)
GorillaMux_ParseAll           250 ± 0%       168 ± 0%  -32.80%  (p=0.000 n=100+100)
GorillaMux_StaticAll        1.41k ± 0%     0.63k ± 0%  -55.56%  (p=0.000 n=100+100)
```

---

</details>


<details>
<summary> Older Xeon Gold, 2.3 GHz </summary>

Before vs After with omit Route flag enabled

(Actual CPU identifier is rather `Intel(R) Xeon(R) Gold 5122 CPU @ 2.30GHz`)
```
goos: linux
goarch: amd64
pkg: github.com/gorilla/mux
cpu: Intel Xeon Processor (Skylake, IBRS)

name                     old time/op    new time/op    delta
GorillaMux_Param           3.02µs ±14%    2.24µs ±12%  -25.60%  (p=0.000 n=100+99)
GorillaMux_Param5          4.20µs ±12%    3.45µs ±10%  -17.84%  (p=0.000 n=95+97)
GorillaMux_Param20         9.86µs ±12%    6.36µs ± 9%  -35.54%  (p=0.000 n=96+93)
GorillaMux_ParamWrite      2.97µs ± 7%    2.21µs ± 5%  -25.40%  (p=0.000 n=96+96)
GorillaMux_GithubStatic    5.82µs ± 6%    3.62µs ± 5%  -37.87%  (p=0.000 n=99+98)
GorillaMux_GithubParam     8.81µs ± 7%    7.90µs ± 5%  -10.37%  (p=0.000 n=97+95)
GorillaMux_GithubAll       3.93ms ± 4%    3.62ms ± 6%   -7.72%  (p=0.000 n=97+99)
GorillaMux_GPlusStatic     2.04µs ± 7%    0.33µs ± 9%  -83.77%  (p=0.000 n=98+99)
GorillaMux_GPlusParam      3.72µs ± 6%    3.14µs ±13%  -15.44%  (p=0.000 n=99+99)
GorillaMux_GPlus2Params    6.89µs ± 8%    6.57µs ± 9%   -4.70%  (p=0.000 n=92+98)
GorillaMux_GPlusAll        56.1µs ± 6%    46.5µs ±11%  -17.14%  (p=0.000 n=95+98)
GorillaMux_ParseStatic     2.43µs ± 6%    0.67µs ±11%  -72.49%  (p=0.000 n=96+99)
GorillaMux_ParseParam      2.91µs ± 8%    2.29µs ±12%  -21.07%  (p=0.000 n=98+97)
GorillaMux_Parse2Params    3.44µs ± 8%    2.84µs ± 8%  -17.53%  (p=0.000 n=98+95)
GorillaMux_ParseAll         111µs ± 5%      80µs ±11%  -27.91%  (p=0.000 n=96+98)
GorillaMux_StaticAll       1.13ms ± 4%    0.71ms ± 5%  -37.32%  (p=0.000 n=97+97)

name                     old alloc/op   new alloc/op   delta
GorillaMux_Param           1.31kB ± 0%    0.86kB ± 0%  -34.15%  (p=0.000 n=100+100)
GorillaMux_Param5          1.38kB ± 0%    0.93kB ± 0%  -32.56%  (p=0.000 n=100+100)
GorillaMux_Param20         3.48kB ± 0%    2.10kB ± 0%  -39.86%  (p=0.000 n=100+79)
GorillaMux_ParamWrite      1.31kB ± 0%    0.86kB ± 0%  -34.15%  (p=0.000 n=100+100)
GorillaMux_GithubStatic    1.01kB ± 0%    0.05kB ± 0%  -95.24%  (p=0.000 n=100+100)
GorillaMux_GithubParam     1.33kB ± 0%    0.88kB ± 0%  -33.73%  (p=0.000 n=100+100)
GorillaMux_GithubAll        258kB ± 0%     149kB ± 0%  -42.37%  (p=0.000 n=80+96)
GorillaMux_GPlusStatic     1.01kB ± 0%    0.05kB ± 0%  -95.24%  (p=0.000 n=100+100)
GorillaMux_GPlusParam      1.31kB ± 0%    0.86kB ± 0%  -34.15%  (p=0.000 n=100+100)
GorillaMux_GPlus2Params    1.33kB ± 0%    0.88kB ± 0%  -33.73%  (p=0.000 n=100+100)
GorillaMux_GPlusAll        16.5kB ± 0%     9.7kB ± 0%  -41.43%  (p=0.000 n=100+100)
GorillaMux_ParseStatic     1.01kB ± 0%    0.05kB ± 0%  -95.24%  (p=0.000 n=100+100)
GorillaMux_ParseParam      1.31kB ± 0%    0.86kB ± 0%  -34.15%  (p=0.000 n=100+100)
GorillaMux_Parse2Params    1.33kB ± 0%    0.88kB ± 0%  -33.73%  (p=0.000 n=100+100)
GorillaMux_ParseAll        31.1kB ± 0%    14.4kB ± 0%  -53.88%  (p=0.000 n=100+100)
GorillaMux_StaticAll        158kB ± 0%       8kB ± 0%  -95.24%  (p=0.000 n=100+100)

name                     old allocs/op  new allocs/op  delta
GorillaMux_Param             10.0 ± 0%       7.0 ± 0%  -30.00%  (p=0.000 n=100+100)
GorillaMux_Param5            10.0 ± 0%       7.0 ± 0%  -30.00%  (p=0.000 n=100+100)
GorillaMux_Param20           12.0 ± 0%       7.0 ± 0%  -41.67%  (p=0.000 n=100+100)
GorillaMux_ParamWrite        10.0 ± 0%       7.0 ± 0%  -30.00%  (p=0.000 n=100+100)
GorillaMux_GithubStatic      9.00 ± 0%      1.00 ± 0%  -88.89%  (p=0.000 n=100+100)
GorillaMux_GithubParam       10.0 ± 0%       7.0 ± 0%  -30.00%  (p=0.000 n=100+100)
GorillaMux_GithubAll        1.99k ± 0%     1.21k ± 0%  -39.57%  (p=0.000 n=100+100)
GorillaMux_GPlusStatic       9.00 ± 0%      1.00 ± 0%  -88.89%  (p=0.000 n=100+100)
GorillaMux_GPlusParam        10.0 ± 0%       7.0 ± 0%  -30.00%  (p=0.000 n=100+100)
GorillaMux_GPlus2Params      10.0 ± 0%       7.0 ± 0%  -30.00%  (p=0.000 n=100+100)
GorillaMux_GPlusAll           128 ± 0%        79 ± 0%  -38.28%  (p=0.000 n=100+100)
GorillaMux_ParseStatic       9.00 ± 0%      1.00 ± 0%  -88.89%  (p=0.000 n=100+100)
GorillaMux_ParseParam        10.0 ± 0%       7.0 ± 0%  -30.00%  (p=0.000 n=100+100)
GorillaMux_Parse2Params      10.0 ± 0%       7.0 ± 0%  -30.00%  (p=0.000 n=100+100)
GorillaMux_ParseAll           250 ± 0%       122 ± 0%  -51.20%  (p=0.000 n=100+100)
GorillaMux_StaticAll        1.41k ± 0%     0.16k ± 0%  -88.89%  (p=0.000 n=100+100)
```

Before vs After

(Actual CPU identifier is rather `Intel(R) Xeon(R) Gold 5122 CPU @ 2.30GHz`)
```
goos: linux
goarch: amd64
pkg: github.com/gorilla/mux
cpu: Intel Xeon Processor (Skylake, IBRS) 

name                     old time/op    new time/op    delta
GorillaMux_Param           3.02µs ±14%    2.27µs ± 8%  -24.70%  (p=0.000 n=100+98)
GorillaMux_Param5          4.20µs ±12%    3.48µs ± 5%  -17.29%  (p=0.000 n=95+98)
GorillaMux_Param20         9.86µs ±12%    6.41µs ± 6%  -35.05%  (p=0.000 n=96+92)
GorillaMux_ParamWrite      2.97µs ± 7%    2.31µs ± 6%  -22.12%  (p=0.000 n=96+98)
GorillaMux_GithubStatic    5.82µs ± 6%    4.69µs ± 5%  -19.52%  (p=0.000 n=99+96)
GorillaMux_GithubParam     8.81µs ± 7%    7.98µs ± 4%   -9.48%  (p=0.000 n=97+96)
GorillaMux_GithubAll       3.93ms ± 4%    3.64ms ± 5%   -7.18%  (p=0.000 n=97+94)
GorillaMux_GPlusStatic     2.04µs ± 7%    1.05µs ±12%  -48.53%  (p=0.000 n=98+94)
GorillaMux_GPlusParam      3.72µs ± 6%    3.09µs ± 5%  -16.76%  (p=0.000 n=99+96)
GorillaMux_GPlus2Params    6.89µs ± 8%    6.18µs ± 5%  -10.28%  (p=0.000 n=92+95)
GorillaMux_GPlusAll        56.1µs ± 6%    46.0µs ± 5%  -17.98%  (p=0.000 n=95+93)
GorillaMux_ParseStatic     2.43µs ± 6%    1.44µs ± 5%  -40.52%  (p=0.000 n=96+95)
GorillaMux_ParseParam      2.91µs ± 8%    2.30µs ± 5%  -20.93%  (p=0.000 n=98+98)
GorillaMux_Parse2Params    3.44µs ± 8%    2.82µs ± 7%  -18.04%  (p=0.000 n=98+94)
GorillaMux_ParseAll         111µs ± 5%      91µs ±13%  -18.12%  (p=0.000 n=96+98)
GorillaMux_StaticAll       1.13ms ± 4%    0.95ms ± 9%  -16.38%  (p=0.000 n=97+96)

name                     old alloc/op   new alloc/op   delta
GorillaMux_Param           1.31kB ± 0%    0.91kB ± 0%  -30.49%  (p=0.000 n=100+100)
GorillaMux_Param5          1.38kB ± 0%    0.98kB ± 0%  -29.07%  (p=0.000 n=100+100)
GorillaMux_Param20         3.48kB ± 0%    2.14kB ± 0%  -38.49%  (p=0.000 n=100+100)
GorillaMux_ParamWrite      1.31kB ± 0%    0.91kB ± 0%  -30.49%  (p=0.000 n=100+100)
GorillaMux_GithubStatic    1.01kB ± 0%    0.50kB ± 0%  -50.79%  (p=0.000 n=100+100)
GorillaMux_GithubParam     1.33kB ± 0%    0.93kB ± 0%  -30.12%  (p=0.000 n=100+100)
GorillaMux_GithubAll        258kB ± 0%     173kB ± 0%  -33.02%  (p=0.000 n=80+100)
GorillaMux_GPlusStatic     1.01kB ± 0%    0.50kB ± 0%  -50.79%  (p=0.000 n=100+100)
GorillaMux_GPlusParam      1.31kB ± 0%    0.91kB ± 0%  -30.49%  (p=0.000 n=100+100)
GorillaMux_GPlus2Params    1.33kB ± 0%    0.93kB ± 0%  -30.12%  (p=0.000 n=100+100)
GorillaMux_GPlusAll        16.5kB ± 0%    11.1kB ± 0%  -32.82%  (p=0.000 n=100+100)
GorillaMux_ParseStatic     1.01kB ± 0%    0.50kB ± 0%  -50.79%  (p=0.000 n=100+100)
GorillaMux_ParseParam      1.31kB ± 0%    0.91kB ± 0%  -30.49%  (p=0.000 n=100+100)
GorillaMux_Parse2Params    1.33kB ± 0%    0.93kB ± 0%  -30.12%  (p=0.000 n=100+100)
GorillaMux_ParseAll        31.1kB ± 0%    19.6kB ± 0%  -37.02%  (p=0.000 n=100+100)
GorillaMux_StaticAll        158kB ± 0%      78kB ± 0%  -50.79%  (p=0.000 n=100+95)

name                     old allocs/op  new allocs/op  delta
GorillaMux_Param             10.0 ± 0%       8.0 ± 0%  -20.00%  (p=0.000 n=100+100)
GorillaMux_Param5            10.0 ± 0%       8.0 ± 0%  -20.00%  (p=0.000 n=100+100)
GorillaMux_Param20           12.0 ± 0%       8.0 ± 0%  -33.33%  (p=0.000 n=100+100)
GorillaMux_ParamWrite        10.0 ± 0%       8.0 ± 0%  -20.00%  (p=0.000 n=100+100)
GorillaMux_GithubStatic      9.00 ± 0%      4.00 ± 0%  -55.56%  (p=0.000 n=100+100)
GorillaMux_GithubParam       10.0 ± 0%       8.0 ± 0%  -20.00%  (p=0.000 n=100+100)
GorillaMux_GithubAll        1.99k ± 0%     1.48k ± 0%  -25.78%  (p=0.000 n=100+100)
GorillaMux_GPlusStatic       9.00 ± 0%      4.00 ± 0%  -55.56%  (p=0.000 n=100+100)
GorillaMux_GPlusParam        10.0 ± 0%       8.0 ± 0%  -20.00%  (p=0.000 n=100+100)
GorillaMux_GPlus2Params      10.0 ± 0%       8.0 ± 0%  -20.00%  (p=0.000 n=100+100)
GorillaMux_GPlusAll           128 ± 0%        96 ± 0%  -25.00%  (p=0.000 n=100+100)
GorillaMux_ParseStatic       9.00 ± 0%      4.00 ± 0%  -55.56%  (p=0.000 n=100+100)
GorillaMux_ParseParam        10.0 ± 0%       8.0 ± 0%  -20.00%  (p=0.000 n=100+100)
GorillaMux_Parse2Params      10.0 ± 0%       8.0 ± 0%  -20.00%  (p=0.000 n=100+100)
GorillaMux_ParseAll           250 ± 0%       168 ± 0%  -32.80%  (p=0.000 n=100+100)
GorillaMux_StaticAll        1.41k ± 0%     0.63k ± 0%  -55.56%  (p=0.000 n=100+100)
```

---

</details>



<details>
<summary> Older Xeon E, 2.4 GHz </summary>

Before vs After with omit Route flag enabled

```
goos: linux
goarch: amd64
pkg: github.com/gorilla/mux
cpu: Intel(R) Xeon(R) CPU E5-2680 v4 @ 2.40GHz

name                     old time/op    new time/op    delta
GorillaMux_Param           3.31µs ±11%    2.44µs ±11%  -26.34%  (p=0.000 n=95+92)
GorillaMux_Param5          4.68µs ± 9%    3.96µs ±14%  -15.22%  (p=0.000 n=97+100)
GorillaMux_Param20         11.1µs ±10%     7.2µs ± 9%  -35.03%  (p=0.000 n=100+97)
GorillaMux_ParamWrite      3.37µs ±10%    2.56µs ± 8%  -24.21%  (p=0.000 n=95+94)
GorillaMux_GithubStatic    6.90µs ± 7%    3.85µs ± 8%  -44.16%  (p=0.000 n=96+91)
GorillaMux_GithubParam     9.85µs ± 6%    8.89µs ±11%   -9.74%  (p=0.000 n=98+96)
GorillaMux_GithubAll       5.15ms ±10%    4.77ms ± 7%   -7.36%  (p=0.000 n=96+96)
GorillaMux_GPlusStatic     2.38µs ± 8%    0.36µs ± 7%  -84.98%  (p=0.000 n=95+94)
GorillaMux_GPlusParam      4.30µs ±11%    3.33µs ±10%  -22.60%  (p=0.000 n=97+95)
GorillaMux_GPlus2Params    7.80µs ± 8%    6.82µs ±10%  -12.55%  (p=0.000 n=98+94)
GorillaMux_GPlusAll        65.8µs ±10%    50.5µs ±10%  -23.24%  (p=0.000 n=97+98)
GorillaMux_ParseStatic     2.98µs ±10%    0.70µs ± 9%  -76.68%  (p=0.000 n=99+94)
GorillaMux_ParseParam      3.31µs ± 9%    2.55µs ± 9%  -23.11%  (p=0.000 n=96+95)
GorillaMux_Parse2Params    3.92µs ± 8%    3.18µs ±12%  -18.86%  (p=0.000 n=97+96)
GorillaMux_ParseAll         125µs ± 9%      90µs ±12%  -28.23%  (p=0.000 n=99+96)
GorillaMux_StaticAll       1.32ms ± 6%    0.79ms ± 6%  -40.41%  (p=0.000 n=99+95)

name                     old alloc/op   new alloc/op   delta
GorillaMux_Param           1.31kB ± 0%    0.86kB ± 0%  -34.15%  (p=0.000 n=100+100)
GorillaMux_Param5          1.38kB ± 0%    0.93kB ± 0%  -32.56%  (p=0.000 n=100+100)
GorillaMux_Param20         3.48kB ± 0%    2.10kB ± 0%  -39.86%  (p=0.000 n=100+79)
GorillaMux_ParamWrite      1.31kB ± 0%    0.86kB ± 0%  -34.15%  (p=0.000 n=100+100)
GorillaMux_GithubStatic    1.01kB ± 0%    0.05kB ± 0%  -95.24%  (p=0.000 n=100+100)
GorillaMux_GithubParam     1.33kB ± 0%    0.88kB ± 0%  -33.73%  (p=0.000 n=100+100)
GorillaMux_GithubAll        258kB ± 0%     149kB ± 0%  -42.37%  (p=0.000 n=79+86)
GorillaMux_GPlusStatic     1.01kB ± 0%    0.05kB ± 0%  -95.24%  (p=0.000 n=100+100)
GorillaMux_GPlusParam      1.31kB ± 0%    0.86kB ± 0%  -34.15%  (p=0.000 n=100+100)
GorillaMux_GPlus2Params    1.33kB ± 0%    0.88kB ± 0%  -33.73%  (p=0.000 n=100+100)
GorillaMux_GPlusAll        16.5kB ± 0%     9.7kB ± 0%  -41.43%  (p=0.000 n=100+100)
GorillaMux_ParseStatic     1.01kB ± 0%    0.05kB ± 0%  -95.24%  (p=0.000 n=100+100)
GorillaMux_ParseParam      1.31kB ± 0%    0.86kB ± 0%  -34.15%  (p=0.000 n=100+100)
GorillaMux_Parse2Params    1.33kB ± 0%    0.88kB ± 0%  -33.73%  (p=0.000 n=100+100)
GorillaMux_ParseAll        31.1kB ± 0%    14.4kB ± 0%  -53.88%  (p=0.000 n=100+100)
GorillaMux_StaticAll        158kB ± 0%       8kB ± 0%  -95.24%  (p=0.000 n=100+100)

name                     old allocs/op  new allocs/op  delta
GorillaMux_Param             10.0 ± 0%       7.0 ± 0%  -30.00%  (p=0.000 n=100+100)
GorillaMux_Param5            10.0 ± 0%       7.0 ± 0%  -30.00%  (p=0.000 n=100+100)
GorillaMux_Param20           12.0 ± 0%       7.0 ± 0%  -41.67%  (p=0.000 n=100+100)
GorillaMux_ParamWrite        10.0 ± 0%       7.0 ± 0%  -30.00%  (p=0.000 n=100+100)
GorillaMux_GithubStatic      9.00 ± 0%      1.00 ± 0%  -88.89%  (p=0.000 n=100+100)
GorillaMux_GithubParam       10.0 ± 0%       7.0 ± 0%  -30.00%  (p=0.000 n=100+100)
GorillaMux_GithubAll        1.99k ± 0%     1.21k ± 0%  -39.57%  (p=0.000 n=100+100)
GorillaMux_GPlusStatic       9.00 ± 0%      1.00 ± 0%  -88.89%  (p=0.000 n=100+100)
GorillaMux_GPlusParam        10.0 ± 0%       7.0 ± 0%  -30.00%  (p=0.000 n=100+100)
GorillaMux_GPlus2Params      10.0 ± 0%       7.0 ± 0%  -30.00%  (p=0.000 n=100+100)
GorillaMux_GPlusAll           128 ± 0%        79 ± 0%  -38.28%  (p=0.000 n=100+100)
GorillaMux_ParseStatic       9.00 ± 0%      1.00 ± 0%  -88.89%  (p=0.000 n=100+100)
GorillaMux_ParseParam        10.0 ± 0%       7.0 ± 0%  -30.00%  (p=0.000 n=100+100)
GorillaMux_Parse2Params      10.0 ± 0%       7.0 ± 0%  -30.00%  (p=0.000 n=100+100)
GorillaMux_ParseAll           250 ± 0%       122 ± 0%  -51.20%  (p=0.000 n=100+100)
GorillaMux_StaticAll        1.41k ± 0%     0.16k ± 0%  -88.89%  (p=0.000 n=100+100)
```

Before vs After

```
goos: linux
goarch: amd64
pkg: github.com/gorilla/mux
cpu: Intel(R) Xeon(R) CPU E5-2680 v4 @ 2.40GHz

name                     old time/op    new time/op    delta
GorillaMux_Param           3.31µs ±11%    2.59µs ±12%  -21.76%  (p=0.000 n=95+96)
GorillaMux_Param5          4.68µs ± 9%    3.80µs ± 7%  -18.79%  (p=0.000 n=97+92)
GorillaMux_Param20         11.1µs ±10%     7.2µs ± 9%  -35.19%  (p=0.000 n=100+99)
GorillaMux_ParamWrite      3.37µs ±10%    2.68µs ± 7%  -20.69%  (p=0.000 n=95+95)
GorillaMux_GithubStatic    6.90µs ± 7%    5.06µs ± 7%  -26.57%  (p=0.000 n=96+91)
GorillaMux_GithubParam     9.85µs ± 6%    8.83µs ± 9%  -10.42%  (p=0.000 n=98+98)
GorillaMux_GithubAll       5.15ms ±10%    4.71ms ± 9%   -8.50%  (p=0.000 n=96+97)
GorillaMux_GPlusStatic     2.38µs ± 8%    1.19µs ±10%  -50.24%  (p=0.000 n=95+100)
GorillaMux_GPlusParam      4.30µs ±11%    3.40µs ± 5%  -21.08%  (p=0.000 n=97+96)
GorillaMux_GPlus2Params    7.80µs ± 8%    6.96µs ± 8%  -10.71%  (p=0.000 n=98+98)
GorillaMux_GPlusAll        65.8µs ±10%    52.2µs ±10%  -20.65%  (p=0.000 n=97+96)
GorillaMux_ParseStatic     2.98µs ±10%    1.61µs ±10%  -46.04%  (p=0.000 n=99+100)
GorillaMux_ParseParam      3.31µs ± 9%    2.63µs ± 7%  -20.46%  (p=0.000 n=96+91)
GorillaMux_Parse2Params    3.92µs ± 8%    3.23µs ± 6%  -17.58%  (p=0.000 n=97+93)
GorillaMux_ParseAll         125µs ± 9%     102µs ± 9%  -18.79%  (p=0.000 n=99+96)
GorillaMux_StaticAll       1.32ms ± 6%    1.07ms ± 7%  -18.78%  (p=0.000 n=99+95)

name                     old alloc/op   new alloc/op   delta
GorillaMux_Param           1.31kB ± 0%    0.91kB ± 0%  -30.49%  (p=0.000 n=100+100)
GorillaMux_Param5          1.38kB ± 0%    0.98kB ± 0%  -29.07%  (p=0.000 n=100+100)
GorillaMux_Param20         3.48kB ± 0%    2.14kB ± 0%  -38.49%  (p=0.000 n=100+100)
GorillaMux_ParamWrite      1.31kB ± 0%    0.91kB ± 0%  -30.49%  (p=0.000 n=100+100)
GorillaMux_GithubStatic    1.01kB ± 0%    0.50kB ± 0%  -50.79%  (p=0.000 n=100+100)
GorillaMux_GithubParam     1.33kB ± 0%    0.93kB ± 0%  -30.12%  (p=0.000 n=100+100)
GorillaMux_GithubAll        258kB ± 0%     173kB ± 0%  -33.02%  (p=0.000 n=79+96)
GorillaMux_GPlusStatic     1.01kB ± 0%    0.50kB ± 0%  -50.79%  (p=0.000 n=100+100)
GorillaMux_GPlusParam      1.31kB ± 0%    0.91kB ± 0%  -30.49%  (p=0.000 n=100+100)
GorillaMux_GPlus2Params    1.33kB ± 0%    0.93kB ± 0%  -30.12%  (p=0.000 n=100+100)
GorillaMux_GPlusAll        16.5kB ± 0%    11.1kB ± 0%  -32.82%  (p=0.000 n=100+100)
GorillaMux_ParseStatic     1.01kB ± 0%    0.50kB ± 0%  -50.79%  (p=0.000 n=100+100)
GorillaMux_ParseParam      1.31kB ± 0%    0.91kB ± 0%  -30.49%  (p=0.000 n=100+100)
GorillaMux_Parse2Params    1.33kB ± 0%    0.93kB ± 0%  -30.12%  (p=0.000 n=100+100)
GorillaMux_ParseAll        31.1kB ± 0%    19.6kB ± 0%  -37.02%  (p=0.000 n=100+100)
GorillaMux_StaticAll        158kB ± 0%      78kB ± 0%  -50.79%  (p=0.000 n=100+100)

name                     old allocs/op  new allocs/op  delta
GorillaMux_Param             10.0 ± 0%       8.0 ± 0%  -20.00%  (p=0.000 n=100+100)
GorillaMux_Param5            10.0 ± 0%       8.0 ± 0%  -20.00%  (p=0.000 n=100+100)
GorillaMux_Param20           12.0 ± 0%       8.0 ± 0%  -33.33%  (p=0.000 n=100+100)
GorillaMux_ParamWrite        10.0 ± 0%       8.0 ± 0%  -20.00%  (p=0.000 n=100+100)
GorillaMux_GithubStatic      9.00 ± 0%      4.00 ± 0%  -55.56%  (p=0.000 n=100+100)
GorillaMux_GithubParam       10.0 ± 0%       8.0 ± 0%  -20.00%  (p=0.000 n=100+100)
GorillaMux_GithubAll        1.99k ± 0%     1.48k ± 0%  -25.78%  (p=0.000 n=100+100)
GorillaMux_GPlusStatic       9.00 ± 0%      4.00 ± 0%  -55.56%  (p=0.000 n=100+100)
GorillaMux_GPlusParam        10.0 ± 0%       8.0 ± 0%  -20.00%  (p=0.000 n=100+100)
GorillaMux_GPlus2Params      10.0 ± 0%       8.0 ± 0%  -20.00%  (p=0.000 n=100+100)
GorillaMux_GPlusAll           128 ± 0%        96 ± 0%  -25.00%  (p=0.000 n=100+100)
GorillaMux_ParseStatic       9.00 ± 0%      4.00 ± 0%  -55.56%  (p=0.000 n=100+100)
GorillaMux_ParseParam        10.0 ± 0%       8.0 ± 0%  -20.00%  (p=0.000 n=100+100)
GorillaMux_Parse2Params      10.0 ± 0%       8.0 ± 0%  -20.00%  (p=0.000 n=100+100)
GorillaMux_ParseAll           250 ± 0%       168 ± 0%  -32.80%  (p=0.000 n=100+100)
GorillaMux_StaticAll        1.41k ± 0%     0.63k ± 0%  -55.56%  (p=0.000 n=100+100)
```

---

</details>


<details>
<summary> Older i7, frequency scaling around 3.4 GHz, n=10 </summary>

Sorry, only 10 iterations each. I do not want to hear the fan for too long :)

Before vs After with omit Route flag enabled

```
goos: linux
goarch: amd64
pkg: github.com/julienschmidt/go-http-routing-benchmark
cpu: Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz

name                     old time/op    new time/op    delta
GorillaMux_Param           1.80µs ± 2%    1.41µs ± 3%  -21.64%  (p=0.000 n=10+10)
GorillaMux_Param5          2.68µs ± 0%    2.34µs ± 1%  -12.43%  (p=0.000 n=10+10)
GorillaMux_Param20         5.92µs ± 0%    4.02µs ± 0%  -32.10%  (p=0.000 n=10+9)
GorillaMux_ParamWrite      1.89µs ± 1%    1.47µs ± 0%  -22.22%  (p=0.000 n=9+8)
GorillaMux_GithubStatic    4.05µs ± 1%    2.80µs ± 0%  -31.04%  (p=0.000 n=9+10)
GorillaMux_GithubParam     6.30µs ± 1%    5.90µs ± 1%   -6.27%  (p=0.000 n=10+10)
GorillaMux_GithubAll       3.14ms ± 1%    2.87ms ± 2%   -8.42%  (p=0.000 n=10+10)
GorillaMux_GPlusStatic     1.29µs ± 1%    0.23µs ± 1%  -82.37%  (p=0.000 n=10+10)
GorillaMux_GPlusParam      2.52µs ± 0%    2.07µs ± 1%  -17.69%  (p=0.000 n=10+10)
GorillaMux_GPlus2Params    4.92µs ± 0%    4.49µs ± 1%   -8.71%  (p=0.000 n=10+10)
GorillaMux_GPlusAll        39.0µs ± 1%    31.5µs ± 1%  -19.28%  (p=0.000 n=10+10)
GorillaMux_ParseStatic     1.58µs ± 1%    0.47µs ± 1%  -70.01%  (p=0.000 n=10+10)
GorillaMux_ParseParam      1.89µs ± 0%    1.46µs ± 1%  -22.70%  (p=0.000 n=10+10)
GorillaMux_Parse2Params    2.31µs ± 2%    1.87µs ± 0%  -18.96%  (p=0.000 n=10+8)
GorillaMux_ParseAll        74.3µs ± 1%    55.4µs ± 0%  -25.48%  (p=0.000 n=10+10)
GorillaMux_StaticAll        797µs ± 0%     561µs ± 1%  -29.66%  (p=0.000 n=10+10)

name                     old alloc/op   new alloc/op   delta
GorillaMux_Param           1.31kB ± 0%    0.86kB ± 0%  -34.15%  (p=0.000 n=10+10)
GorillaMux_Param5          1.38kB ± 0%    0.93kB ± 0%  -32.56%  (p=0.000 n=10+10)
GorillaMux_Param20         3.48kB ± 0%    2.09kB ± 0%  -39.87%  (p=0.000 n=10+10)
GorillaMux_ParamWrite      1.31kB ± 0%    0.86kB ± 0%  -34.15%  (p=0.000 n=10+10)
GorillaMux_GithubStatic    1.01kB ± 0%    0.05kB ± 0%  -95.24%  (p=0.000 n=10+10)
GorillaMux_GithubParam     1.33kB ± 0%    0.88kB ± 0%  -33.73%  (p=0.000 n=10+10)
GorillaMux_GithubAll        258kB ± 0%     149kB ± 0%  -42.37%  (p=0.000 n=10+9)
GorillaMux_GPlusStatic     1.01kB ± 0%    0.05kB ± 0%  -95.24%  (p=0.000 n=10+10)
GorillaMux_GPlusParam      1.31kB ± 0%    0.86kB ± 0%  -34.15%  (p=0.000 n=10+10)
GorillaMux_GPlus2Params    1.33kB ± 0%    0.88kB ± 0%  -33.73%  (p=0.000 n=10+10)
GorillaMux_GPlusAll        16.5kB ± 0%     9.7kB ± 0%  -41.43%  (p=0.000 n=10+10)
GorillaMux_ParseStatic     1.01kB ± 0%    0.05kB ± 0%  -95.24%  (p=0.000 n=10+10)
GorillaMux_ParseParam      1.31kB ± 0%    0.86kB ± 0%  -34.15%  (p=0.000 n=10+10)
GorillaMux_Parse2Params    1.33kB ± 0%    0.88kB ± 0%  -33.73%  (p=0.000 n=10+10)
GorillaMux_ParseAll        31.1kB ± 0%    14.4kB ± 0%  -53.88%  (p=0.000 n=10+10)
GorillaMux_StaticAll        158kB ± 0%       8kB ± 0%  -95.24%  (p=0.000 n=10+10)

name                     old allocs/op  new allocs/op  delta
GorillaMux_Param             10.0 ± 0%       7.0 ± 0%  -30.00%  (p=0.000 n=10+10)
GorillaMux_Param5            10.0 ± 0%       7.0 ± 0%  -30.00%  (p=0.000 n=10+10)
GorillaMux_Param20           12.0 ± 0%       7.0 ± 0%  -41.67%  (p=0.000 n=10+10)
GorillaMux_ParamWrite        10.0 ± 0%       7.0 ± 0%  -30.00%  (p=0.000 n=10+10)
GorillaMux_GithubStatic      9.00 ± 0%      1.00 ± 0%  -88.89%  (p=0.000 n=10+10)
GorillaMux_GithubParam       10.0 ± 0%       7.0 ± 0%  -30.00%  (p=0.000 n=10+10)
GorillaMux_GithubAll        1.99k ± 0%     1.21k ± 0%  -39.57%  (p=0.000 n=10+10)
GorillaMux_GPlusStatic       9.00 ± 0%      1.00 ± 0%  -88.89%  (p=0.000 n=10+10)
GorillaMux_GPlusParam        10.0 ± 0%       7.0 ± 0%  -30.00%  (p=0.000 n=10+10)
GorillaMux_GPlus2Params      10.0 ± 0%       7.0 ± 0%  -30.00%  (p=0.000 n=10+10)
GorillaMux_GPlusAll           128 ± 0%        79 ± 0%  -38.28%  (p=0.000 n=10+10)
GorillaMux_ParseStatic       9.00 ± 0%      1.00 ± 0%  -88.89%  (p=0.000 n=10+10)
GorillaMux_ParseParam        10.0 ± 0%       7.0 ± 0%  -30.00%  (p=0.000 n=10+10)
GorillaMux_Parse2Params      10.0 ± 0%       7.0 ± 0%  -30.00%  (p=0.000 n=10+10)
GorillaMux_ParseAll           250 ± 0%       122 ± 0%  -51.20%  (p=0.000 n=10+10)
GorillaMux_StaticAll        1.41k ± 0%     0.16k ± 0%  -88.89%  (p=0.000 n=10+10)
```

Before vs After
```
goos: linux
goarch: amd64
pkg: github.com/julienschmidt/go-http-routing-benchmark
cpu: Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz

name                     old time/op    new time/op    delta
GorillaMux_Param           1.80µs ± 2%    1.38µs ± 2%  -23.29%  (p=0.000 n=10+10)
GorillaMux_Param5          2.68µs ± 0%    2.29µs ± 1%  -14.51%  (p=0.000 n=10+10)
GorillaMux_Param20         5.92µs ± 0%    3.95µs ± 1%  -33.32%  (p=0.000 n=10+10)
GorillaMux_ParamWrite      1.89µs ± 1%    1.46µs ± 2%  -22.59%  (p=0.000 n=9+10)
GorillaMux_GithubStatic    4.05µs ± 1%    3.41µs ± 1%  -15.89%  (p=0.000 n=9+9)
GorillaMux_GithubParam     6.30µs ± 1%    5.80µs ± 1%   -7.91%  (p=0.000 n=10+10)
GorillaMux_GithubAll       3.14ms ± 1%    2.89ms ± 1%   -7.79%  (p=0.000 n=10+10)
GorillaMux_GPlusStatic     1.29µs ± 1%    0.65µs ± 1%  -49.87%  (p=0.000 n=10+10)
GorillaMux_GPlusParam      2.52µs ± 0%    2.14µs ± 1%  -15.03%  (p=0.000 n=10+10)
GorillaMux_GPlus2Params    4.92µs ± 0%    4.46µs ± 1%   -9.40%  (p=0.000 n=10+10)
GorillaMux_GPlusAll        39.0µs ± 1%    33.0µs ± 1%  -15.53%  (p=0.000 n=10+10)
GorillaMux_ParseStatic     1.58µs ± 1%    0.93µs ± 1%  -41.36%  (p=0.000 n=10+10)
GorillaMux_ParseParam      1.89µs ± 0%    1.51µs ± 2%  -20.23%  (p=0.000 n=10+9)
GorillaMux_Parse2Params    2.31µs ± 2%    1.92µs ± 1%  -16.95%  (p=0.000 n=10+10)
GorillaMux_ParseAll        74.3µs ± 1%    60.9µs ± 1%  -17.99%  (p=0.000 n=10+10)
GorillaMux_StaticAll        797µs ± 0%     699µs ± 0%  -12.25%  (p=0.000 n=10+10)

name                     old alloc/op   new alloc/op   delta
GorillaMux_Param           1.31kB ± 0%    0.91kB ± 0%  -30.49%  (p=0.000 n=10+10)
GorillaMux_Param5          1.38kB ± 0%    0.98kB ± 0%  -29.07%  (p=0.000 n=10+10)
GorillaMux_Param20         3.48kB ± 0%    2.14kB ± 0%  -38.48%  (p=0.000 n=10+10)
GorillaMux_ParamWrite      1.31kB ± 0%    0.91kB ± 0%  -30.49%  (p=0.000 n=10+10)
GorillaMux_GithubStatic    1.01kB ± 0%    0.50kB ± 0%  -50.79%  (p=0.000 n=10+10)
GorillaMux_GithubParam     1.33kB ± 0%    0.93kB ± 0%  -30.12%  (p=0.000 n=10+10)
GorillaMux_GithubAll        258kB ± 0%     173kB ± 0%  -33.02%  (p=0.000 n=10+10)
GorillaMux_GPlusStatic     1.01kB ± 0%    0.50kB ± 0%  -50.79%  (p=0.000 n=10+10)
GorillaMux_GPlusParam      1.31kB ± 0%    0.91kB ± 0%  -30.49%  (p=0.000 n=10+10)
GorillaMux_GPlus2Params    1.33kB ± 0%    0.93kB ± 0%  -30.12%  (p=0.000 n=10+10)
GorillaMux_GPlusAll        16.5kB ± 0%    11.1kB ± 0%  -32.82%  (p=0.000 n=10+10)
GorillaMux_ParseStatic     1.01kB ± 0%    0.50kB ± 0%  -50.79%  (p=0.000 n=10+10)
GorillaMux_ParseParam      1.31kB ± 0%    0.91kB ± 0%  -30.49%  (p=0.000 n=10+10)
GorillaMux_Parse2Params    1.33kB ± 0%    0.93kB ± 0%  -30.12%  (p=0.000 n=10+10)
GorillaMux_ParseAll        31.1kB ± 0%    19.6kB ± 0%  -37.02%  (p=0.000 n=10+10)
GorillaMux_StaticAll        158kB ± 0%      78kB ± 0%  -50.79%  (p=0.000 n=10+10)

name                     old allocs/op  new allocs/op  delta
GorillaMux_Param             10.0 ± 0%       8.0 ± 0%  -20.00%  (p=0.000 n=10+10)
GorillaMux_Param5            10.0 ± 0%       8.0 ± 0%  -20.00%  (p=0.000 n=10+10)
GorillaMux_Param20           12.0 ± 0%       8.0 ± 0%  -33.33%  (p=0.000 n=10+10)
GorillaMux_ParamWrite        10.0 ± 0%       8.0 ± 0%  -20.00%  (p=0.000 n=10+10)
GorillaMux_GithubStatic      9.00 ± 0%      4.00 ± 0%  -55.56%  (p=0.000 n=10+10)
GorillaMux_GithubParam       10.0 ± 0%       8.0 ± 0%  -20.00%  (p=0.000 n=10+10)
GorillaMux_GithubAll        1.99k ± 0%     1.48k ± 0%  -25.78%  (p=0.000 n=10+10)
GorillaMux_GPlusStatic       9.00 ± 0%      4.00 ± 0%  -55.56%  (p=0.000 n=10+10)
GorillaMux_GPlusParam        10.0 ± 0%       8.0 ± 0%  -20.00%  (p=0.000 n=10+10)
GorillaMux_GPlus2Params      10.0 ± 0%       8.0 ± 0%  -20.00%  (p=0.000 n=10+10)
GorillaMux_GPlusAll           128 ± 0%        96 ± 0%  -25.00%  (p=0.000 n=10+10)
GorillaMux_ParseStatic       9.00 ± 0%      4.00 ± 0%  -55.56%  (p=0.000 n=10+10)
GorillaMux_ParseParam        10.0 ± 0%       8.0 ± 0%  -20.00%  (p=0.000 n=10+10)
GorillaMux_Parse2Params      10.0 ± 0%       8.0 ± 0%  -20.00%  (p=0.000 n=10+10)
GorillaMux_ParseAll           250 ± 0%       168 ± 0%  -32.80%  (p=0.000 n=10+10)
GorillaMux_StaticAll        1.41k ± 0%     0.63k ± 0%  -55.56%  (p=0.000 n=10+10)
```

---

</details>


---

</details>

---

</details>

If you read this far, please consider running benchmarks for your own use cases
 of `mux` and report back any changes. Thanks!

<details>
<summary> go.mod override </summary>

You can use the following override in your `go.mod` file:
```
replace github.com/gorilla/mux v1.8.1 => github.com/das7pad/mux v1.8.1-0.20220803193445-4e593050ec93
```
Optionally, you can enable the flag for not storing the `Route` in the request context:
```
m := mux.NewRouter()
m.OmitRouteFromContext(true)
```
</details>


amustaque97: Thanks for the PR @das7pad. hopefully, in the coming weekend, I should take a look at the PR. 
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/gorilla/mux/pull/691?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gorilla) Report
> Merging [#691](https://app.codecov.io/gh/gorilla/mux/pull/691?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gorilla) (367ad61) into [main](https://app.codecov.io/gh/gorilla/mux/commit/85123bf20e069b156415b871dea10517f6a8938a?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gorilla) (85123bf) will **increase** coverage by `0.69%`.
> The diff coverage is `95.08%`.

```diff
@@            Coverage Diff             @@
##             main     #691      +/-   ##
==========================================
+ Coverage   78.01%   78.71%   +0.69%     
==========================================
  Files           5        5              
  Lines         887      902      +15     
==========================================
+ Hits          692      710      +18     
+ Misses        140      135       -5     
- Partials       55       57       +2     
```


| [Files Changed](https://app.codecov.io/gh/gorilla/mux/pull/691?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gorilla) | Coverage Δ | |
|---|---|---|
| [route.go](https://app.codecov.io/gh/gorilla/mux/pull/691?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gorilla#diff-cm91dGUuZ28=) | `68.19% <ø> (-0.28%)` | :arrow_down: |
| [mux.go](https://app.codecov.io/gh/gorilla/mux/pull/691?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gorilla#diff-bXV4Lmdv) | `85.29% <87.50%> (+1.76%)` | :arrow_up: |
| [regexp.go](https://app.codecov.io/gh/gorilla/mux/pull/691?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gorilla#diff-cmVnZXhwLmdv) | `83.53% <100.00%> (+0.47%)` | :arrow_up: |


luisdavim: @das7pad looks like this project is active again, would you be able to rebase this PR?
das7pad: > @das7pad looks like this project is active again, would you be able to rebase this PR?

Sure, done :)

Below are the latest benchmark results

<details>

<summary> mux project benchmarks </summary>

You can reproduce these benchmarks using docker, pinned to CPU 1:
```
$ docker run --rm --pull always -v /logs:/logs --cpuset-cpus 1 -d golang:1.21 bash -exc 'git clone https://github.com/das7pad/mux.git && cd mux && for branch in baseline perf-cut-allocations; do git checkout "$branch" && go test -benchmem -bench . -count 100 -timeout 1h > "/logs/$branch-all.txt"; done; go install golang.org/x/perf/cmd/benchstat@latest; benchstat /logs/baseline-all.txt /logs/perf-cut-allocations-all.txt > /logs/compare-all.txt'
```

<details>
<summary> Modern Xeon E, 3.4 GHz </summary>

``` 
goos: linux
goarch: amd64
pkg: github.com/gorilla/mux
cpu: Intel(R) Xeon(R) E-2278G CPU @ 3.40GHz
                                    │ /logs/baseline-all.txt │  /logs/perf-cut-allocations-all.txt  │
                                    │         sec/op         │   sec/op     vs base                 │
Mux                                             1057.0n ± 0%   943.4n ± 0%  -10.75% (n=100)
MuxSimple/default                                659.1n ± 0%   328.2n ± 0%  -50.20% (n=100)
MuxSimple/omit_route_from_ctx                    653.8n ± 0%   158.3n ± 0%  -75.79% (n=100)
MuxAlternativeInRegexp                           1.588µ ± 0%   1.353µ ± 0%  -14.77% (n=100)
ManyPathVariables                                1.543µ ± 0%   1.457µ ± 0%   -5.61% (n=100)
PopulateContext/no_populated_vars                665.6n ± 0%   329.6n ± 0%  -50.48% (n=100)
PopulateContext/empty_var                        929.0n ± 0%   796.1n ± 0%  -14.31% (n=100)
PopulateContext/populated_vars                   959.3n ± 1%   839.4n ± 1%  -12.51% (n=100)
PopulateContext/omit_route_/static               665.0n ± 0%   159.1n ± 1%  -76.07% (n=100)
PopulateContext/omit_route_/dynamic              923.6n ± 0%   758.3n ± 0%  -17.91% (n=100)
_findQueryKey/0                                  143.8n ± 0%   146.2n ± 0%   +1.67% (p=0.000 n=100)
_findQueryKey/1                                  227.2n ± 0%   227.8n ± 0%        ~ (p=0.058 n=100)
_findQueryKey/2                                  796.7n ± 0%   806.3n ± 1%   +1.20% (p=0.000 n=100)
_findQueryKey/3                                  888.2n ± 0%   893.9n ± 0%   +0.65% (p=0.021 n=100)
_findQueryKey/4                                  3.690n ± 0%   3.722n ± 0%   +0.85% (p=0.000 n=100)
_findQueryKeyGoLib/0                             776.5n ± 0%   752.4n ± 0%   -3.10% (n=100)
_findQueryKeyGoLib/1                             405.5n ± 0%   398.3n ± 0%   -1.76% (n=100)
_findQueryKeyGoLib/2                             2.549µ ± 0%   2.513µ ± 0%   -1.39% (p=0.000 n=100)
_findQueryKeyGoLib/3                             3.355µ ± 0%   3.295µ ± 0%   -1.79% (n=100)
_findQueryKeyGoLib/4                             3.569n ± 0%   3.724n ± 0%   +4.34% (p=0.000 n=100)
geomean                                          474.4n        368.4n       -22.34%

                                    │ /logs/baseline-all.txt │   /logs/perf-cut-allocations-all.txt    │
                                    │          B/op          │     B/op      vs base                   │
Mux                                            1024.0 ± 0%       768.0 ± 0%  -25.00% (n=100)
MuxSimple/default                               720.0 ± 0%       352.0 ± 0%  -51.11% (n=100)
MuxSimple/omit_route_from_ctx                  720.00 ± 0%       48.00 ± 0%  -93.33% (n=100)
MuxAlternativeInRegexp                        2.000Ki ± 0%     1.500Ki ± 0%  -25.00% (n=100)
ManyPathVariables                              1230.0 ± 0%       972.0 ± 0%  -20.98% (n=100)
PopulateContext/no_populated_vars               720.0 ± 0%       352.0 ± 0%  -51.11% (n=100)
PopulateContext/empty_var                      1040.0 ± 0%       784.0 ± 0%  -24.62% (n=100)
PopulateContext/populated_vars                 1024.0 ± 0%       768.0 ± 0%  -25.00% (n=100)
PopulateContext/omit_route_/static             720.00 ± 0%       48.00 ± 0%  -93.33% (n=100)
PopulateContext/omit_route_/dynamic            1040.0 ± 0%       736.0 ± 0%  -29.23% (n=100)
_findQueryKey/0                                 0.000 ± 0%       0.000 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKey/1                                 40.00 ± 0%       40.00 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKey/2                                 483.0 ± 0%       483.0 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKey/3                                 543.0 ± 0%       543.0 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKey/4                                 0.000 ± 0%       0.000 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKeyGoLib/0                            864.0 ± 0%       864.0 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKeyGoLib/1                            432.0 ± 0%       432.0 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKeyGoLib/2                          1.506Ki ± 0%     1.506Ki ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKeyGoLib/3                          1.938Ki ± 0%     1.938Ki ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKeyGoLib/4                            0.000 ± 0%       0.000 ± 0%        ~ (p=1.000 n=100) ¹
geomean                                                    ²                 -34.86%                 ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                                    │ /logs/baseline-all.txt │  /logs/perf-cut-allocations-all.txt   │
                                    │       allocs/op        │ allocs/op   vs base                   │
Mux                                             8.000 ± 0%     7.000 ± 0%  -12.50% (n=100)
MuxSimple/default                               7.000 ± 0%     3.000 ± 0%  -57.14% (n=100)
MuxSimple/omit_route_from_ctx                   7.000 ± 0%     1.000 ± 0%  -85.71% (n=100)
MuxAlternativeInRegexp                          16.00 ± 0%     14.00 ± 0%  -12.50% (n=100)
ManyPathVariables                               12.00 ± 0%     11.00 ± 0%   -8.33% (n=100)
PopulateContext/no_populated_vars               7.000 ± 0%     3.000 ± 0%  -57.14% (n=100)
PopulateContext/empty_var                       9.000 ± 0%     8.000 ± 0%  -11.11% (n=100)
PopulateContext/populated_vars                  8.000 ± 0%     7.000 ± 0%  -12.50% (n=100)
PopulateContext/omit_route_/static              7.000 ± 0%     1.000 ± 0%  -85.71% (n=100)
PopulateContext/omit_route_/dynamic             9.000 ± 0%     7.000 ± 0%  -22.22% (n=100)
_findQueryKey/0                                 0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKey/1                                 3.000 ± 0%     3.000 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKey/2                                 10.00 ± 0%     10.00 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKey/3                                 11.00 ± 0%     11.00 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKey/4                                 0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKeyGoLib/0                            8.000 ± 0%     8.000 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKeyGoLib/1                            4.000 ± 0%     4.000 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKeyGoLib/2                            24.00 ± 0%     24.00 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKeyGoLib/3                            28.00 ± 0%     28.00 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKeyGoLib/4                            0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=100) ¹
geomean                                                    ²               -27.54%                 ²
¹ all samples are equal
² summaries must be >0 to compute geomean
``` 

---

</details>

<details>
<summary> Older Xeon Gold, 2.3 GHz </summary>

(Actual CPU identifier is rather `Intel(R) Xeon(R) Gold 5122 CPU @ 2.30GHz`)

```
goos: linux
goarch: amd64
pkg: github.com/gorilla/mux
cpu: Intel Xeon Processor (Skylake, IBRS)
                                    │ /logs/baseline-all.txt │  /logs/perf-cut-allocations-all.txt  │
                                    │         sec/op         │   sec/op     vs base                 │
Mux                                              2.615µ ± 2%   2.408µ ± 3%   -7.93% (p=0.000 n=100)
MuxSimple/default                               1600.5n ± 3%   798.9n ± 2%  -50.08% (n=100)
MuxSimple/omit_route_from_ctx                   1549.5n ± 2%   399.8n ± 2%  -74.20% (n=100)
MuxAlternativeInRegexp                           3.703µ ± 2%   3.187µ ± 3%  -13.95% (n=100)
ManyPathVariables                                3.924µ ± 4%   3.591µ ± 3%   -8.50% (p=0.000 n=100)
PopulateContext/no_populated_vars               1572.5n ± 2%   825.7n ± 3%  -47.49% (n=100)
PopulateContext/empty_var                        2.237µ ± 2%   1.871µ ± 3%  -16.36% (n=100)
PopulateContext/populated_vars                   2.352µ ± 3%   1.973µ ± 4%  -16.10% (n=100)
PopulateContext/omit_route_/static              1517.5n ± 3%   394.6n ± 4%  -74.00% (n=100)
PopulateContext/omit_route_/dynamic              2.182µ ± 3%   1.776µ ± 3%  -18.63% (n=100)
_findQueryKey/0                                  337.6n ± 3%   351.6n ± 3%   +4.12% (p=0.005 n=100)
_findQueryKey/1                                  462.6n ± 1%   467.6n ± 2%        ~ (p=0.660 n=100)
_findQueryKey/2                                  1.681µ ± 2%   1.709µ ± 1%        ~ (p=0.093 n=100)
_findQueryKey/3                                  1.833µ ± 1%   1.869µ ± 2%   +1.94% (p=0.011 n=100)
_findQueryKey/4                                  9.438n ± 4%   8.826n ± 2%   -6.48% (p=0.000 n=100)
_findQueryKeyGoLib/0                             1.736µ ± 2%   1.790µ ± 3%   +3.11% (p=0.000 n=100)
_findQueryKeyGoLib/1                             876.6n ± 1%   865.2n ± 2%        ~ (p=0.156 n=100)
_findQueryKeyGoLib/2                             5.473µ ± 1%   5.481µ ± 2%        ~ (p=0.663 n=100)
_findQueryKeyGoLib/3                             7.538µ ± 1%   7.252µ ± 2%   -3.79% (p=0.000 n=100)
_findQueryKeyGoLib/4                             8.777n ± 2%   9.817n ± 4%  +11.86% (p=0.000 n=100)
geomean                                          1.098µ        863.1n       -21.38%

                                    │ /logs/baseline-all.txt │   /logs/perf-cut-allocations-all.txt    │
                                    │          B/op          │     B/op      vs base                   │
Mux                                            1024.0 ± 0%       768.0 ± 0%  -25.00% (n=100)
MuxSimple/default                               720.0 ± 0%       352.0 ± 0%  -51.11% (n=100)
MuxSimple/omit_route_from_ctx                  720.00 ± 0%       48.00 ± 0%  -93.33% (n=100)
MuxAlternativeInRegexp                        2.000Ki ± 0%     1.500Ki ± 0%  -25.00% (n=100)
ManyPathVariables                              1235.0 ± 0%       980.0 ± 0%  -20.65% (n=100)
PopulateContext/no_populated_vars               720.0 ± 0%       352.0 ± 0%  -51.11% (n=100)
PopulateContext/empty_var                      1040.0 ± 0%       784.0 ± 0%  -24.62% (n=100)
PopulateContext/populated_vars                 1024.0 ± 0%       768.0 ± 0%  -25.00% (n=100)
PopulateContext/omit_route_/static             720.00 ± 0%       48.00 ± 0%  -93.33% (n=100)
PopulateContext/omit_route_/dynamic            1040.0 ± 0%       736.0 ± 0%  -29.23% (n=100)
_findQueryKey/0                                 0.000 ± 0%       0.000 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKey/1                                 40.00 ± 0%       40.00 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKey/2                                 483.0 ± 0%       483.0 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKey/3                                 543.0 ± 0%       543.0 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKey/4                                 0.000 ± 0%       0.000 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKeyGoLib/0                            864.0 ± 0%       864.0 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKeyGoLib/1                            432.0 ± 0%       432.0 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKeyGoLib/2                          1.506Ki ± 0%     1.506Ki ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKeyGoLib/3                          1.938Ki ± 0%     1.938Ki ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKeyGoLib/4                            0.000 ± 0%       0.000 ± 0%        ~ (p=1.000 n=100) ¹
geomean                                                    ²                 -34.85%                 ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                                    │ /logs/baseline-all.txt │  /logs/perf-cut-allocations-all.txt   │
                                    │       allocs/op        │ allocs/op   vs base                   │
Mux                                             8.000 ± 0%     7.000 ± 0%  -12.50% (n=100)
MuxSimple/default                               7.000 ± 0%     3.000 ± 0%  -57.14% (n=100)
MuxSimple/omit_route_from_ctx                   7.000 ± 0%     1.000 ± 0%  -85.71% (n=100)
MuxAlternativeInRegexp                          16.00 ± 0%     14.00 ± 0%  -12.50% (n=100)
ManyPathVariables                               12.00 ± 0%     11.00 ± 0%   -8.33% (n=100)
PopulateContext/no_populated_vars               7.000 ± 0%     3.000 ± 0%  -57.14% (n=100)
PopulateContext/empty_var                       9.000 ± 0%     8.000 ± 0%  -11.11% (n=100)
PopulateContext/populated_vars                  8.000 ± 0%     7.000 ± 0%  -12.50% (n=100)
PopulateContext/omit_route_/static              7.000 ± 0%     1.000 ± 0%  -85.71% (n=100)
PopulateContext/omit_route_/dynamic             9.000 ± 0%     7.000 ± 0%  -22.22% (n=100)
_findQueryKey/0                                 0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKey/1                                 3.000 ± 0%     3.000 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKey/2                                 10.00 ± 0%     10.00 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKey/3                                 11.00 ± 0%     11.00 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKey/4                                 0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKeyGoLib/0                            8.000 ± 0%     8.000 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKeyGoLib/1                            4.000 ± 0%     4.000 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKeyGoLib/2                            24.00 ± 0%     24.00 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKeyGoLib/3                            28.00 ± 0%     28.00 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKeyGoLib/4                            0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=100) ¹
geomean                                                    ²               -27.54%                 ²
¹ all samples are equal
² summaries must be >0 to compute geomean
```

---

</details>

<details>
<summary> Older Xeon E, 2.4 GHz </summary>

``` 
goos: linux
goarch: amd64
pkg: github.com/gorilla/mux
cpu: Intel(R) Xeon(R) CPU E5-2680 v4 @ 2.40GHz
                                    │ /logs/baseline-all.txt │  /logs/perf-cut-allocations-all.txt  │
                                    │         sec/op         │   sec/op     vs base                 │
Mux                                              1.698µ ± 1%   1.524µ ± 1%  -10.25% (n=100)
MuxSimple/default                               1031.5n ± 1%   523.9n ± 1%  -49.21% (n=100)
MuxSimple/omit_route_from_ctx                   1023.0n ± 0%   262.9n ± 0%  -74.31% (n=100)
MuxAlternativeInRegexp                           2.463µ ± 0%   2.143µ ± 1%  -13.01% (n=100)
ManyPathVariables                                2.619µ ± 1%   2.401µ ± 1%   -8.30% (n=100)
PopulateContext/no_populated_vars               1025.0n ± 0%   516.4n ± 0%  -49.62% (n=100)
PopulateContext/empty_var                        1.421µ ± 0%   1.257µ ± 1%  -11.58% (n=100)
PopulateContext/populated_vars                   1.481µ ± 1%   1.298µ ± 1%  -12.33% (n=100)
PopulateContext/omit_route_/static              1026.0n ± 0%   255.3n ± 1%  -75.12% (n=100)
PopulateContext/omit_route_/dynamic              1.427µ ± 1%   1.154µ ± 1%  -19.17% (n=100)
_findQueryKey/0                                  234.3n ± 0%   235.2n ± 0%        ~ (p=0.057 n=100)
_findQueryKey/1                                  373.7n ± 0%   372.4n ± 0%        ~ (p=0.317 n=100)
_findQueryKey/2                                  1.310µ ± 1%   1.308µ ± 1%        ~ (p=0.772 n=100)
_findQueryKey/3                                  1.469µ ± 1%   1.482µ ± 1%        ~ (p=0.097 n=100)
_findQueryKey/4                                  6.343n ± 0%   6.304n ± 0%   -0.61% (p=0.000 n=100)
_findQueryKeyGoLib/0                             1.180µ ± 1%   1.181µ ± 1%        ~ (p=0.674 n=100)
_findQueryKeyGoLib/1                             631.4n ± 1%   624.8n ± 1%   -1.05% (p=0.000 n=100)
_findQueryKeyGoLib/2                             4.064µ ± 1%   4.064µ ± 1%        ~ (p=0.717 n=100)
_findQueryKeyGoLib/3                             5.479µ ± 1%   5.494µ ± 0%        ~ (p=0.535 n=100)
_findQueryKeyGoLib/4                             6.323n ± 0%   6.352n ± 0%   +0.46% (p=0.015 n=100)
geomean                                          759.6n        594.1n       -21.79%

                                    │ /logs/baseline-all.txt │   /logs/perf-cut-allocations-all.txt    │
                                    │          B/op          │     B/op      vs base                   │
Mux                                            1024.0 ± 0%       768.0 ± 0%  -25.00% (n=100)
MuxSimple/default                               720.0 ± 0%       352.0 ± 0%  -51.11% (n=100)
MuxSimple/omit_route_from_ctx                  720.00 ± 0%       48.00 ± 0%  -93.33% (n=100)
MuxAlternativeInRegexp                        2.000Ki ± 0%     1.500Ki ± 0%  -25.00% (n=100)
ManyPathVariables                              1254.5 ± 2%       998.0 ± 0%  -20.45% (n=100)
PopulateContext/no_populated_vars               720.0 ± 0%       352.0 ± 0%  -51.11% (n=100)
PopulateContext/empty_var                      1040.0 ± 0%       784.0 ± 0%  -24.62% (n=100)
PopulateContext/populated_vars                 1024.0 ± 0%       768.0 ± 0%  -25.00% (n=100)
PopulateContext/omit_route_/static             720.00 ± 0%       48.00 ± 0%  -93.33% (n=100)
PopulateContext/omit_route_/dynamic            1040.0 ± 0%       736.0 ± 0%  -29.23% (n=100)
_findQueryKey/0                                 0.000 ± 0%       0.000 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKey/1                                 40.00 ± 0%       40.00 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKey/2                                 483.0 ± 0%       483.0 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKey/3                                 543.0 ± 0%       543.0 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKey/4                                 0.000 ± 0%       0.000 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKeyGoLib/0                            864.0 ± 0%       864.0 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKeyGoLib/1                            432.0 ± 0%       432.0 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKeyGoLib/2                          1.506Ki ± 0%     1.506Ki ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKeyGoLib/3                          1.938Ki ± 0%     1.938Ki ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKeyGoLib/4                            0.000 ± 0%       0.000 ± 0%        ~ (p=1.000 n=100) ¹
geomean                                                    ²                 -34.84%                 ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                                    │ /logs/baseline-all.txt │  /logs/perf-cut-allocations-all.txt   │
                                    │       allocs/op        │ allocs/op   vs base                   │
Mux                                             8.000 ± 0%     7.000 ± 0%  -12.50% (n=100)
MuxSimple/default                               7.000 ± 0%     3.000 ± 0%  -57.14% (n=100)
MuxSimple/omit_route_from_ctx                   7.000 ± 0%     1.000 ± 0%  -85.71% (n=100)
MuxAlternativeInRegexp                          16.00 ± 0%     14.00 ± 0%  -12.50% (n=100)
ManyPathVariables                               12.00 ± 0%     11.00 ± 0%   -8.33% (n=100)
PopulateContext/no_populated_vars               7.000 ± 0%     3.000 ± 0%  -57.14% (n=100)
PopulateContext/empty_var                       9.000 ± 0%     8.000 ± 0%  -11.11% (n=100)
PopulateContext/populated_vars                  8.000 ± 0%     7.000 ± 0%  -12.50% (n=100)
PopulateContext/omit_route_/static              7.000 ± 0%     1.000 ± 0%  -85.71% (n=100)
PopulateContext/omit_route_/dynamic             9.000 ± 0%     7.000 ± 0%  -22.22% (n=100)
_findQueryKey/0                                 0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKey/1                                 3.000 ± 0%     3.000 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKey/2                                 10.00 ± 0%     10.00 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKey/3                                 11.00 ± 0%     11.00 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKey/4                                 0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKeyGoLib/0                            8.000 ± 0%     8.000 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKeyGoLib/1                            4.000 ± 0%     4.000 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKeyGoLib/2                            24.00 ± 0%     24.00 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKeyGoLib/3                            28.00 ± 0%     28.00 ± 0%        ~ (p=1.000 n=100) ¹
_findQueryKeyGoLib/4                            0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=100) ¹
geomean                                                    ²               -27.54%                 ²
¹ all samples are equal
² summaries must be >0 to compute geomean
``` 

---

</details>

</details>

---


<details>
<summary> Popular go-http-routing-benchmark </summary>

I pushed three branches for comparison to my fork:
https://github.com/das7pad/go-http-routing-benchmark

- `before`, this is the baseline branch mentioned above
- `after`, this is the PR revision
- `after-omit-route`, like `after` with the `OmitRouteFromContext` flag enabled

You can reproduce these benchmarks using docker, pinned to CPU 1:

```bash
docker run --rm --pull always -v /logs:/logs --cpuset-cpus 1 -d golang:1.18 bash -exc 'git clone https://github.com/das7pad/go-http-routing-benchmark.git && cd go-http-routing-benchmark && for branch in before after after-omit-route; do git checkout "$branch" && go test -benchmem -bench Gorilla -count 100 -timeout 1h > "/logs/$branch.txt"; done; go install golang.org/x/perf/cmd/benchstat@latest; benchstat /logs/before.txt /logs/after.txt > /logs/compare-before-vs-after.txt; benchstat /logs/before.txt /logs/after-omit-route.txt > /logs/compare-before-vs-after-omit-route.txt'
```

<details>
<summary> Modern Xeon E, 3.4 GHz </summary>

Before vs After with omit Route flag enabled
``` 
goos: linux
goarch: amd64
pkg: github.com/julienschmidt/go-http-routing-benchmark
cpu: Intel(R) Xeon(R) E-2278G CPU @ 3.40GHz
                        │ /logs/before.txt │  /logs/after-omit-route.txt  │
                        │      sec/op      │   sec/op     vs base         │
GorillaMux_Param               1.455µ ± 0%   1.116µ ± 0%  -23.27% (n=100)
GorillaMux_Param5              2.200µ ± 0%   1.864µ ± 0%  -15.28% (n=100)
GorillaMux_Param20             5.438µ ± 0%   3.575µ ± 0%  -34.26% (n=100)
GorillaMux_ParamWrite          1.496µ ± 0%   1.168µ ± 0%  -21.93% (n=100)
GorillaMux_GithubStatic        3.315µ ± 0%   2.384µ ± 0%  -28.08% (n=100)
GorillaMux_GithubParam         5.021µ ± 0%   4.729µ ± 0%   -5.83% (n=100)
GorillaMux_GithubAll           2.620m ± 0%   2.514m ± 0%   -4.03% (n=100)
GorillaMux_GPlusStatic         973.9n ± 0%   187.0n ± 0%  -80.80% (n=100)
GorillaMux_GPlusParam          1.915µ ± 0%   1.625µ ± 0%  -15.15% (n=100)
GorillaMux_GPlus2Params        3.786µ ± 0%   3.507µ ± 0%   -7.37% (n=100)
GorillaMux_GPlusAll            30.20µ ± 0%   24.71µ ± 0%  -18.16% (n=100)
GorillaMux_ParseStatic        1199.0n ± 0%   398.4n ± 0%  -66.77% (n=100)
GorillaMux_ParseParam          1.477µ ± 0%   1.166µ ± 0%  -21.06% (n=100)
GorillaMux_Parse2Params        1.795µ ± 0%   1.491µ ± 0%  -16.94% (n=100)
GorillaMux_ParseAll            59.11µ ± 0%   45.33µ ± 0%  -23.32% (n=100)
GorillaMux_StaticAll           662.8µ ± 1%   491.1µ ± 0%  -25.91% (n=100)
geomean                        6.957µ        4.869µ       -30.01%

                        │ /logs/before.txt │  /logs/after-omit-route.txt   │
                        │       B/op       │     B/op      vs base         │
GorillaMux_Param               1024.0 ± 0%     720.0 ± 0%  -29.69% (n=100)
GorillaMux_Param5              1088.0 ± 0%     784.0 ± 0%  -27.94% (n=100)
GorillaMux_Param20            3.087Ki ± 0%   1.905Ki ± 0%  -38.28% (n=100)
GorillaMux_ParamWrite          1024.0 ± 0%     720.0 ± 0%  -29.69% (n=100)
GorillaMux_GithubStatic        720.00 ± 0%     48.00 ± 0%  -93.33% (n=100)
GorillaMux_GithubParam         1040.0 ± 0%     736.0 ± 0%  -29.23% (n=100)
GorillaMux_GithubAll          195.0Ki ± 0%   121.8Ki ± 0%  -37.54% (n=100)
GorillaMux_GPlusStatic         720.00 ± 0%     48.00 ± 0%  -93.33% (n=100)
GorillaMux_GPlusParam          1024.0 ± 0%     720.0 ± 0%  -29.69% (n=100)
GorillaMux_GPlus2Params        1040.0 ± 0%     736.0 ± 0%  -29.23% (n=100)
GorillaMux_GPlusAll          12.484Ki ± 0%   7.906Ki ± 0%  -36.67% (n=100)
GorillaMux_ParseStatic         720.00 ± 0%     48.00 ± 0%  -93.33% (n=100)
GorillaMux_ParseParam          1024.0 ± 0%     720.0 ± 0%  -29.69% (n=100)
GorillaMux_Parse2Params        1040.0 ± 0%     736.0 ± 0%  -29.23% (n=100)
GorillaMux_ParseAll           23.08Ki ± 0%   11.77Ki ± 0%  -49.02% (n=100)
GorillaMux_StaticAll        110.395Ki ± 0%   7.359Ki ± 0%  -93.33% (n=100)
geomean                       2.688Ki        1.008Ki       -62.49%

                        │ /logs/before.txt │  /logs/after-omit-route.txt  │
                        │    allocs/op     │  allocs/op   vs base         │
GorillaMux_Param                8.000 ± 0%    6.000 ± 0%  -25.00% (n=100)
GorillaMux_Param5               8.000 ± 0%    6.000 ± 0%  -25.00% (n=100)
GorillaMux_Param20             10.000 ± 0%    6.000 ± 0%  -40.00% (n=100)
GorillaMux_ParamWrite           8.000 ± 0%    6.000 ± 0%  -25.00% (n=100)
GorillaMux_GithubStatic         7.000 ± 0%    1.000 ± 0%  -85.71% (n=100)
GorillaMux_GithubParam          8.000 ± 0%    6.000 ± 0%  -25.00% (n=100)
GorillaMux_GithubAll           1.588k ± 0%   1.038k ± 0%  -34.63% (n=100)
GorillaMux_GPlusStatic          7.000 ± 0%    1.000 ± 0%  -85.71% (n=100)
GorillaMux_GPlusParam           8.000 ± 0%    6.000 ± 0%  -25.00% (n=100)
GorillaMux_GPlus2Params         8.000 ± 0%    6.000 ± 0%  -25.00% (n=100)
GorillaMux_GPlusAll            102.00 ± 0%    68.00 ± 0%  -33.33% (n=100)
GorillaMux_ParseStatic          7.000 ± 0%    1.000 ± 0%  -85.71% (n=100)
GorillaMux_ParseParam           8.000 ± 0%    6.000 ± 0%  -25.00% (n=100)
GorillaMux_Parse2Params         8.000 ± 0%    6.000 ± 0%  -25.00% (n=100)
GorillaMux_ParseAll             198.0 ± 0%    106.0 ± 0%  -46.46% (n=100)
GorillaMux_StaticAll           1099.0 ± 0%    157.0 ± 0%  -85.71% (n=100)
geomean                         21.46         10.11       -52.91%
``` 


Before vs After

```
goos: linux
goarch: amd64
pkg: github.com/julienschmidt/go-http-routing-benchmark
cpu: Intel(R) Xeon(R) E-2278G CPU @ 3.40GHz
                        │ /logs/before.txt │       /logs/after.txt        │
                        │      sec/op      │   sec/op     vs base         │
GorillaMux_Param               1.455µ ± 0%   1.199µ ± 0%  -17.60% (n=100)
GorillaMux_Param5              2.200µ ± 0%   1.953µ ± 0%  -11.21% (n=100)
GorillaMux_Param20             5.438µ ± 0%   3.667µ ± 0%  -32.57% (n=100)
GorillaMux_ParamWrite          1.496µ ± 0%   1.264µ ± 0%  -15.51% (n=100)
GorillaMux_GithubStatic        3.315µ ± 0%   2.809µ ± 0%  -15.26% (n=100)
GorillaMux_GithubParam         5.021µ ± 0%   4.782µ ± 0%   -4.76% (n=100)
GorillaMux_GithubAll           2.620m ± 0%   2.510m ± 1%   -4.20% (n=100)
GorillaMux_GPlusStatic         973.9n ± 0%   489.2n ± 1%  -49.76% (n=100)
GorillaMux_GPlusParam          1.915µ ± 0%   1.707µ ± 0%  -10.86% (n=100)
GorillaMux_GPlus2Params        3.786µ ± 0%   3.575µ ± 0%   -5.57% (n=100)
GorillaMux_GPlusAll            30.20µ ± 0%   26.44µ ± 0%  -12.45% (n=100)
GorillaMux_ParseStatic        1199.0n ± 0%   707.7n ± 0%  -40.98% (n=100)
GorillaMux_ParseParam          1.477µ ± 0%   1.238µ ± 0%  -16.19% (n=100)
GorillaMux_Parse2Params        1.795µ ± 0%   1.565µ ± 0%  -12.81% (n=100)
GorillaMux_ParseAll            59.11µ ± 0%   49.76µ ± 0%  -15.82% (n=100)
GorillaMux_StaticAll           662.8µ ± 1%   577.4µ ± 0%  -12.89% (n=100)
geomean                        6.957µ        5.669µ       -18.52%

                        │ /logs/before.txt │        /logs/after.txt        │
                        │       B/op       │     B/op      vs base         │
GorillaMux_Param               1024.0 ± 0%     768.0 ± 0%  -25.00% (n=100)
GorillaMux_Param5              1088.0 ± 0%     832.0 ± 0%  -23.53% (n=100)
GorillaMux_Param20            3.087Ki ± 0%   1.952Ki ± 0%  -36.76% (n=100)
GorillaMux_ParamWrite          1024.0 ± 0%     768.0 ± 0%  -25.00% (n=100)
GorillaMux_GithubStatic         720.0 ± 0%     352.0 ± 0%  -51.11% (n=100)
GorillaMux_GithubParam         1040.0 ± 0%     784.0 ± 0%  -24.62% (n=100)
GorillaMux_GithubAll          195.0Ki ± 0%   140.3Ki ± 0%  -28.04% (n=100)
GorillaMux_GPlusStatic          720.0 ± 0%     352.0 ± 0%  -51.11% (n=100)
GorillaMux_GPlusParam          1024.0 ± 0%     768.0 ± 0%  -25.00% (n=100)
GorillaMux_GPlus2Params        1040.0 ± 0%     784.0 ± 0%  -24.62% (n=100)
GorillaMux_GPlusAll          12.484Ki ± 0%   9.016Ki ± 0%  -27.78% (n=100)
GorillaMux_ParseStatic          720.0 ± 0%     352.0 ± 0%  -51.11% (n=100)
GorillaMux_ParseParam          1024.0 ± 0%     768.0 ± 0%  -25.00% (n=100)
GorillaMux_Parse2Params        1040.0 ± 0%     784.0 ± 0%  -24.62% (n=100)
GorillaMux_ParseAll           23.08Ki ± 0%   15.48Ki ± 0%  -32.90% (n=100)
GorillaMux_StaticAll         110.39Ki ± 0%   53.97Ki ± 0%  -51.11% (n=100)
geomean                       2.688Ki        1.775Ki       -33.97%

                        │ /logs/before.txt │       /logs/after.txt        │
                        │    allocs/op     │  allocs/op   vs base         │
GorillaMux_Param                8.000 ± 0%    7.000 ± 0%  -12.50% (n=100)
GorillaMux_Param5               8.000 ± 0%    7.000 ± 0%  -12.50% (n=100)
GorillaMux_Param20             10.000 ± 0%    7.000 ± 0%  -30.00% (n=100)
GorillaMux_ParamWrite           8.000 ± 0%    7.000 ± 0%  -12.50% (n=100)
GorillaMux_GithubStatic         7.000 ± 0%    3.000 ± 0%  -57.14% (n=100)
GorillaMux_GithubParam          8.000 ± 0%    7.000 ± 0%  -12.50% (n=100)
GorillaMux_GithubAll           1.588k ± 0%   1.277k ± 0%  -19.58% (n=100)
GorillaMux_GPlusStatic          7.000 ± 0%    3.000 ± 0%  -57.14% (n=100)
GorillaMux_GPlusParam           8.000 ± 0%    7.000 ± 0%  -12.50% (n=100)
GorillaMux_GPlus2Params         8.000 ± 0%    7.000 ± 0%  -12.50% (n=100)
GorillaMux_GPlusAll            102.00 ± 0%    83.00 ± 0%  -18.63% (n=100)
GorillaMux_ParseStatic          7.000 ± 0%    3.000 ± 0%  -57.14% (n=100)
GorillaMux_ParseParam           8.000 ± 0%    7.000 ± 0%  -12.50% (n=100)
GorillaMux_Parse2Params         8.000 ± 0%    7.000 ± 0%  -12.50% (n=100)
GorillaMux_ParseAll             198.0 ± 0%    142.0 ± 0%  -28.28% (n=100)
GorillaMux_StaticAll           1099.0 ± 0%    471.0 ± 0%  -57.14% (n=100)
geomean                         21.46         15.15       -29.40%
```

---

</details>


<details>
<summary> Older Xeon Gold, 2.3 GHz </summary>

Before vs After with omit Route flag enabled

(Actual CPU identifier is rather `Intel(R) Xeon(R) Gold 5122 CPU @ 2.30GHz`)
```
goos: linux
goarch: amd64
pkg: github.com/julienschmidt/go-http-routing-benchmark
cpu: Intel Xeon Processor (Skylake, IBRS)
                        │ /logs/before.txt │      /logs/after-omit-route.txt      │
                        │      sec/op      │   sec/op     vs base                 │
GorillaMux_Param               3.345µ ± 3%   2.649µ ± 3%  -20.80% (n=100)
GorillaMux_Param5              5.206µ ± 2%   5.023µ ± 2%   -3.52% (p=0.046 n=100)
GorillaMux_Param20            12.866µ ± 2%   9.010µ ± 3%  -29.97% (n=100)
GorillaMux_ParamWrite          3.450µ ± 4%   2.999µ ± 2%  -13.06% (n=100)
GorillaMux_GithubStatic        7.957µ ± 2%   5.759µ ± 3%  -27.62% (n=100)
GorillaMux_GithubParam         12.54µ ± 1%   11.91µ ± 3%   -4.97% (p=0.000 n=100)
GorillaMux_GithubAll           6.019m ± 3%   5.616m ± 3%   -6.69% (p=0.000 n=100)
GorillaMux_GPlusStatic        2277.5n ± 1%   479.2n ± 3%  -78.96% (n=100)
GorillaMux_GPlusParam          5.246µ ± 2%   3.969µ ± 2%  -24.34% (n=100)
GorillaMux_GPlus2Params       10.060µ ± 3%   9.068µ ± 3%   -9.86% (p=0.000 n=100)
GorillaMux_GPlusAll            71.76µ ± 2%   58.00µ ± 3%  -19.18% (n=100)
GorillaMux_ParseStatic         2.753µ ± 3%   1.020µ ± 5%  -62.95% (n=100)
GorillaMux_ParseParam          3.557µ ± 2%   2.825µ ± 2%  -20.59% (n=100)
GorillaMux_Parse2Params        4.501µ ± 3%   3.750µ ± 2%  -16.68% (n=100)
GorillaMux_ParseAll            142.6µ ± 3%   111.9µ ± 3%  -21.50% (n=100)
GorillaMux_StaticAll           1.545m ± 2%   1.177m ± 3%  -23.80% (n=100)
geomean                        16.76µ        12.05µ       -28.12%

                        │ /logs/before.txt │  /logs/after-omit-route.txt   │
                        │       B/op       │     B/op      vs base         │
GorillaMux_Param               1024.0 ± 0%     720.0 ± 0%  -29.69% (n=100)
GorillaMux_Param5              1088.0 ± 0%     784.0 ± 0%  -27.94% (n=100)
GorillaMux_Param20            3.087Ki ± 0%   1.905Ki ± 0%  -38.28% (n=100)
GorillaMux_ParamWrite          1024.0 ± 0%     720.0 ± 0%  -29.69% (n=100)
GorillaMux_GithubStatic        720.00 ± 0%     48.00 ± 0%  -93.33% (n=100)
GorillaMux_GithubParam         1040.0 ± 0%     736.0 ± 0%  -29.23% (n=100)
GorillaMux_GithubAll          195.0Ki ± 0%   121.8Ki ± 0%  -37.54% (n=100)
GorillaMux_GPlusStatic         720.00 ± 0%     48.00 ± 0%  -93.33% (n=100)
GorillaMux_GPlusParam          1024.0 ± 0%     720.0 ± 0%  -29.69% (n=100)
GorillaMux_GPlus2Params        1040.0 ± 0%     736.0 ± 0%  -29.23% (n=100)
GorillaMux_GPlusAll          12.484Ki ± 0%   7.906Ki ± 0%  -36.67% (n=100)
GorillaMux_ParseStatic         720.00 ± 0%     48.00 ± 0%  -93.33% (n=100)
GorillaMux_ParseParam          1024.0 ± 0%     720.0 ± 0%  -29.69% (n=100)
GorillaMux_Parse2Params        1040.0 ± 0%     736.0 ± 0%  -29.23% (n=100)
GorillaMux_ParseAll           23.08Ki ± 0%   11.77Ki ± 0%  -49.02% (n=100)
GorillaMux_StaticAll        110.394Ki ± 0%   7.359Ki ± 0%  -93.33% (n=100)
geomean                       2.688Ki        1.008Ki       -62.49%

                        │ /logs/before.txt │  /logs/after-omit-route.txt  │
                        │    allocs/op     │  allocs/op   vs base         │
GorillaMux_Param                8.000 ± 0%    6.000 ± 0%  -25.00% (n=100)
GorillaMux_Param5               8.000 ± 0%    6.000 ± 0%  -25.00% (n=100)
GorillaMux_Param20             10.000 ± 0%    6.000 ± 0%  -40.00% (n=100)
GorillaMux_ParamWrite           8.000 ± 0%    6.000 ± 0%  -25.00% (n=100)
GorillaMux_GithubStatic         7.000 ± 0%    1.000 ± 0%  -85.71% (n=100)
GorillaMux_GithubParam          8.000 ± 0%    6.000 ± 0%  -25.00% (n=100)
GorillaMux_GithubAll           1.588k ± 0%   1.038k ± 0%  -34.63% (n=100)
GorillaMux_GPlusStatic          7.000 ± 0%    1.000 ± 0%  -85.71% (n=100)
GorillaMux_GPlusParam           8.000 ± 0%    6.000 ± 0%  -25.00% (n=100)
GorillaMux_GPlus2Params         8.000 ± 0%    6.000 ± 0%  -25.00% (n=100)
GorillaMux_GPlusAll            102.00 ± 0%    68.00 ± 0%  -33.33% (n=100)
GorillaMux_ParseStatic          7.000 ± 0%    1.000 ± 0%  -85.71% (n=100)
GorillaMux_ParseParam           8.000 ± 0%    6.000 ± 0%  -25.00% (n=100)
GorillaMux_Parse2Params         8.000 ± 0%    6.000 ± 0%  -25.00% (n=100)
GorillaMux_ParseAll             198.0 ± 0%    106.0 ± 0%  -46.46% (n=100)
GorillaMux_StaticAll           1099.0 ± 0%    157.0 ± 0%  -85.71% (n=100)
geomean                         21.46         10.11       -52.91%
``` 

Before vs After

(Actual CPU identifier is rather `Intel(R) Xeon(R) Gold 5122 CPU @ 2.30GHz`)
```
goos: linux
goarch: amd64
pkg: github.com/julienschmidt/go-http-routing-benchmark
cpu: Intel Xeon Processor (Skylake, IBRS)
                        │ /logs/before.txt │           /logs/after.txt            │
                        │      sec/op      │   sec/op     vs base                 │
GorillaMux_Param               3.345µ ± 3%   2.933µ ± 3%  -12.29% (n=100)
GorillaMux_Param5              5.206µ ± 2%   4.460µ ± 4%  -14.33% (n=100)
GorillaMux_Param20            12.866µ ± 2%   9.037µ ± 4%  -29.76% (n=100)
GorillaMux_ParamWrite          3.450µ ± 4%   3.323µ ± 2%   -3.68% (p=0.023 n=100)
GorillaMux_GithubStatic        7.957µ ± 2%   6.610µ ± 3%  -16.93% (n=100)
GorillaMux_GithubParam         12.54µ ± 1%   11.76µ ± 5%   -6.17% (p=0.000 n=100)
GorillaMux_GithubAll           6.019m ± 3%   5.687m ± 3%   -5.52% (p=0.000 n=100)
GorillaMux_GPlusStatic         2.277µ ± 1%   1.144µ ± 2%  -49.77% (n=100)
GorillaMux_GPlusParam          5.246µ ± 2%   4.133µ ± 3%  -21.23% (n=100)
GorillaMux_GPlus2Params       10.060µ ± 3%   9.003µ ± 2%  -10.50% (p=0.000 n=100)
GorillaMux_GPlusAll            71.76µ ± 2%   64.25µ ± 3%  -10.46% (p=0.000 n=100)
GorillaMux_ParseStatic         2.753µ ± 3%   1.715µ ± 2%  -37.70% (n=100)
GorillaMux_ParseParam          3.557µ ± 2%   2.958µ ± 2%  -16.84% (n=100)
GorillaMux_Parse2Params        4.501µ ± 3%   4.009µ ± 2%  -10.92% (p=0.000 n=100)
GorillaMux_ParseAll            142.6µ ± 3%   121.4µ ± 3%  -14.90% (n=100)
GorillaMux_StaticAll           1.545m ± 2%   1.301m ± 2%  -15.80% (n=100)
geomean                        16.76µ        13.69µ       -18.32%

                        │ /logs/before.txt │        /logs/after.txt        │
                        │       B/op       │     B/op      vs base         │
GorillaMux_Param               1024.0 ± 0%     768.0 ± 0%  -25.00% (n=100)
GorillaMux_Param5              1088.0 ± 0%     832.0 ± 0%  -23.53% (n=100)
GorillaMux_Param20            3.087Ki ± 0%   1.952Ki ± 0%  -36.76% (n=100)
GorillaMux_ParamWrite          1024.0 ± 0%     768.0 ± 0%  -25.00% (n=100)
GorillaMux_GithubStatic         720.0 ± 0%     352.0 ± 0%  -51.11% (n=100)
GorillaMux_GithubParam         1040.0 ± 0%     784.0 ± 0%  -24.62% (n=100)
GorillaMux_GithubAll          195.0Ki ± 0%   140.3Ki ± 0%  -28.04% (n=100)
GorillaMux_GPlusStatic          720.0 ± 0%     352.0 ± 0%  -51.11% (n=100)
GorillaMux_GPlusParam          1024.0 ± 0%     768.0 ± 0%  -25.00% (n=100)
GorillaMux_GPlus2Params        1040.0 ± 0%     784.0 ± 0%  -24.62% (n=100)
GorillaMux_GPlusAll          12.484Ki ± 0%   9.016Ki ± 0%  -27.78% (n=100)
GorillaMux_ParseStatic          720.0 ± 0%     352.0 ± 0%  -51.11% (n=100)
GorillaMux_ParseParam          1024.0 ± 0%     768.0 ± 0%  -25.00% (n=100)
GorillaMux_Parse2Params        1040.0 ± 0%     784.0 ± 0%  -24.62% (n=100)
GorillaMux_ParseAll           23.08Ki ± 0%   15.48Ki ± 0%  -32.90% (n=100)
GorillaMux_StaticAll         110.39Ki ± 0%   53.97Ki ± 0%  -51.11% (n=100)
geomean                       2.688Ki        1.775Ki       -33.97%

                        │ /logs/before.txt │       /logs/after.txt        │
                        │    allocs/op     │  allocs/op   vs base         │
GorillaMux_Param                8.000 ± 0%    7.000 ± 0%  -12.50% (n=100)
GorillaMux_Param5               8.000 ± 0%    7.000 ± 0%  -12.50% (n=100)
GorillaMux_Param20             10.000 ± 0%    7.000 ± 0%  -30.00% (n=100)
GorillaMux_ParamWrite           8.000 ± 0%    7.000 ± 0%  -12.50% (n=100)
GorillaMux_GithubStatic         7.000 ± 0%    3.000 ± 0%  -57.14% (n=100)
GorillaMux_GithubParam          8.000 ± 0%    7.000 ± 0%  -12.50% (n=100)
GorillaMux_GithubAll           1.588k ± 0%   1.277k ± 0%  -19.58% (n=100)
GorillaMux_GPlusStatic          7.000 ± 0%    3.000 ± 0%  -57.14% (n=100)
GorillaMux_GPlusParam           8.000 ± 0%    7.000 ± 0%  -12.50% (n=100)
GorillaMux_GPlus2Params         8.000 ± 0%    7.000 ± 0%  -12.50% (n=100)
GorillaMux_GPlusAll            102.00 ± 0%    83.00 ± 0%  -18.63% (n=100)
GorillaMux_ParseStatic          7.000 ± 0%    3.000 ± 0%  -57.14% (n=100)
GorillaMux_ParseParam           8.000 ± 0%    7.000 ± 0%  -12.50% (n=100)
GorillaMux_Parse2Params         8.000 ± 0%    7.000 ± 0%  -12.50% (n=100)
GorillaMux_ParseAll             198.0 ± 0%    142.0 ± 0%  -28.28% (n=100)
GorillaMux_StaticAll           1099.0 ± 0%    471.0 ± 0%  -57.14% (n=100)
geomean                         21.46         15.15       -29.40%
```

---

</details>



<details>
<summary> Older Xeon E, 2.4 GHz </summary>

Before vs After with omit Route flag enabled

```
goos: linux
goarch: amd64
pkg: github.com/julienschmidt/go-http-routing-benchmark
cpu: Intel(R) Xeon(R) CPU E5-2680 v4 @ 2.40GHz
                        │ /logs/before.txt │      /logs/after-omit-route.txt      │
                        │      sec/op      │   sec/op     vs base                 │
GorillaMux_Param               2.429µ ± 1%   2.092µ ± 1%  -13.84% (n=100)
GorillaMux_Param5              3.664µ ± 1%   3.230µ ± 1%  -11.85% (n=100)
GorillaMux_Param20             9.108µ ± 1%   6.534µ ± 1%  -28.27% (n=100)
GorillaMux_ParamWrite          2.477µ ± 1%   2.165µ ± 1%  -12.60% (n=100)
GorillaMux_GithubStatic        5.673µ ± 1%   3.775µ ± 1%  -33.47% (n=100)
GorillaMux_GithubParam         8.213µ ± 0%   7.861µ ± 1%   -4.29% (n=100)
GorillaMux_GithubAll           4.464m ± 1%   4.359m ± 1%   -2.34% (p=0.000 n=100)
GorillaMux_GPlusStatic        1712.0n ± 1%   334.9n ± 1%  -80.44% (n=100)
GorillaMux_GPlusParam          3.270µ ± 1%   2.849µ ± 1%  -12.86% (n=100)
GorillaMux_GPlus2Params        6.418µ ± 1%   5.953µ ± 0%   -7.25% (n=100)
GorillaMux_GPlusAll            50.79µ ± 1%   43.23µ ± 1%  -14.88% (n=100)
GorillaMux_ParseStatic        2065.0n ± 1%   662.1n ± 1%  -67.94% (n=100)
GorillaMux_ParseParam          2.544µ ± 1%   2.152µ ± 1%  -15.41% (n=100)
GorillaMux_Parse2Params        3.062µ ± 1%   2.691µ ± 1%  -12.15% (n=100)
GorillaMux_ParseAll           102.66µ ± 2%   77.35µ ± 1%  -24.66% (n=100)
GorillaMux_StaticAll          1139.7µ ± 1%   757.3µ ± 1%  -33.55% (n=100)
geomean                        11.81µ        8.455µ       -28.42%

                        │ /logs/before.txt │  /logs/after-omit-route.txt   │
                        │       B/op       │     B/op      vs base         │
GorillaMux_Param               1024.0 ± 0%     720.0 ± 0%  -29.69% (n=100)
GorillaMux_Param5              1088.0 ± 0%     784.0 ± 0%  -27.94% (n=100)
GorillaMux_Param20            3.087Ki ± 0%   1.905Ki ± 0%  -38.28% (n=100)
GorillaMux_ParamWrite          1024.0 ± 0%     720.0 ± 0%  -29.69% (n=100)
GorillaMux_GithubStatic        720.00 ± 0%     48.00 ± 0%  -93.33% (n=100)
GorillaMux_GithubParam         1040.0 ± 0%     736.0 ± 0%  -29.23% (n=100)
GorillaMux_GithubAll          195.0Ki ± 0%   121.8Ki ± 0%  -37.54% (n=100)
GorillaMux_GPlusStatic         720.00 ± 0%     48.00 ± 0%  -93.33% (n=100)
GorillaMux_GPlusParam          1024.0 ± 0%     720.0 ± 0%  -29.69% (n=100)
GorillaMux_GPlus2Params        1040.0 ± 0%     736.0 ± 0%  -29.23% (n=100)
GorillaMux_GPlusAll          12.484Ki ± 0%   7.906Ki ± 0%  -36.67% (n=100)
GorillaMux_ParseStatic         720.00 ± 0%     48.00 ± 0%  -93.33% (n=100)
GorillaMux_ParseParam          1024.0 ± 0%     720.0 ± 0%  -29.69% (n=100)
GorillaMux_Parse2Params        1040.0 ± 0%     736.0 ± 0%  -29.23% (n=100)
GorillaMux_ParseAll           23.08Ki ± 0%   11.77Ki ± 0%  -49.02% (n=100)
GorillaMux_StaticAll        110.394Ki ± 0%   7.359Ki ± 0%  -93.33% (n=100)
geomean                       2.688Ki        1.008Ki       -62.49%

                        │ /logs/before.txt │  /logs/after-omit-route.txt  │
                        │    allocs/op     │  allocs/op   vs base         │
GorillaMux_Param                8.000 ± 0%    6.000 ± 0%  -25.00% (n=100)
GorillaMux_Param5               8.000 ± 0%    6.000 ± 0%  -25.00% (n=100)
GorillaMux_Param20             10.000 ± 0%    6.000 ± 0%  -40.00% (n=100)
GorillaMux_ParamWrite           8.000 ± 0%    6.000 ± 0%  -25.00% (n=100)
GorillaMux_GithubStatic         7.000 ± 0%    1.000 ± 0%  -85.71% (n=100)
GorillaMux_GithubParam          8.000 ± 0%    6.000 ± 0%  -25.00% (n=100)
GorillaMux_GithubAll           1.588k ± 0%   1.038k ± 0%  -34.63% (n=100)
GorillaMux_GPlusStatic          7.000 ± 0%    1.000 ± 0%  -85.71% (n=100)
GorillaMux_GPlusParam           8.000 ± 0%    6.000 ± 0%  -25.00% (n=100)
GorillaMux_GPlus2Params         8.000 ± 0%    6.000 ± 0%  -25.00% (n=100)
GorillaMux_GPlusAll            102.00 ± 0%    68.00 ± 0%  -33.33% (n=100)
GorillaMux_ParseStatic          7.000 ± 0%    1.000 ± 0%  -85.71% (n=100)
GorillaMux_ParseParam           8.000 ± 0%    6.000 ± 0%  -25.00% (n=100)
GorillaMux_Parse2Params         8.000 ± 0%    6.000 ± 0%  -25.00% (n=100)
GorillaMux_ParseAll             198.0 ± 0%    106.0 ± 0%  -46.46% (n=100)
GorillaMux_StaticAll           1099.0 ± 0%    157.0 ± 0%  -85.71% (n=100)
geomean                         21.46         10.11       -52.91%
```
 
Before vs After

```
goos: linux
goarch: amd64
pkg: github.com/julienschmidt/go-http-routing-benchmark
cpu: Intel(R) Xeon(R) CPU E5-2680 v4 @ 2.40GHz
                        │ /logs/before.txt │       /logs/after.txt        │
                        │      sec/op      │   sec/op     vs base         │
GorillaMux_Param               2.429µ ± 1%   2.097µ ± 1%  -13.63% (n=100)
GorillaMux_Param5              3.664µ ± 1%   3.197µ ± 1%  -12.75% (n=100)
GorillaMux_Param20             9.108µ ± 1%   6.308µ ± 1%  -30.75% (n=100)
GorillaMux_ParamWrite          2.477µ ± 1%   2.264µ ± 1%   -8.60% (n=100)
GorillaMux_GithubStatic        5.673µ ± 1%   4.556µ ± 0%  -19.70% (n=100)
GorillaMux_GithubParam         8.213µ ± 0%   7.829µ ± 0%   -4.68% (n=100)
GorillaMux_GithubAll           4.464m ± 1%   4.320m ± 1%   -3.21% (n=100)
GorillaMux_GPlusStatic        1712.0n ± 1%   869.9n ± 1%  -49.19% (n=100)
GorillaMux_GPlusParam          3.270µ ± 1%   2.920µ ± 1%  -10.69% (n=100)
GorillaMux_GPlus2Params        6.418µ ± 1%   6.161µ ± 1%   -4.00% (n=100)
GorillaMux_GPlusAll            50.79µ ± 1%   46.16µ ± 1%   -9.12% (n=100)
GorillaMux_ParseStatic         2.065µ ± 1%   1.249µ ± 1%  -39.52% (n=100)
GorillaMux_ParseParam          2.544µ ± 1%   2.253µ ± 1%  -11.44% (n=100)
GorillaMux_Parse2Params        3.062µ ± 1%   2.797µ ± 1%   -8.65% (n=100)
GorillaMux_ParseAll           102.66µ ± 2%   86.81µ ± 1%  -15.44% (n=100)
GorillaMux_StaticAll          1139.7µ ± 1%   927.5µ ± 0%  -18.62% (n=100)
geomean                        11.81µ        9.758µ       -17.38%

                        │ /logs/before.txt │        /logs/after.txt        │
                        │       B/op       │     B/op      vs base         │
GorillaMux_Param               1024.0 ± 0%     768.0 ± 0%  -25.00% (n=100)
GorillaMux_Param5              1088.0 ± 0%     832.0 ± 0%  -23.53% (n=100)
GorillaMux_Param20            3.087Ki ± 0%   1.952Ki ± 0%  -36.76% (n=100)
GorillaMux_ParamWrite          1024.0 ± 0%     768.0 ± 0%  -25.00% (n=100)
GorillaMux_GithubStatic         720.0 ± 0%     352.0 ± 0%  -51.11% (n=100)
GorillaMux_GithubParam         1040.0 ± 0%     784.0 ± 0%  -24.62% (n=100)
GorillaMux_GithubAll          195.0Ki ± 0%   140.3Ki ± 0%  -28.04% (n=100)
GorillaMux_GPlusStatic          720.0 ± 0%     352.0 ± 0%  -51.11% (n=100)
GorillaMux_GPlusParam          1024.0 ± 0%     768.0 ± 0%  -25.00% (n=100)
GorillaMux_GPlus2Params        1040.0 ± 0%     784.0 ± 0%  -24.62% (n=100)
GorillaMux_GPlusAll          12.484Ki ± 0%   9.016Ki ± 0%  -27.78% (n=100)
GorillaMux_ParseStatic          720.0 ± 0%     352.0 ± 0%  -51.11% (n=100)
GorillaMux_ParseParam          1024.0 ± 0%     768.0 ± 0%  -25.00% (n=100)
GorillaMux_Parse2Params        1040.0 ± 0%     784.0 ± 0%  -24.62% (n=100)
GorillaMux_ParseAll           23.08Ki ± 0%   15.48Ki ± 0%  -32.90% (n=100)
GorillaMux_StaticAll         110.39Ki ± 0%   53.97Ki ± 0%  -51.11% (n=100)
geomean                       2.688Ki        1.775Ki       -33.97%

                        │ /logs/before.txt │       /logs/after.txt        │
                        │    allocs/op     │  allocs/op   vs base         │
GorillaMux_Param                8.000 ± 0%    7.000 ± 0%  -12.50% (n=100)
GorillaMux_Param5               8.000 ± 0%    7.000 ± 0%  -12.50% (n=100)
GorillaMux_Param20             10.000 ± 0%    7.000 ± 0%  -30.00% (n=100)
GorillaMux_ParamWrite           8.000 ± 0%    7.000 ± 0%  -12.50% (n=100)
GorillaMux_GithubStatic         7.000 ± 0%    3.000 ± 0%  -57.14% (n=100)
GorillaMux_GithubParam          8.000 ± 0%    7.000 ± 0%  -12.50% (n=100)
GorillaMux_GithubAll           1.588k ± 0%   1.277k ± 0%  -19.58% (n=100)
GorillaMux_GPlusStatic          7.000 ± 0%    3.000 ± 0%  -57.14% (n=100)
GorillaMux_GPlusParam           8.000 ± 0%    7.000 ± 0%  -12.50% (n=100)
GorillaMux_GPlus2Params         8.000 ± 0%    7.000 ± 0%  -12.50% (n=100)
GorillaMux_GPlusAll            102.00 ± 0%    83.00 ± 0%  -18.63% (n=100)
GorillaMux_ParseStatic          7.000 ± 0%    3.000 ± 0%  -57.14% (n=100)
GorillaMux_ParseParam           8.000 ± 0%    7.000 ± 0%  -12.50% (n=100)
GorillaMux_Parse2Params         8.000 ± 0%    7.000 ± 0%  -12.50% (n=100)
GorillaMux_ParseAll             198.0 ± 0%    142.0 ± 0%  -28.28% (n=100)
GorillaMux_StaticAll           1099.0 ± 0%    471.0 ± 0%  -57.14% (n=100)
geomean                         21.46         15.15       -29.40%
``` 

</details>

</details>
das7pad: FWIW: The commits of this PR were cherry-picked into [MinIO's fork of mux](https://github.com/minio/mux/commits/master). The fork powered the MinIO server for the past 7 months, via https://github.com/minio/minio/pull/16456. This exposure gave the PR a very decent "manual test in production".
(For context: MinIO is a popular self-hosted S3 compatible object storage server.)
luisdavim: @coreydaley , any chances you could have a look and maybe get this merged? Thanks.
coreydaley: @AlexVulaj AFAIK this looks fine, would you mind also taking a look at it?
AlexVulaj: Left a few comments - it also looks like something is triggering a flag with the security scan for go 1.21, but the output isn't being super helpful about what the issue is.

Patch:
diff --git a/mux.go b/mux.go
index c0c37ec..bcab454 100644
--- a/mux.go
+++ b/mux.go
@@ -9,6 +9,7 @@ import (
 	"errors"
 	"fmt"
 	"net/http"
+	"net/url"
 	"path"
 	"regexp"
 )
@@ -84,6 +85,9 @@ type routeConf struct {
 	// will not redirect
 	skipClean bool
 
+	// If true, the http.Request context will not contain the Route.
+	omitRouteFromContext bool
+
 	// Manager for the variables from host and path.
 	regexp routeRegexpGroup
 
@@ -180,15 +184,7 @@ func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
 		}
 		// Clean path to canonical form and redirect.
 		if p := cleanPath(path); p != path {
-
-			// Added 3 lines (Philip Schlump) - It was dropping the query string and #whatever from query.
-			// This matches with fix in go 1.2 r.c. 4 for same problem.  Go Issue:
-			// http://code.google.com/p/go/issues/detail?id=5252
-			url := *req.URL
-			url.Path = p
-			p = url.String()
-
-			w.Header().Set("Location", p)
+			w.Header().Set("Location", replaceURLPath(req.URL, p))
 			w.WriteHeader(http.StatusMovedPermanently)
 			return
 		}
@@ -197,8 +193,15 @@ func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
 	var handler http.Handler
 	if r.Match(req, &match) {
 		handler = match.Handler
-		req = requestWithVars(req, match.Vars)
-		req = requestWithRoute(req, match.Route)
+		if handler != nil {
+			// Populate context for custom handlers
+			if r.omitRouteFromContext {
+				// Only populate the match vars (if any) into the context.
+				req = requestWithVars(req, match.Vars)
+			} else {
+				req = requestWithRouteAndVars(req, match.Route, match.Vars)
+			}
+		}
 	}
 
 	if handler == nil && match.MatchErr == ErrMethodMismatch {
@@ -260,6 +263,16 @@ func (r *Router) SkipClean(value bool) *Router {
 	return r
 }
 
+// OmitRouteFromContext defines the behavior of omitting the Route from the
+//
+//	http.Request context.
+//
+// CurrentRoute will yield nil with this option.
+func (r *Router) OmitRouteFromContext(value bool) *Router {
+	r.omitRouteFromContext = value
+	return r
+}
+
 // UseEncodedPath tells the router to match the encoded original path
 // to the routes.
 // For eg. "/path/foo%2Fbar/to" will match the path "/path/{var}/to".
@@ -445,13 +458,25 @@ func CurrentRoute(r *http.Request) *Route {
 	return nil
 }
 
+// requestWithRouteAndVars adds the matched vars to the request ctx.
+// It shortcuts the operation when the vars are empty.
 func requestWithVars(r *http.Request, vars map[string]string) *http.Request {
+	if len(vars) == 0 {
+		return r
+	}
 	ctx := context.WithValue(r.Context(), varsKey, vars)
 	return r.WithContext(ctx)
 }
 
-func requestWithRoute(r *http.Request, route *Route) *http.Request {
+// requestWithRouteAndVars adds the matched route and vars to the request ctx.
+// It saves extra allocations in cloning the request once and skipping the
+//
+//	population of empty vars, which in turn mux.Vars can handle gracefully.
+func requestWithRouteAndVars(r *http.Request, route *Route, vars map[string]string) *http.Request {
 	ctx := context.WithValue(r.Context(), routeKey, route)
+	if len(vars) > 0 {
+		ctx = context.WithValue(ctx, varsKey, vars)
+	}
 	return r.WithContext(ctx)
 }
 
@@ -478,6 +503,14 @@ func cleanPath(p string) string {
 	return np
 }
 
+// replaceURLPath prints an url.URL with a different path.
+func replaceURLPath(u *url.URL, p string) string {
+	// Operate on a copy of the request url.
+	u2 := *u
+	u2.Path = p
+	return u2.String()
+}
+
 // uniqueVars returns an error if two slices contain duplicated strings.
 func uniqueVars(s1, s2 []string) error {
 	for _, v1 := range s1 {
diff --git a/regexp.go b/regexp.go
index 5d05cfa..b577073 100644
--- a/regexp.go
+++ b/regexp.go
@@ -324,16 +324,18 @@ type routeRegexpGroup struct {
 func (v routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route) {
 	// Store host variables.
 	if v.host != nil {
-		host := getHost(req)
-		if v.host.wildcardHostPort {
-			// Don't be strict on the port match
-			if i := strings.Index(host, ":"); i != -1 {
-				host = host[:i]
+		if len(v.host.varsN) > 0 {
+			host := getHost(req)
+			if v.host.wildcardHostPort {
+				// Don't be strict on the port match
+				if i := strings.Index(host, ":"); i != -1 {
+					host = host[:i]
+				}
+			}
+			matches := v.host.regexp.FindStringSubmatchIndex(host)
+			if len(matches) > 0 {
+				m.Vars = extractVars(host, matches, v.host.varsN, m.Vars)
 			}
-		}
-		matches := v.host.regexp.FindStringSubmatchIndex(host)
-		if len(matches) > 0 {
-			extractVars(host, matches, v.host.varsN, m.Vars)
 		}
 	}
 	path := req.URL.Path
@@ -342,31 +344,36 @@ func (v routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route) {
 	}
 	// Store path variables.
 	if v.path != nil {
-		matches := v.path.regexp.FindStringSubmatchIndex(path)
-		if len(matches) > 0 {
-			extractVars(path, matches, v.path.varsN, m.Vars)
-			// Check if we should redirect.
-			if v.path.options.strictSlash {
-				p1 := strings.HasSuffix(path, "/")
-				p2 := strings.HasSuffix(v.path.template, "/")
-				if p1 != p2 {
-					u, _ := url.Parse(req.URL.String())
-					if p1 {
-						u.Path = u.Path[:len(u.Path)-1]
-					} else {
-						u.Path += "/"
-					}
-					m.Handler = http.RedirectHandler(u.String(), http.StatusMovedPermanently)
+		if len(v.path.varsN) > 0 {
+			matches := v.path.regexp.FindStringSubmatchIndex(path)
+			if len(matches) > 0 {
+				m.Vars = extractVars(path, matches, v.path.varsN, m.Vars)
+			}
+		}
+		// Check if we should redirect.
+		if v.path.options.strictSlash {
+			p1 := strings.HasSuffix(path, "/")
+			p2 := strings.HasSuffix(v.path.template, "/")
+			if p1 != p2 {
+				p := req.URL.Path
+				if p1 {
+					p = p[:len(p)-1]
+				} else {
+					p += "/"
 				}
+				u := replaceURLPath(req.URL, p)
+				m.Handler = http.RedirectHandler(u, http.StatusMovedPermanently)
 			}
 		}
 	}
 	// Store query string variables.
 	for _, q := range v.queries {
-		queryURL := q.getURLQuery(req)
-		matches := q.regexp.FindStringSubmatchIndex(queryURL)
-		if len(matches) > 0 {
-			extractVars(queryURL, matches, q.varsN, m.Vars)
+		if len(q.varsN) > 0 {
+			queryURL := q.getURLQuery(req)
+			matches := q.regexp.FindStringSubmatchIndex(queryURL)
+			if len(matches) > 0 {
+				m.Vars = extractVars(queryURL, matches, q.varsN, m.Vars)
+			}
 		}
 	}
 }
@@ -381,8 +388,12 @@ func getHost(r *http.Request) string {
 	return r.Host
 }
 
-func extractVars(input string, matches []int, names []string, output map[string]string) {
+func extractVars(input string, matches []int, names []string, output map[string]string) map[string]string {
 	for i, name := range names {
+		if output == nil {
+			output = make(map[string]string, len(names))
+		}
 		output[name] = input[matches[2*i+2]:matches[2*i+3]]
 	}
+	return output
 }
diff --git a/route.go b/route.go
index e8f11df..8a9e754 100644
--- a/route.go
+++ b/route.go
@@ -98,9 +98,6 @@ func (r *Route) Match(req *http.Request, match *RouteMatch) bool {
 	if match.Handler == nil {
 		match.Handler = r.handler
 	}
-	if match.Vars == nil {
-		match.Vars = make(map[string]string)
-	}
 
 	// Set variables.
 	r.regexp.setMatch(req, match, r)


Test patch:
diff --git a/bench_test.go b/bench_test.go
index 522156d..c1f86ca 100644
--- a/bench_test.go
+++ b/bench_test.go
@@ -21,6 +21,38 @@ func BenchmarkMux(b *testing.B) {
 	}
 }
 
+func BenchmarkMuxSimple(b *testing.B) {
+	router := new(Router)
+	handler := func(w http.ResponseWriter, r *http.Request) {}
+	router.HandleFunc("/status", handler)
+
+	testCases := []struct {
+		name                 string
+		omitRouteFromContext bool
+	}{
+		{
+			name:                 "default",
+			omitRouteFromContext: false,
+		},
+		{
+			name:                 "omit route from ctx",
+			omitRouteFromContext: true,
+		},
+	}
+	for _, tc := range testCases {
+		b.Run(tc.name, func(b *testing.B) {
+			router.OmitRouteFromContext(tc.omitRouteFromContext)
+
+			request, _ := http.NewRequest("GET", "/status", nil)
+			b.ReportAllocs()
+			b.ResetTimer()
+			for i := 0; i < b.N; i++ {
+				router.ServeHTTP(nil, request)
+			}
+		})
+	}
+}
+
 func BenchmarkMuxAlternativeInRegexp(b *testing.B) {
 	router := new(Router)
 	handler := func(w http.ResponseWriter, r *http.Request) {}
diff --git a/mux_test.go b/mux_test.go
index 5898688..cb3bbe0 100644
--- a/mux_test.go
+++ b/mux_test.go
@@ -59,7 +59,7 @@ func TestHost(t *testing.T) {
 			title:       "Host route match",
 			route:       new(Route).Host("aaa.bbb.ccc"),
 			request:     newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
-			vars:        map[string]string{},
+			vars:        nil,
 			host:        "aaa.bbb.ccc",
 			path:        "",
 			shouldMatch: true,
@@ -68,7 +68,7 @@ func TestHost(t *testing.T) {
 			title:       "Host route, wrong host in request URL",
 			route:       new(Route).Host("aaa.bbb.ccc"),
 			request:     newRequest("GET", "http://aaa.222.ccc/111/222/333"),
-			vars:        map[string]string{},
+			vars:        nil,
 			host:        "aaa.bbb.ccc",
 			path:        "",
 			shouldMatch: false,
@@ -77,7 +77,7 @@ func TestHost(t *testing.T) {
 			title:       "Host route with port, match",
 			route:       new(Route).Host("aaa.bbb.ccc:1234"),
 			request:     newRequest("GET", "http://aaa.bbb.ccc:1234/111/222/333"),
-			vars:        map[string]string{},
+			vars:        nil,
 			host:        "aaa.bbb.ccc:1234",
 			path:        "",
 			shouldMatch: true,
@@ -86,7 +86,7 @@ func TestHost(t *testing.T) {
 			title:       "Host route with port, wrong port in request URL",
 			route:       new(Route).Host("aaa.bbb.ccc:1234"),
 			request:     newRequest("GET", "http://aaa.bbb.ccc:9999/111/222/333"),
-			vars:        map[string]string{},
+			vars:        nil,
 			host:        "aaa.bbb.ccc:1234",
 			path:        "",
 			shouldMatch: false,
@@ -95,7 +95,7 @@ func TestHost(t *testing.T) {
 			title:       "Host route, match with host in request header",
 			route:       new(Route).Host("aaa.bbb.ccc"),
 			request:     newRequestHost("GET", "/111/222/333", "aaa.bbb.ccc"),
-			vars:        map[string]string{},
+			vars:        nil,
 			host:        "aaa.bbb.ccc",
 			path:        "",
 			shouldMatch: true,
@@ -104,7 +104,7 @@ func TestHost(t *testing.T) {
 			title:       "Host route, wrong host in request header",
 			route:       new(Route).Host("aaa.bbb.ccc"),
 			request:     newRequestHost("GET", "/111/222/333", "aaa.222.ccc"),
-			vars:        map[string]string{},
+			vars:        nil,
 			host:        "aaa.bbb.ccc",
 			path:        "",
 			shouldMatch: false,
@@ -113,7 +113,7 @@ func TestHost(t *testing.T) {
 			title:       "Host route with port, match with request header",
 			route:       new(Route).Host("aaa.bbb.ccc:1234"),
 			request:     newRequestHost("GET", "/111/222/333", "aaa.bbb.ccc:1234"),
-			vars:        map[string]string{},
+			vars:        nil,
 			host:        "aaa.bbb.ccc:1234",
 			path:        "",
 			shouldMatch: true,
@@ -122,7 +122,7 @@ func TestHost(t *testing.T) {
 			title:       "Host route with port, wrong host in request header",
 			route:       new(Route).Host("aaa.bbb.ccc:1234"),
 			request:     newRequestHost("GET", "/111/222/333", "aaa.bbb.ccc:9999"),
-			vars:        map[string]string{},
+			vars:        nil,
 			host:        "aaa.bbb.ccc:1234",
 			path:        "",
 			shouldMatch: false,
@@ -232,7 +232,7 @@ func TestPath(t *testing.T) {
 			title:       "Path route, match",
 			route:       new(Route).Path("/111/222/333"),
 			request:     newRequest("GET", "http://localhost/111/222/333"),
-			vars:        map[string]string{},
+			vars:        nil,
 			host:        "",
 			path:        "/111/222/333",
 			shouldMatch: true,
@@ -241,7 +241,7 @@ func TestPath(t *testing.T) {
 			title:       "Path route, match with trailing slash in request and path",
 			route:       new(Route).Path("/111/"),
 			request:     newRequest("GET", "http://localhost/111/"),
-			vars:        map[string]string{},
+			vars:        nil,
 			host:        "",
 			path:        "/111/",
 			shouldMatch: true,
@@ -250,7 +250,7 @@ func TestPath(t *testing.T) {
 			title:        "Path route, do not match with trailing slash in path",
 			route:        new(Route).Path("/111/"),
 			request:      newRequest("GET", "http://localhost/111"),
-			vars:         map[string]string{},
+			vars:         nil,
 			host:         "",
 			path:         "/111",
 			pathTemplate: `/111/`,
@@ -261,7 +261,7 @@ func TestPath(t *testing.T) {
 			title:        "Path route, do not match with trailing slash in request",
 			route:        new(Route).Path("/111"),
 			request:      newRequest("GET", "http://localhost/111/"),
-			vars:         map[string]string{},
+			vars:         nil,
 			host:         "",
 			path:         "/111/",
 			pathTemplate: `/111`,
@@ -271,7 +271,7 @@ func TestPath(t *testing.T) {
 			title:        "Path route, match root with no host",
 			route:        new(Route).Path("/"),
 			request:      newRequest("GET", "/"),
-			vars:         map[string]string{},
+			vars:         nil,
 			host:         "",
 			path:         "/",
 			pathTemplate: `/`,
@@ -286,7 +286,7 @@ func TestPath(t *testing.T) {
 				r.RequestURI = "/"
 				return r
 			}(),
-			vars:         map[string]string{},
+			vars:         nil,
 			host:         "",
 			path:         "/",
 			pathTemplate: `/`,
@@ -296,7 +296,7 @@ func TestPath(t *testing.T) {
 			title:       "Path route, wrong path in request in request URL",
 			route:       new(Route).Path("/111/222/333"),
 			request:     newRequest("GET", "http://localhost/1/2/3"),
-			vars:        map[string]string{},
+			vars:        nil,
 			host:        "",
 			path:        "/111/222/333",
 			shouldMatch: false,
@@ -468,7 +468,7 @@ func TestPathPrefix(t *testing.T) {
 			title:       "PathPrefix route, match",
 			route:       new(Route).PathPrefix("/111"),
 			request:     newRequest("GET", "http://localhost/111/222/333"),
-			vars:        map[string]string{},
+			vars:        nil,
 			host:        "",
 			path:        "/111",
 			shouldMatch: true,
@@ -477,7 +477,7 @@ func TestPathPrefix(t *testing.T) {
 			title:       "PathPrefix route, match substring",
 			route:       new(Route).PathPrefix("/1"),
 			request:     newRequest("GET", "http://localhost/111/222/333"),
-			vars:        map[string]string{},
+			vars:        nil,
 			host:        "",
 			path:        "/1",
 			shouldMatch: true,
@@ -486,7 +486,7 @@ func TestPathPrefix(t *testing.T) {
 			title:       "PathPrefix route, URL prefix in request does not match",
 			route:       new(Route).PathPrefix("/111"),
 			request:     newRequest("GET", "http://localhost/1/2/3"),
-			vars:        map[string]string{},
+			vars:        nil,
 			host:        "",
 			path:        "/111",
 			shouldMatch: false,
@@ -548,7 +548,7 @@ func TestSchemeHostPath(t *testing.T) {
 			title:        "Host and Path route, match",
 			route:        new(Route).Host("aaa.bbb.ccc").Path("/111/222/333"),
 			request:      newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
-			vars:         map[string]string{},
+			vars:         nil,
 			scheme:       "http",
 			host:         "aaa.bbb.ccc",
 			path:         "/111/222/333",
@@ -560,7 +560,7 @@ func TestSchemeHostPath(t *testing.T) {
 			title:        "Scheme, Host, and Path route, match",
 			route:        new(Route).Schemes("https").Host("aaa.bbb.ccc").Path("/111/222/333"),
 			request:      newRequest("GET", "https://aaa.bbb.ccc/111/222/333"),
-			vars:         map[string]string{},
+			vars:         nil,
 			scheme:       "https",
 			host:         "aaa.bbb.ccc",
 			path:         "/111/222/333",
@@ -572,7 +572,7 @@ func TestSchemeHostPath(t *testing.T) {
 			title:        "Host and Path route, wrong host in request URL",
 			route:        new(Route).Host("aaa.bbb.ccc").Path("/111/222/333"),
 			request:      newRequest("GET", "http://aaa.222.ccc/111/222/333"),
-			vars:         map[string]string{},
+			vars:         nil,
 			scheme:       "http",
 			host:         "aaa.bbb.ccc",
 			path:         "/111/222/333",
@@ -669,7 +669,7 @@ func TestHeaders(t *testing.T) {
 			title:       "Headers route, match",
 			route:       new(Route).Headers("foo", "bar", "baz", "ding"),
 			request:     newRequestHeaders("GET", "http://localhost", map[string]string{"foo": "bar", "baz": "ding"}),
-			vars:        map[string]string{},
+			vars:        nil,
 			host:        "",
 			path:        "",
 			shouldMatch: true,
@@ -678,7 +678,7 @@ func TestHeaders(t *testing.T) {
 			title:       "Headers route, bad header values",
 			route:       new(Route).Headers("foo", "bar", "baz", "ding"),
 			request:     newRequestHeaders("GET", "http://localhost", map[string]string{"foo": "bar", "baz": "dong"}),
-			vars:        map[string]string{},
+			vars:        nil,
 			host:        "",
 			path:        "",
 			shouldMatch: false,
@@ -687,7 +687,7 @@ func TestHeaders(t *testing.T) {
 			title:       "Headers route, regex header values to match",
 			route:       new(Route).HeadersRegexp("foo", "ba[zr]"),
 			request:     newRequestHeaders("GET", "http://localhost", map[string]string{"foo": "baw"}),
-			vars:        map[string]string{},
+			vars:        nil,
 			host:        "",
 			path:        "",
 			shouldMatch: false,
@@ -696,7 +696,7 @@ func TestHeaders(t *testing.T) {
 			title:       "Headers route, regex header values to match",
 			route:       new(Route).HeadersRegexp("foo", "ba[zr]"),
 			request:     newRequestHeaders("GET", "http://localhost", map[string]string{"foo": "baz"}),
-			vars:        map[string]string{},
+			vars:        nil,
 			host:        "",
 			path:        "",
 			shouldMatch: true,
@@ -717,7 +717,7 @@ func TestMethods(t *testing.T) {
 			title:       "Methods route, match GET",
 			route:       new(Route).Methods("GET", "POST"),
 			request:     newRequest("GET", "http://localhost"),
-			vars:        map[string]string{},
+			vars:        nil,
 			host:        "",
 			path:        "",
 			methods:     []string{"GET", "POST"},
@@ -727,7 +727,7 @@ func TestMethods(t *testing.T) {
 			title:       "Methods route, match POST",
 			route:       new(Route).Methods("GET", "POST"),
 			request:     newRequest("POST", "http://localhost"),
-			vars:        map[string]string{},
+			vars:        nil,
 			host:        "",
 			path:        "",
 			methods:     []string{"GET", "POST"},
@@ -737,7 +737,7 @@ func TestMethods(t *testing.T) {
 			title:       "Methods route, bad method",
 			route:       new(Route).Methods("GET", "POST"),
 			request:     newRequest("PUT", "http://localhost"),
-			vars:        map[string]string{},
+			vars:        nil,
 			host:        "",
 			path:        "",
 			methods:     []string{"GET", "POST"},
@@ -747,7 +747,7 @@ func TestMethods(t *testing.T) {
 			title:       "Route without methods",
 			route:       new(Route),
 			request:     newRequest("PUT", "http://localhost"),
-			vars:        map[string]string{},
+			vars:        nil,
 			host:        "",
 			path:        "",
 			methods:     []string{},
@@ -770,7 +770,7 @@ func TestQueries(t *testing.T) {
 			title:           "Queries route, match",
 			route:           new(Route).Queries("foo", "bar", "baz", "ding"),
 			request:         newRequest("GET", "http://localhost?foo=bar&baz=ding"),
-			vars:            map[string]string{},
+			vars:            nil,
 			host:            "",
 			path:            "",
 			query:           "foo=bar&baz=ding",
@@ -782,7 +782,7 @@ func TestQueries(t *testing.T) {
 			title:           "Queries route, match with a query string",
 			route:           new(Route).Host("www.example.com").Path("/api").Queries("foo", "bar", "baz", "ding"),
 			request:         newRequest("GET", "http://www.example.com/api?foo=bar&baz=ding"),
-			vars:            map[string]string{},
+			vars:            nil,
 			host:            "",
 			path:            "",
 			query:           "foo=bar&baz=ding",
@@ -796,7 +796,7 @@ func TestQueries(t *testing.T) {
 			title:           "Queries route, match with a query string out of order",
 			route:           new(Route).Host("www.example.com").Path("/api").Queries("foo", "bar", "baz", "ding"),
 			request:         newRequest("GET", "http://www.example.com/api?baz=ding&foo=bar"),
-			vars:            map[string]string{},
+			vars:            nil,
 			host:            "",
 			path:            "",
 			query:           "foo=bar&baz=ding",
@@ -810,7 +810,7 @@ func TestQueries(t *testing.T) {
 			title:           "Queries route, bad query",
 			route:           new(Route).Queries("foo", "bar", "baz", "ding"),
 			request:         newRequest("GET", "http://localhost?foo=bar&baz=dong"),
-			vars:            map[string]string{},
+			vars:            nil,
 			host:            "",
 			path:            "",
 			queriesTemplate: "foo=bar,baz=ding",
@@ -857,7 +857,7 @@ func TestQueries(t *testing.T) {
 			title:           "Queries route with regexp pattern, regexp does not match",
 			route:           new(Route).Queries("foo", "{v1:[0-9]+}"),
 			request:         newRequest("GET", "http://localhost?foo=a"),
-			vars:            map[string]string{},
+			vars:            nil,
 			host:            "",
 			path:            "",
 			queriesTemplate: "foo={v1:[0-9]+}",
@@ -892,7 +892,7 @@ func TestQueries(t *testing.T) {
 			title:           "Queries route with regexp pattern with quantifier, regexp does not match",
 			route:           new(Route).Queries("foo", "{v1:[0-9]{1}}"),
 			request:         newRequest("GET", "http://localhost?foo=12"),
-			vars:            map[string]string{},
+			vars:            nil,
 			host:            "",
 			path:            "",
 			queriesTemplate: "foo={v1:[0-9]{1}}",
@@ -915,7 +915,7 @@ func TestQueries(t *testing.T) {
 			title:           "Queries route with regexp pattern with quantifier, additional variable in query string, regexp does not match",
 			route:           new(Route).Queries("foo", "{v1:[0-9]{1}}"),
 			request:         newRequest("GET", "http://localhost?foo=12"),
-			vars:            map[string]string{},
+			vars:            nil,
 			host:            "",
 			path:            "",
 			queriesTemplate: "foo={v1:[0-9]{1}}",
@@ -974,7 +974,7 @@ func TestQueries(t *testing.T) {
 			title:           "Queries route with empty value, should match",
 			route:           new(Route).Queries("foo", ""),
 			request:         newRequest("GET", "http://localhost?foo=bar"),
-			vars:            map[string]string{},
+			vars:            nil,
 			host:            "",
 			path:            "",
 			query:           "foo=",
@@ -986,7 +986,7 @@ func TestQueries(t *testing.T) {
 			title:           "Queries route with empty value and no parameter in request, should not match",
 			route:           new(Route).Queries("foo", ""),
 			request:         newRequest("GET", "http://localhost"),
-			vars:            map[string]string{},
+			vars:            nil,
 			host:            "",
 			path:            "",
 			queriesTemplate: "foo=",
@@ -997,7 +997,7 @@ func TestQueries(t *testing.T) {
 			title:           "Queries route with empty value and empty parameter in request, should match",
 			route:           new(Route).Queries("foo", ""),
 			request:         newRequest("GET", "http://localhost?foo="),
-			vars:            map[string]string{},
+			vars:            nil,
 			host:            "",
 			path:            "",
 			query:           "foo=",
@@ -1009,7 +1009,7 @@ func TestQueries(t *testing.T) {
 			title:           "Queries route with overlapping value, should not match",
 			route:           new(Route).Queries("foo", "bar"),
 			request:         newRequest("GET", "http://localhost?foo=barfoo"),
-			vars:            map[string]string{},
+			vars:            nil,
 			host:            "",
 			path:            "",
 			queriesTemplate: "foo=bar",
@@ -1020,7 +1020,7 @@ func TestQueries(t *testing.T) {
 			title:           "Queries route with no parameter in request, should not match",
 			route:           new(Route).Queries("foo", "{bar}"),
 			request:         newRequest("GET", "http://localhost"),
-			vars:            map[string]string{},
+			vars:            nil,
 			host:            "",
 			path:            "",
 			queriesTemplate: "foo={bar}",
@@ -1043,7 +1043,7 @@ func TestQueries(t *testing.T) {
 			title:           "Queries route, bad submatch",
 			route:           new(Route).Queries("foo", "bar", "baz", "ding"),
 			request:         newRequest("GET", "http://localhost?fffoo=bar&baz=dingggg"),
-			vars:            map[string]string{},
+			vars:            nil,
 			host:            "",
 			path:            "",
 			queriesTemplate: "foo=bar,baz=ding",
@@ -1136,7 +1136,7 @@ func TestMatcherFunc(t *testing.T) {
 			title:       "MatchFunc route, match",
 			route:       new(Route).MatcherFunc(m),
 			request:     newRequest("GET", "http://aaa.bbb.ccc"),
-			vars:        map[string]string{},
+			vars:        nil,
 			host:        "",
 			path:        "",
 			shouldMatch: true,
@@ -1145,7 +1145,7 @@ func TestMatcherFunc(t *testing.T) {
 			title:       "MatchFunc route, non-match",
 			route:       new(Route).MatcherFunc(m),
 			request:     newRequest("GET", "http://aaa.222.ccc"),
-			vars:        map[string]string{},
+			vars:        nil,
 			host:        "",
 			path:        "",
 			shouldMatch: false,
@@ -1246,7 +1246,7 @@ func TestSubRouter(t *testing.T) {
 		{
 			route:        subrouter3.Path("/"),
 			request:      newRequest("GET", "http://localhost/foo/"),
-			vars:         map[string]string{},
+			vars:         nil,
 			host:         "",
 			path:         "/foo/",
 			pathTemplate: `/foo/`,
@@ -1255,7 +1255,7 @@ func TestSubRouter(t *testing.T) {
 		{
 			route:        subrouter3.Path(""),
 			request:      newRequest("GET", "http://localhost/foo"),
-			vars:         map[string]string{},
+			vars:         nil,
 			host:         "",
 			path:         "/foo",
 			pathTemplate: `/foo`,
@@ -1265,7 +1265,7 @@ func TestSubRouter(t *testing.T) {
 		{
 			route:        subrouter4.Path("/"),
 			request:      newRequest("GET", "http://localhost/foo/bar/"),
-			vars:         map[string]string{},
+			vars:         nil,
 			host:         "",
 			path:         "/foo/bar/",
 			pathTemplate: `/foo/bar/`,
@@ -1274,7 +1274,7 @@ func TestSubRouter(t *testing.T) {
 		{
 			route:        subrouter4.Path(""),
 			request:      newRequest("GET", "http://localhost/foo/bar"),
-			vars:         map[string]string{},
+			vars:         nil,
 			host:         "",
 			path:         "/foo/bar",
 			pathTemplate: `/foo/bar`,
@@ -1302,7 +1302,7 @@ func TestSubRouter(t *testing.T) {
 			title:        "Mismatch method specified on parent route",
 			route:        new(Route).Methods("POST").PathPrefix("/foo").Subrouter().Path("/"),
 			request:      newRequest("GET", "http://localhost/foo/"),
-			vars:         map[string]string{},
+			vars:         nil,
 			host:         "",
 			path:         "/foo/",
 			pathTemplate: `/foo/`,
@@ -1312,7 +1312,7 @@ func TestSubRouter(t *testing.T) {
 			title:        "Match method specified on parent route",
 			route:        new(Route).Methods("POST").PathPrefix("/foo").Subrouter().Path("/"),
 			request:      newRequest("POST", "http://localhost/foo/"),
-			vars:         map[string]string{},
+			vars:         nil,
 			host:         "",
 			path:         "/foo/",
 			pathTemplate: `/foo/`,
@@ -1322,7 +1322,7 @@ func TestSubRouter(t *testing.T) {
 			title:        "Mismatch scheme specified on parent route",
 			route:        new(Route).Schemes("https").Subrouter().PathPrefix("/"),
 			request:      newRequest("GET", "http://localhost/"),
-			vars:         map[string]string{},
+			vars:         nil,
 			host:         "",
 			path:         "/",
 			pathTemplate: `/`,
@@ -1332,7 +1332,7 @@ func TestSubRouter(t *testing.T) {
 			title:        "Match scheme specified on parent route",
 			route:        new(Route).Schemes("http").Subrouter().PathPrefix("/"),
 			request:      newRequest("GET", "http://localhost/"),
-			vars:         map[string]string{},
+			vars:         nil,
 			host:         "",
 			path:         "/",
 			pathTemplate: `/`,
@@ -1342,7 +1342,7 @@ func TestSubRouter(t *testing.T) {
 			title:        "No match header specified on parent route",
 			route:        new(Route).Headers("X-Forwarded-Proto", "https").Subrouter().PathPrefix("/"),
 			request:      newRequest("GET", "http://localhost/"),
-			vars:         map[string]string{},
+			vars:         nil,
 			host:         "",
 			path:         "/",
 			pathTemplate: `/`,
@@ -1352,7 +1352,7 @@ func TestSubRouter(t *testing.T) {
 			title:        "Header mismatch value specified on parent route",
 			route:        new(Route).Headers("X-Forwarded-Proto", "https").Subrouter().PathPrefix("/"),
 			request:      newRequestWithHeaders("GET", "http://localhost/", "X-Forwarded-Proto", "http"),
-			vars:         map[string]string{},
+			vars:         nil,
 			host:         "",
 			path:         "/",
 			pathTemplate: `/`,
@@ -1362,7 +1362,7 @@ func TestSubRouter(t *testing.T) {
 			title:        "Header match value specified on parent route",
 			route:        new(Route).Headers("X-Forwarded-Proto", "https").Subrouter().PathPrefix("/"),
 			request:      newRequestWithHeaders("GET", "http://localhost/", "X-Forwarded-Proto", "https"),
-			vars:         map[string]string{},
+			vars:         nil,
 			host:         "",
 			path:         "/",
 			pathTemplate: `/`,
@@ -1372,7 +1372,7 @@ func TestSubRouter(t *testing.T) {
 			title:        "Query specified on parent route not present",
 			route:        new(Route).Headers("key", "foobar").Subrouter().PathPrefix("/"),
 			request:      newRequest("GET", "http://localhost/"),
-			vars:         map[string]string{},
+			vars:         nil,
 			host:         "",
 			path:         "/",
 			pathTemplate: `/`,
@@ -1382,7 +1382,7 @@ func TestSubRouter(t *testing.T) {
 			title:        "Query mismatch value specified on parent route",
 			route:        new(Route).Queries("key", "foobar").Subrouter().PathPrefix("/"),
 			request:      newRequest("GET", "http://localhost/?key=notfoobar"),
-			vars:         map[string]string{},
+			vars:         nil,
 			host:         "",
 			path:         "/",
 			pathTemplate: `/`,
@@ -1392,7 +1392,7 @@ func TestSubRouter(t *testing.T) {
 			title:        "Query match value specified on subroute",
 			route:        new(Route).Queries("key", "foobar").Subrouter().PathPrefix("/"),
 			request:      newRequest("GET", "http://localhost/?key=foobar"),
-			vars:         map[string]string{},
+			vars:         nil,
 			host:         "",
 			path:         "/",
 			pathTemplate: `/`,
@@ -1473,7 +1473,7 @@ func TestStrictSlash(t *testing.T) {
 			title:          "Redirect path without slash",
 			route:          r.NewRoute().Path("/111/"),
 			request:        newRequest("GET", "http://localhost/111"),
-			vars:           map[string]string{},
+			vars:           nil,
 			host:           "",
 			path:           "/111/",
 			shouldMatch:    true,
@@ -1483,7 +1483,7 @@ func TestStrictSlash(t *testing.T) {
 			title:          "Do not redirect path with slash",
 			route:          r.NewRoute().Path("/111/"),
 			request:        newRequest("GET", "http://localhost/111/"),
-			vars:           map[string]string{},
+			vars:           nil,
 			host:           "",
 			path:           "/111/",
 			shouldMatch:    true,
@@ -1493,7 +1493,7 @@ func TestStrictSlash(t *testing.T) {
 			title:          "Redirect path with slash",
 			route:          r.NewRoute().Path("/111"),
 			request:        newRequest("GET", "http://localhost/111/"),
-			vars:           map[string]string{},
+			vars:           nil,
 			host:           "",
 			path:           "/111",
 			shouldMatch:    true,
@@ -1503,7 +1503,7 @@ func TestStrictSlash(t *testing.T) {
 			title:          "Do not redirect path without slash",
 			route:          r.NewRoute().Path("/111"),
 			request:        newRequest("GET", "http://localhost/111"),
-			vars:           map[string]string{},
+			vars:           nil,
 			host:           "",
 			path:           "/111",
 			shouldMatch:    true,
@@ -1513,7 +1513,7 @@ func TestStrictSlash(t *testing.T) {
 			title:          "Propagate StrictSlash to subrouters",
 			route:          r.NewRoute().PathPrefix("/static/").Subrouter().Path("/images/"),
 			request:        newRequest("GET", "http://localhost/static/images"),
-			vars:           map[string]string{},
+			vars:           nil,
 			host:           "",
 			path:           "/static/images/",
 			shouldMatch:    true,
@@ -1523,7 +1523,7 @@ func TestStrictSlash(t *testing.T) {
 			title:          "Ignore StrictSlash for path prefix",
 			route:          r.NewRoute().PathPrefix("/static/"),
 			request:        newRequest("GET", "http://localhost/static/logo.png"),
-			vars:           map[string]string{},
+			vars:           nil,
 			host:           "",
 			path:           "/static/",
 			shouldMatch:    true,
@@ -2913,6 +2913,128 @@ func TestGetVarNames(t *testing.T) {
 	}
 }
 
+func getPopulateContextTestCases() []struct {
+	name                 string
+	path                 string
+	omitRouteFromContext bool
+	wantVar              string
+	wantStaticRoute      bool
+	wantDynamicRoute     bool
+} {
+	return []struct {
+		name                 string
+		path                 string
+		omitRouteFromContext bool
+		wantVar              string
+		wantStaticRoute      bool
+		wantDynamicRoute     bool
+	}{
+		{
+			name:            "no populated vars",
+			path:            "/static",
+			wantVar:         "",
+			wantStaticRoute: true,
+		},
+		{
+			name:             "empty var",
+			path:             "/dynamic/",
+			wantVar:          "",
+			wantDynamicRoute: true,
+		},
+		{
+			name:             "populated vars",
+			path:             "/dynamic/foo",
+			wantVar:          "foo",
+			wantDynamicRoute: true,
+		},
+		{
+			name:                 "omit route /static",
+			path:                 "/static",
+			omitRouteFromContext: true,
+			wantVar:              "",
+			wantStaticRoute:      false,
+		},
+		{
+			name:                 "omit route /dynamic",
+			path:                 "/dynamic/",
+			omitRouteFromContext: true,
+			wantVar:              "",
+			wantDynamicRoute:     false,
+		},
+	}
+}
+
+func TestPopulateContext(t *testing.T) {
+	testCases := getPopulateContextTestCases()
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			matched := false
+			r := NewRouter()
+			r.OmitRouteFromContext(tc.omitRouteFromContext)
+			var static *Route
+			var dynamic *Route
+			fn := func(w http.ResponseWriter, r *http.Request) {
+				matched = true
+				if got := Vars(r)["x"]; got != tc.wantVar {
+					t.Fatalf("wantVar=%q, got=%q", tc.wantVar, got)
+				}
+				switch {
+				case tc.wantDynamicRoute:
+					r2 := CurrentRoute(r)
+					if r2 != dynamic || r2.GetName() != "dynamic" {
+						t.Fatalf("expected dynmic route in ctx, got %v", r2)
+					}
+				case tc.wantStaticRoute:
+					r2 := CurrentRoute(r)
+					if r2 != static || r2.GetName() != "static" {
+						t.Fatalf("expected static route in ctx, got %v", r2)
+					}
+				default:
+					if r2 := CurrentRoute(r); r2 != nil {
+						t.Fatalf("expected no route in ctx, got %v", r2)
+					}
+				}
+				w.WriteHeader(http.StatusNoContent)
+			}
+			static = r.Name("static").Path("/static").HandlerFunc(fn)
+			dynamic = r.Name("dynamic").Path("/dynamic/{x:.*}").HandlerFunc(fn)
+			req := newRequest(http.MethodGet, "http://localhost"+tc.path)
+			rec := NewRecorder()
+			r.ServeHTTP(rec, req)
+			if !matched {
+				t.Fatal("Expected route to match")
+			}
+		})
+	}
+}
+
+func BenchmarkPopulateContext(b *testing.B) {
+	testCases := getPopulateContextTestCases()
+	for _, tc := range testCases {
+		b.Run(tc.name, func(b *testing.B) {
+			matched := false
+			r := NewRouter()
+			r.OmitRouteFromContext(tc.omitRouteFromContext)
+			fn := func(w http.ResponseWriter, r *http.Request) {
+				matched = true
+				w.WriteHeader(http.StatusNoContent)
+			}
+			r.Name("static").Path("/static").HandlerFunc(fn)
+			r.Name("dynamic").Path("/dynamic/{x:.*}").HandlerFunc(fn)
+			req := newRequest(http.MethodGet, "http://localhost"+tc.path)
+			rec := NewRecorder()
+			b.ReportAllocs()
+			b.ResetTimer()
+			for i := 0; i < b.N; i++ {
+				r.ServeHTTP(rec, req)
+			}
+			if !matched {
+				b.Fatal("Expected route to match")
+			}
+		})
+	}
+}
+
 // mapToPairs converts a string map to a slice of string pairs
 func mapToPairs(m map[string]string) []string {
 	var i int