http://www.developer.com/

Back to article

Performance Impact of Using Spring.NET Dependency Injection


August 10, 2009

Spring.NET provides Dependency Injection (DI)/Inversion of Control (IoC) as well as several other capabilities including Data, Messaging, etc. While DI provides a lot of flexibility, it comes at a cost. There is some added complexity; however, the real cost is performance.

To test the performance impact of Spring.NET DI, we need to create a series of interfaces and concrete classes that implement the interfaces. Then we will create two console apps to run the tests. The first console application will be a standard C# .NET console app without Spring.Net performing native functions. The second console application will perform the same set of functions, but using Spring.NET and DI. Within these two application we will add a series of timing functions to keep track of how long each tasks takes to run.

Interfaces

Starting off we need to create the interfaces we will need to use in the project as listed below:

  iSimpleMath
  
  public interface iSimpleMath 
  { 
     double Add(double a, double b); 
     double Subtract(double a, double b); 
  }
  
  iComplexMath
  
  public interface iComplexMath 
  { 
     double Multiply(double a, double b); 
     double Divide(double a, double b); 
  }
  
  iCalculate
  
  interface iCalculate 
  { 
     double Calculate(double starting); 
  } 

These interfaces are extremely simple and the concrete classes are just as simple.

Concrete Classes

For these tests, we need to create a couple of interfaces/classes which we can use to judge the performance impact. Next we have the concrete classes used to implement the interfaces.

  ConcreteSimpleMath
  
  class ConcreteSimpleMath : Interfaces.iSimpleMath 
  { 
     public double Add(double a, double b) 
     { 
        return a + b; 
     } 
  
     public double Subtract(double a, double b) 
     { 
        return a - b; 
     } 
  }
  
  ConcreteComplexMath
  
  class ConcreteComplexMath : Interfaces.iComplexMath 
  { 
     public double Multiply(double a, double b) 
     { 
        return a * b; 
     } 
     
     public double Divide(double a, double b) 
     {
        return a / b; 
     } 
  } 
  
  
  ConcreteCalculate
  
  class ConcreteCalculate : Interfaces.iCalculate 
  { 
     private Interfaces.iSimpleMath simpleMath; 
     private Interfaces.iComplexMath complexMath; 
  
     public ConcreteCalculate(Interfaces.iSimpleMath sMath, Interfaces.iComplexMath cMath) 
     { 
        simpleMath = sMath; 
        complexMath = cMath; 
     } 
  
     public double Calculate(double starting) 
     { 
        starting = simpleMath.Add(starting, 15); 
        starting = complexMath.Multiply(starting, 30); 
        starting = simpleMath.Subtract(starting, 5); 
        starting = complexMath.Divide(starting, 2); 
        return starting; 
     } 
  }

The first two classes ConcreteSimpleMath and ConcreteComplexMath provide a simple implementation of the corresponding interface. The ConcreteCalculate class does implement the iCalculate interface, but it also a constructor accepting members for the iSimpleMath and iComplexMath interfaces. The Calculate method does perform 4 calls to the simple/complex math functions; however, the calls are not really important as the point is to perform a minimal amount of work.

Now that we have a series of class we can create the application. First we can create the native .NET application. as listed below to give us some base timing information of native .NET.

.NET Test Application

  static void Main(string[] args) 
  { 
     Console.WriteLine("Testing .Net"); 
     Console.WriteLine("Running Creating Objects"); 
     DateTime start = DateTime.Now; 
  
     for (double i = 0; i < 100000; i++) 
     { 
        Interfaces.iCalculate cal = new Concrete.ConcreteCalculate(new Concrete.ConcreteSimpleMath(), new Concrete.ConcreteComplexMath()); 
        cal.Calculate(i); 
     } 
  
     Console.WriteLine("Done."); 
     Console.WriteLine(DateTime.Now.Subtract(start).TotalMilliseconds.ToString()+"ms"); 
     Console.WriteLine(); 
  
     Console.WriteLine("Running Use Single Object"); 
     start = DateTime.Now; 
     Interfaces.iCalculate cal2 = new Concrete.ConcreteCalculate(new Concrete.ConcreteSimpleMath(), new Concrete.ConcreteComplexMath()); 
  
     for (double i = 0; i < 100000; i++) 
     { 
        cal2.Calculate(i); 
     }
   
     Console.WriteLine("Done"); 
     Console.WriteLine(DateTime.Now.Subtract(start).TotalMilliseconds.ToString() + "ms"); 
     Console.ReadLine(); 
  }

This application performs two tests, first it will time how long it takes collectively to create a new CalculateObject, SimpleMath, ComplexMath and Perform the calculation 100,000 times. Then it will run the same test and use only a single set of objects to perform the calculation on. At the conclusion of each test it will display the time the test took in milliseconds. After running the application you should see something similar to the image below:


Figure 1 - Testing .NET Application Results

Depending on your machine configuration the actual timing information will vary. However, its important to note that the time in miliseconds is mainly intended to be used as a comparison point between native .NET and Spring.NET.

Testing Spring.NET

