The Short Happy Life of Dorkus McDweeb
(an Analysis of Keeping ViewState out of the Page)
By Peter A. Bromberg, Ph.D.

Peter Bromberg

I recall that some years ago there was a nerdy guy on TV (I think it was PBS) who had a show about technology and his favorite saying was "There are two kinds of people in the world, those that believe there are two kinds of people in the world, and those that do not" - the perfect Godel-Escher-Bach self-referential, self-recursive construct. Even Hofstadter, writing in the preface to the 20 year anniversary edition of his book, says that most people didn't "get" what his book was really about: "GEB is a very personal attempt to say how it is that animate beings can come out of inanimate matter. What is a self, and how can a self come out of stuff that is as selfless as a stone or a puddle?"



My personal famous GEB - style quote is, "There are two kinds of programmers in the world - those that boot up with the NumLock key on, and those that do not".   I am forever in the second camp. The analogy really means, there are programmers who go through their careers finding snippets of other people's code they can cut and paste to solve their problems, and those who do the same (as I do), but who -- in addition -- also feel compelled to UNDERSTAND what that code is doing and how it really works. It is when we take ownership of the concept, analyze and fully understand it, that we can grow. It is that analytical, studious, lateral-thinking demeanor which is the hallmark difference between a drone, and a real programmer who has learned to think independently, which is what we programmers are really paid to do, IMHO.

The Dorkus McDweebs of the world will continue to end up being shot in the back of the head as they aim for the Code Buffalo, and they will lose the girl of their dreams to Wilson, the mustachioed Safari Guide, because they do not and will not analyze and do not wish to learn and grow. We're not talking about literary taste here, if you'd rather listen to Hamster Dance Remix than read James Joyce's Ulysses, fine with me. What I'm talking about is the ability (and willingness) to think. So now, we could read Hemingway for inspiration, but let's talk about ViewState and performance.

ViewState is a great invention. Since 99% of the people reading this already have a pretty good idea of what it is and how it works, we'll skip all the intro stuff. The problem with ViewState is that when you have a lot of controls on your page, or a DataGrid or some other complex animal, ViewState, which becomes part of the HTML baggage going back and forth over the wire, can grow very large. That's a drag on performance for the convenience you have of storing state "in the page".

However, you can have your ViewState and Lean it too - by taking it out of the page and keeping it on the server. There are some drawbacks to doing this, but in many cases, they are not an issue. How do you get ViewState "out of the page"? Well, you simply override the mechanism that stores and retrieves it, namely SavePageStateToPersistenceMedium and LoadPageStateFromPersistenceMedium. By overriding these two intrinsic methods, we can intercept the ViewState process and perform our magic. What we are doing is simply this: You have a bunch of baggage. Probably way too much. But you only really need it when you visit the server (such as on a postback). So, instead of making you carry it all with you (which can slow down the plane and make other passengers very unhappy), we are going to arrange for you to leave it in a special place at the server, and pick it up again when you get back there. Everything works the same; you just don't have to lug it all with you. Make sense?

