Add benchmarks that include some HTTP method matching.
Clean up names and name like-kinded benchmarks alphabetically.
Matcher*Benchmark -> E2E including HTTP method selection
MatcherSelectCandidates*Benchmark -> Focused on just URL path processing
* Introduce RoutePattern
Introduces RoutePattern - a new parser and representation for routing
templates, defaults, and constraints.
This is a new representation for all of the 'inputs' to routing that is
immutable and captures 'out of line' information for defaults and
constraints.
This will allow us to unify the handling of constraints and values from
attribute style routes and conventional style routes.
* Add benchmarks for RVD
There are the scenarios that are critical for URL matching performance.
* Reimplement RouteValueDictionary
Improves the scenarios with benchmarks by about 30%
* Fix benchmark
* PR feedback
* More feedback and tests
Here's a code dump of the parts of the Dispatcher prototype codebase
that are needed to get us off the ground.
This first cut attempts to use part of routing where possible, and not
all of those changes will be long-lasting.
I'll leave comments through thoughout the PR for education.
This change adds the Template as a top level abstraction. URL templating
is now a two-stage process.
First you use a 'key' to look up a Template, then you use the Template
to create the URL.
This change also has some cleanup of the way RoutePatternBinder gets
instantiated. I added a factory service so that most of the complex
things can be made internal to Dispatcher. Now it's much easier to
constuct and use. These impacts some pubternal APIs that we already
broke, but makes them actually nice :)
Also cleaned up some tests and fixed one that was broken and not
running.
-Renamed RouteTemplate -> RoutePattern
-Made immutable
-Added Builder
-Lots of fixes to parser to support new design
There are a few small issues logged for follow-up but this is mostly in
the place I want it design-wise.
This is a very small micro-optimization: When LowercaseUrls and/or AppendTrailingSlash
options are enabled, on every call to RouteCollection.NormalizeVirtualPath a new
char[] { '?', '#' } is being allocated.
This makes endpoints and addresses easier to work with by dropping the
'metadata first' approach for the the things that are really at the core
of the dispatcher.
This refactor introduces two major changes
1. Now creating the 'handler' delegate happens inside the endpoint
middleware. This allows you to short circuit even harder, AND to create
endpoint funcs that capture and use 'next' to rejoin the middleware
pipeline.
2. Relayered the implementation to have routing plug into the dispatcher.
It wasn't immediately apparent to me that this was the right thing to do,
but I think we will need to do things this way to deliver the kind of
back-compat experience we need to do.
The idea that I have is that 'attribute routing' will be the 'default'
entry in the dispatcher. Adding additional conventional routes or other
IRouter-based extensibility will be possible through adapters - but the
default experience will be to add items to the 'attribute route'.
So. We will need to port the attribute routing infrastructure to the
dispatcher library.
We may also need to make RVD into a subclass of something in the
dispatcher assembly.
This fixes the case described in the comments in TemplateBinder.
This case is much more common for pages which is why we're only seeing it
now. We've had this issue for all of 1.0.0 in both conventional and
attribute routing.
This change avoid calling List.Clear() and new List(IEnumerable<T>) which
both end up calling into native methods via the Array static class. These
methods are designed to be performant for large collections, and for our
needs this collection has at most 1-4 items. This is worth 2-3% in
techempower plaintext.
This changes TemplateMatcher to mutate RouteData.Values directly instead
of creating a new dictionary and then merging in values. This is one the
biggest single costs in routing in terms of both allocations and execution
time.
So Match now becomes TryMatch. This will dirty the state of the RVD, so
the caller needs to snapshot it before calling into it (handled
inside the TreeRouter or RouteCollection).
Some subtle changes were needed to how/when values are added to be
compatible with the existing tests. The general idea is that we add null
values for non-parameter defaults or catchalls, but only if they don't
trounce an existing value. This logic used to live in MergeValues but now
it's in TryMatch since TryMatch might be working from existing data.
Also fixed the .sln to avoid building a package that we use as shared
source.
- aspnet/Coherence-Signed#187
- remove `<RootNamespace>` settings but maintain other unique aspects e.g. `<DnxInvisibleContent ... />`
- in a few cases, standardize on VS version `14.0` and not something more specific
Replaces a bunch of dictionary operations with indexing into an array by
doing some caching. Also eliminating an enumerator allocation by changing
from IReadOnlyDictionary to RouteValueDictionary.
This is consistent with other constraint types in routing, and avoids a
naming conflict with MVC.
This is a change **away** from the names used in System.Web and
System.Web.Http.Routing, but it seems worth doing for consistency and
clarity.
Constraint code ported from WebAPI2. Tests are new.
Also a bunch of misc cleanup for constraints.
- Move IRouteConstraint to abstractions
- Fix namespace of a constraint
- Some general style cleanup
- use RouteValueDictionary in the public API
Adds IRouterHandler, an abstraction for endpoints in the routing system
that can't chain (example: delegates). The idea is that some kinds of
routes aren't really friendly to chaining. If you don't support chaining,
then accept IRouteHandler and work with that rather than IRouter.
There's one implementation of IRouteHandler, RouteHandler. It implements
both IRouter and IRouteHandler.
Adds RouteBase as a base class for routes based on our template syntax and
defaults/constraints/data-tokens. Updated a lot of signatures to be
get/set virtual and mutable to facilitate or bigger variety of usage
scenarios.
Renamed TemplateRoute to just Route, now inherits from RouteBase.
Adds IRoutingFeature for middleware scenarios where you don't have access
to the route context.
Also adds some basic extension methods for accessing route values.
This change optimizes allocations by RouteValueDictionary based on usage.
First, implement a struct Enumerator, and expose the concrete RVD type
from all extensibility points. We wanted to try and decouple this code
from RVD originally and use IDictionary everywhere. After doing that we've
found that it allocates an unacceptable number of enumerators.
Secondly, optimize copies of RVD for the case where you're copying an RVC
to another (common case). When doing this we can copy the count to get the
right capacity, and copy the entries without allocating an enumerator.
Lastly, optimize RVD for the case where it's a wrapper around a poco
object. We 'upgrade' to a writable full dictionary if you try to write to
it, or call one of a number of APIs that are uncommonly used. We could
produce optimized versions of things like `Keys` and `CopyTo` if necessary
in the future.
This change removes the call to string.Split and a few substrings, and
replaces it with a tokenizer API. The tokenizer isn't really optimized
right now for compute - it should probably be an iterator- but it's a
significant improvement on what we're doing.
This change makes the allocation of DataTokens and Values on RouteData
lazy, and elides copies when copying an 'empty' RouteData.
In our current architecture this change will eliminiate 2 * (N + 1)
dictionary allocations/copies per request, where N is the number of routes
processed. In a large system with lots of attribute routes, this number
could be very significant.
For a small MVC site (ModelBinding, Validation, Views) with one route, it
still shows a modest reduction of dictionary allocations without adding
much complexity.