Tuldok Lambat's Tech Blog

Tuesday, November 29, 2005

Ugly-Printing: Reducing Your Page Content Size

Previously we talked about the idea of compressing the ViewState and the idea of persisting it on the server to reduce the content size of your .aspx page. In addition to those ideas I’ll be discussing with you yet another way of speeding up your .aspx web pages.

Pretty-printing…

The developers of ASP.NET had put lots of effort in formatting the HTML source of our .aspx page and did so pretty well, just right click your browser and select View Source to see what I mean (Figure 1). These were all well and good for those who know how and care to peek under the covers of your web pages but does it really matter to the average user? For all we know, the average user care less if the underlying HTML source is pretty-printed or not. What matters to them, besides your content of course, is the speed at how it delivers its content in front of them.

Free Image Hosting at www.ImageShack.us

Figure 1. Pretty printed HTML source.

The Filter property

The HttpResponse class exposes a Filter property that you could peruse to filter (and modify) the contents of the resulting HTML. This is just what we need to remove the extraneous character (mostly whitespace characters like carriage-returns and tabs) that ASP.NET deliberately put in for pretty-printing, in addition we could also extend our filtering scheme to include removing HTML comment, script comments, spaces between operators (like a + b when a+b will suffice) and etc. for further reduction. You can also try obfuscating your scripts using this scheme. Think of the endless possibilities.

Digression

Staying young and beautiful almost always has a tradeoff. You’ll spend lots of effort and time to maintain one’s beauty, just asks my missus and she’ll tell you. So why stay young and die early in the process (because you worry often that you’re getting older) when you can get old earlier and live longer.

Ugly-printing…

The digression above was just rhetorical but it holds true in computer processing. Agile method proponents always wants to keep everything simple because they acknowledge that most user care less if the system is aesthetically pleasing as long as it work and gets the job done. Most likely such system will be used longer than a system that is aesthetically pleasing yet doesn’t work at all. What I’m really saying is, why pretty-print when we can ugly-print for better user experience.

Using the HttpResponse.Filter and setting up our filter mechanism is not complicated but it’s not trivial either. What we need is a class that derives from the abstract Stream class with the required members implemented (see your MSDN documentation), especially the Write method, where the actual filtering process takes places.

public class UglyPrint : Stream
.
.
public override void Write(byte[] buffer, int offset, int count)
{
byte[] data = new byte[count];
Buffer.BlockCopy(buffer, offset, data, 0, count);
string inputstring = Encoding.ASCII.GetString(data);

inputstring = Regex.Replace(inputstring, @">[\s\S]*?<", new MatchEvaluator(Evaluate));

data = Encoding.ASCII.GetBytes(inputstring);
strSink.Write(data, 0, inputstring.Length);
}

protected string Evaluate(Match m)
{
string ms = m.ToString();
ms = Regex.Replace(ms, @"\r\n\s*", "");
return ms;
}
.
.
}

Here I made use of the Regular Expression class (Regex) to find the pattern “>[\s\S]*?<”, the pattern basically meant, all white spaces and/or non-white space between the “>” and “<” symbol. If we found such pattern, it will call the Evaluate function and further search the matched string with symbols “\r\n\s*” which meant all carriage-returns followed by zero or more white spaces and replaces them with an empty string.

Now, that we have setup the filtering class. It’s now time to use in our page. Just add the following snippet in the Page_Load event.

this.Response.Filter = new UglyPrint(this.Response.Filter);

or inherit from the ZipPage class (included in the example), like so:

public partial class DynamicNodes_aspx : ZipPage
{
.
.
}

This System.Web.UI.Page derived ZipPage class implements the UglyPrint class and the classes from the ViewState Compression and Persistence blog.

That’s it and you’re done. Run the page and try viewing its source in the browser (Figure 2).

Free Image Hosting at www.ImageShack.us

Figure 2. After ugly-printing the page.

Now, that’s what I call a mess. It’s ugly but it’s faster and because it’s hard to read you are well off from other prying eyes. Also from the snapshot below (Figure 3) you’ll see how much the page was reduced from 17,881 to 16,861. This may seem not much but that’s just because our filtering scheme is simple and did not strip of white spaces inside the scripts. But like I mentioned earlier we could still extend the filtering scheme and make it more robust.

Free Image Hosting at www.ImageShack.us Free Image Hosting at www.ImageShack.us
Figure 3. Shows the size difference before and after ugly-printing the page.

Conclusion

Think of the combine effect of compressing the ViewState or persisting it in the server with this idea of ugly-printing and you’ll get an idea of how much less your .aspx page now weigh. You need not worry how long this overhead takes because it depends on the complication of your filtering scheme but nonetheless a lighter page means a faster delivery.

Note:
Please take note that the above example leaves lot to be done. The filtering logic in the code above is simple and just serves as a proof-of-concept.

Downloads: http://www.megaupload.com/?d=2HHKPJ8J

Wednesday, November 23, 2005

ViewState Compression and Persistence

For most web developers, one of the breakthroughs that the .net framework brought them is the ViewState. That enabling technology that save/restore page state between postback. But this cool stuff carries with it an overhead. Enabled by default and for the most part ignored during development, the ViewState may bloat to a size that may affect performance especially during page load.

So what do we do?

