Tuesday, October 12, 2010

Using .Net Profiler with Visual Studio

Using a profiler is indispensable for any non-trivial project. It helps avoiding any guessing on what can take the longest in the program. Let's see how we can use it with Visual Studio. Consider this method of converting a class to an XML string in C#:


internal static string GetString(string msg)
{
    byte[] bytes = null;


    atype aType = new atype();

    aType.a = msg;
    using (MemoryStream bodyStream = new MemoryStream(256))
    {
        XmlSerializer serializer = new XmlSerializer(typeof(atype));
        serializer.Serialize(bodyStream, aType);
        bytes = bodyStream.GetBuffer();
    }


    string retVal = Encoding.UTF8.GetString(bytes);
    return retVal;
}

It turns out that the code above, which is called over and over, sometimes takes a lot of time.
How can we fix it? What is the statement that takes most of the time?
The class atype is a very simple one, converted to a C# file from an XSD definition file by .Net Framework:

[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd""3.5.30729.1")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(TypeName="a-type", Namespace="http://www.w3.org/2001/10")]
[System.Xml.Serialization.XmlRootAttribute("a",
Namespace =
"http://www.w3.org/2001/10",
IsNullable =
false)]
public class atype
{
    private string aField;    [System.Xml.Serialization.XmlAttributeAttribute(Form=System.Xml.Schema.XmlSchemaForm.Qualified, Namespace="http://www.w3.org/XML/1998/namespace")]
    public string a
    {
        get
        {
            return this.aField;
        }
        set
        {
            this.aField = value;
        }
    }
}

Let's use the profiler to figure out what is going on.
In Visual Studio (probably you need at least a professional one) go to Analyze -> Launch Performance Wizard. There are two options, select the most complete one, Instrumentation.
You will get an extra window called “Performance Explorer”.
Click on the “Launch with Profiler” button there.

Here are the results you get after running the original code:

Functions With Most Individual Work
NameTime (msec)%
System.Xml.Serialization.XmlSerializer..ctor(class System.Type)
783.2489.36
System.Xml.Serialization.XmlSerializer.Serialize(class System.IO.Stream,object)
68.927.86
profiler.Program.Main(string[])
8.400.96
profiler.Program.GetString(string)
4.430.50
System.Text.Encoding.GetString(uint8[])
3.520.40

So we can see right away that the answer is the XmlSerializer constructor.
(now we can even say that it kind of makes sense, considering all reflection operations going on there).
Once we identify the problem, the solution is clear: create a static XmlSerializer once and just reuse it!
After one line change - moving serializer declaration from inside the method outside and making it static - calling this function multiple times performs about 10 times faster!

No comments:

Post a Comment