Alik Levin's

Clarity, Technology, and Solving Problems | PracticeThis.com 

July, 2008

  • Alik Levin's

    Best ASP.NET Performance Winner For Data Binding - Hands Up To Response.Write()

    • 21 Comments

    Alik Levin     To achieve best performance you need to make decisions based on trade-off between coolness, coding productivity, and personal engineering values. I never thought I would be recommending my customer considering using old fashion Response.Write() in his Internet facing ASP.NET web application in order to significantly improve the application’s performance.

    Customer Case Study

    During load/stress testing customer’s ASP.NET web application we identified high CPU utilization (up to 90%). After quick investigation we noticed that %Time in GC performance counter is less than optimal. Our assumption was that the application uses memory allocation techniques that are less than optimal. From GC Performance Counters:

    "First thing you may want to look at is “% Time in GC”... What is a health value for this counter? It’s hard to say. It depends on what your app does. But if you are seeing a really high value (like 50% or more) then it’s a reasonable time to look at what’s going on inside of the managed heap."

    Another resource we used is timeless patterns & practices’ Chapter 15 — Measuring .NET Application Performance:

    .NET CLR Memory\% Time in GC

    "…The most common cause of a high value is making too many allocations, which may be the case if you are allocating on a per-request basis for ASP.NET applications. You need to study the allocation profile for your application if this counter shows a higher value."

    So we headed to looking into the code and this is what we found out.

    Analysis

    During performance code inspection we identified massive usage of collections. The collections were used to transfer the data between the logical layers and then the collections were transferred into datatables to be bindable for DataGrid (yes, it is .Net 1.1 app).

    Eureka! We just spotted 3 performance anti-patterns. Massive memory allocation, massive loops, massive type conversions. I’ve shown it to 4 very respected professionals and everyone was saying the same – current situation is pure performance anti-pattern. Here are few suggestions that came up:

    • Bind collections directly to DataGrid eliminating additional memory allocations and loops.
    • Create Datatable directly from XML skipping collection creation step eliminating additional memory allocations and loops.
    • Use Xslt transformation transforming original Xml into Html table using Xslt elminating memory allocations and loops for both collections and datatables.
    • Use Response.Write() as it’s suggested by patterns & practices:

    "Use the Response.Write method. It is one of the fastest ways to return output back to the browser."

    Case close? Not really...

    Secretly I’ve built Visual Studio 2003 project with these implementations and ran simple stress test using TinyGet utility. The results left us all a bit surprised.

    Converting Collection To Datatable (Current Situation)

    The code:

       1: //create custom collection
       2: MyCollection myCollection = (MyCollection)SampleServices.GenerateCollection(200);
       3:  
       4: //convert collection to datatable
       5: DataTable datatable = SampleServices.ConvertCollectionTableIntoDataTalbe(myCollection);
       6:  
       7: //bind datatalbe to dynamically created datagrid
       8: datagrid.DataSource = datatable;
       9: datagrid.DataBind();
     
    The stress test:
     
    tinyget.exe  -srv:192.168.50.68 -uri:/dynamiccontrolsloadingrelease/UseDataTable.aspx -loop:100 -threads:15
     
    The result:
     
    image

    Bind Collection Directly To Grid

    The code:

       1: MyCollection myCollection = (MyCollection)SampleServices.GenerateCollection(200);
       2:  
       3: //bind datatalbe to dynamically created datagrid
       4: datagrid.DataSource = myCollection;
       5: datagrid.DataBind();

    The stress test:

    tinyget.exe  -srv:192.168.50.68 -uri:/dynamiccontrolsloadingrelease/UseCustomCollection.aspx -loop:100 -threads:15

    The result:

    image

     

    Create Datatable From Xml

    The code:

       1: string xml = SampleServices.GenerateXml(200);
       2:  
       3: StringReader theReader = new StringReader(xml);
       4: DataSet theDataSet = new DataSet();
       5: theDataSet.ReadXml(theReader);
       6:  
       7: datagrid.DataSource = theDataSet.Tables[0].DefaultView;;
       8: datagrid.DataBind();

    The stress test:

    tinyget.exe  -srv:192.168.50.68 -uri:/dynamiccontrolsloadingrelease/LoadXmlIntoDataTable.aspx -loop:100 -threads:15

    The result:

    image 

    Use Xslt Transformation To Create Html Table

    The code:

       1: Xml1.DocumentContent = SampleServices.GenerateXml(200);
       2: Xml1.TransformSource=@"xsl.xml";

    The stress test:

    tinyget.exe  -srv:192.168.50.68 -uri:/dynamiccontrolsloadingrelease/XmlXslTransformation.aspx -loop:100 -threads:15

    The result:

     image

    Use Response.Write()

    The code:

       1: MyCollection myCollection = (MyCollection)SampleServices.GenerateCollection(200);
       2:  
       3: // Put user code to initialize the page here
       4: Response.Write("<table>");
       5:  
       6: foreach(MyModelItem item in  myCollection)
       7: {
       8:  
       9:     Response.Write("<tr>");
      10:     Response.Write("<td>" +  item.Address  + "<td>");
      11:     Response.Write("<td>" +  item.City  + "<td>");
      12:     Response.Write("<td>" +  item.Education+ "<td>");
      13:     Response.Write("<td>" +  item.Family  + "<td>");
      14:     Response.Write("<td>" +  item.Name  + "<td>");
      15:     Response.Write("</tr>");
      16: }
      17: Response.Write("</table>");

    The stress test:

    tinyget.exe  -srv:192.168.50.68 -uri:/dynamiccontrolsloadingrelease/ResponseWrite.aspx -loop:100 -threads:15

    The result:

    image 

    Sample Visual Studio 2003 Project

    Interested in testing it yourself? Grab the source code from my SkyDrive here:

    Conclusion

    After conducting this simple test these are the conclusions I’ve made:

    • “Don't be afraid to challenge the pros, even in their own backyard." - How to Get Things Done - Colin Powell Version
    • Testing IS DA thing. Assumptions are good but nothing speaks louder than facts.
    • Test early - avoid massive rework afterwards. Create POC's (Proof of concept) early in architecture/design stages.
    • Best performance comes on expense of productivity and coolness.

    Related Materials

  • Alik Levin's

    Use FREE Tools From IIS Resource Kit To Warm Up Your ASP.NET 1.1 Application By Batch Compilation

    • 5 Comments

    Have you noticed that when ASP.NET web application is accessed for the first time the response is slow? The reason for such behavior is batch compilation that occurs on the first hit.

    ASP.NET batch compilation is the process of compiling ASP.NET markup (content of aspx files) into temporary dll’s. Compilation requires invoking compiler (csc.exe for C#) – that is pretty heavy activity. Process Explorer shows it clearly:

    ASP.NET Batch compilation

    ASP.NET batch compilation occurs on per folder basis. Said that, if your application divided into multiple sub-folders that contain ASP.NET pages each time any of the folders accessed for the first time the batch compilation is invoked.

    Note that starting with ASP.NET 2.0 compilation model has changed. Also, there is a tool Aspnet_compiler.exe that allows pre-compile your ASP.NET web application to improve performance.

    Customer’s case study

    Customer’s web application is built with ASP.NET 1.1. It is divided into multiple subfolders reflecting logical modules that are hosted across about 20 application pools. The application connects to Oracle database.

    QA team complains that the application responds slowly each time any of the modules (subfolders) accessed for the first time.

    Using Process Explorer and profiler we identified three main latency points:

    • Creating the application pool – w3wp.exe.
    • Batch compiling the application for each subfolder.
    • Creating Oracle connection pool when Oracle is accessed for the first time.

    The solution

    We decided to create a Warmer – solution that will hit each subfolder’s page in unattended manner thus warming up the application before the first user hits it.

    For the solution we used free tools from IIS resource kit:

    • LogPrser.exe to identify the URL’s of the pages to hit.
    • TinyGet.exe to actually hit the pages identified by LogParser.

    To identify what pages to hit we took IIS log files from QA environment and than we ran the following query using LogParser:

    LogParser.exe "SELECT DISTINCT STRCAT('XXX', cs-uri-stem) AS cs-uri-stem-strcat INTO 'C:\result.txt' FROM 'C:\yourIISlogFile.log' WHERE INDEX_OF(cs-uri-stem, 'aspx') > 0" -o:w3c

    Notice XXX – it has nothing to do with XXX rated content rather it is a placeholder to replace it with tinyget command.

    Open resulting yourIISlogFile.log file in Notepad, hit Ctrl+H for “Replace” and replace all occurrences of XXX with the following command:

    tinyget -srv:www.YourServer.com -uri:

    ASP.NET Batch Compilation

     

    yourIISlogFile.log before the Replace:

    image

    yourIISlogFile.log after the Replace:

    image

    Remove the header and save the file with BAT extension  - your Warmer is ready for action. Run it each time you deploy new version.

    Do not forget to remove old temporary files in ASP.NET temporary folder:

    C:\Windows\Microsoft.NET\Framework\<<NET FX VERSION>>\Temporary ASP.NET Files\

    CAUTION. This action may potentially corrupt your application if you do not provide proper exception handling. On one hand it is good check to make. on other hand – be aware of it and do not do it on production sites unless you are completely sure it will not corrupt the application.

    Related materials

  • Alik Levin's

    Security Code Review – String Search Patterns For Finding Input Validation Vulnerabilities

    • 2 Comments

    Well defined set of search patterns helps significantly reduce time (cost) when performing security code inspections. This post focuses on input validation vulnerabilities commonly found in ASP.NET web applications.

    SQL Injection and Cross Site Scripting (XSS) String search patterns

    SQL Injections and XSS attacks are most common that exploit improper data access and lack of output encoding. Following are the how-to’s on finding these vulnerabilities:

    Input Validation vulnerabilities String Search Patterns

    To search and find security vulnerabilities you start asking questions or better yet create a list of the questions. Here is the example how - Generate Your Own Security Code Review Checklist Document Using Outlook 2007.

    Use search utility similar to FindStr to perform your searches (look at Performing Text Searches). When Visual Studio is available then you can use it - Visual Studio 2005 As General Code Search Tool. Any other search tool is just fine. Following are the most common questions and search patterns.

    • Does the code rely on client-side validation?

    If the code does not use Validators or Regex there is a potential vulnerability. Review each control how it is validated for type, length, range, string format. In the searches I assume there is no inline code and developers use code behind technique to separate markup from code.

    ASP.NET pages

    findstr /S /I ".Validator" *.aspx

    User Controls

    findstr /S /I ".Validator" *.ascx

    Source code

    findstr /S /I "Regex" *.cs

    • Is the code susceptible to canonicalization attacks?

    Review that there is no external input involved in building paths and file names.

    findstr /S /I “File" *.cs

    findstr /S /I “Path" *.cs

    • Does the code validate data from all sources?

    Using Cookies and QueryStrings poses a risk of the tampering threat (review STRIDE Explained to understand threats). If there is a use of Params property there is a chance for CSRF attack - Cross-Site Request Forgery Attack explained

    Cookies

    findstr /S /I “Cookies" *.*

    Query Strings

    findstr /S /I “QueryString" *.*

    Params

    findstr /S /I “Params" *.*

    • Does the code use MapPath?

    If there is a usage of MapPath review that it does not use external input parameters and it is restricted to access only application file space. Make sure its third parameter set to false.

    findstr /S /I “MapPath" *.*

    How To Mitigate Input And Data Validation Vulnerabilities

    Below are detailed step-by-step guidelines for writing code that is not vulnerable to SQL Injections and XSS attacks:

  • How To: Prevent Cross-Site Scripting in ASP.NET
  • How To: Protect From Injection Attacks in ASP.NET
  • How To: Protect From SQL Injection in ASP.NET
  • How To: Use Regular Expressions to Constrain Input in ASP.NET
  • Microsoft Anti-Cross Site Scripting Library V1.5 
  • Share Your Practices

    If you’ve got more search patterns to suggest – please do so! Let’s make the World [Wide Web] a more secure place together.

    My Related Posts

Page 1 of 3 (7 items) 123