In testing the Spring.NET application we first need to create the spring configuration in the app.config. I've included the snippet from the config including this section.

  <spring> 
     <context> 
        <resource uri="config://spring/objects" /> 
     </context> 
     <objects xmlns="http://www.springframework.net"> 
        <object name="SimpleMath" 
              type="TestingSpringNet.Concrete.ConcreteSimpleMath,TestingSpringNet" 
              singleton="false" />
  
        <object name="ComplexMath" 
              type="TestingSpringNet.Concrete.ConcreteComplexMath,TestingSpringNet" 
              singleton="false" />
  
        <object name="ConcreteCalculate" 
              type="TestingSpringNet.Concrete.ConcreteCalculate,TestingSpringNet" 
              singleton="false">
              <constructor-arg name="sMath" ref="SimpleMath" /> 
              <constructor-arg name="cMath" ref="ComplexMath" /> 
        </object> 
  
        <object name="SimpleMathSingleton" 
              type="TestingSpringNet.Concrete.ConcreteSimpleMath,TestingSpringNet" 
              singleton="true" /> 
  
        <object name="ComplexMathSingleton" 
              type="TestingSpringNet.Concrete.ConcreteComplexMath,TestingSpringNet" 
              singleton="true" /> 
  
        <object name="ConcreteCalculateUsingSingletons" 
              type="TestingSpringNet.Concrete.ConcreteCalculate,TestingSpringNet" 
              singleton="false"> 
              <constructor-arg name="sMath" ref="SimpleMathSingleton" /> 
              <constructor-arg name="cMath" ref="ComplexMathSingleton" /> 
        </object> 
  
        <object name="ConcreteCalculateSingleton" 
              type="TestingSpringNet.Concrete.ConcreteCalculate,TestingSpringNet" 
              singleton="true"> 
              <constructor-arg name="sMath" ref="SimpleMathSingleton" /> 
              <constructor-arg name="cMath" ref="ComplexMathSingleton" /> 
        </object> 
  
     </objects> 
  </spring> 

This context defines not only SimpleMath, ComplexMath and Calculate objects for testing, it also defines several others which are used to test the timing of varying levels of injection. Next, we can jump into the testing Spring.NET application.

  static void Main(string[] args) 
  { 
     using (IApplicationContext ctx = ContextRegistry.GetContext()) 
     { 
        Console.WriteLine("Testing Spring.Net"); 
        Console.WriteLine("Running Creating Objects"); 
        DateTime start = DateTime.Now; 
  
        for (double i=0; i < 100000; i++) 
        { 
           // Get a new instance of the object 
           Interfaces.iCalculate cal = (Interfaces.iCalculate)ctx.GetObject("ConcreteCalculate"); 
           cal.Calculate(i); 
        } 
  
        Console.WriteLine("Done"); 
        Console.WriteLine(DateTime.Now.Subtract(start).TotalMilliseconds.ToString() +"ms"); 
        Console.WriteLine(); 
  
        Console.WriteLine("Running Use Single Object"); 
        start = DateTime.Now; 
        Interfaces.iCalculate cal2 = (Interfaces.iCalculate)ctx.GetObject("ConcreteCalculate"); 
  
        for (double i = 0; i < 100000; i++) 
        { 
           cal2.Calculate(i);    
        } 
  
        Console.WriteLine("Done"); 
        Console.WriteLine(DateTime.Now.Subtract(start).TotalMilliseconds.ToString() + "ms"); 
        Console.WriteLine(); 
  
        Console.WriteLine("Running using Singletons"); 
        start = DateTime.Now; 
  
        for (double i = 0; i < 100000; i++) 
        { 
           Interfaces.iCalculate cal3 = (Interfaces.iCalculate)ctx.GetObject("ConcreteCalculateUsingSingletons"); 
           cal3.Calculate(i); 
        } 
  
        Console.WriteLine("Done"); 
        Console.WriteLine(DateTime.Now.Subtract(start).TotalMilliseconds.ToString() + "ms"); 
        Console.WriteLine(); 
  
        Console.WriteLine("Running using only Singletons"); 
        start = DateTime.Now;
   
        for (double i = 0; i < 100000; i++) 
        { 
           Interfaces.iCalculate cal4 = (Interfaces.iCalculate)ctx.GetObject("ConcreteCalculateSingleton"); 
           cal4.Calculate(i); 
        } 
  
        Console.WriteLine("Done"); 
        Console.WriteLine(DateTime.Now.Subtract(start).TotalMilliseconds.ToString() + "ms"); 
        Console.ReadLine(); 
     } 
  }

This application performs 4 tests. The first two tests are the same as those performed in the native .NET application above, but performed using Spring.NET. The last two tests test the performance of using varying levels of injection. Test #3 creates a ConcreteCalculate object each time, but uses Simple and Complex math singletons. Test #4 uses ConcreteCalculate, Simple and Complex math singletons. After running this application you should see similar results to the image below:


Figure 2 - Testing Spring.NET Application Results

Review the Results

To make the review simpler, I've created the following table to pull in the results from the two test applications.

Test Native .Net Spring.Net
Test #1: Create Objects 15.625ms 7015.625ms
Test #2: Use Single Object 15.625ms 15.625ms
Test #3: Pass Singletons N/A 3890.625ms
Test #4: Use All Singletons N/A 156.25ms

The import test to take note of is Test #1 where both applications created an object 100,000 times. For Spring.NET this is obviously a very expensive operation compared to native .NET code. Test #2 confirms that after the object is created the performance is the same with or without Spring.NET. Test #3 and #4 illustrate that Spring.NET performs better when it is able to reuse objects. Still while reusing objects, the performance is significantly more time consuming than native .NET code.

Conclusion

Spring.NET certainly provides flexibility, however, the flexibility comes at a performance price. For many applications the performance impact is minimal. If your application creates most or all of it's objects upon startup, then the only cost is slower application startup. On the other hand, if you need to create objects very rapidly and performance is a concern, then you probably should use another framework or minimize the use of dynamic injection. It is also important to note that as of the time of this writing, Spring.NET is in version 1.2. Future versions of the framework may have improved the performance for creating objects. The intent of this article is not to try to turn people away from using Spring.NET. However, when using Spring.NET we need to be aware of the performance cost for the added flexibility.

Download the source code by clicking here.

About the Author

Chris Bennett is with Crowe Horwath LLP in the Indianapolis office. He can be reached at chris .bennett@crowehorwath.com

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date