Filed under: Java
Believe it or not, Google Guice is 1000% faster than Spring. This google product wins every aspect of performance compare to Spring. From load module/context to resolve binding object, Guice is the absolute winner.I personally benchmark this two popular Dependency Injection framework myself. And I have made some scenario as if the real world application of the both framework work.
In a real world there are framework client that require objects to be injected. Typically these client performing action, receive request from actor and process the request and send the result back to the actor. These client are instance of actions in struts or webwork, or even a servlet. These action does not contains business logic, usually we put some business logic in a facade/service/manager, these facade are the instance that will injected into the client by framework.
Facade instance are managed by the framework, usually the perform logic for ex. “read user by name from access object” and do something with it. That means facade instance required dependency of Data Access Object service. Typically these service could be singleton or even prototype. It depends the architecture we built.
Data Access Objects are another instance that managed by the framework. Typically this object performing actions to access the persistent framework session, or entity manager.
In a real world, the dependency of an objects can be very complex. As your application grows, the need of an object to be able to communicate one and another or switching old object to new object are managed by the DI framework and configurable.
Based on above scenario, I made benchmark of two popular framework Google Guice and Spring.
(MainService)
|_______ServiceOne
|________ServiceFour
|________ServiceFive
|________ServiceSix
|_______ServiceTwo
|________ServiceSeven
|________ServiceEight
|________ServiceNine
|_______ServiceThree
|________ServiceTen
|________ServiceEleven
|________ServiceTwelve
MainService is the Facade, ServiceOne, ServiceTwo, ServiceThree are the manager, and ServiceFour until ServiceTwelve are the DAOs.
Typically as the application grows the dependency hierarchy could be more deeper and complex. But for the sake of benchmark ill keep it simple.
As I have mention before the service can be singleton or prototype. It depends on the architecture, so I will test in both fashion, in Singleton and prototype.
To give more objective on both framework, I will test the framework performance starting from one clients , and continues to 10,100,10000 client calling for request to be binded their dependency by the framework.
And also to make sure that the client is actually get the object that they want, the client will call method in service so the main service will the objects that depends on. This could be a mechanism to validate is the framework works.
The Service
public interface Service {
public void doService();
public Date getCreatedOn();
}
The service contains the information on when the service object is being created and method doService to perform service functionality.
All the implementation class from MainService to ServiceTwelve is implementing Service interface.
The StopWatch
public class StopWatch {
private static final List serviceInstances = new LinkedList();
/**
* @return the serviceInstances
*/
public List getServiceInstances() {
return serviceInstances;
}
public static void recordInstance(Service service){
serviceInstances.add(service);
}
public static void clear(){
serviceInstances.clear();
}
public static long averageInstanceCreationInMs(){
long total = 0;
Date date = null;
for (Service service : serviceInstances) {
if(date == null){
date = service.getCreatedOn();
}else{
total = total + (service.getCreatedOn().getTime() - date.getTime());
}
}
return total/serviceInstances.size();
}
public static int totalInstance() {
return serviceInstances.size();
}
}
The stop watch is an object that will record the service instantiation time, and calculate the lag time between one creation of service object and another. By Counting this service creation time, and average the time, then we will find how much does the framework actually perform to binding object and inject it to the host in millisecond.
The Configuration
I separate configuration into two different files, first is the configuration for dependency that the objects are prototype and the other is singleton.
Guice Module Prototype
public class GuiceModulePrototype extends AbstractModule{
@Override
protected void configure() {
bind(Service.class).annotatedWith(Client.ClientService.class).to(MainService.class);
bind(Service.class).annotatedWith(MainService.ServiceOne.class).to(ServiceOne.class);
bind(Service.class).annotatedWith(MainService.ServiceTwo.class).to(ServiceTwo.class);
bind(Service.class).annotatedWith(MainService.ServiceThree.class).to(ServiceThree.class);
bind(Service.class).annotatedWith(ServiceOne.ServiceFour.class).to(ServiceFour.class);
bind(Service.class).annotatedWith(ServiceOne.ServiceFive.class).to(ServiceFive.class);
bind(Service.class).annotatedWith(ServiceOne.ServiceSix.class).to(ServiceSix.class);
bind(Service.class).annotatedWith(ServiceTwo.ServiceSeven.class).to(ServiceSeven.class);
bind(Service.class).annotatedWith(ServiceTwo.ServiceEight.class).to(ServiceEight.class);
bind(Service.class).annotatedWith(ServiceTwo.ServiceNine.class).to(ServiceNine.class);
bind(Service.class).annotatedWith(ServiceThree.ServiceTen.class).to(ServiceTen.class);
bind(Service.class).annotatedWith(ServiceThree.ServiceEleven.class).to(ServiceEleven.class);
bind(Service.class).annotatedWith(ServiceThree.ServiceTwelve.class).to(ServiceTwelve.class);
}
}
Guice Module Singleton
public class GuiceModuleSingleton extends AbstractModule{
@Override
protected void configure() {
bind(Service.class).annotatedWith(Client.ClientService.class).to(MainService.class).in(Scopes.SINGLETON);
bind(Service.class).annotatedWith(MainService.ServiceOne.class).to(ServiceOne.class).in(Scopes.SINGLETON);
bind(Service.class).annotatedWith(MainService.ServiceTwo.class).to(ServiceTwo.class).in(Scopes.SINGLETON);
bind(Service.class).annotatedWith(MainService.ServiceThree.class).to(ServiceThree.class).in(Scopes.SINGLETON);
bind(Service.class).annotatedWith(ServiceOne.ServiceFour.class).to(ServiceFour.class).in(Scopes.SINGLETON);
bind(Service.class).annotatedWith(ServiceOne.ServiceFive.class).to(ServiceFive.class).in(Scopes.SINGLETON);
bind(Service.class).annotatedWith(ServiceOne.ServiceSix.class).to(ServiceSix.class).in(Scopes.SINGLETON);
bind(Service.class).annotatedWith(ServiceTwo.ServiceSeven.class).to(ServiceSeven.class).in(Scopes.SINGLETON);
bind(Service.class).annotatedWith(ServiceTwo.ServiceEight.class).to(ServiceEight.class).in(Scopes.SINGLETON);
bind(Service.class).annotatedWith(ServiceTwo.ServiceNine.class).to(ServiceNine.class).in(Scopes.SINGLETON);
bind(Service.class).annotatedWith(ServiceThree.ServiceTen.class).to(ServiceTen.class).in(Scopes.SINGLETON);
....
}
}
spring-module-singleton.xml
<beans>
<bean id="mainService" class="guice.comparison.service.MainService" >
<property name="serviceOne">
<ref bean="serviceOne"/>
</property>
<property name="serviceTwo">
<ref bean="serviceTwo"/>
</property>
<property name="serviceThree">
<ref bean="serviceThree"/>
</property>
</bean>
<bean id="serviceOne" class="guice.comparison.service.ServiceOne" >
<property name="serviceFour">
<ref bean="serviceFour"/>
</property>
.....
</beans>
spring-module-prototype.xml
<beans>
<bean id="mainService" class="guice.comparison.service.MainService" singleton="false">
<property name="serviceOne">
<ref bean="serviceOne"/>
</property>
<property name="serviceTwo">
<ref bean="serviceTwo"/>
</property>
<property name="serviceThree">
<ref bean="serviceThree"/>
</property>
</bean>
<bean id="serviceOne" class="guice.comparison.service.ServiceOne" singleton="false">
<property name="serviceFour">
<ref bean="serviceFour"/>
</property>
....
</bean>
spring-module-client.xml
<beans>
<bean name="0" class="guice.comparison.Client" singleton="false">
<property name="service"><ref bean="mainService"/></property>
<property name="name"><value>0</value></property>
</bean>
....
....
</beans>
Above are the configuration for the spring. For Spring we still do need to create a context for clients, because in spring the client it self must be configured so the spring can inject the client dependency object.
To make it dynamic I made a simple class to create an spring xml configuration. That will automatically create xml client of n beans. “can u imagine creating xml configuration of 10000 bean by hand
that can take days to make sure everything is in its place, Imagine you create 10000 actions that depend 13 service that means you configuration actions bloated !!”
The Main
public static void main(String[] args) throws IOException{
boolean singleton = false;
boolean callMethod = true;
int client = 1;
StopWatch.clear();
long loadModule = springClientRequest(client,singleton,callMethod);
System.out.println("Springt "+client+"t"+singleton+"t"+callMethod+"t"+StopWatch.averageInstanceCreationInMs()+"t"+StopWatch.totalInstance()+"t"+loadModule);
StopWatch.clear();
loadModule = guiceClientRequest(client,singleton,callMethod);
System.out.println("Guicet "+client+"t"+singleton+"t"+callMethod+"t"+StopWatch.averageInstanceCreationInMs()+"t"+StopWatch.totalInstance()+"t"+loadModule);
}
The main method will perform to create framework modules and start benchmark.
For spring framework, the client will be pulled from the spring context, as the behavior of spring where the client it self is being managed by the container.
For guice framework, the client will be instantiated and injected into the injector, and then injector bind all the object that client needs.
The result as following
#C number of client S : Service are singleton C : Method doPerfom is called I : Instance created in framework T : Average time of framework binding object into bean in ms M : module/context startup time in ms #C S C I T M spring 1 y y 13 34 601 guice 1 y y 13 0 407 spring 10 y y 13 26 607 guice 10 y y 13 0 393 spring 100 y y 13 28 768 guice 100 y y 13 0 456 spring 1000 y y 13 53 1300 guice 1000 y y 13 0 492 spring 10000 y y 13 35 3910 guice 10000 y y 13 0 417 spring 1 y n 13 20 528 guice 1 y n 13 0 457 spring 10 y n 13 25 530 guice 10 y n 13 0 392 spring 100 y n 13 21 744 guice 100 y n 13 0 537 spring 1000 y n 13 34 1445 guice 1000 y n 13 0 591 spring 10000 y n 13 34 3819 guice 10000 y n 13 0 401 spring 1 n n 13 35 660 guice 1 n n 13 0 419 spring 10 n n 130 88 612 guice 10 n n 130 0 406 spring 100 n n 1300 221 708 guice 100 n n 1300 6 415 spring 1000 n n 13000 877 1243 guice 1000 n n 13000 55 386 spring 10000 n n 130000 3858 3765 guice 10000 n n 130000 202 400 spring 1 n y 13 31 567 guice 1 n y 13 0 393 spring 10 n y 130 67 564 guice 10 n y 130 1 407 spring 100 n y 1300 223 693 guice 100 n y 1300 7 435 spring 1000 n y 13000 957 1259 guice 1000 n y 13000 43 424 spring 10000 n y 130000 2931 3859 guice 10000 n y 130000 208 405
As we can see the average time that both framework perform when injecting the bean to the client, GUICE is far more faster than Spring. In 10000 bean of client that requested to be injected of object which type is Proxy(not singleton), GUICE shows its superior where the performance is 1000% percent faster than Spring !
In Singleton service that will injected into client, GUICE has outstanding performance where almost more than 50 times faster than spring.
The results really remarkable, as we can see, the Spring load time is getting higher as the configuration getting big. In contrary Guice is relatively constant, I set Guice Stage in Production so I was expecting GUICE will perform slower, then I was wrong, GUICE does not have problems with bigger client request.
If we realize this is the benefit of GUICE architecture, GUICE does not manage all the client instance, GUICE only inject what instance that client need.
In the end, GUICE architecture has given remarkable new horizon in the new breed of ultra light weight container.
10 Comments so far
Leave a comment
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <pre> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>
You’re data is so valid. Thanks for doing the work and giving us this perspective. You’re the man rif
Comment by joshua July 5, 2007 @ 10:12 amThanks Joshua
Cheers
Comment by gorif July 5, 2007 @ 10:33 amYou seem to be hammering on the xml configuration, but have you ever taken a look at the other configuration possibilities of Spring? I guess not because all your posts are about XML.
I would like to see a comparison between Spring xml, java config, annotations.
Also your comparison isn’t completly fair, Spring does more then simply inject beans it also provides the beans with a (hefty?) lifecycle and lifecycle operations (as you stated yourself). Guice doesn’t have any of those so yes it is faster because is doesn’t have that overhead.
Comment by Marten Deinum July 5, 2007 @ 9:45 pm@Marten
Spring does indeed have lifecycle management (though nothing hefty–see PicoContainer if you want sophisticated lifecycle).
However I cannot see where gorif is invoking any lifecycle methods on his spring beans. They are on equal footing.
Nonetheless I am skeptical of micro-benches like this. I wonder how much clock time is spent parsing xml and in the gc. Bob Lee’s bench is somewhat better.
But lifecycle or not, 1000x or 10x, whatever other factors–there is absolutely no doubt that guice is *significantly* faster than spring in both bootstrap and per-service-injection.
Comment by Dhanji July 6, 2007 @ 9:35 am@Marten
As I have mention before, I try to make the comparison as is in the real world. Most people do it by using xml configuration not to mention I had several project developed using spring too
Yes, Spring does provide lifecycle once we used application context and capability trigger event into the context. But, have you see the example code, it is pure beanFactory, im not using applicationcontext.
Comment by gorif July 6, 2007 @ 11:01 am[...] After that, Guice (1.0) seems to be only 10 times faster than Spring (2.0.6). Have a look at another performance test here. [...]
Pingback by Comparison between Guice, PicoContainer and Spring July 27, 2007 @ 2:47 pmora ngerti gor!!! pake bahasa GSM/UMTS siy hehehe
Comment by kakayanu August 3, 2007 @ 8:39 amkeep the good work Pak!
duh..
Comment by andyan August 10, 2007 @ 4:39 amni pada ngebahas apaan toh gor?
asli gue kagak ngerti sama sekali..
pakai english pulak..
ONLINE – DRUGSTORE!
PRICES of ALL MEDICINES!
FIND THAT NECESSARY…
VIAGRA, CIALIS, PHENTERMINE, SOMA… and other pills!
Welcome please: pills-prices.blogspot.com
NEW INFORMATION ABOUT PAYDAY LOANS!
Welcome please: payday-d-loans.blogspot.com
GOOD LUCK!
Comment by karpMaitari November 1, 2007 @ 12:13 pm[...] http://gorif.wordpress.com/2007/07/05/google-guice-1000-faster-than-spring/ [...]
Pingback by A simple dependency injection component « Venkat - Straight from the heart January 18, 2008 @ 5:21 am