views:

1570

answers:

6

A little benchmark with ASP.NET MVC. Viewpage code:

    public string Bechmark(Func<string> url)
    {
        var s = new Stopwatch();
        var n = 1000;

        s.Reset();
        s.Start();
        for (int i = 0; i < n; i++)
        {
            var u = url();
        }
        s.Stop();
        return s.ElapsedMilliseconds + " ms, " + ((s.ElapsedMilliseconds) / (float)n) + " ms per link<br/>";
    }

View code:

<%= Bechmark(() => Url.Action("Login", "Account")) %>

<%= Bechmark(() => Url.Action("Login", "Account", new {username="bla", password="bla2", returnurl="blabla32", rememberme=false} )) %>

<%= Bechmark(() => Html.BuildUrlFromExpression<AccountController>(a=>a.ChangePassword("bla", "bla", "ya")) ) %>

Running this on a typical Core2 notebook on the default new project template with ASP.NET MVC Beta yields these results:

38 ms, 0,038 ms per link

120 ms, 0,12 ms per link

54 ms, 0,054 ms per link

Running the same benchmark on a production project with about 10 controllers that have all in all around 100 methods and 30 routing table entries, the performance degrades greatly for the expression-based method:

31 ms, 0,031 ms per link

112 ms, 0,112 ms per link

450 ms, 0,45 ms per link

We use this method quite a lot (maintainability) and doing some performance benchmarking, this degrades the performance of the site greatly - pages quickly contain around 30 or more of such links, that means 10ms of additional overhead on a single page. Even 0.112ms per an URL is around 4ms of pure CPU overhead.

It should be noted that performance of all the three URL generation calls between MVC Preview 3 and Beta (released yesterday) got improved by a factor of 5.

Stack Overflow is supposedly powered by the same framework, how have you guys tackled this scaling problem? Liberal caching of the front page (lots of links) and prerendered controls?

Any other production websites in ASP.NET MVC with performance issues or some good tips?

Thanks.

+1  A: 

Caching links would probably be a good suggestion for the team, as they won't change for the life of the process (for most apps anyway).

Until you start defining your routes in a configurable form (such as web.config or in a database) then you'd have to scale back a bit.

I suspect that a big portion of the delay on the middle example is the anonymous type that gets automatically converted to a dictionary. Caching the URL wouldn't help here b/c you'd still need to reflect that type.

In the meantime, you can create your own helper methods for some of those dictionary-based links that take the exact input you require. Then you can handle the caching yourself.

Ben Scheirman
+1  A: 

Ok, two additional metrics on the blank template project:

<%= Bechmark(() => Url.Action("Login", "Account", new Dictionary<string, object> {{"username", "bla"}, {"password", "bla2"}, {"returnurl", "blabla32"}, {"rememberme", "false"}})) %>

<%= Bechmark(() => Url.Action("Login", "Account", new RouteValueDictionary(new Dictionary<string, object> {{"username", "bla"}, {"password", "bla2"}, {"returnurl", "blabla32"}, {"rememberme", "false"}}))) %>

Results:

71 ms, 0,071 ms per link

35 ms, 0,035 ms per link

Much better performance with way nastier code. Too bad.

rudib
A: 

We found the same issues with URL generation performance. Probably because of the heavy use of reflection. Oh, and I have no idea what the answer is.

This is my first non-answer answer.

CVertex
make a comment next time ;)
TheSoftwareJedi
A: 

Caching links would probably be a good suggestion for the team, as they won't change for the life of the process (for most apps anyway).

How can you cache links, you can't do that as far as I know, because you need to cache the method that gets executed, which happens after the route gets resolved, which is the slow part.

bashmohandes
+4  A: 

I asked this question on the MS forums, which got an answer from an MS MVC developer.

The post

The answer

From MVC Preview 2 to the recently released MVC Beta from yesterday there have been a lot of changes to Routing. Some of those changes include performance improvements. Here are some tricks to make URL generation more performant in your application:

  1. Use named routes. Named routes are an optional feature of routing. The names only apply to URL generation - they are never used for matching incoming URLs. When you specify a name when generating a URL we will only try to match that one route. This means that even if the named route you specified is the 100th route in the route table we'll jump straight to it and try to match.

  2. Put your most common routes at the beginning of the route table. This will improve performance of both URL generation as well as processing incoming URLs. Routing works based on the rule that the first match wins. If the first match is the 100th route in your route table, then that means it had to try 99 other routes and none of them matched.

  3. Don't use URL generation. Some people like it, some people don't. It's a bit tricky to master. It's nice to use if your URLs are very dynamic but it can be a bit of a hassle when you have very few URLs to begin with and perhaps you don't care about exactly what they look like.

My favorite option is #1 since it's super easy to use and it also makes URL generation more deterministic from the app developer's perspective (that's you!).

CVertex
Question: Use named routes <-- what's a named route? what's the overload to use? cheers :)
Pure.Krome
In case you still care: in your global.asax you use the routes.MapRoute("routeName", yadiyadiya);Then in your actions you can doe a RedirectToRoute("routeName", viewData); There's more of that somewhere probably
borisCallens
A: 

I wrote a post regarding this very issue and how to improve performance.

http://www.chadmoran.com/blog/2009/4/21/optimizing-url-generation-in-aspnet-mvc-part-1.html

Chad Moran