1 /**
2  * Copyright (C) 2006 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.inject;
18 
19 import static java.lang.annotation.RetentionPolicy.RUNTIME;
20 import static junit.framework.Assert.assertEquals;
21 import static junit.framework.Assert.assertSame;
22 
23 import org.springframework.beans.MutablePropertyValues;
24 import org.springframework.beans.factory.config.ConstructorArgumentValues;
25 import org.springframework.beans.factory.config.RuntimeBeanReference;
26 import org.springframework.beans.factory.support.DefaultListableBeanFactory;
27 import org.springframework.beans.factory.support.RootBeanDefinition;
28 
29 import java.lang.annotation.Retention;
30 import java.text.DecimalFormat;
31 import java.util.concurrent.Callable;
32 
33 /**
34  * A semi-useless microbenchmark. Spring and Guice constuct the same object
35  * graph a bunch of times, and we see who can construct the most per second.
36  * As of this writing Guice is more than 50X faster. Also useful for comparing
37  * pure Java configuration options.
38  *
39  * @author crazybob@google.com (Bob Lee)
40  */
41 public class PerformanceComparison {
42 
main(String[] args)43   public static void main(String[] args) throws Exception {
44     // Once warm up. Takes lazy loading out of the equation and ensures we
45     // created the graphs properly.
46     validate(springFactory);
47     validate(juiceFactory);
48     validate(byHandFactory);
49 
50     for (int i2 = 0; i2 < 10; i2++) {
51       iterate(springFactory, "Spring:  ");
52       iterate(juiceFactory,  "Guice:   ");
53       iterate(byHandFactory, "By Hand: ");
54 
55       System.err.println();
56     }
57 
58     System.err.println("Concurrent:");
59 
60     for (int i2 = 0; i2 < 10; i2++) {
61       concurrentlyIterate(springFactory, "Spring:  ");
62       concurrentlyIterate(juiceFactory,  "Guice:   ");
63       concurrentlyIterate(byHandFactory, "By Hand: ");
64 
65       System.err.println();
66     }
67   }
68 
69   static final Callable<Foo> springFactory = new Callable<Foo>() {
70 
71     final DefaultListableBeanFactory beanFactory;
72 
73     {
74       beanFactory = new DefaultListableBeanFactory();
75 
76       RootBeanDefinition tee = new RootBeanDefinition(TeeImpl.class, true);
77       tee.setLazyInit(true);
78       ConstructorArgumentValues teeValues = new ConstructorArgumentValues();
79       teeValues.addGenericArgumentValue("test");
80       tee.setConstructorArgumentValues(teeValues);
81 
82       RootBeanDefinition bar = new RootBeanDefinition(BarImpl.class, false);
83       ConstructorArgumentValues barValues = new ConstructorArgumentValues();
84       barValues.addGenericArgumentValue(new RuntimeBeanReference("tee"));
85       barValues.addGenericArgumentValue(5);
86       bar.setConstructorArgumentValues(barValues);
87 
88       RootBeanDefinition foo = new RootBeanDefinition(Foo.class, false);
89       MutablePropertyValues fooValues = new MutablePropertyValues();
90       fooValues.addPropertyValue("i", 5);
91       fooValues.addPropertyValue("bar", new RuntimeBeanReference("bar"));
92       fooValues.addPropertyValue("copy", new RuntimeBeanReference("bar"));
93       fooValues.addPropertyValue("s", "test");
94       foo.setPropertyValues(fooValues);
95 
96       beanFactory.registerBeanDefinition("foo", foo);
97       beanFactory.registerBeanDefinition("bar", bar);
98       beanFactory.registerBeanDefinition("tee", tee);
99     }
100 
101     public Foo call() throws Exception {
102       return (Foo) beanFactory.getBean("foo");
103     }
104   };
105 
106   static final Callable<Foo> juiceFactory = new Callable<Foo>() {
107     final Provider<Foo> fooProvider;
108     {
109       Injector injector;
110       try {
111         injector = Guice.createInjector(new AbstractModule() {
112           protected void configure() {
113             bind(Tee.class).to(TeeImpl.class);
114             bind(Bar.class).to(BarImpl.class);
115             bind(Foo.class);
116             bindConstant().annotatedWith(I.class).to(5);
117             bindConstant().annotatedWith(S.class).to("test");
118           }
119         });
120       } catch (CreationException e) {
121         throw new RuntimeException(e);
122       }
123       fooProvider = injector.getProvider(Foo.class);
124     }
125 
126     public Foo call() throws Exception {
127       return fooProvider.get();
128     }
129   };
130 
131   static final Callable<Foo> byHandFactory = new Callable<Foo>() {
132     final Tee tee = new TeeImpl("test");
133     public Foo call() throws Exception {
134       Foo foo = new Foo();
135       foo.setI(5);
136       foo.setS("test");
137       Bar bar = new BarImpl(tee, 5);
138       Bar copy = new BarImpl(tee, 5);
139       foo.setBar(bar);
140       foo.setCopy(copy);
141       return foo;
142     }
143   };
144 
validate(Callable<Foo> t)145   static void validate(Callable<Foo> t) throws Exception {
146     Foo foo = t.call();
147     assertEquals(5, foo.i);
148     assertEquals("test", foo.s);
149     assertSame(foo.bar.getTee(), foo.copy.getTee());
150     assertEquals(5, foo.bar.getI());
151     assertEquals("test", foo.bar.getTee().getS());
152   }
153 
154   static final DecimalFormat format = new DecimalFormat();
155 
iterate(Callable<Foo> callable, String label)156   static void iterate(Callable<Foo> callable, String label) {
157     int count = 100000;
158 
159     long time = System.currentTimeMillis();
160 
161     for (int i = 0; i < count; i++) {
162       try {
163         callable.call();
164       }
165       catch (Exception e) {
166         throw new RuntimeException(e);
167       }
168     }
169 
170     time = System.currentTimeMillis() - time;
171 
172     System.err.println(label
173         + format.format(count * 1000 / time) + " creations/s");
174   }
175 
concurrentlyIterate(final Callable<Foo> callable, String label)176   static void concurrentlyIterate(final Callable<Foo> callable, String label) {
177     int threadCount = 10;
178     final int count = 10000;
179 
180     Thread[] threads = new Thread[threadCount];
181 
182     for (int i = 0; i < threadCount; i++) {
183       threads[i] = new Thread() {
184         public void run() {
185           for (int i = 0; i < count; i++) {
186             try {
187               validate(callable);
188             }
189             catch (Exception e) {
190               throw new RuntimeException(e);
191             }
192           }
193         }
194       };
195     }
196 
197 
198     long time = System.currentTimeMillis();
199 
200     for (int i = 0; i < threadCount; i++) {
201       threads[i].start();
202     }
203 
204     for (int i = 0; i < threadCount; i++) {
205       try {
206         threads[i].join();
207       }
208       catch (InterruptedException e) {
209         throw new RuntimeException(e);
210       }
211     }
212 
213     time = System.currentTimeMillis() - time;
214 
215     System.err.println(label
216         + format.format(count * 1000 / time) + " creations/s");
217   }
218 
219   public static class Foo {
220 
221     Bar bar;
222     Bar copy;
223     String s;
224     int i;
225 
226     @Inject
setI(@ int i)227     public void setI(@I int i) {
228       this.i = i;
229     }
230 
231     @Inject
setBar(Bar bar)232     public void setBar(Bar bar) {
233       this.bar = bar;
234     }
235 
236     @Inject
setCopy(Bar copy)237     public void setCopy(Bar copy) {
238       this.copy = copy;
239     }
240 
241     @Inject
setS(@ String s)242     public void setS(@S String s) {
243       this.s = s;
244     }
245   }
246 
247   interface Bar {
248 
getTee()249     Tee getTee();
getI()250     int getI();
251   }
252 
253   public static class BarImpl implements Bar {
254 
255     final int i;
256     final Tee tee;
257 
258     @Inject
BarImpl(Tee tee, @I int i)259     public BarImpl(Tee tee, @I int i) {
260       this.tee = tee;
261       this.i = i;
262     }
263 
getTee()264     public Tee getTee() {
265       return tee;
266     }
267 
getI()268     public int getI() {
269       return i;
270     }
271   }
272 
273   interface Tee {
274 
getS()275     String getS();
276   }
277 
278   @Singleton
279   public static class TeeImpl implements Tee {
280 
281     final String s;
282 
283     @Inject
TeeImpl(@ String s)284     public TeeImpl(@S String s) {
285       this.s = s;
286     }
287 
getS()288     public String getS() {
289       return s;
290     }
291   }
292 
293   @Retention(RUNTIME)
294   @BindingAnnotation @interface I {}
295 
296   @Retention(RUNTIME)
297   @BindingAnnotation @interface S {}
298 }
299