Inside MSXML3 Performance

 

Chris Lovett
Microsoft Corporation

March 20, 2000

Download Msxml3perf.exe.

Contents

Improved XSL Transformations
In the Real World
Conclusion
References

Thanks for all your awesome comments on my previous column, Inside MSXML Performance. It looks like I hit a nerve. Obviously you're not a bunch of XML novices after all, so hold fast, here comes some more Extreme XML! My favorite comment was this one:

msxml
msxml sux big time...

- Anonymous 8-Mar-2000.

Just kidding. Whoever wrote that, your comment looks a little out of place don't you think? MSXML rocks! I'm glad to see people are seriously thinking about the performance of MSXML. This is a good sign. People are obviously building real applications with it.

I promised to write more about the new Microsoft® XML Parser (MSXML) features, as several of you requested. This month's column coincides with the March 2000 Microsoft® XML Parser (MSXML) Technology Preview release—called MSXML3. Naturally, I couldn't resist the temptation to use this newest version of the DLL while writing this article.

In the last column, I described several performance metrics that interest most of you as you develop your XML-based Web applications. The first two, working set and megabytes per second, were covered in some detail. In this article, we'll discuss a typical business-to-business scenario, and how MSXML3 can improve performance in the areas of the third and fourth metrics, requests per second and scaling.

I'll continue using JScript for the examples because of its brevity, but I'll include some real C++ code in the downloadable zip&html;as I did last time.

All measurements in this article were taken from computers running Microsoft® Windows® 2000. All the charts and numbers below were measured using the C++ program, because that is the best way to get reliable numbers.

In a scenario where a lot of small XML documents and style sheets are being pumped through a processing pipeline, the raw megabytes per second of the parser can be overshadowed by the setup and tear down costs of creating XML document objects and compiling XSL style sheets. Performance in such a scenario can also be hindered by contention problems on shared resources, which will actually show up as poor scaling on multi-processor computers. The good news is that MSXML3 can deliver a lot of performance improvements in this area, especially in multi-threaded scenarios.

Improved XSL Transformations

The most amazing improvement MSXML3 provides is demonstrated by my little C++ application that pounds transformNode from multiple threads using a shared, pre-loaded, free-threaded XML document and XSL style sheet. In this case, I loaded contacts.xml (an ADO-persisted recordset containing five rows of contact information) and a little style sheet called ado2.xsl, which converts that data into an HTML table. Compare the performance of MSXML and MSXML3 on a Dell PowerEdge 6300 Intel® Pentium® III Xeon® 550-MHz quad-processor computer with 512mb RAM:

In this chart, you see that MSXML3 scales linearly up to four threads on a quad-processor computer. In other words, there is virtually no contention in MSXML3 for this scenario. The same should be true on an eight-processor computer&html;with linear scaling up to eight threads.

In the Real World

In a typical e-commerce scenario, the application doesn't just transform the same loaded document over and over again. It is much more likely that different incoming data is transformed using a set of cached style sheets. For example, you may receive data from a specific business partner on a regular basis, and you've already figured out how you need to transform that data so that your internal system can process it.

To model this, I chose the portfolio.xml example from the previous article. It turns out that I can use this one example to demonstrate cross-threading, plus new XSLT template caching, XSLT features, and Schema caching.

First, let's continue our discussion of cross-threading models. As I pointed out in the previous article, when you have a free-threaded style sheet in MSXML 2.5, you must also have a free-threaded data document. For example, you might write code like this:

<%@LANGUAGE=JScript%>
<%
    var xsl = Application("xsl");
    if (! xsl)
    {
        xsl = Server.CreateObject("Microsoft.FreeThreadedXMLDOM");
        xsl.async = false;
        xsl.load(Server.MapPath("../portfolio.xsl"))
        Application("xsl") = xsl;
    }
    var doc = Server.CreateObject("Microsoft.FreeThreadedXMLDOM");
    doc.async = false;
    doc.load(Server.MapPath("../portfolio.xml"))
    Response.Write(doc.transformNode(xsl));
%>