This is far from a new idea; there are a number of implementations of this technique. The best one I found is by Robert Boedigheimer here. Robert's solution provides a new BasePage class which handles these operations, and a viewStateServerMgr class that handles the serialization and deserialization. His implementation is the only one I've seen that also accounts for the fact that with IE, users can hit the back button and we would need to "go back" to the previous viewstate for that page in the cache. Robert made the assumption that since ViewState is unique to each user, he would store and retrieve the ViewState for each page in Session state. However, there are at least three other "places" that we can stick our serialized ViewState object, and we can still account for unique users with a unique key such as the SessionID. If we do this, we can set Session to be readonly (assuming we don't need to write to it) and thus get an additional performance boost. Those options are Application, Cache, and a static global class instance holding, for example, a Hashtable.

A simplified code example of this model, with a different implementation of a unique user key, could be as follows:

protected override void SavePageStateToPersistenceMedium(object viewState)
{
string str = "VIEWSTATE_" + Request.UserHostAddress + "_" + DateTime.Now.Ticks.ToString();
Cache.Add(str, viewState, null, DateTime.Now.AddMinutes(Session.Timeout),TimeSpan.Zero, CacheItemPriority.Default, null);
RegisterHiddenField("__VIEWSTATE_KEY", str);
RegisterHiddenField("__VIEWSTATE", "");
}

protected override object LoadPageStateFromPersistenceMedium()
{
string str = Request.Form["__VIEWSTATE_KEY"];
if (!str.StartsWith("VIEWSTATE_")) {
throw new Exception("Invalid viewstate key:" + str);
}
return Cache[str];
}

So what I did was to wire up Robert's class with some choices that could be set in the web.config -- to make it automatically use Session, Application, Cache, or my global static class, depending on the web.config setting. I then set about to create two pages. The first page uses Robert's BasePage class to inherit from, and implements the server - side ViewState scheme. The second page inherits from the typical System.Web.UI.Page class, and Viewstate is stored "in the page" as usual. Each page is essentially the same, and each page creates 1000 small viewstate entries in the Page_Load handler on first load. Each page also has a bit of javascript written to it with "Defer" - which makes the script wait until the page and all other script is rendered, and then it causes the page to postback. In this manner, I could easily run a series of web stress tests with Homer (now called "Web Application Stress") and I wouldn't have to bother with making a GET and a POST in my tests. All I am really interested in is the throughput and average requests per second under load for each scenario.

As an informational note, many developers will ask "Why didn't you use ACT from Visual Studio.NET?". ACT is great for demos where you want to show people a nice real-time graph, but my personal opinion is that it is nowhere near as reliable or feature-rich as Homer. After a number of years of this testing thing, including several eye-opening weeks at the MS Testing Lab in Charlotte, I have come to trust Homer.

Before going any further, let's take a look at the test results, which may surprise you. We ran this test with a client machine and a server machine over a wireless network. The server is a P4 with 1 GB RAM running Windows Server 2003 and IIS in Worker Process Isolation (HTTP.SYS) mode. We ran a two minute test with 20 simultaneous threads and no delay. This can be considered moderate stress. None of the tests had any HTTP or socket errors. The regular ViewState test without the server-side scheme is in the first column, then the four server side schemes follow:

  VIEWSTATE WEB STRESS COMPARISON  
TEST NAME VIEWSTATE SESSION APPLICATION CACHE GLOBALS
NUMBER OF HITS 3322 18483 18476 20170 16723
REQUESTS /SEC 27.64 153.8 153.74 167.84 140.32
TOTAL BYTES REC'D (KB) 68016.63 22923.25 22977.13 25074.51 20798
PERCENT DIFFERENCE
N/A
456.44% 456.22% 507.24% 407.67%

As can be seen above, the winner is - surprise - storing the ViewState in Cache, at almost 168 requests per second. That is an approximate 500 percent improvement in throughput over regular ViewState. All of the server-side options outperformed regular ViewState, with static global Hashtable storage being the worst performer. You can download the solution I used to create these tests below, and tweak it if you like. I'd be interested in anyone else's results in this area.

Conclusion: ASP.NET ViewState is a great invention that makes the developer's life easier. It also can create a lot of HTML baggage that slows down performance. By storing ViewState in either Session or Cache, you can have your ViewState and eat it, too.

Taking the time to develop a quality stress testing scenario to prove out (or disprove) one's hypotheses provides the scientific basis in fact to help developers make educated, sound decisions about their application architecture.

Download the Solution that accompanies this article


Peter Bromberg is a C# MVP, MCP, and .NET consultant who has worked in the banking and financial industry for 20 years. He has architected and developed web - based corporate distributed application solutions since 1995, and focuses exclusively on the .NET Platform.
Article Discussion: