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
17package com.google.inject;
18
19import static java.lang.annotation.RetentionPolicy.RUNTIME;
20import static junit.framework.Assert.assertEquals;
21import static junit.framework.Assert.assertSame;
22
23import org.springframework.beans.MutablePropertyValues;
24import org.springframework.beans.factory.config.ConstructorArgumentValues;
25import org.springframework.beans.factory.config.RuntimeBeanReference;
26import org.springframework.beans.factory.support.DefaultListableBeanFactory;
27import org.springframework.beans.factory.support.RootBeanDefinition;
28
29import java.lang.annotation.Retention;
30import java.text.DecimalFormat;
31import 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 */
41public class PerformanceComparison {
42
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
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
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
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
227    public void setI(@I int i) {
228      this.i = i;
229    }
230
231    @Inject
232    public void setBar(Bar bar) {
233      this.bar = bar;
234    }
235
236    @Inject
237    public void setCopy(Bar copy) {
238      this.copy = copy;
239    }
240
241    @Inject
242    public void setS(@S String s) {
243      this.s = s;
244    }
245  }
246
247  interface Bar {
248
249    Tee getTee();
250    int getI();
251  }
252
253  public static class BarImpl implements Bar {
254
255    final int i;
256    final Tee tee;
257
258    @Inject
259    public BarImpl(Tee tee, @I int i) {
260      this.tee = tee;
261      this.i = i;
262    }
263
264    public Tee getTee() {
265      return tee;
266    }
267
268    public int getI() {
269      return i;
270    }
271  }
272
273  interface Tee {
274
275    String getS();
276  }
277
278  @Singleton
279  public static class TeeImpl implements Tee {
280
281    final String s;
282
283    @Inject
284    public TeeImpl(@S String s) {
285      this.s = s;
286    }
287
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