In the post below I am going to run down a short-list of some of the less-popular performance tips and the reasoning behind them.  There are loads of blog postings and articles on asp.net performance enhancements, and I urge you to check those out as well.  Onto the good stuff...

Session.ReadWrite versus Session.ReadOnly

It goes without saying that using the Session object will incur some performance overhead so if possible it should be turned off in the web.config.
That said, If you’re using the Session object to maintain session in your application...
Ensure Session.ReadOnly is true rather than using Session.ReadWrite across the application.  There is more information in the link below, however the short story is there is no ReadWriterLock done if Session.ReadOnly = true.  From this MSDN link, “ReaderWriterLock allows multiple threads to read a resource concurrently, but requires a thread to wait for an exclusive lock in order to write to the resource.”  This will make a fairly significant difference if sessions are being managed in a state database, in which case only one-trip to the database is made when using ReadOnly, rather than two while using ReadWrite...which leads me to...

Optimizing your Session when using Out-Of-Process (E.g., State Server or State Database) Session Management

When using out-of-process session management methods objects must be serialized on their way into the state server or state database.  Naturally, this means the data within the session object must be deserialized on the way back out [to the application].  In order for this to work you'll need to add the [Serializable] attribute to the beginning of the class that needs to go into the session object.  For example...

[Serializable]
public class User{
    public string firstName; //Small text field
    public string lastName; //Another small text field
    public string usersLifeStory; //Is a large text field
}

As you may infer from my not-so-clever comments, there may be a problem with this.  Let's say the user object needs to be stored in my session object [and therefore needs to be serializable], but there are some large, or otherwise inefficient fields in this object (such as non-primitive types).  For the purposes of this example, let's also say that I don't need to store my user's life story in my session object because it's only displayed on one page where I can pull it from the application's database.  The .NET framework makes it very easy to exclude properties within my object from being serialized.  In fact, it's as easy as adding a few more attributes. 

[Serializable]
public class User{
    public string firstName; //Small text field
    public string lastName; //Another small text field
    [NonSerialized]  //Prevents usersLifeStory from being serialized
    public string usersLifeStory; //Is a large text field
}
Easy peasy.  Note that this means the "usersLifeStory" variable will not be accessible via the session object, of course.

Cache and...

think about your caching strategy before you go developing pages or user controls willy nilly.  This will make using the .NET frameworks caching features much easier and will hopefully prevent you from having to make some logic work differently just so you can cache...this will make life especially easy when using features like output cache substitution and creating methods/strategies for querying your DB.  Again, caching is something you should be thinking about before you launch your IDE.

There are tons of resources for learning about output caching, data caching, and the other features the .NET framework provides, so I'm going to skip that.  I am, however, going to mention a really easy way to automagically cache return values from WebMethods/Web-services because I don't see it mentioned very much. 

public class ExampleService{
    //The cache duration attribute handles ALL caching for you
    
    [System.Web.Services.WebMethod(CacheDuration=60)]
    public string GetSomethingFromSomewhere(){
        //Logic to get some data from a data-source
    }
}

That's it...Just add the CacheDuration attribute and you're good to go.

Update [1/5/2009]: Scott Mitchell has just posted a great, and concise article on per-request-caching.  This is easy-to-use and powerful functionality.  Read the article here.

Use HTTP Compression!!!

Maybe it's the luck of the draw here, but in my experiences most developers/sys admins/etc for some reason or another don't enable HTTP Compression, either deflate or GZIP.  I'm sure there are some legitimate reasons for not enabling this functionality for some applications, but everyone else should absolutely have it turned on.  Yes, it's going to cost some CPU but you can play with the settings.  Bottom line is you will see a dramatic difference in page response times.  Keep in mind you can configure HTTP Compression to compress a number of file-types, including CSS/JS/HTML/ASPX/ASMX/ASHX etc. 

Also, I came across this post doing some research the other night - looks like Mads did some testing on GZIP versus Deflate.  The results are interesting...I have yet to do some testing on my end.

For some reason or another the IIS6 team didn't give us an GUI to turn on/off HTTP Compression, so you need to go into the metabase.  It's really only confusing the first time you do it.  There are a number of good tutorials on the web.  I think this is the one I used.  You'll probably want to ensure HTTP Compression is on by using a tool like HTTPWatch, or its FireFox equal...the name of which currently slips my mind.  Google is your friend :)

Be careful with the App_Themes folder!

I don't have any screen shots handy, and I'm not at my office so you're going to have to trust me on this but...Be careful what you put into app_themes.  No matter what you reference in your pages using the <style> tag, ASP.NET/VS is going to load all the style-sheets you have in app_themes.  This means if you're using a very specific style-sheet on one page (say, for a prototype, jQuery, or script.alicio.us library) that the JavaScript is going to get loaded every.single.time.   So, just to be clear here, no matter what you do, VS is going include all the CSS (or other) files you have in your app_themes folder.  Best idea IMHO is to use this strictly, well, for your themes and create another folder for your CSS, and another for your scripts.  Just don't use app_themes as your miscellaneous include folder.

Be careful how you populate objects!

Okay, I'm sliding this one in as a general tip just because it's a bug I discovered in some code I was going through.  Okay, it *may* have been my code, but no one can prove anything.  In any case, I was populating an object and calling a second method within the context of a USING block.

public Employee GetEmployee(int EmpId){
    using (Connection)
    {
        SqlDataReader dr = SqlHelper.ExecuteReader(Connection, CommandType.StoredProcedure, "GetUser", New SqlParameter("@ID",EmpId);
        _firstName = dr[0];
        _lastName = dr[1];
        _photo = GetEmpPhoto(EmpId);
    }
}
 
public byte[] GetEmpPhoto(int EmpId){
   using (Connection)
   {
       return SqlHelper.ExecuteReader(Connection, CommandType.StoredProcedure, "GetPhoto", New SqlParameter("@ID",EmpId);
   }
 
}

What happens in the above scenario?  I'll give you a hint, it's not good.  The call to GetEmpPhoto() in the GetEmployee() method spawns a new connection to the database within the GetEmpPhoto() method.  The problem is the connection spawned within GetEmpPhoto() never gets closed (or returned to the pool).  This is probably a pretty subjective case, but in any case doing stuff like this is basically asking for trouble.

There are probably an infinite number of applications tweaks to increase performance, including some of the more popular ones listed below that you can easily Google and find information on.  In this entry I've tried to explain some of the lesser-known/lesser-implemented tips I found useful.  Be sure to get the following items out of the way when you're looking for performance gains.

- Disable viewstate (In web.config or on a control-by-control basis.  Don't forget about control-state).   Also, be sure not to store any non-primitive types in viewState as you'll big a large price in terms of performance.  I have seen some suggestions on forums informing people to use the session object, instead.  I'd be wary of this as well.

- Set debug="false"  This shouldn't need much explanation.

- Check for isPostBack, and isCallBack in page.Load() to avoid re-loading unnecessarily

- Minify CSS/JS

- Compress images

- Use AJAX not only when convenient, but when it will enhance performance

Omar Al Zabir does a pretty good job of explaining some of these items.  I would check out this article on codeProject and his blog for info on the above or just do some googling.

Hope this helped!

Sanjay

kick it on DotNetKicks.com