Performance bottleneck Url.Action - can I workaround it?
asp.net-3.5, asp.net-4.0, asp.net-mvc-2, asp.net-mvc-3, asp.net-mvc-routing
Solution
There are solved problem similar to yours: First call to Url.Action on a page is slow there are conclusion about routing constraints with regexp constraints that is very slow.
Problem
I have an application that I recently upgraded from ASP.NET MVC1 to ASP.NET MVC4 rc1. It uses the Webforms viewengine. It has performance issues whenever Url.Action(action,controller) is used. I can reproduce the issue in ASP.NET MVC3. I need 3ms to render views that have 10 instances of the Url.Action helper in it in ASP.NET MVC1 and 40ms to render the same in ASP.NET MVC3. I already found some ways to make it render faster: I moved the default route to the top I removed Url.Action and used static links This does not feel right: the application is pretty large and I need the goodness of a decent working routing in it. I am also not confident that I found all performance bottlenecks. Routing is a central part of MVC: if there is something performing badly it will pop up in different parts of the application. I have the impression that MVC3 introduced some routing features (like regex constraints) that even if I dont use them lead to a badly performing application. Is there something I can do like turning of features of routing or using a different set of URL-helpers? This code reproduces the issue: Index action ``` public ActionResult Index() { return View(); } ``` index.aspx ``` <%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head > <title></title> <link href="../../Content/Site.css" rel="stylesheet" type="text/css" /> </head> <body> <div class="page"> <%= Url.Action("Action1", "Controller1") %> <%= Url.Action("Action2", "Controller2") %> <%= Url.Action("Action3", "Controller3") %> <%= Url.Action("Action4", "Controller4") %> <%= Url.Action("Action5", "Controller5") %> <%= Url.Action("Action6", "Controller6") %> <%= Url.Action("Action7", "Controller7") %> <%= Url.Action("Action8", "Controller8") %> <%= Url.Action("Action9", "Controller9") %> <%= Url.Action("Action10", "Controller10") %> </div> </body> </html> ``` Route registration This looks strange: but I just want to simulate my not very complicated routing. This is not the 600 routes of SO! ``` public static void RegisterRoutesSlow(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.IgnoreRoute("{language}/Content/{*pathInfo}"); routes.IgnoreRoute("images/{*pathinfo}"); routes.IgnoreRoute("scripts/{*pathinfo}"); routes.IgnoreRoute("content/{*pathinfo}"); routes.IgnoreRoute("{file}.gif"); routes.IgnoreRoute("{file}.jpg"); routes.IgnoreRoute("{file}.js"); routes.IgnoreRoute("{file}.css"); routes.IgnoreRoute("{file}.png"); routes.IgnoreRoute("{file}.pdf"); routes.IgnoreRoute("{file}.htm"); routes.IgnoreRoute("{file}.html"); routes.IgnoreRoute("{file}.swf"); routes.IgnoreRoute("{file}.txt"); routes.IgnoreRoute("{file}.xml"); routes.IgnoreRoute("{*favicon}", new { favicon = @"(.*/)?favicon.ico(/.*)?" }); for (int i = 0; i <= 10; i++) { routes.MapRoute( // Route name "RouteName" + i.ToString(), // URL with parameters "{language}/{controller}/{action}/{para1}", // Parameter defaults new { action = "Index", language = "de", para1 = 0 }, //Parameter constraints new { language = "de|en", controller = "SomeNameOfAnActualController" + i.ToString() } ); } routes.MapRoute( "DefaulRoute", // Route name "{controller}/{action}", // URL with parameters new { controller = "Home", action = "Index", } ); routes.MapRoute("404-PageNotFound", "{*url}", new { controller = "Error", action = "PageNotFound", language = "de" }); } ``` EDIT The sample code was compiled against MVC2 now. In VS2010 MVC2 can be compiled against .NET 3.5 or 4.0. The performance with 3.5 is good and 4.0 is bad. I guess this means that the poorly performing part is not in a MVC assembly but in a framework assembly (like System.Web.Routing.dll). The question is still the same: Can I do something about it? An accepted answer would also be: No, the code is slow because from version 3.5 to 4.0 MS changed XXX EDIT-2 I decompiled the part of System.Web.Routing.dll that takes to long. It uses a compiled regular expression. There is a code path (constraint2.Match ) that returns without executing the regex, but I did not check yet if it internally uses a different expensive operation. ``` protected virtual bool ProcessConstraint(HttpContextBase httpContext, object constraint, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) { object obj2; IRouteConstraint constraint2 = constraint as IRouteConstraint; if (constraint2 != null) { return constraint2.Match(httpContext, this, parameterName, values, routeDirection); } string str = constraint as string; if (str == null) { throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("Route_ValidationMustBeStringOrCustomConstraint"), new object[] { parameterName, this.Url })); } values.TryGetValue(parameterName, out obj2); string input = Convert.ToString(obj2, CultureInfo.InvariantCulture); string pattern = "^(" + str + ")$"; return Regex.IsMatch(input, pattern, RegexOptions.CultureInvariant | RegexOptions.Compiled | RegexOptions.IgnoreCase); } ```