On my fast quad-processor computer (actually, it's not mine; it belongs to the guys in our performance lab), my C++ version of this running MSXML 2.5 can barely squeeze out 28 requests per second. So, the first thing to do is to make use of the fact that MSXML3 allows cross-threading models on transformNode by making the data document rental threaded.

    var doc = Server.CreateObject("Microsoft.XMLDOM");

When I run this using MSXML3, my peak throughput jumps to 37 requests per second, which is a healthy 32 percent improvement.

IXSLTemplate

The next step is to use the IXSLTemplate interface to cache the actual compiled XSL template; otherwise, MSXML has to re-compile it every time you call transformNode.

<%@LANGUAGE=JScript%>
<%
    var template = Application("xsltemplate");
    if (! template)
    {
        var xsl = Server.CreateObject("Microsoft.FreeThreadedXMLDOM");
        xsl.async = false;
        xsl.load(Server.MapPath("../portfolio.xsl"));
        var template = Server.CreateObject("MSXML2.XSLTemplate");
        template.stylesheet = xsl;
        Application("xsltemplate") = template;
    }
    var doc = Server.CreateObject("Microsoft.XMLDOM");
    doc.async = false;
    doc.load(Server.MapPath("../portfolio.xml"))
    var proc = template.createProcessor();
    proc.input = doc;
    proc.output = Response;
    proc.transform();
%>

This yields 46 requests per second, which is another 24 percent improvement, or 64 percent improvement over MSXML 2.5. And we haven't even gotten to the fun stuff yet.

Upgrade to new XSLT Syntax

The next step to improve performance is more work, but well worth the effort. Here, we convert our old MSXML XSL style sheet to the new XSLT standard. MSXML originally shipped in March 1999, before the W3C XSL Working group finished the official standard. Between March and November, when the recommendation was made, a lot of cool new stuff was added to the language. Taking these changes into consideration, our XSL style sheet upgrade goes something like this:

  1. Convert the old XSL namespace URN to http://www.w3.org/1999/XSL/Transform.

  2. Add the required version="1.0" to the root <xsl:stylesheet> element.

  3. Change the order-by attribute to an <xsl:sort> element and change the DHTML script code accordingly so that you can re-sort the page dynamically.

  4. Replace the <xsl:eval> call to the averageChange() function with a standard XPath expression:

    <xsl:value-of
    select="format-number(sum(/portfolio/stock/percent)
                          div count(/portfolio/stock),'#.#')"/>
    
  5. Replace the xsl:eval call to totalVolume() with another standard expression:

    <xsl:value-of select=
    "format-number(sum(/portfolio/stock/volume),'#')"/>
    
  6. Remove the old <xsl:script> block altogether since it is no longer needed.

  7. Replace all the other <xsl:eval> calls with direct format-number calls, for example the price and change template now contains:

    <xsl:value-of
    select="format-number(.,'0.00')"/>
    
  8. Change the old <xsl:if expr="..."> to use a new XPath expression to do the same thing:

    <xsl:if test=". &lt; -5">
    

The result is portfolio3.xsl. To use this new style sheet, we simply change the following line:

xsl.load(Server.MapPath("../portfolio3.xsl"));

Using this style sheet results in a whopping jump to 99 requests per second, a 115 percent improvement over the old XSL style sheet. The total sample's throughput is now 253 percent better than MSXML 2.5. Most of the increase comes from the fact that we successfully removed all invocations of JScript from the style sheet.

Schema Collections

We're not done yet. The portfolio.xml file loads an external schema (portfolio-schema.xml), which it uses to type the numeric price, change, percent, and volume elements as decimal integers so that sorting works correctly. MSXML does not automatically cache any external schemas. In our hypothetical e-commerce scenario, let's assume that we can pre-determine which schemas our business partners use and cache those also. Using the IXMLDOMSchemaCollection interface, we can do exactly this.

The co-creatable schema collection object is called MSXML2.XMLSchemaCache. You can add schemas to it and associate those schemas with a namespace URN as follows:

<%
    var sc = Application("schemas");
    if (! sc)
    {
        sc = Server.CreateObject("MSXML2.XMLSchemaCache");
        sc.add("x-schema:portfolio-schema.xml",
                  Server.MapPath("../portfolio-schema.xml"));
        Application("schemas") = sc;
    }
%>

This will load the portfolio-schema.xml file once and store it in the shared application scope. To use this new collection, you simply set the schemas property on IXMLDOMDocument2 before calling doc.load on the document as follows:

<%
    var doc = Server.CreateObject("Microsoft.XMLDOM");
    doc.schemas = sc;
    doc.async = false;
    doc.load(Server.MapPath("../portfolio.xml"))
%>

This means that doc.load no longer has to retrieve a separate external schema file. This can be a huge win for performance, especially if the external file is being retrieved via HTTP. Note that this also allows you to get away from using the "x-schema:url" namespace format and allows you to use more general URNs.

The result of this, as measured by my C++ program, is another 11 percent improvement, which leaves us with a total 293 percent improvement over MSXML 2.5.

Conclusion

The following graph sums it up beautifully. It illustrates each additional performance improvement that you get when using the new MSXML3 features.

All those wild claims I made in the previous article about the wonderful new features of MSXML seem to be true (whew!). Quite frankly, I am blown away by the above results. In this business-to-business scenario, MSXML3 is almost three times faster than MSXML. Now I definitely need to go buy those MSXML guys some lattes!

MSXML is well on the way to becoming a solid component of the Web services platform, and the XML Team at Microsoft is focusing on the business-to-business scenarios where using XML is very compelling.

References

For further reading, see Kurt Cagle's article Enhancing XSL, which shows how to do even more stuff with the IXSLTemplate interface.

Download or browse msxml3perf.exe, (including the Readme.asp file).

Chris Lovett is a program manager for Microsoft's XML team.