Make dictionary allocations lazy on RouteData
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.
This commit is contained in:
parent
59b698c8b2
commit
f95ffb57ae
|
|
@ -1,4 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<add key="AspNetVNext" value="https://www.myget.org/F/aspnetcidev/api/v3/index.json" />
|
||||
|
|
|
|||
|
|
@ -11,14 +11,16 @@ namespace Microsoft.AspNet.Routing
|
|||
/// </summary>
|
||||
public class RouteData
|
||||
{
|
||||
private Dictionary<string, object> _dataTokens;
|
||||
private RouteValueDictionary _values;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="RouteData"/> instance.
|
||||
/// </summary>
|
||||
public RouteData()
|
||||
{
|
||||
DataTokens = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
|
||||
// Perf: Avoid allocating DataTokens and RouteValues unless needed.
|
||||
Routers = new List<IRouter>();
|
||||
Values = new RouteValueDictionary();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -32,24 +34,55 @@ namespace Microsoft.AspNet.Routing
|
|||
throw new ArgumentNullException(nameof(other));
|
||||
}
|
||||
|
||||
DataTokens = new Dictionary<string, object>(other.DataTokens, StringComparer.OrdinalIgnoreCase);
|
||||
Routers = new List<IRouter>(other.Routers);
|
||||
Values = new RouteValueDictionary(other.Values);
|
||||
|
||||
// Perf: Avoid allocating DataTokens and RouteValues unless we need to make a copy.
|
||||
if (other._dataTokens != null)
|
||||
{
|
||||
_dataTokens = new Dictionary<string, object>(other._dataTokens, StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
if (other._values != null)
|
||||
{
|
||||
_values = new RouteValueDictionary(other._values);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the data tokens produced by routes on the current routing path.
|
||||
/// </summary>
|
||||
public IDictionary<string, object> DataTokens { get; private set; }
|
||||
public IDictionary<string, object> DataTokens
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_dataTokens == null)
|
||||
{
|
||||
_dataTokens = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
return _dataTokens;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of <see cref="IRouter"/> instances on the current routing path.
|
||||
/// </summary>
|
||||
public List<IRouter> Routers { get; private set; }
|
||||
public List<IRouter> Routers { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the set of values produced by routes on the current routing path.
|
||||
/// </summary>
|
||||
public IDictionary<string, object> Values { get; private set; }
|
||||
public IDictionary<string, object> Values
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_values == null)
|
||||
{
|
||||
_values = new RouteValueDictionary();
|
||||
}
|
||||
|
||||
return _values;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -133,7 +133,14 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
var oldRouteData = context.RouteData;
|
||||
|
||||
var newRouteData = new RouteData(oldRouteData);
|
||||
MergeValues(newRouteData.DataTokens, _dataTokens);
|
||||
|
||||
// Perf: Avoid accessing data tokens if you don't need to write to it, these dictionaries are all
|
||||
// created lazily.
|
||||
if (_dataTokens.Count > 0)
|
||||
{
|
||||
MergeValues(newRouteData.DataTokens, _dataTokens);
|
||||
}
|
||||
|
||||
newRouteData.Routers.Add(_target);
|
||||
MergeValues(newRouteData.Values, values);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue