A Faster TestTypeResolutionService

In February, I wrote about unit testing CodeDomSerializers, but although it was a good initial attempt, there was room for improvement (as I also described in the article). Back then, my main priority was just to get my unit tests working, so other issues were less important to me at the time.

Since then, I've used this method quite a bit, and although I've been generally happy with it, test performance was not good. Each serialization test caused my CPU utilization to jump to 100% and took perhaps 5-10 seconds to perform. When you have a dozen of these tests, running a test suite takes several minutes, which works against the goal of agile development that you should refactor and run unit tests, refactor and run unit tests, etc.

I knew exactly from were my performance issue originated: The GetType method of the TestTypeResolutionService loops through all referenced assemblies looking for a type with the specified type name. This is quite an expensive operation, as some of the referenced assemblies (like mscorlib and System) contain thousands of types.

However, often the name input parameter contains the assembly qualified type name, and if this is the case, Type.GetType is quite a lot faster. Using Type.GetType as the first attempt to resolve the type, and only looping through referenced assemblies as a second attempt dramatically improves test performance. Here's the updated method:

 public Type GetType(string name, bool throwOnError,
    bool ignoreCase)
{
    Type returnType = Type.GetType(name, false, ignoreCase);
    if (returnType != null)
    {
        return returnType;
    }
 
    AssemblyName[] assemblyNames =
        Assembly.GetExecutingAssembly().
        GetReferencedAssemblies();
    foreach (AssemblyName an in assemblyNames)
    {
        Assembly a = Assembly.Load(an);
        Type[] types = a.GetTypes();
        foreach (Type t in types)
        {
            if (t.FullName == name)
            {
                this.cachedTypes_[name] = t;
                return t;
            }
        }
    }
 
    if (throwOnError)
    {
        throw new ArgumentException();
    }
    return null;
}

Type.GetType is called with the throwOnError parameter set to false, which causes the method to return null if the type could not be resolved. This simple change has caused my serialization tests to run 5-15 times faster than before, and running the whole test suite is now something that takes approximately 10 seconds.

If you find it difficult to reproduce the TestTypeResolutionService class from my disparate blog entries, I've included it as an attachment to this post.

TestTypeResolutionService.cs