[Perf] Fast Generate Url for specific cases to avoid string allocations

This commit is contained in:
mnltejaswini 2016-05-13 10:29:43 -07:00
parent 0f8542e4b2
commit 74357b3ed8
2 changed files with 90 additions and 2 deletions

View File

@ -233,6 +233,15 @@ namespace Microsoft.AspNetCore.Mvc.Routing
// VirtualPathData.VirtualPath returns string.Empty instead of null.
Debug.Assert(pathData.VirtualPath != null);
// Perf: In most of the common cases, GenerateUrl is called with a null protocol, host and fragment.
// In such cases, we might not need to build any URL as the url generated is mostly same as the virtual path available in pathData.
// For such common cases, this FastGenerateUrl method saves a string allocation per GenerateUrl call.
string url;
if (TryFastGenerateUrl(protocol, host, pathData, fragment, out url))
{
return url;
}
var builder = GetStringBuilder();
try
{
@ -266,5 +275,35 @@ namespace Microsoft.AspNetCore.Mvc.Routing
builder.Clear();
}
}
private bool TryFastGenerateUrl(
string protocol,
string host,
VirtualPathData pathData,
string fragment,
out string url)
{
var pathBase = HttpContext.Request.PathBase;
url = null;
if (string.IsNullOrEmpty(protocol)
&& string.IsNullOrEmpty(host)
&& string.IsNullOrEmpty(fragment)
&& !pathBase.HasValue)
{
if (pathData.VirtualPath.Length == 0)
{
url = "/";
return true;
}
else if (pathData.VirtualPath.StartsWith("/", StringComparison.Ordinal))
{
url = pathData.VirtualPath;
return true;
}
}
return false;
}
}
}

View File

@ -939,6 +939,38 @@ namespace Microsoft.AspNetCore.Mvc.Routing
Assert.Equal(expected, builder.ToString());
}
[Theory]
[InlineData(null, null, null, "/", null, "/")]
[InlineData(null, null, null, "/", null, "/")]
[InlineData(null, null, null, "/Hello", null, "/Hello" )]
[InlineData(null, null, null, "Hello", null, "/Hello")]
[InlineData(null, null, null, "/Hello", null, "/Hello")]
[InlineData("/", null, null, "", null, "/")]
[InlineData("/hello/", null, null, "/world", null, "/hello/world")]
[InlineData("/hello/", "https", "myhost", "/world", "fragment-value", "https://myhost/hello/world#fragment-value")]
public void GenerateUrl_FastAndSlowPathsReturnsExpected(
string appBase,
string protocol,
string host,
string virtualPath,
string fragment,
string expected)
{
// Arrage
var router = Mock.Of<IRouter>();
var pathData = new VirtualPathData(router, virtualPath)
{
VirtualPath = virtualPath
};
var urlHelper = CreateUrlHelper(appBase, router);
// Act
var url = urlHelper.GenerateUrl(protocol, host, pathData, fragment);
// Assert
Assert.Equal(expected, url);
}
private static HttpContext CreateHttpContext(
IServiceProvider services,
string appRoot)
@ -1002,13 +1034,13 @@ namespace Microsoft.AspNetCore.Mvc.Routing
return new UrlHelper(actionContext);
}
private static UrlHelper CreateUrlHelper(string appBase, IRouter router)
private static TestUrlHelper CreateUrlHelper(string appBase, IRouter router)
{
var services = CreateServices();
var context = CreateHttpContext(services, appBase);
var actionContext = CreateActionContext(context, router);
return new UrlHelper(actionContext);
return new TestUrlHelper(actionContext);
}
private static UrlHelper CreateUrlHelperWithRouteCollection(
@ -1095,5 +1127,22 @@ namespace Microsoft.AspNetCore.Mvc.Routing
return Task.FromResult(false);
}
}
private class TestUrlHelper : UrlHelper
{
public TestUrlHelper(ActionContext actionContext) :
base(actionContext)
{
}
public new string GenerateUrl(string protocol, string host, VirtualPathData pathData, string fragment)
{
return base.GenerateUrl(
protocol,
host,
pathData,
fragment);
}
}
}
}