Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 72 lines (40 sloc) 7.049 kb
d60d8c5 @gredler Add readme text.
authored
1 Spriths
2 =======
3
4 **SPRI**ng **TH**readed **S**tartup
5
6 An experiment in [Spring](http://www.springsource.org/about/) application startup optimization.
7
8 *NOT FOR PRODUCTION USE! THIS IS JUST AN EXPERIMENT!*
9
10 Introduction
11 ------------
12
9c0f6d1 @gredler Tweaks to the readme file.
authored
13 Applications built around Spring tend to spend most of their startup time initializing singleton beans. These beans are instantiated serially on a single thread. However, computers have started to scale in terms of CPU cores, rather than in terms of CPU speed. As a result, it is very likely that computer upgrades going forward will exhibit only marginal performance improvements in terms of Spring application startup time.
d60d8c5 @gredler Add readme text.
authored
14
92eca3f @gredler Readme tweaks.
authored
15 The solution is to parallelize the initialization of all eager-init Spring singleton beans in order to take advantage of all available CPU cores. This quick hack is an experiment in such an optimization, based heavily on the TestNG test [parallelization](http://beust.com/weblog/?p=531) [work](http://beust.com/weblog/?p=532) [done](http://code.google.com/p/testng/source/browse/trunk/src/org/testng/internal/thread/GroupThreadPoolExecutor.java) previously by Cedric Beust.
d60d8c5 @gredler Add readme text.
authored
16
c61ea3c @gredler readme++
authored
17 Approach
18 --------
19
92eca3f @gredler Readme tweaks.
authored
20 My main design goal was to keep the Spriths code as unobtrusive and isolated as possible (no pulling the Spring codebase and refactoring it). I was therefore constrained to working with the Spring codebase as it currently exists, subclassing the standard Spring classes.
c61ea3c @gredler readme++
authored
21
7888e56 @gredler Readme tweaks.
authored
22 The actual implementation looks very similar to Cedric's TestNG solution: a `ThreadPoolExecutor` which performs a topological sort, re-evaluating the graph state every time a node is processed. Beans are initialized only when all of the beans which they depend on have already been initialized. I also borrowed the idea of generating a sequence of graph images as a way of visualizing the initialization process (there's an example graph sequence in the `sample` directory).
23
24 ![graph cutout](http://github.com/gredler/spriths/raw/master/doc/graph-cutout.png)
c61ea3c @gredler readme++
authored
25
26 The implementation was complicated by the fact that there are so many different types of dependencies between Spring beans: "normal" bean references, parent bean references, references to AOP aspects created by Spring internally, bean references inside of collections, references determined dynamically at runtime via introspection of the other beans in the application context, etc.
27
7888e56 @gredler Readme tweaks.
authored
28 Another issue is that `DefaultSingletonBeanRegistry` has a very coarse lock guarding its `getSingleton(...)` method variants. I had to override one of these methods and remove the lock, making it non-threadsafe, in order to get any performance wins. Though this change doesn't affect the very controlled tests I ran, it's not something you would want out in the wild. However, bug [SPR-5360](http://jira.springframework.org/browse/SPR-5360) has already been raised concerning this lock, so maybe something a little more thread-friendly will replace it in the official distribution.
c61ea3c @gredler readme++
authored
29
30 Step by step
31 ------------
32
85c0343 @gredler Readme tweak.
authored
33 The images below illustrate the effects of parallelizing the initialization of eager-init singleton Spring beans, as well as the effects of the `getSingleton(...)` lock. They're screenshots of the thread visualization available in the (very excellent) [YourKit](http://yourkit.com/) profiler. Each row represents a thread; threads named `spriths-xxx` are bean initialization threads spawned by Spriths. The green portions of the bars represent time spent by a thread actually running code; red portions indicate that a thread was blocked, waiting for another thread to release a lock somewhere.
c61ea3c @gredler readme++
authored
34
35 The first image illustrates the _status quo ante_. This is how Spring currently behaves: a single thread, chugging away at the work.
36
37 ![no extra threads](http://github.com/gredler/spriths/raw/master/doc/no-extra-threads.png)
38
7888e56 @gredler Readme tweaks.
authored
39 In the second image, Spriths is being used to farm the work out to different threads, but we've left the `getSingleton(...)` lock in the code. This image shows lots of lock contention (red) and not much concurrent initialization (green).
c61ea3c @gredler readme++
authored
40
41 ![threads with coarse lock](http://github.com/gredler/spriths/raw/master/doc/threads-with-coarse-lock.png)
42
92eca3f @gredler Readme tweaks.
authored
43 The final image illustrates the situation with Spriths after removing the lock. Spriths' threads are no longer blocking each other and are free to do useful work in parallel; there's much more green and much less red. As a result, the graph is also much shorter along the horizontal axis (less total time spent on initialization). Mission accomplished!
c61ea3c @gredler readme++
authored
44
45 ![threads without coarse lock](http://github.com/gredler/spriths/raw/master/doc/threads-without-coarse-lock.png)
46
47 Results
48 -------
49
92eca3f @gredler Readme tweaks.
authored
50 So is something like this worth the effort? Well, probably; it'll depend on the specific usecase. The sample application context contained in this project loads in about 3.2 seconds on a 2.8 GHz Intel Core 2 Duo (two CPU cores) using the standard `ClassPathXmlApplicationContext`, and in about 1.8 seconds using the custom `ThreadedClassPathXmlApplicationContext` provided by this project. That's a savings of about 40%.
c61ea3c @gredler readme++
authored
51
7888e56 @gredler Readme tweaks.
authored
52 However, using this optimization in a couple of real-world applications showed gains more in the 15% to 20% range. Specifically, these applications were using Hibernate for JPA; JPA persistence context initialization was triggering retrieval of metadata from the database on startup. The network I/O generated by a single bean was consuming most of the startup time.
c61ea3c @gredler readme++
authored
53
7888e56 @gredler Readme tweaks.
authored
54 In general, this sort of approach will pay greater dividends for application contexts featuring an equitable work distribution amongst the various beans; if one bean is consuming most of the startup time, it might be more productive to try to optimize that single bean's performance.
c61ea3c @gredler readme++
authored
55
56 Can I try this myself?
d60d8c5 @gredler Add readme text.
authored
57 ----------------------
58
59 Yep. Just pull the code and run:
60
61 mvn test
62 dot2png
63 png2html
64
7888e56 @gredler Readme tweaks.
authored
65 The first step runs a test, loading a sample Spring application context in both the standard `ClassPathXmlApplicationContext` and in the custom `ThreadedClassPathXmlApplicationContext` provided by this project. The run times of each load will be printed to the console.
d60d8c5 @gredler Add readme text.
authored
66
7888e56 @gredler Readme tweaks.
authored
67 The test also generates a series of graphs illustrating the bean initialization process. Each graph shows the dependencies between the various beans, as well as the state of each bean. White nodes represent beans that are waiting to be initialized but are waiting on their dependent beans to be initialized; yellow nodes represent beans that are waiting to be initialized and which are not blocked by any uninitialized dependencies; green nodes represent beans that are in the process of being initialized; finally, grey nodes represent beans which have been successfully initialized.
d60d8c5 @gredler Add readme text.
authored
68
7888e56 @gredler Readme tweaks.
authored
69 The second step above converts these graphs into PNG images, and the third step incorporates these images into an HTML file. This overview file will be available at `target/graphs/graphs.html`. There's a sample overview file in the `sample` directory.
b055cf5 @gredler Add Graphviz note.
authored
70
7888e56 @gredler Readme tweaks.
authored
71 **NOTE:** The second step (`dot2png`) requires that you have [Graphviz](http://www.graphviz.org/) installed.
Something went wrong with that request. Please try again.