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