We can turn viewstate off all together. A not so good idea because that would mean throwing an extra programming effort for you to take care of the page state. Although a nicer idea would be to turn viewstate off for only the controls that need don't need it. But this won't entirely solve the problem of a bloated viewstate.

So what are the other options?

Other option I've seen on the net entails compressing the viewstate. I've tried it and it actually reduced the size of my viewstate by more than a half! Below is my port of it in .NET 2.0 (FYI, .NET 2.0 now comes with a compression namespace but you can always opt to use 3rd party products like ShapNZipLib, ComponentOne.Zip, etc).

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.IO.Compression;

public class GZip
{
public static byte[] Compress(byte[] b)
{
MemoryStream ms = new MemoryStream();
GZipStream zs = new GZipStream(ms, CompressionMode.Compress, true);
zs.Write(b, 0, b.Length);
zs.Close();
return ms.ToArray();
}

public static byte[] Decompress(byte[] b)
{
MemoryStream ms = new MemoryStream();
GZipStream zs = new GZipStream(new MemoryStream(b), CompressionMode.Decompress, true);
byte[] buffer = new byte[4096];
int size;
while (true)
{
size = zs.Read(buffer, 0, buffer.Length);
if (size > 0) ms.Write(buffer, 0, size);
else break;
}
zs.Close();
return ms.ToArray();
}
}

public class Deflate
{
public static byte[] Compress(byte[] b)
{
MemoryStream ms = new MemoryStream();
DeflateStream zs = new DeflateStream(ms, CompressionMode.Compress, true);
zs.Write(b, 0, b.Length);
zs.Close();
return ms.ToArray();
}

public static byte[] Decompress(byte[] b)
{
MemoryStream ms = new MemoryStream();
DeflateStream zs = new DeflateStream(new MemoryStream(b), CompressionMode.Decompress, true);
byte[] buffer = new byte[4096];
int size;
while (true)
{
size = zs.Read(buffer, 0, buffer.Length);
if (size > 0) ms.Write(buffer, 0, size);
else break;
}
zs.Close();
return ms.ToArray();
}
}

//Please see code snippet below on how to use these classes.

Is that it?

The buck doesn't stop here. I've read Dino Esposito's Programming Microsoft ASP.NET (ISBN:0735619034) and in one of the advance topics in his book he mentioned saving the ViewState in a file at the server. I was having second thoughts on this idea at first. Since this would mean lots of files and disk read/write activities in the server but then I thought of the advantages (and it outweighs the dis in some ways). First, security-wise, the ViewState won't get manipulated. Second, at least you have control of the server's resources and you can scale it up all you want rather than upgrading all of the client workstation. Third, I can't think of anything but I'm sure with this, I'll be saving the client some load up time =). See how it's implemented below:

using System;
using System.Collections.Generic;
using System.Text;
using System.Web.UI;
using System.IO;

public class ZipPage : Page
{

protected override void OnPreLoad(EventArgs e)
{
this.Session["_storeViewStateInServer"] = false;
base.OnPreLoad(e);
}

public bool StoreViewStateInServer
{
get
{
return (bool)this.Session["_storeViewStateInServer"];
}
set
{
this.Session["_storeViewStateInServer"] = value;
}
}

protected override object LoadPageStateFromPersistenceMedium()
{
LosFormatter f = new LosFormatter();
string vstate;
if (StoreViewStateInServer)
{
StreamReader sr = new StreamReader(GetFileName());
vstate = sr.ReadToEnd();
sr.Close();
}
else
{
vstate = this.Request.Form["__ZIPSTATE"];
byte[] b = Convert.FromBase64String(vstate);
b = Deflate.Decompress(b);
vstate = Convert.ToBase64String(b);
}

return f.Deserialize(vstate);
}

protected override void SavePageStateToPersistenceMedium(object state)
{
LosFormatter f = new LosFormatter();
if (StoreViewStateInServer)
{
StreamWriter sw = new StreamWriter(GetFileName());
f.Serialize(sw, state);
sw.Close();
}
else
{
StringWriter sw = new StringWriter();
f.Serialize(sw, state);
byte[] b = Convert.FromBase64String(sw.ToString());
b = Deflate.Compress(b);
ClientScript.RegisterHiddenField("__ZIPSTATE", Convert.ToBase64String(b));
}
}

private string GetFileName()
{
string url = Request.ServerVariables["Path_Info"];
url = url.Replace("/", "_");

// Place the file in a temp folder (with write permissions)
string fileName = "{0}/{1}_{2}.viewstate";
fileName = String.Format(fileName, "Temp", Session.SessionID, url);
return Server.MapPath(fileName);
}

}

Now you'll just have to inherit from this class and you're on the go. By default the ViewState won't be save on the server, I'll let you set it up by setting the StoreViewStateInServer property. Note that opting to save the ViewState in the server won't compress it since it doesn't matter anyway. Also please don't forget to give ASPNET machine account proper rights to the folder you'll be writing those temp tables or else it won't be able to persist the ViewState and exceptions will occur.

Conclusion

The two solutions cited to reduce the load up time of your aspx page are just one of the many things you can do the tweak you web app performance. Next blog I'll be showing you how to compress the HTML rendered on the page by removing the extra characters inserted by ASP.NET during page rendering.

Tuesday, November 22, 2005

Welcome!

Welcome to my very first technical blog. This is where I'll be sharing my experiences in the information technology field.