ASP.NET Internals - Server controls fields and performance.
Introduction
It’s not a secret that the less server controls you have in an ASP.NET web page the better the performance. The memory allocation of the objects is fast but processing will vary depending on many factors. Additionally, the garbage collector needs to expend time releasing the memory. Furthermore, server controls are processed recursively several times during request, in the processing of events such as init or rendering. Also, the more controls you have the more memory you consume.
It’s very common to have a big number of instances of server controls. A simple grid page can contain hundreds of instances. And since they are created and destroyed in each request their number can be staggering. Imagine a web server under load serving thousands of requests per second.
Furthermore, the internal implementation of the System.Web.UI.Control class (the base class for all server controls in the web forms page, including the page itself, user controls and many others) can have an impact on performance when used one way or another.
Occasionally used fields and rarely used fields
The class contains a big number of private fields used to support a large array of features. Some of these fields are used only occasionally, so to optimize the size of the controls, the ASP.NET team moved them to a different class. The control class contains a reference to this class and the first time the private field is needed, an instance of the occasional fields is created.
In addition to this, there is a group of fields that is used only rarely, so these fields were moved to yet another class and a reference was included in the occasional field class. With the instantiation happening pretty much the same way than the occasional fields.
The code below ilustrates this relationship. The control class will have the ocassional and rare fields encapsulated in private properties. Take a look at the reflector desassembly for the Control class for the actual implementation. Note that accessing a rare field will instantiate both the rare and the occasional field objects.
In that case, server controls using occasional or rare fields can easily double or triple the instance of object created. We have verified through performance runs that reducing the number of total instances improves the performance (and the memory consumption) of ASP.NET requests. Reducing the number of controls that instantiate occasional and rare fields also improves performance and memory usage.
ASP.NET 4.0 optimization.
In fact, during .NET 4.0 development, we analyzed which fields were used more often than others in several scenarios and re-organized them accordingly. Moving the less used fields to the occasional and rare fields classes, or move them back to the control class itself when they were used very often. This optimization improved the performance (throughput and working set usage) of the scenarios used.
In 2.0 the concept of ocassional and rare fields existed. So this was a concern back then too. However, with the years we've been adding new features that required new private fields in that class. So, the initial optimization was kind of stale. During 4.0 we added even more fields, and we noticied regressions in performance. So we decided it was time for a re-optimization. The regression disappeared and we even saw better performance in some scenarios.
Using Reflector on System.Web.dll on 2.0 and 4.0 versions will show the differences on implementation of the Control class.
Measuring your server controls usage.
Unfortunately, it’s hard from a customer stand point to control when the occasional fields and the rare fields are instantiated. Because this is triggered by the usage of specific features that is not evident that will use those fields.
The only thing we can do is to measure the number of controls and which ones are instantiating the occasional and rare fields. Seeing what type generates the rare and occasional fields might allow you to improve the performance of your pages, if this is vital in your scenario. In general, reducing the number of controls will improve your performance. But it’s better to reduce those with occasional and/or rare fields.
Note that, like all performance improvement, you usually have a trade off to make. For instance, we’ve seen the user controls (.ascx files) instantiate the occasional fields. However, user controls has other advantages so you need to find a balance. A typical place to reduce controls is the ones created inside grid cells, since they are replicated in the table.
Using an http module to view your controls.
To view the fields’ usage in your server controls, we created an http module that uses reflection to hack into the controls hierarchy, starting with the page. Another notification is used to attach a table with a report so that can be seen when the page is requested, pretty much the same way trace.axd report is concatenated when trace=”true” is used on a page. We could have used global.asax instead but this solution seems cleaner to me.
You can use the module to calculate the controls usage in your own pages. You can deploy the mentioned module to your app and request your pages. You probably want to do that on a development environment. You don’t want your customers to see the report table on all your pages. Plus, an additional module in the pipeline has a performance cost, especially since we use reflection.
To use the module, you can copy the correct language version to your App_Code folder. I have provided a C# and a VB.NET versions in the attachment of this blog. Click here to get the modules source code.
You also need to edit your web.config to enable the module to run. The setting changed depends on the pipeline mode you are using. For a integrated pipeline mode, you have to use the <system.webServer/modules> section:
<configuration>
<!-- ... -->
<system.webServer>
<!-- ... -->
<modules>
<add name="ControlFieldsStatsModule" preCondition="managedHandler" type="ControlFieldsStatsModule, App_Code"/>
<!-- ... -->
For Classic mode (includes Visual Studio mini web server, IIS6 etc) you use the <system.web/httpModules> section:
<configuration>
<!-- ... -->
<system.web>
<!-- ... -->
<httpModules>
<add name="ControlFieldsStatsModule" type="ControlFieldsStatsModule, App_Code"/>
<!-- ... -->
The report.
Requesting the page, will attach a table like below, with all control types and if they use occasional and/or rare fields. The stats shown were obtained from the default page on a freshly created Dynamic Data project, connected to Northwind database:
Server Control Fields Stats:
Type |
Count |
Occasional |
Rare |
System.Web.UI.ScriptManagerProxy |
1 |
1 |
0 |
System.Web.UI.LiteralControl |
38 |
0 |
0 |
System.Web.UI.HtmlControls.HtmlLink |
1 |
1 |
0 |
System.Web.DynamicData.DynamicHyperLink |
13 |
13 |
0 |
System.Web.UI.WebControls.GridViewRow |
15 |
0 |
0 |
System.Web.UI.HtmlControls.HtmlTitle |
1 |
0 |
0 |
System.Web.UI.WebControls.ContentPlaceHolder |
2 |
1 |
0 |
System.Web.UI.HtmlControls.HtmlAnchor |
1 |
1 |
0 |
System.Web.UI.WebControls.ChildTable |
1 |
0 |
0 |
System.Web.UI.ScriptManager |
1 |
0 |
0 |
System.Web.UI.HtmlControls.HtmlForm |
1 |
0 |
0 |
System.Web.UI.WebControls.DataControlFieldCell |
14 |
0 |
0 |
System.Web.UI.WebControls.DataControlFieldHeaderCell |
1 |
0 |
0 |
System.Web.UI.WebControls.GridView |
1 |
1 |
1 |
System.Web.UI.HtmlControls.HtmlHead |
1 |
1 |
0 |
ASP.default_aspx |
1 |
1 |
0 |
System.Web.UI.HtmlControls.HtmlImage |
1 |
1 |
0 |
ASP.site_master |
1 |
1 |
0 |
95 |
22 |
1 |
Conclusion
This information could potentially be used to select what types of controls are more likely to improve the performance of the page if they are removed. The field’s usage was optimized in .Net 4.0, but it's impossible to do that for all scenarios.
Other performance related articles: https://blogs.msdn.com/josere